Реклама в Telegram-каналах DzikPic и dev.by теперь дешевле. Узнать подробности 👨🏻‍💻
Support us

Шаблон для проектов на С++ с помощью Boost, CMake. Файловая система

Оставить комментарий
Шаблон для проектов на С++ с помощью Boost, CMake. Файловая система

Иногда наступает момент, когда начинаешь задавать вопрос: А как лучше хранить исходный код, чтобы он в итоге не превращался в кучу файлов с циклическими зависимостями?

Всё началось несколько лет назад, когда я решил попробовать сделать свою библиотеку. Уже тогда я попробовал Boost и его The Unit Test Framework, и начал вливаться в CMake, как для кроссплатформенности, так и для простоты перехода между компиляторами. В общем к данному времени вопрос как хранить исходные коды, файлы сборки, вспомогательные и тестовые файлы получил несколько ответов и вариантов из которых по объективным, а иногда и по соображениям удобства, был сделан выбор.

Мне интересно поделиться выработанным шаблоном, чтобы услышать критику и возомжно ответить на вопрос, как лучше систематизировать исходных код

Чтобы усложнить понимание я, конечно, дам несколько ссылок.

  1. Пример библиотеки с которой и пишется этот пост: https://github.com/sidorovis/system_utilities.
  2. Boost Unit Test Framework: http://www.boost.org/doc/libs/1_52_0/libs/test/doc/html/index.html.
  3. CMake: http://cmake.org/

Но в последствии буду пытаться объяснять и описывать так, как будто вы их не прочитали. 

Проекту нужен Boost, версии 1_47 будет достаточно. Надеюсь более поздние версии тоже подойдут.

Начнём со структуры каталога проекта.

$ ls -A system_utilities/
3rd_party
CMakeLists.txt
README
_cmake_scripts
_gcc_gen.sh
_msvc_gen_2005_32.bat
_msvc_gen_2005_64.bat
_msvc_gen_2010_64_debug.bat
_msvc_gen_2010_64_release.bat
_start_msvc.bat
_xcode_gen.sh
environment.cmake
sources
tests

Намного удобней параллельно изучать исходный код используя Web интерфейс Github, но в любом случае отмечу, что все исходные коды языка C++ находятся в каталогах "sources" и "tests".

Каталог проекта содержит следующие каталоги/файлы. Это описание проекта с точки зрения организации исходного кода на жёстком диске:

  1. Каталог 3rd_party - содержит скрипты компиляции библиотеки Boost под Windows для 32-х и 64-х битных версий.
  2. Файл CMakeLists.txt - входной файл для CMake, первый файл, который просматривается для генерации файлов проектов/решений (projects/solutions) или Makefile'ов.
  3. Файл README - краткое описание библиотеки и модулей библиотеки.
  4. _cmake_scripts - каталог содержащий мои скрипты CMake, которые объединяют и упрощают описание зависимостей каждого модуля.
  5. _gcc_gen.sh, _msvc_gen_2010_64_debug.bat, ..., _xcode_gen.sh - скрипты запуска CMake. Они содержат путь к библиотеке Boost (при необходимости), а также хитрость, которая позволяет размещать все генерируемые в отдельном подкаталоге, на засоряя исходный код.
  6. environment.cmake - вспомогательный файл для CMake, его я не буду разбирать в этой статье.
  7. Каталог sources - исходные коды модулей библиотеки.
  8. Каталог tests - исходные коды тестов для модулей библиотеки.

Рассмотрим _msvc_gen_2005_64.bat - скрипт, который генерирует solution для MSVC 2005 64-х битной версии. Его можно запустить без параметров (он сгенерирует solution для Debug версии в каталог _build_Debug_64) и с параметром "Release", для Release версии. Для MSVC можно было бы генерировать один solution, но во-первых для Linux такая политика не является удачной, и во-вторых гораздо проще писать последующие проекты - если они не смогут найти каталог "_build_Release_64" - значит скомпилировать 64-х битную Release версию не удастся.

$ cat ./_msvc_gen_2005_64.bat
set BOOST_ROOT=d:\usr\boost_1_47_0
set BUILD_TYPE=Debug
if [%1]==[Release] (
	set BUILD_TYPE=Release
)
set BUILD_FOLDER=_build_%BUILD_TYPE%_64
if not exist %BUILD_FOLDER% (
	mkdir %BUILD_FOLDER%
)
cd %BUILD_FOLDER%
cmake -DVERBOSE=OFF -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSOLUTION_NAME=system_utilities -G "Visual Studio 8 2005 Win64" ../ 
cd ../
echo "%BUILD_FOLDER%/system_utilities.sln" > _start_msvc.bat

Главной задачей для этого файла является создание каталога _build_<BUILD_TYPE>_<ADDRESS_MODEL>, в котором будут сгенерированы все необходимые для компиляции файлы/каталоги. Скрипт также генерирует скрипт запуска IDE (_start_msvc.bat). Жирным шрифтом выделено то, что позволяет генерировать файлы проекта в отдельном каталоге и тем самым не засоряет каталог с исходным кодом.

Библиотека устроена таким образом, что ни один модуль не может быть создан без соответствующего каталога с тестирующим исполнительным файлом. Что само по себе заставляет разработчиков модулей, как минимум задуматься о необходимости создания пустых тестов.

Рассмотрим точку входа для генерации solution. Файл CMakeLists.txt в корне system_utilities.

cmake_minimum_required(VERSION 2.8)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set( CMAKE_CONFIGURATION_TYPES ${CMAKE_BUILD_TYPE} CACHE STRING "Configurations" FORCE )
project( ${SOLUTION_NAME} )
include( environment.cmake required )

if(WIN32)
	modules( time_tracker ts_queue property_reader task_processor logger ts_logger queue_logger file_logger limited_file_logger timer system_processor windows_service )
elseif(UNIX)
	modules( time_tracker timer logger ts_logger ts_queue property_reader task_processor queue_logger file_logger limited_file_logger system_processor )
endif(WIN32)

Этот файл загружает все написанные мной CMake функции и макросы, которые впоследствии упростят механизм генерации проектов с учётом зависимостей.

Макрос modules - устанавливает список модулей и последовательно запускает для генерации подкаталоги "sources", "tests".

Каталог "sources", содержит каталоги всех модулей библиотеки и файл CMakeLists.txt - на который опирается CMake для генерации файлов проектов модулей библиотеки, содержимое этого файла:

compile_modules()

Вроде пока не сложно и не так много кода. Как выглядят каталоги модулей рассмотрим на примере limited_file_logger. Этот модуль журналирует события в файл с ограничением файла по размеру.

Каталог модуля состоит из следующих файлов:

CMakeLists.txt
limited_file_logger.cpp
limited_file_logger.h

На самом деле файлов с исходным кодом (*.cpp, *.h) может быть сколько угодно - главное, чтобы они компилировались учитывая настройки и зависимости проекта, которые хранятся в файле CMakeLists.txt этого каталога.

find_package( Boost 1.41 ${SEARCH_PARAMETERS} COMPONENTS
	filesystem
	thread
	regex
	date_time
	system
)

compile_project( ${module_name} "*.cpp" "*.h" STATIC libraries file_logger logger Boost )

В этом файле две команды:

  1. Найти необходимые для данного проекта библиотеки Boost.
  2. Сгенерировать цель для компиляции (MSVC-проект), со следующими параметрами:
    1. Наименование цели - это название модуля ("limited_file_logger");
    2. Включить все файлы "*.cpp" в качестве файлов исходного кода;
    3. Включить все файлы "*.h: в качестве файлов заголовков;
    4. Модуль будет статически линковаться;
    5. Зависимостями, являются "file_logger", "logger", все найденные библиотеки "Boost".

Почему я не ищу все библиотеки, а тут не перечисляю только необходимые? Таким образом я никогда не забуду о том, какие именно библиотеки буста линкуются. Строка генерации цели выглядит лаконичной и простой. Поиск компонент - это стандарт языка CMake и поэтому он не вызывает сложности у людей ранее использовавших CMake.

Перейдём к каталогу "tests".

Он также содержит набор каталогов для тестирования каждого модуля (и система не соберёт solution, если таких каталогов будет не хватать) и файл CMakeLists.txt:

compile_tests()

Каждый каталог содержит все необходимые файлы для создания исполнимого файла - который тестирует каждый модуль.

Рассмотрим на примере теста для модуля limited_file_logger. Каталог tests/limited_file_logger содержит:

CMakeLists.txt
limited_file_logger_tests.cpp
test_registrator.cpp
test_registrator.h

Файлы test_registrator.cpp и test_registrator.h - это файлы организующие порядок запуска тестов. Файл limited_file_logger_tests.cpp - содержит непосредственно код тестов модуля. И файл CMakeLists.txt содержит следующее:

find_package( Boost 1.41 ${SEARCH_PARAMETERS} COMPONENTS
filesystem
thread
regex
date_time
system
unit_test_framework
)
compile_project( ${tests_name} "*.cpp" "*.h" BINARY tests ${module_name} file_logger logger queue_logger task_processor ts_queue time_tracker Boost )
register_test( ${tests_name} 1.0 1.5 )

В отличии от предыдущего проекты, мы генерируем исполнимый файл (BINARY). В зависимостях появляются модули queue_logger, task_processor ts_queue и time_tracker.

А также строка register_test - которая зарегистрирует тест в CTest (он будет запускаться при вызове "cmake test"). Цифры 1.0, 1.5 - это допустимое время работы теста соответственно для Debug (1 секунда) и Release (1.5 секунды) версий.

Какие плюсы я вижу при такой организации файловой системы.

  1. Все генерируемые файлы лежат себе отдельно и не смогут попасть в систему контроля версий "случайно". Их легко удалять при необходимости и заново перегенерировать всё с нуля. Поэтому даже если ваша IDE испортит файлы проектов - то их всегда можно заново сгенерировать.
  2. Написать модуль без создания заглушки для тестирования не представляется возможным.
  3. Код по организации файлов в цели (проекты) минималистичен и прост к изучению как молодыми и неопытными разработчиками, так и опытными но не имевшими дело с CMake разработчиками.
  4. Собрать такую библиотеку любому разработчику не составит трудностей (намного больше трудностей появится из-за необходимости скомпилировать Boost под Windows).

И конечно же недоработки и минусы:

  1. Отсутствие возможности скомпилировать одной кнопкой. К сожалению, я оттягиваю это на тот момент, когда под Windows появится менеджер пакетов - с помощью которого компиляция всех возможных (и необходимых) Boost станет простой. На данный момент используя стандартные настройки компилирования Boost, мы получим только 32-х или 64-х битную версию.
  2. Необходимость большого количества скриптов запуска CMake (фактически можно обойтись небольшим количеством если вынести все настройки в параметры скриптов).

Жду комментариев или сурового порицательного молчания.

Новый рекламный формат в наших телеграм-каналах.

Купить 500 символов за $150

Читайте также
Состоялся первый публичный релиз децентрализованной платформы совместной разработки Radicle
Состоялся первый публичный релиз децентрализованной платформы совместной разработки Radicle
Состоялся первый публичный релиз децентрализованной платформы совместной разработки Radicle
GitHub представил программу сертификации для разработчиков
GitHub представил программу сертификации для разработчиков
GitHub представил программу сертификации для разработчиков
Хакер стирает Git-репозитории и требует выкуп
Хакер стирает Git-репозитории и требует выкуп
Хакер стирает Git-репозитории и требует выкуп
Топ-10 языков программирования с самым активным ростом по версии GitHub
Топ-10 языков программирования с самым активным ростом по версии GitHub
Топ-10 языков программирования с самым активным ростом по версии GitHub

Хотите сообщить важную новость? Пишите в Telegram-бот

Главные события и полезные ссылки в нашем Telegram-канале

Обсуждение
Комментируйте без ограничений

Релоцировались? Теперь вы можете комментировать без верификации аккаунта.

Комментариев пока нет.