Самый простой, но полный пример CMake
Как-то я совершенно запутался в том, как работает CMake. Каждый раз, когда я думаю, что я приближаюсь к пониманию того, как CMake должен быть написан, он исчезает в следующем примере, который я читаю. Все, что я хочу знать, это то, как я должен структурировать свой проект, чтобы мой CMake требовал наименьшего количества обслуживания в будущем. Например, я не хочу обновлять свой CMakeList.txt когда я добавляю новую папку в моем дереве src, это работает точно так же, как и все другие папки src.
Это как я представляю себе структуру моего проекта, но, пожалуйста, это только пример. Если рекомендуемый способ отличается, пожалуйста, скажите мне, и скажите мне, как это сделать.
myProject
src/
module1/
module1.h
module1.cpp
module2/
[...]
main.cpp
test/
test1.cpp
resources/
file.png
bin
[execute cmake ..]
кстати, важно, чтобы моя программа знала, где находятся ресурсы. Я хотел бы знать рекомендуемый способ управления ресурсами. Я не хочу получать доступ к своим ресурсам"../файл ресурсов.png"
2 ответа:
после некоторых исследований у меня теперь есть своя версия самого простого, но полного примера cmake. Вот он, и он пытается охватить большинство основ, включая ресурсы и упаковку.
одна вещь, которую он делает нестандартно, - это обработка ресурсов. По умолчанию cmake хочет поместить их в /usr/ share/,/usr/local/ share / и что-то эквивалентное на windows. Я хотел иметь простой zip / tar.gz, который вы можете извлечь в любом месте и запустить. Поэтому ресурсы загружаются относительно выполнимый.
основным правилом для понимания команд cmake является следующий синтаксис:
<function-name>(<arg1> [<arg2> ...])
без запятой или точки с запятой. Каждый аргумент является строкой.foobar(3.0)
иfoobar("3.0")
- это то же самое. вы можете установить списки / переменные с помощьюset(args arg1 arg2)
. С этой переменной setfoobar(${args})
иfoobar(arg1 arg2)
фактически то же самое. Несуществующая переменная эквивалентна пустому списку. Список-это внутренняя строка с точками с запятой для разделения элементов. Поэтому список только с одним элементом является определение только этого элемента, никакого бокса не происходит. Переменные являются глобальными. Встроенные функции предлагают некоторую форму именованные аргументы тем, что они ожидают некоторые идентификаторы, какPUBLIC
илиDESTINATION
в списке аргументов, чтобы сгруппировать аргументы. Но это не языковая функция, эти идентификаторы также являются просто строками и анализируются реализацией функции.вы можете клонировать все от github
cmake_minimum_required(VERSION 3.0) project(example_project) ############################################################################### ## file globbing ############################################################## ############################################################################### # these instructions search the directory tree when cmake is # invoked and put all files that match the pattern in the variables # `sources` and `data` file(GLOB_RECURSE sources src/main/*.cpp src/main/*.h) file(GLOB_RECURSE sources_test src/test/*.cpp) file(GLOB_RECURSE data resources/*) # you can use set(sources src/main.cpp) etc if you don't want to # use globing to find files automatically ############################################################################### ## target definitions ######################################################### ############################################################################### # add the data to the target, so it becomes visible in some IDE add_executable(example ${sources} ${data}) # just for example add some compiler flags target_compile_options(example PUBLIC -std=c++1y -Wall -Wfloat-conversion) # this lets me include files relative to the root src dir with a <> pair target_include_directories(example PUBLIC src/main) # this copies all resource files in the build directory # we need this, because we want to work with paths relative to the executable file(COPY ${data} DESTINATION resources) ############################################################################### ## dependencies ############################################################### ############################################################################### # this defines the variables Boost_LIBRARIES that contain all library names # that we need to link to find_package(Boost 1.36.0 COMPONENTS filesystem system REQUIRED) target_link_libraries(example PUBLIC ${Boost_LIBRARIES} # here you can add any library dependencies ) ############################################################################### ## testing #################################################################### ############################################################################### # this is for our testing framework # we don't add REQUIRED because it's just for testing find_package(GTest) if(GTEST_FOUND) add_executable(unit_tests ${sources_test} ${sources}) # we add this define to prevent collision with the main # this might be better solved by not adding the source with the main to the # testing target target_compile_definitions(unit_tests PUBLIC UNIT_TESTS) # this allows us to use our executable as a link library # therefore we can inherit all compiler options and library dependencies set_target_properties(example PROPERTIES ENABLE_EXPORTS on) target_link_libraries(unit_tests PUBLIC ${GTEST_BOTH_LIBRARIES} example ) target_include_directories(unit_tests PUBLIC ${GTEST_INCLUDE_DIRS} # doesn't do anything on Linux ) endif() ############################################################################### ## packaging ################################################################## ############################################################################### # all install commands get the same destination. this allows us to use paths # relative to the executable. install(TARGETS example DESTINATION example_destination) # this is basically a repeat of the file copy instruction that copies the # resources in the build directory, but here we tell cmake that we want it # in the package install(DIRECTORY resources DESTINATION example_destination) # now comes everything we need, to create a package # there are a lot more variables you can set, and some # you need to set for some package types, but we want to # be minimal here set(CPACK_PACKAGE_NAME "MyExample") set(CPACK_PACKAGE_VERSION "1.0.0") # we don't want to split our program up into several things set(CPACK_MONOLITHIC_INSTALL 1) # This must be last include(CPack)
самый простой, но полный пример можно найти в CMake, например, учебник:
cmake_minimum_required (VERSION 2.6) project (Tutorial) add_executable(Tutorial tutorial.cxx)
для вашего примера проекта вы можете иметь:
cmake_minimum_required (VERSION 2.6) project (MyProject) add_executable(myexec src/module1/module1.cpp src/module2/module2.cpp src/main.cpp) add_executable(mytest test1.cpp)
для вашего дополнительного вопроса, один из способов снова в учебнике: создать настраиваемый файл заголовка, который вы включаете в свой код. Для этого сделайте файл
configuration.h.in
следующего содержания:#define RESOURCES_PATH "@RESOURCES_PATH@"
затем в
CMakeLists.txt
добавить:set(RESOURCES_PATH "${PROJECT_SOURCE_DIR}/resources/" # configure a header file to pass some of the CMake settings # to the source code configure_file ( "${PROJECT_SOURCE_DIR}/configuration.h.in" "${PROJECT_BINARY_DIR}/configuration.h" ) # add the binary tree to the search path for include files # so that we will find TutorialConfig.h include_directories("${PROJECT_BINARY_DIR}")
наконец, где вам нужно путь в коде, вы можете сделать:
#include "configuration.h" ... string resourcePath = string(RESOURCE_PATH) + "file.png";