Самый простой, но полный пример 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 ..]

Кстати, важно, чтобы моя программа знала, где находятся ресурсы. Я хотел бы знать рекомендуемый способ управления ресурсами. Я не хочу получать доступ к своим ресурсам с помощью «../resources/file.png»

93

Решение

после некоторых исследований у меня теперь есть своя собственная версия самого простого, но полного примера 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), С этой переменной установлено foobar(${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)
70

Другие решения

Самый простой, но полный пример можно найти в 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";
33

По вопросам рекламы [email protected]