Najprostszy, ale kompletny przykład CMake
Jakoś jestem całkowicie zdezorientowany tym, jak działa CMake. Za każdym razem, gdy myślę, że jestem coraz bliżej do zrozumienia, jak ma być napisane CMake, znika w następnym przykładzie, który przeczytałem. Wszystko, co chcę wiedzieć, to jak mam uporządkować swój projekt, aby mój CMake wymagał jak najmniejszej konserwacji w przyszłości. Na przykład, nie chcę aktualizować mojej CMakeList.txt kiedy dodaję nowy folder w moim drzewie src, który działa dokładnie tak jak wszystkie inne foldery src.
This is how I wyobraź sobie strukturę mojego projektu, ale proszę, to tylko przykład. Jeśli zalecany sposób różni się, proszę powiedz mi, i powiedz mi, jak to zrobić.
myProject
src/
module1/
module1.h
module1.cpp
module2/
[...]
main.cpp
test/
test1.cpp
resources/
file.png
bin
[execute cmake ..]
Przy okazji, ważne jest, żeby mój program wiedział, gdzie są zasoby. Chciałbym poznać zalecany sposób zarządzania zasobami. Nie chcę korzystać z moich zasobów "../ resources / file.png " 2 answers
Po kilku badaniach mam teraz własną wersję najprostszego, ale kompletnego przykładu cmake. Oto ona i stara się objąć większość podstaw, w tym zasoby i opakowania.
Jedną z rzeczy, które robi niestandardowo, jest obsługa zasobów. Domyślnie cmake chce umieścić je w /usr/ share/,/usr/local/ share / i coś równoważnego w systemie windows. Chciałem mieć prosty zip / tar.gz, który możesz wydobyć w dowolnym miejscu i uruchomić. Dlatego zasoby są ładowane względem wykonywalny.
Podstawową zasadą rozumienia poleceń cmake jest następująca składnia:
<function-name>(<arg1> [<arg2> ...])
bez przecinka lub średnika. Każdy argument jest ciągiem znaków. foobar(3.0)
i foobar("3.0")
to to samo. możesz ustawić listy / zmienne za pomocą set(args arg1 arg2)
. Z tym zestawem zmiennych foobar(${args})
i foobar(arg1 arg2)
są faktycznie takie same. Nieistniejąca zmienna jest równoznaczna z pustą listą. Lista jest wewnętrznie ciągiem znaków ze średnikami, aby oddzielić elementy. Dlatego lista zawierająca tylko jeden element jest z definicji tylko tym elementem, boks nie ma miejsca. Zmienne są globalne. Wbudowane funkcje oferują pewną formę nazwanych argumentów przez fakt, że oczekują pewnych identyfikatorów, takich jak PUBLIC
lub DESTINATION
w ich liście argumentów, do grupowania argumentów. Ale to nie jest funkcja języka, te identyfikatory są również po prostu ciągami znaków i przetwarzane przez implementację funkcji.
Możesz sklonować wszystko z 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)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-23 12:42:04
Najbardziej podstawowy, ale kompletny przykład można znaleźć w cmake tutorial :
cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(Tutorial tutorial.cxx)
Dla przykładu projektu możesz mieć:
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)
Jeśli chodzi o dodatkowe pytanie, jednym ze sposobów jest samouczek: Utwórz konfigurowalny plik nagłówkowy, który umieścisz w kodzie. W tym celu utwórz plik configuration.h.in
o następującej treści:
#define RESOURCES_PATH "@RESOURCES_PATH@"
Następnie w Twoim CMakeLists.txt
dodaj:
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}")
Wreszcie, gdzie potrzebujesz ścieżki w kodzie, możesz zrobić:
#include "configuration.h"
...
string resourcePath = string(RESOURCE_PATH) + "file.png";
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-16 13:47:24