0.导语
最近做的项目使用CLION构建,而这个采用CMakeLists.txt管理,因此为了更好的学习,故找到了一篇大牛级别的入门文章,有文章有代码,本文是花了一点时间把这篇文章学习后的重要点记录吧,原作者github地址:https://github.com/wzpan/cmake-demo。
1.单个源文件
CMakeLists.txt 的语法比较简单,由命令、注释和空格组成,其中命令是不区分大小写的。符号 # 后面的内容被认为是注释。命令由命令名称、小括号和参数组成,参数之间使用空格进行间隔。
首先创建一个main.cpp
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
编写第一个CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
project(cmakeLearn)
add_executable(main main.cpp)
-
cmake_minimum_required:指定运行此配置文件所需的 CMake 的最低版本;
-
project:参数值是 cmakeLearn,该命令表示项目的名称是 cmakeLearn 。
-
add_executable:将名为main.cpp的源文件编译成一个名称为 cmakeLearn 的可执行文件。
现在查看一下路径:
light@city:~/CLionProjects/cmakeLearn$ ls
CMakeLists.txt main.cpp
输入下列命令进行cmake
cmake .
cmake过程
light@city:~/CLionProjects/cmakeLearn$ cmake .
-- The C compiler identification is GNU 5.5.0
-- The CXX compiler identification is GNU 5.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/light/CLionProjects/cmakeLearn
cmake 后 生成了Makefile
light@city:~/CLionProjects/cmakeLearn$ ls
CMakeCache.txt cmake_install.cmake main.cpp
CMakeFiles CMakeLists.txt Makefile
然后执行make
light@city:~/CLionProjects/cmakeLearn$ make
Scanning dependencies of target main
[ 50%] Building CXX object CMakeFiles/main.dir/main.cpp.o
[100%] Linking CXX executable main
[100%] Built target main
执行
light@city:~/CLionProjects/cmakeLearn$ ./main
Hello, World!
2.多个源文件
2.1 同一目录,多个源文件
在1中的cmake添加下面这行:
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
当本地源文件很多,如果将源文件都加到里面就很烦,所以这里采用aux_source_directory。CMake 会将当前目录所有源文件的文件名赋值给变量 DIR_SRCS ,再指示变量 DIR_SRCS 中的源文件需要编译成一个名称为 Demo 的可执行文件。
这样就避免出现下面这种情况:
# 指定生成目标
add_executable(Demo main.cc MathFunctions.cc xx.cc ....cc)
小结:
-
aux_source_directory(. DIR_SRCS)
查找当前目录下的所有源文件,并将名称保存到 DIR_SRCS 变量。
2.2 多个目录,多个源文件
此时目录架构:
./Demo3
|
+--- main.cc
|
+--- math/
|
+--- MathFunctions.cc
|
+--- MathFunctions.h
对于这种情况,需要分别在项目根目录 Demo3 和 math 目录里各编写一个 CMakeLists.txt 文件。
为了方便,我们可以先将 math 目录里的文件编译成静态库再由 main 函数调用。
这里与上述2.1CMakeLists不同之处是在上面基础上加上了:
# 添加 math 子目录
add_subdirectory(math)
# 添加链接库
target_link_libraries(Demo MathFunctions)
以此完成:
-
子目录添加
-
链接库添加
最后,在子目录下指定链接库名字:
子目录中的 CMakeLists.txt:
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)
# 生成链接库
add_library (MathFunctions ${DIR_LIB_SRCS})
小结:
-
add_library(MathFunctions ${DIR_LIB_SRCS})
将 src 目录中的源文件编译为静态链接库。
3.自定义编译选项
CMake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。
例如,可以将 MathFunctions 库设为一个可选的库,如果该选项为 ON ,就使用该库定义的数学函数来进行运算。否则就调用标准库中的数学函数库。
本节CMake与2不同如下三块:
(1)加入一个配置头文件,用于处理 CMake 对源码的设置
# 加入一个配置头文件,用于处理 CMake 对源码的设置
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
configure_file 命令用于加入一个配置头文件 config.h ,这个文件由 CMake 从 config.h.in生成,通过这样的机制,将可以通过预定义一些参数和变量来控制代码的生成。
例如:修改main.cc:
#include "config.h"
...
...
...
#ifdef USE_MYMATH
#include "math/MathFunctions.h"
#else
#include
#endif
根据 USE_MYMATH 的预定义值来决定是否调用标准库还是 MathFunctions 库。
上修改main.cc中引用了一个 config.h 文件,这个文件预定义了 USE_MYMATH 的值。但我们并不直接编写这个文件,为了方便从 CMakeLists.txt 中导入配置,我们编写一个 config.h.in 文件:
#cmakedefine USE_MYMATH
这样 CMake 会自动根据 CMakeLists 配置文件中的设置自动生成 config.h 文件。
这里使用了ccmake进行可视化编译选择,Ubuntu上安装:
sudo apt-get install cmake-curses-gui
运行ccmake .后:
hjkl控制方向移动,enter 键可以修改该选项。修改完成后可以按下 c 选项完成配置,之后再按 g 键确认生成 Makefile 。ccmake 的其他操作可以参考窗口下方给出的指令提示。
下面来看一下config.h文件内容:
USE_MYMATH 为 ON
#define USE_MYMATH
USE_MYMATH 为 OFF
/* #undef USE_MYMATH */
(2)是否加入 MathFunctions 库
# 是否加入 MathFunctions 库
if (USE_MYMATH)
include_directories ("${PROJECT_SOURCE_DIR}/math")
add_subdirectory (math)
set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
option 命令添加了一个 USE_MYMATH 选项,并且默认值为 ON 。
(3)是否使用自己的 MathFunctions 库
# 是否使用自己的 MathFunctions 库
option (USE_MYMATH
"Use provided math implementation" ON)
根据 USE_MYMATH 变量的值来决定是否使用我们自己编写的 MathFunctions 库。
4.安装和测试
4.1 安装
之前在编译一些源代码程序的时候,先make后make install,这样会把一些头文件与静态/动态库安装到指定的目录下。
那在CMAKE中同样可以这么做,如下:
首先先在 math/CMakeLists.txt 文件里添加下面两行:
指定 MathFunctions 库的安装路径
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)
指明 MathFunctions 库的安装路径。之后同样修改根目录的 CMakeLists 文件,在末尾添加下面几行:
# 指定安装路径
install (TARGETS Demo DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/config.h"
DESTINATION include)
通过上面定制,后安装如下:
light@city:~/cmake-demo/Demo5$ sudo make install
[ 50%] Built target MathFunctions
[100%] Built target Demo
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/bin/Demo
-- Installing: /usr/local/include/config.h
-- Installing: /usr/local/lib/libMathFunctions.a
-- Installing: /usr/local/include/MathFunctions.h
据此完成了安装工作。
4.2 测试
CMake 提供了一个称为 CTest 的测试工具。我们要做的只是在项目根目录的 CMakeLists 文件中调用一系列的 add_test 命令。
启动测试:
# 启用测试
enable_testing()
(1)无帮助信息测试
# 测试程序是否成功运行
add_test (test_run Demo 5 2)
(2)有帮助信息测试
# 测试 2 的 10 次方
add_test (test_2_10 Demo 2 10)
set_tests_properties (test_2_10)
PROPERTIES PASS_REGULAR_EXPRESSION "is 1024")
其中 PASS_REGULAR_EXPRESSION 用来测试输出是否包含后面跟着的字符串。
5.支持gdb
让 CMake 支持 gdb 的设置也很容易,只需要指定 Debug 模式下开启 -g 选项:
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
6.添加版本号
在编写项目中给项目添加版本号:
set (Demo_VERSION_MAJOR 1)
set (Demo_VERSION_MINOR 0)
分别指定当前的项目的主版本号和副版本号。
如果想在代码中获取信息,可以修改config.h.in文件,添加两个预定义变量:
// the configured options and settings for Tutorial
#define Demo_VERSION_MAJOR @Demo_VERSION_MAJOR@
#define Demo_VERSION_MINOR @Demo_VERSION_MINOR@
主程序调用,打印:
printf("%s Version %d.%d\n",
argv[0],
Demo_VERSION_MAJOR,
Demo_VERSION_MINOR);
7.生成安装包
如何配置生成各种平台上的安装包,包括二进制安装包和源码安装包。为了完成这个任务,我们需要用到 CPack ,它同样也是由 CMake 提供的一个工具,专门用于打包。
我们做如下三个工作:
-
导入 InstallRequiredSystemLibraries 模块,以便之后导入 CPack 模块;
-
设置一些 CPack 相关变量,包括版权信息和版本信息,其中版本信息用了上一节定义的版本号;
-
导入 CPack 模块。
对应CMakeLists.txt如下:
# 构建一个 CPack 安装包
include (InstallRequiredSystemLibraries)
set (CPACK_RESOURCE_FILE_LICENSE
"${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set (CPACK_PACKAGE_VERSION_MAJOR "${Demo_VERSION_MAJOR}")
set (CPACK_PACKAGE_VERSION_MINOR "${Demo_VERSION_MINOR}")
include (CPack)
下面就是如何使用:
输入cpack .,也可以指定二进制与源码安装包:
-
生成二进制安装包:
cpack -C CPackConfig.cmake
-
生成源码安装包
cpack -C CPackSourceConfig.cmake
cpack安装:
light@city:~/cmake-demo/Demo8$ cpack -CPackConfig.cmake
CPack: Create package using STGZ
CPack: Install projects
CPack: - Run preinstall target for: Demo8
CPack: - Install project: Demo8
CPack: Create package
CPack: - package: /home/light/cmake-demo/Demo8/Demo8-1.0.1-Linux.sh generated.
CPack: Create package using TGZ
CPack: Install projects
CPack: - Run preinstall target for: Demo8
CPack: - Install project: Demo8
CPack: Create package
CPack: - package: /home/light/cmake-demo/Demo8/Demo8-1.0.1-Linux.tar.gz generated.
CPack: Create package using TZ
CPack: Install projects
CPack: - Run preinstall target for: Demo8
CPack: - Install project: Demo8
CPack: Create package
CPack: - package: /home/light/cmake-demo/Demo8/Demo8-1.0.1-Linux.tar.Z generated.
此时会在本地目录下创建3个不同格式的二进制包文件:
light@city:~/cmake-demo/Demo8$ ls Demo8*
Demo8-1.0.1-Linux.sh Demo8-1.0.1-Linux.tar.gz Demo8-1.0.1-Linux.tar.Z
随便选择一个安装,例如sh:
sh Demo8-1.0.1-Linux.sh
使用:
light@city:~/cmake-demo/Demo8$ ./Demo8-1.0.1-Linux/bin/Demo 5 2
Now we use our own Math library.
5 ^ 2 is 25