C++ 最简单但完整的 CMake 示例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21163188/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Most simple but complete CMake example
提问by Arne
Somehow I am totally confused by how CMake works. Every time I think that I am getting closer to understand how CMake is meant to be written, it vanishes in the next example I read. All I want to know is, how should I structure my project, so that my CMake requires the least amount of maintainance in the future. For example, I don't want to update my CMakeList.txt when I am adding a new folder in my src tree, that works exactly like all other src folders.
不知何故,我对 CMake 的工作方式感到完全困惑。每次我认为我越来越接近于理解 CMake 的编写方式时,它在我阅读的下一个示例中消失了。我只想知道,我应该如何构建我的项目,以便我的 CMake 在未来需要最少的维护。例如,当我在我的 src 树中添加一个新文件夹时,我不想更新我的 CMakeList.txt,它的工作原理与所有其他 src 文件夹完全一样。
This is how I imagine my project's structure, but please this is only an example. If the recommended way differs, please tell me, and tell me how to do it.
这就是我想象我的项目结构的方式,但这只是一个例子。如果推荐的方式不同,请告诉我,并告诉我如何去做。
myProject
src/
module1/
module1.h
module1.cpp
module2/
[...]
main.cpp
test/
test1.cpp
resources/
file.png
bin
[execute cmake ..]
By the way, it is important that my program knows where the resources are. I would like to know the recommended way of managing resources. I do not want to access my resources with "../resources/file.png"
顺便说一句,重要的是我的程序知道资源在哪里。我想知道推荐的资源管理方式。我不想使用“../resources/file.png”访问我的资源
采纳答案by Arne
after some research I have now my own version of the most simple but complete cmake example. Here it is, and it tries to cover most of the basics, including resources and packaging.
经过一番研究,我现在有了自己版本的最简单但完整的 cmake 示例。在这里,它试图涵盖大部分基础知识,包括资源和包装。
one thing it does non-standard is resource handling. By default cmake wants to put them in /usr/share/, /usr/local/share/ and something equivalent on windows. I wanted to have a simple zip/tar.gz that you can extract anywhere and run. Therefore resources are loaded relative to the executable.
它不标准的一件事是资源处理。默认情况下,cmake 想把它们放在 /usr/share/、/usr/local/share/ 和 Windows 上的等价物。我想要一个简单的 zip/tar.gz,你可以在任何地方提取并运行它。因此,资源是相对于可执行文件加载的。
the basic rule to understand cmake commands is the following syntax:
<function-name>(<arg1> [<arg2> ...])
without comma or semicolor. Each argument is a string. foobar(3.0)
and foobar("3.0")
is the same. you can set lists/variables with set(args arg1 arg2)
. With this variable set foobar(${args})
and foobar(arg1 arg2)
are effectively the same. A non existent variable is equivalent to an empty list. A list is internally just a string with semicolons to separate the elements. Therefore a list with just one element is by definition just that element, no boxing takes place. Variables are global. Builtin functions offer some form of named argumentsby the fact that they expect some ids like PUBLIC
or DESTINATION
in their argument list, to group the arguments. But that's not a language feature, those ids are also just strings, and parsed by the function implementation.
理解 cmake 命令的基本规则是以下语法:
<function-name>(<arg1> [<arg2> ...])
没有逗号或分号。每个参数都是一个字符串。foobar(3.0)
并且foobar("3.0")
是一样的。您可以使用set(args arg1 arg2)
. 与此变量设置foobar(${args})
和foobar(arg1 arg2)
实际上是相同的。一个不存在的变量相当于一个空列表。列表在内部只是一个用分号分隔元素的字符串。因此,根据定义,只有一个元素的列表就是该元素,不会发生装箱。变量是全局的。内置函数提供了某种形式的命名参数,因为它们需要一些像PUBLIC
或DESTINATION
在他们的参数列表中,对参数进行分组。但这不是语言功能,这些 id 也只是字符串,并由函数实现解析。
you can clone everything from github
你可以从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)
回答by sgvd
The most basic but complete example can be found in the CMake tutorial:
最基本但完整的示例可以在CMake 教程中找到:
cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(Tutorial tutorial.cxx)
For your project example you may have:
对于您的项目示例,您可能有:
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)
For your additional question, one way to go is again in the tutorial: create a configurable header file that you include in your code. For this, make a file configuration.h.in
with the following contents:
对于您的其他问题,本教程中的另一种方法是:创建一个包含在代码中的可配置头文件。为此,制作一个configuration.h.in
包含以下内容的文件:
#define RESOURCES_PATH "@RESOURCES_PATH@"
Then in your CMakeLists.txt
add:
然后在您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}")
Finally, where you need the path in your code, you can do:
最后,在代码中需要路径的地方,您可以执行以下操作:
#include "configuration.h"
...
string resourcePath = string(RESOURCE_PATH) + "file.png";
回答by MinamiTouma
Here I write a most simple but complete CMakeLists.txt files sample.
这里我写了一个最简单但完整的 CMakeLists.txt 文件示例。
- Tutorials from hello world to cross platform Android/iOS/Web/Desktop.
- Each platform I released a sample application.
- The 08-cross_platform file struct is verified by my work
- It may be not perfect but useful & best practice for a team on my own
- 从 hello world 到跨平台 Android/iOS/Web/Desktop 的教程。
- 每个平台我都发布了一个示例应用程序。
- 08-cross_platform 文件结构由我的工作验证
- 对于我自己的团队来说,这可能并不完美,但很有用且是最佳实践
After that, I offered document for the details.
之后,我提供了详细信息的文件。
If you have any questions, you can contact me and I'd like to explain it.
如果您有任何问题,可以联系我,我想解释一下。