概叙 你或许听过好几种 Make 工具,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。
CMake就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeLists.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。
入门示例 这是一个对输入的数求平方根的示例,用编译配置开关控制是调用系统数学函数(sqrt)还是自定义函数(mysqrt)。主要包含设置版本号、配置头文件(可选项开关)、添加静态库的使用。
1. 目录结构
build目录自己创建,内容执行cmake命令自动生成
MathFunctions目录是自定义的数学函数库(会编译成静态库)
tuorial.cpp主入口文件
2. cmake命令使用
配置环境
测试环境:windows11
需要安装CMake 和 MinGW,MinGW 就是 GCC 的 Windows 移植版本。
构建命令,只需要第一次执行一次
cmake -G”MinGW Makefiles” ..
构建系统是需要指定 CMakeLists.txt 所在路径,此时在 build 目录下,所以用 .. 表示 CMakeLists.txt 在上一级目录。
Windows 下,CMake 默认使用微软的 MSVC 作为编译器,我想使用 MinGW 编译器,可以通过 -G 参数来进行指定,只有第一次构建项目时需要指定。
执行完会在 build 目录下会生成 Makefile 文件。
编译、链接可执行程序
cmake –build .
–build 指定编译生成的文件存放目录,其中就包括可执行文件,. 表示存放到当前目录
执行完会在 build 目录下生成一个 Tutorial.exe 可执行文件
设置配置的可选项开关
cmake -DUSE_MYMATH=OFF ..
把USE_MYMATH设置为OFF,此设置将存储在缓存中,以便用户不需要在每次构建项目时设置该值。
3. 源码 所有细节看详细注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <cmath> #include <iostream> #include <string> #include "TutorialConfig.h" #ifdef USE_MYMATH #include "MathFunctions.h" #endif int main (int argc, char * argv[]) { if (argc < 2 ) { std::cout << argv[0 ] << " Version " << Tutorial_VERSION_MAJOR << "." << Tutorial_VERSION_MINOR << std::endl; std::cout << "Usage: " << argv[0 ] << " number" << std::endl; return 2 ; } const double inputValue = std::stod (argv[1 ]); #ifdef USE_MYMATH const double outputValue = mysqrt (inputValue); #else const double outputValue = sqrt (inputValue); #endif std::cout << "The square root of " << inputValue << " is " << outputValue << std::endl; return 0 ; }
1 2 3 double mysqrt (double value) ;
1 2 3 4 5 6 7 8 #include "MathFunctions.h" double mysqrt (double value) { return 1.0 ; }
1 2 3 4 5 6 7 8 #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@ #cmakedefine USE_MYMATH
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 cmake_minimum_required (VERSION 3.15 )project (Tutorial VERSION 1.0 )configure_file (TutorialConfig.h.in TutorialConfig.h)set (CMAKE_CXX_STANDARD 11 )set (CMAKE_CXX_STANDARD_REQUIRED True )option (USE_MYMATH "Use tutorial provided math implementation" ON )if (USE_MYMATH) add_subdirectory (MathFunctions) list (APPEND EXTRA_LIBS MathFunctions) endif ()SET (SRC_LIST tutorial.cpp)add_executable (${PROJECT_NAME} ${SRC_LIST} ) target_link_libraries (${PROJECT_NAME} PUBLIC ${EXTRA_LIBS} ) target_include_directories (${PROJECT_NAME} PUBLIC ${PROJECT_BINARY_DIR} )
1 2 3 4 5 6 7 8 9 add_library (MathFunctions mysqrt.cpp)target_include_directories (MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} )
4. 执行结果
进阶 1. 安装程序
程序写完,我们可以通过命令把可执行文件和头文件安装到指定目录。
接着上面工程,在两个CMakeLists.txt文件最后添加安装设置。
1 2 3 install (TARGETS Tutorial DESTINATION bin)install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" DESTINATION include )
1 2 3 install (TARGETS MathFunctions DESTINATION bin) install (FILES MathFunctions.h DESTINATION include )
执行命令:
cmake --install . --prefix "C:\Users\KL179\Desktop\cmake_test\install"
2. 测试程序
在根目录的CMakeLists.txt(主)最后添加测试用例。
测试用例主要就是两个接口:
add_test 添加用例
set_tests_properties 验证结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 enable_testing ()add_test (test_run Tutorial 8 )add_test (test_usage Tutorial)set_tests_properties (test_usage PROPERTIES PASS_REGULAR_EXPRESSION "Usage: .* base exponent" )macro (do_test arg1 result)add_test (test_${arg1} Tutorial ${arg1} )set_tests_properties (test_${arg1} PROPERTIES PASS_REGULAR_EXPRESSION ${result} )endmacro (do_test)do_test (1 "is 1" ) do_test (4 "is 2" ) do_test (16 "is 4" )
运行ctest执行(或者make test)
3. 支持gdb配置 让 CMake 支持 gdb 的设置也很容易,只需要指定 Debug 模式下开启 -g 选项:
1 2 3 4 5 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" )
4. 检测接口是否存在 一般是使用平台相关特性时,下面我们以检测系统是否有sqrt函数为例。
首先在主CMakeLists.txt文件里configure_file之前添加下面设置:
1 2 3 include (${CMAKE_ROOT} /Modules/CheckFunctionExists.cmake)check_function_exists (sqrt HAVE_SQRT)
接下来修改 TutorialConfig.h.in 文件,预定义HAVE_SQRT宏变量。
最后就可以使用HAVE_SQRT来判断了。
1 2 3 4 5 6 7 #ifdef HAVE_SQRT std::cout << "check_function_exists sqrt is exist" << std::endl; #else std::cout << "check_function_exists sqrt is noexist" << std::endl; #endif
5. 生成安装包 生成各种平台上的安装包,包括二进制安装包和源码安装包。为了完成这个任务,我们需要用到 CPack ,它同样也是由 CMake 提供的一个工具,专门用于打包。
注意:CPack打包依赖 NSIS 需要安装。
只需要在顶层的 CMakeLists.txt 文件尾部添加下面几行:
1 2 3 4 5 6 7 8 9 10 include (InstallRequiredSystemLibraries)set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt" )set (CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}" )set (CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}" )include (CPack)
然后命令行执行cmake打包命令就可以打包了。
1 cpack -C CPackConfig.cmake
1 cpack -C CPackSourceConfig.cmake
生成的安装包,双击安装执行截图如下: