Для начала, есть несколько обычных имен для каталогов, которые нельзя игнорировать, они основаны на давней традиции файловой системы Unix. Эти:
trunk
├── bin : for all executables (applications)
├── lib : for all other binaries (static and shared libraries (.so or .dll))
├── include : for all header files
├── src : for source files
└── doc : for documentation
Вероятно, будет хорошей идеей придерживаться этого базового макета, по крайней мере, на верхнем уровне.
Что касается разделения файлов заголовков и исходных файлов (cpp), обе схемы довольно распространены. Однако я предпочитаю хранить их вместе, просто для повседневных задач удобнее хранить файлы вместе. Кроме того, когда весь код находится в одной папке верхнего уровня, то есть в trunk/src/
папке, вы можете заметить, что все другие папки (bin, lib, include, doc и, возможно, некоторые тестовые папки) находятся на верхнем уровне, в дополнение к каталог «build» для сборки вне исходного кода - это все папки, которые не содержат ничего, кроме файлов, созданных в процессе сборки. Таким образом, необходимо создать резервную копию только папки src или, что еще лучше, хранить ее в системе / сервере контроля версий (например, Git или SVN).
А когда дело доходит до установки ваших файлов заголовков в целевой системе (если вы хотите в конечном итоге распространить свою библиотеку), в CMake есть команда для установки файлов (неявно создает цель «install», чтобы выполнить «make install»), которая вы можете использовать, чтобы поместить все заголовки в /usr/include/
каталог. Я просто использую для этого следующий макрос cmake:
# custom macro to register some headers as target for installation:
# setup_headers("/path/to/header/something.h" "/relative/install/path")
macro(setup_headers HEADER_FILES HEADER_PATH)
foreach(CURRENT_HEADER_FILE ${HEADER_FILES})
install(FILES "${SRCROOT}${CURRENT_HEADER_FILE}" DESTINATION "${INCLUDEROOT}${HEADER_PATH}")
endforeach(CURRENT_HEADER_FILE)
endmacro(setup_headers)
Где SRCROOT
находится переменная cmake, которую я установил в папку src, и INCLUDEROOT
переменная cmake, которую я настраиваю, куда бы ни направлялись заголовки. Конечно, есть много других способов сделать это, и я уверен, что мой способ не лучший. Дело в том, что нет причин разделять заголовки и источники только потому, что в целевой системе должны быть установлены только заголовки, потому что очень легко, особенно с CMake (или CPack), выбрать и настроить заголовки для быть установленным без необходимости их размещения в отдельном каталоге. И это то, что я видел в большинстве библиотек.
Цитата: Во-вторых, я хотел бы использовать Google C ++ Testing Framework для модульного тестирования моего кода, поскольку он кажется довольно простым в использовании. Вы предлагаете связать это с моим кодом, например, в папке «inc / gtest» или «contrib / gtest»? Если в комплекте, вы предлагаете использовать сценарий fuse_gtest_files.py для уменьшения количества файлов или оставить все как есть? Если не в комплекте, как обрабатывается эта зависимость?
Не связывайте зависимости с вашей библиотекой. Как правило, это довольно ужасная идея, и я всегда ненавижу ее, когда я застреваю, пытаясь создать библиотеку, которая сделала бы это. Это должно быть вашим последним средством и остерегайтесь подводных камней. Часто люди связывают зависимости со своей библиотекой либо потому, что они нацелены на ужасную среду разработки (например, Windows), либо потому, что они поддерживают только старую (устаревшую) версию рассматриваемой библиотеки (зависимости). Основная ошибка заключается в том, что ваша связанная зависимость может конфликтовать с уже установленными версиями той же библиотеки / приложения (например, вы связали gtest, но у человека, пытающегося создать вашу библиотеку, уже установлена более новая (или старая) версия gtest, тогда эти двое могут столкнуться и вызвать у этого человека очень неприятную головную боль). Итак, как я уже сказал, делайте это на свой страх и риск, и я бы сказал только в крайнем случае. Просить людей установить несколько зависимостей до того, как вы сможете скомпилировать вашу библиотеку, - гораздо меньшее зло, чем попытка разрешить конфликты между вашими связанными зависимостями и существующими установками.
Цитата: Когда дело доходит до написания тестов, как они обычно организованы? Я думал иметь один файл cpp для каждого класса (например, test_vector3.cpp), но все они скомпилированы в один двоичный файл, чтобы их можно было легко запускать вместе?
На мой взгляд, один файл cpp на класс (или небольшую связную группу классов и функций) более обычен и практичен. Однако определенно не компилируйте их все в один двоичный файл только для того, чтобы «все они могли работать вместе». Это действительно плохая идея. Обычно, когда дело доходит до кодирования, вы хотите разделить вещи настолько, насколько это разумно. В случае модульных тестов вы не хотите, чтобы один двоичный файл запускал все тесты, потому что это означает, что любое небольшое изменение, которое вы вносите во что-либо в своей библиотеке, вероятно, приведет к почти полной перекомпиляции этой программы модульного тестирования. , и это всего лишь минуты / часы, потерянные в ожидании перекомпиляции. Просто придерживайтесь простой схемы: 1 юнит = 1 программа юнит-тестирования. Затем,
Цитата: Поскольку библиотека gtest обычно строится с использованием cmake и make, я подумал, что имеет смысл построить и мой проект таким же образом? Если бы я решил использовать следующий макет проекта:
Я бы предпочел такой макет:
trunk
├── bin
├── lib
│ └── project
│ └── libvector3.so
│ └── libvector3.a products of installation / building
├── docs
│ └── Doxyfile
├── include
│ └── project
│ └── vector3.hpp
│_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
│
├── src
│ └── CMakeLists.txt
│ └── Doxyfile.in
│ └── project part of version-control / source-distribution
│ └── CMakeLists.txt
│ └── vector3.hpp
│ └── vector3.cpp
│ └── test
│ └── test_vector3.cpp
│_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
│
├── build
└── test working directories for building / testing
└── test_vector3
Здесь следует отметить несколько моментов. Во-первых, подкаталоги вашего каталога src должны отражать подкаталоги вашего каталога include, это просто для того, чтобы вещи были интуитивно понятными (также постарайтесь, чтобы структура подкаталогов была достаточно плоской (неглубокой), потому что глубокая вложенность папок часто доставляет больше хлопот, чем что-либо другое). Во-вторых, каталог include - это просто каталог установки, его содержимое - это все заголовки, выбранные из каталога src.
В-третьих, система CMake предназначена для распределения по исходным подкаталогам, а не как один файл CMakeLists.txt на верхнем уровне. Благодаря этому вещи остаются локальными, и это хорошо (в духе разделения на независимые части). Если вы добавляете новый источник, новый заголовок или новую тестовую программу, все, что вам нужно, это отредактировать один небольшой и простой файл CMakeLists.txt в соответствующем подкаталоге, не затрагивая ничего другого. Это также позволяет легко реструктурировать каталоги (списки CMakeList являются локальными и содержатся в перемещаемых подкаталогах). CMakeLists верхнего уровня должен содержать большинство конфигураций верхнего уровня, таких как настройка каталогов назначения, пользовательские команды (или макросы) и поиск пакетов, установленных в системе. CMakeLists нижнего уровня должны содержать только простые списки заголовков, источников,
Цитата: Как должен выглядеть файл CMakeLists.txt, чтобы он мог либо собирать только библиотеку, либо библиотеку и тесты?
Основной ответ заключается в том, что CMake позволяет вам специально исключать определенные цели из «всех» (что и создается при вводе «make»), а также вы можете создавать определенные пакеты целей. Я не могу сделать здесь учебник по CMake, но это довольно просто выяснить самому. Однако в этом конкретном случае рекомендуемым решением, конечно же, является использование CTest, который представляет собой просто дополнительный набор команд, которые вы можете использовать в файлах CMakeLists для регистрации ряда целей (программ), которые помечены как единичные. тесты. Итак, CMake поместит все тесты в специальную категорию сборок, и это именно то, о чем вы просили, поэтому проблема решена.
Цитата: Также я видел довольно много проектов, в которых есть сборка и каталог bin. Выполняется ли сборка в каталоге сборки, а затем двоичные файлы перемещаются в каталог bin? Будут ли двоичные файлы для тестов и библиотека жить в одном месте? Или было бы разумнее структурировать его следующим образом:
Наличие каталога сборки вне исходного кода (сборка «вне исходного кода») - это действительно единственное разумное решение, которое в наши дни является стандартом де-факто. Так что определенно создайте отдельный каталог «build» вне исходного каталога, как рекомендуют специалисты CMake и как это делает каждый программист, которого я когда-либо встречал. Что касается каталога bin, ну, это соглашение, и, вероятно, будет хорошей идеей придерживаться его, как я сказал в начале этого поста.
Цитата: Я также хотел бы использовать doxygen для документирования моего кода. Можно ли заставить это автоматически запускаться с помощью cmake и make?
Да. Это более чем возможно, это круто. В зависимости от того, насколько нарядно вы хотите выглядеть, есть несколько возможностей. CMake имеет модуль для Doxygen (т.е. find_package(Doxygen)
), который позволяет вам регистрировать цели, которые будут запускать Doxygen для некоторых файлов. Если вы хотите делать более необычные вещи, например, обновлять номер версии в Doxyfile или автоматически вводить дату / дату / дату для исходных файлов и т. Д., Все это возможно с помощью небольшого количества CMake kung-fu. Как правило, для этого необходимо сохранить исходный Doxyfile (например, «Doxyfile.in», который я поместил в макет папки выше), в котором есть токены, которые необходимо найти и заменить командами синтаксического анализа CMake. В моем файле CMakeLists верхнего уровня вы найдете один такой кусок CMake kung-fu, который вместе с cmake-doxygen делает несколько причудливых вещей.