CMake从入门到放弃
一直都用cmake,但是也就是在GitHub上的项目写一点简单的东西,并没有正儿八经学过,因此抽空看了一点。主要从CMake官网的tutorial上学。
官网分为12个step,都在CMake源码的Help/guide/tutorial
文件夹中可以找到。能看多少就看多少。
Step 1
首先最开始是最简单的CMakeLists.txt
,只有三行
1 | cmake_minimum_required(VERSION 3.10) |
分别指定了cmake的最低版本,项目的名称和生成的目标文件,就简单的执行:
1 | mkdir build |
在Unix下就可以生成可执行文件了。
然后接着要将版本号传递给可执行文件,并且随着版本号变化可以不用修改源码。
首先修改CMakeLists.txt
,
1 | cmake_minimum_required(VERSION 3.10) |
然后在CMakeLists.txt同级目录下编写TutorialConfig.h.in
:
1 | // the configured options and settings for Tutorial |
对于configure_file,cmake定义大概是这样的:将文件复制到另一个位置并修改其内容。当然,这里的修改其内容也不是任意地修改,也是遵循一定的规则:将input文件复制到output文件,并在输入文件内容中的变量,替换引用为@VAR@或${VAR}的变量值。每个变量引用将替换为该变量的当前值,如果未定义该变量,则为空字符串。
所以配置的时候就会在build文件夹下,生成TutorialConfig.h
。如下:
1 | // the configured options and settings for Tutorial |
就可以控制版本号了。而这个版本号是从CMakeLists.txt
中获取,所以就不用修改源码,十分方便。
然后我又试了一下,发现可以指定configure_file
的输出路径:
修改如下:
1 | configure_file(TutorialConfig.h.in ${PROJECT_BINARY_DIR}/test/TutorialConfig.h) |
执行:
1 | mkdir build |
可以看到build文件夹下有test文件夹,里吗有输出的头文件。
这个时候通过尝试我发现有两个方法:
要么修改CMakeLists.txt
:
1 | target_include_directories(Tutorial PUBLIC |
要么修改调用头文件:
1 |
|
然后就是用C++ 11标准。
在add_executable
前面加入:
1 | set(CMAKE_CXX_STANDARD 11) |
然后将代码换成C++ 11的标准,发现也可以用。
Step 2
在Step 1的基础上,加入了一个子目录MathFunctions
。里面放了一个自己实现的mysqrt.cxx
和对应的头文件MathFunctions.h
。
首先在子目录下加入CMakeLists.txt:
1 | add_library(MathFunctions mysqrt.cxx) |
这里的作用将mysqrt.cxx编译成libMathFunctions.a
。
然后在这个项目的CMakeLists.txt
中写入:
1 | # add the MathFunctions library |
首先第一行add_subdirectory
就是去找到子目录。target_link_libraries
则说明Tutorial
要与库libMathFunctions链接。在这里target_link_libraries
和上面的add_library
的名字要相同,与子目录的名字不一定要相同。
target_include_directories
则用于找到头文件。
然后在其中加入选项来控制是否用使用自己实现的函数。
在CMakeLists中进行修改:
1 | # configure a header file to pass some of the CMake settings |
首先在TutorialConfig.h.in
中加入:
1 |
对源文件做一定修改:
1 | // A simple program that computes the square root of a number |
注意:需要先包含头文件”TutorialConfig.h”。这样在cmake的时候可以用选项控制ifdef
。最开始我把#include "TutorialConfig.h"
放到了
1 | #ifdef USE_MYMATH |
下面,一直显示# include "MathFunctions.h"
无法显示。因为没有用cmakedefine
进行定义。
最后就可以用
1 | cmake .. -DUSE_MYMATH=ON |
来控制执行哪段代码。
Step 3
在这里将Step 2中CMakeLists.txt中的EXTRA_INCLUDES
都删了,取代的方式是在MathFunctions
中使用target_include_directories
来让外部的函数调用。如下:
1 | target_include_directories(MathFunctions |
在这里用INTERFACE
关键字,文档中解释说只给消费者使用不给生产者使用。这个关键词有很多文章解释,暂时不管。
Step 4
这一步是指定安装目录和做一些简单的测试。
要安装MathFunctions
库,我们可以加入如下语句到MathFunctions/CMakeLists.txt
中去:
1 | install(TARGETS MathFunctions DESTINATION lib) |
其中DESTINATION
是相对路径。
而如果想要把Tutorial
也安装到对应路径,可以用
1 | install(TARGETS Tutorial DESTINATION bin) |
放在根目录的CMakeLists.txt
。
这样子默认就会安装在根目录的/bin
,/lib
和/include
如果要安装到指定目录,有两种方法:
在项目根目录到
CMakeLists.txt
的Project
选项下方,加上:1
set(CMAKE_INSTALL_PREFIX /root/cmake-tutorial/install_path)
在cmake配置的时候,用:
1
cmake -DCMAKE_INSTALL_PREFIX="/root/install" ..
这两种方法都可以改变安装目录。
CMake的test
一般似乎是先启动测试:enable_testing()
,然后用add_test()
添加测试内容,然后最后用set_tests_properties()
来设置测试的属性。
在原来的CMakelists.txt
后加入:
1 | enable_testing() |
其中的PASS_REGULAR_EXPRESSION
是正则匹配,后面的字符串预期会出现在输出中。
在编译好后用ctest
命令即可看出测试效果,也可以用ctest -VV
或ctest -N
获取更多输出。
Step 5
Step 5是根据系统情况决定如何编译,在这里我觉得核心是
1 | include(CheckSymbolExists) |
这个函数。
以上语句用于检测math.h中是否提供log函数。然后用一个if语句判断,如果提供的话,用target_compile_definitions
或者add_definitions
将宏传递给代码。
Step 6
在这里用一个程序生成了一个开方表,然后用add_custom_command
来执行:
1 | add_executable(MakeTable MakeTable.cxx) |
在这里首先编译得到MakeTable
,然后在add_custom_command
中指定了输出、命令和依赖。接下来就是把Table.h
加入到库目录和include目录中去。
注意要用add_library
和target_include_directories
1 | add_library(MathFunctions |
如果不用add_library
则会出现Table.h无法生成,不将目录加入include的路径则会出现mysqrt.cxx
无法去目标目录找到Table.h。