CMake
CMake
Building a Basic Project
- CmakeLists.txt:根目录下一定要有的文件
- cmake_minimum_required():设置cmake的版本
- project():传入项目名字与项目版本号,相当于Visual Studio中的解决方案
- add_executable():设置一个可执行的库,相当于Visual Studio中的一个可运行的项目(main)
Specifying the C++ Standard
Cmake中有很多预设变量,详情可以看这里。
其中设置C++版本可以使用一下两个变量:
- CMAKE_CXX_STANDARD
- CMAKE_CXX_STANDARD_REQUIRED
在CMakeLists.txt文件中添加一下代码,就是为工程设置C++11编码标准:
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
Adding a Version Number and Configured Header File
CMake中有很多预设变量,这些变量可以在c++中使用。需要在CMakeLists文件使用configure_file
语句。用法如下:
configure_file(TutorialConfig.h.in TutorialConfig.h)
这里就是把与CMakeLists.txt
同目录下的TutorialConfig.h.in
文件拷贝到build
文件夹里。当然复制目录和生成目录都可以自定义设置。
使用CMake中的变量时,需要使用@
包裹着变量,实例如下:
#define CMAKE_PROJECT_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
Adding a Library
添加一个库工程,也就是Visual Studio中没有main
函数的项目。使用一个文件夹作为库工程的根目录,在该根目录中需要加入CMakeLists.txt
文件。在该文件中使用add_library()。使用实例如下:
add_library(lib_name header.h file1.cxx)
第一个参数为库名字,后面加上需要编入库的文件,当然可以使用set
、file
等命令来把多个文件集合为一个变量。
把库工程连接到执行工程中,首先需要链接库的CMakeLists文件,使用add_subdirectory()
命令。传入库文件夹的相对于执行工程的CMakeLists文件的目录即可。如下目录:
│ CMakeLists.txt
│ tutorial.cxx
│ TutorialConfig.h.in
│
└───MathFunctions
CMakeLists.txt
MathFunctions.h
mysqrt.cxx
使用add_subdirectory(MathFunctions)
即可,然后使用target_link_libraries()
命令把库链接到执行文件中。
Making Our Library Optional
在项目中会使用一些配置来控制工程,使用option(<variable> "<help_text>" [value])命令来控制。
在configure_file
文件中使用cmakedefine
可以把option
中设置的变量设置为宏变量,使用如下:
#cmakedefine USE_MAMATH
Adding Usage Requirements for a Library
以下为可以配置使用要求的命令:
- target_compile_definitions()
- target_compile_options()
- target_include_directories()
- target_link_directories()
- target_link_options()
- target_precompile_headers()
- target_sources()
其中使用需求有INTERFACE
,PUBLIC
,PRIVATE
。他们的不同可以看这里。
举个例子:
add_library(MathFunctions mysqrt.cxx)
target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
该命令是把链接该库的所有target都自动添加引用路径。
Adding Generator Expressions
可以设置生成器的一些特殊构建信息。文档
Setting the C++ Standard with Interface Libraries
使用一个公共库来设置编译要求,如设置c++版本,前面讲到使用set(CMAKE_CXX_STANDARD 11)
该命令设置,这里可以创建一个公共库,专门来配置生成器的编译配置,示例如下:
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
# 最终连接该库即可
target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
Adding Compiler Warning Flags with Generator Expressions
Installing and Testing
Install Rules
安装就是把工程打包可执行文件与lib文件,打出目录如下:
├───bin
│ Tutorial.exe
│
├───include
│ MathFunctions.h
│ TutorialConfig.h
│
└───lib
MathFunctions.lib
文件目录可以自定义,主要使用install()命令进行配置。主要分为三种:
- bin:可执行文件
install(TARGETS Tutorial DESTINATION bin)
- include:头文件
install(FILES MathFunctions.h DESTINATION include)
- lib:库文件
set(installable_libs MathFunctions tutorial_compiler_flags) install(TARGETS ${installable_libs} DESTINATION lib)
值得注意的是,需要把库或可执行文件全部配置完成后,再执行安装命令。
安装命令,需要先执行build命令再执行安装
cd build
cmake ..
cmake --build . --config Release
# 安装到默认文件夹
cmake --install . --config Release
# 指定安装目录
cmake --install . --config Release --prefix "/home/myuser/installdir"
Testing Support
enable_testing()
# Add a test called Runs which runs the following command:
# $ Tutorial 25
add_test(NAME Runs COMMAND Tutorial 25)
# Add a test called Usage which runs the following command:
# $ Tutorial
# Make sure the expected output is displayed.
# Hint: Use the PASS_REGULAR_EXPRESSION property with "Usage.*number"
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number")
# Add a test which runs the following command:
# $ Tutorial 4
# Make sure the result is correct.
# Hint: Use the PASS_REGULAR_EXPRESSION property with "4 is 2"
add_test(NAME StandardUse COMMAND Tutorial 4)
set_tests_properties(StandardUse
PROPERTIES PASS_REGULAR_EXPRESSION "4 is 2"
)
# Add more tests. Create a function called do_test to avoid copy +
# paste. Test the following values: 4, 9, 5, 7, 25, -25 and 0.0001.
function(do_test target arg result)
add_test(NAME Comp${arg} COMMAND ${target} ${arg})
set_tests_properties(Comp${arg}
PROPERTIES PASS_REGULAR_EXPRESSION ${result}
)
endfunction()
# do a bunch of result based tests
do_test(Tutorial 4 "4 is 2")
do_test(Tutorial 9 "9 is 3")
do_test(Tutorial 5 "5 is 2.236")
do_test(Tutorial 7 "7 is 2.645")
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is (-nan|nan|0)")
do_test(Tutorial 0.0001 "0.0001 is 0.01")
Adding Support for a Testing Dashboard
可以设置后台编译结果查看,式例:https://my.cdash.org/index.php?project=CMakeTutorial
设置如下:
set(CTEST_PROJECT_NAME "CMakeTutorial")
set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "my.cdash.org")
set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
set(CTEST_DROP_SITE_CDASH TRUE)
Adding System Introspection
根据不同平台可以的标准库不同,来设置宏:
include(CheckCXXSourceCompiles)
# Use check_cxx_source_compiles with simple C++ code to verify
# availability of:
# * std::log
# * std::exp
# Store the results in HAVE_LOG and HAVE_EXP respectively.
check_cxx_source_compiles("
#include <cmath>
int main(){
std::log(1.0);
return 0;
}
" HAVE_LOG)
if(HAVE_LOG)
target_compile_definitions(MathFunctions PRIVATE "HAVE_LOG")
endif()
Adding a Custom Command and Generated File
在构建时,可以执行特殊命令来生成文件,命令是由一个C++的可执行的库组成的。文档。这有两种方式调用:
- 直接运行
- 监听构建过程,指定某个阶段执行
- PRE_BUILD
- PRE_LINK
- POST_BUILD
add_custom_command(
OUTPUT table.csv
COMMAND makeTable -i ${CMAKE_CURRENT_SOURCE_DIR}/input.dat
-o table.csv
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/input.dat
VERBATIM)
add_custom_target(generate_table_csv DEPENDS table.csv)
add_custom_command(
OUTPUT foo.cxx
COMMAND genFromTable -i table.csv -case foo -o foo.cxx
DEPENDS table.csv # file-level dependency
generate_table_csv # target-level dependency
VERBATIM)
add_library(foo foo.cxx)
add_custom_command(
OUTPUT bar.cxx
COMMAND genFromTable -i table.csv -case bar -o bar.cxx
DEPENDS table.csv # file-level dependency
generate_table_csv # target-level dependency
VERBATIM)
add_library(bar bar.cxx)
Packaging an Installer
在顶层CMakeLists.txt
最后加上以下代码:
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}")
set(CPACK_SOURCE_GENERATOR "TGZ")
include(CPack)
接着运行以下命令:
cmake --build .
# 默认打包设置,使用nsis
cpack
# 同样可以指定设置打包压缩文件
# cpack -G ZIP -C Debug
# 也可以指定打包设置
# cpack --config CPackSourceConfig.cmake
这里生成的就是install
配置安装的包体。
Selecting Static or Shared Libraries
使用BUILD_SHARED_LIBS
字段来控制所有库共享属性,使用如下:
option(BUILD_SHARED_LIBS "Global flag to cause add_library() to create shared libraries if on" ON)
开关设置为ON
/OFF
。
Adding Export Configuration
设置自定义命令
Packaging Debug and Release
常用命令
直接使用cmake
命令在拥有CMakeLists.txt
的目录下,会自动识别当前的运行的平台,来设置编译器和平台等。
需要特殊设置可以使用以下命令:
cmake -B $CMakeBuildPath -G $Generator -A $Arch -DCMAKE_TOOLCHAIN_FILE="$Toolchain" -DCMAKE_BUILD_TYPE=Release
- B 输出构建工程的路径
- G 编译器
- A 平台,64位或32位等
以下两个也可以在CMakeLists.txt
中使用set
命令直接设置:
- DCMAKE_TOOLCHAIN_FILE 工具链,特殊平台编译需要使用的一些预设
- DCMAKE_BUILD_TYPE 打包的配置
Windows的几个编译配置:
- Release: high optimization level, no debug info, code or asserts.
- Debug: No optimization, asserts enabled, [custom debug (output) code enabled],
debug info included in executable (so you can step through the code with a
debugger and have address to source-file:line-number translation). - RelWithDebInfo: optimized, with debug info, but no debug (output) code or asserts.
- MinSizeRel: same as Release but optimizing for size rather than speed.
常用变量
- PROJECT_SOURCE_DIR: 使用
project
命令的目录 - PROJECT_BINARY_DIR:build目录
- CMAKE_CURRENT_SOURCE_DIR:当前CMakeLists文件目录
常用语句设置
target_include_directories
target_include_directories(Tutorial PUBLIC ${PROJECT_BINARY_DIR})
对一个Target指定一个路径,在写#include
时,可以省略写指定的路径。有3个范围限定:
- INTERFACE:仅有头文件,没有实现。设置为公共库引用,可以把公共接口抽象出来,并将其与库实现分离。
- PUBLIC: 在头文件和实现中使用,对应的路径
- PRIVATE:只在实现中使用,对应的路径
list
可以把一些变量存放到一个列表中,并对该列表进行维护。文档
set
建议直接使用set
命令 而不是file(GLOB_RECURSE)
,因为后者有以下缺点:
- GLOB 命令只会在 CMake 首次运行时执行一次,并且不会更新文件列表,因此如果你添加或删除了文件,则不会自动更新 CMake 构建。
- 对于大型项目来说,GLOB 命令可能会变得很慢,因为它需要扫描整个源目录树。
- GLOB 命令无法处理重命名和移动文件的情况。
可以使用set
命令直接储存文件,使用如下:
set(SOURCES
src/foo.cpp
src/bar.cpp
)
add_library(my_lib ${SOURCES})
设置VS使用文件夹包裹
if (CMAKE_GENERATOR MATCHES "Visual Studio")
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
endif()
cmake_dependent_option
https://cmake.org/cmake/help/latest/module/CMakeDependentOption.html
cmake_dependent_option(USE_FOO "Use Foo" ON "USE_BAR" OFF)
如果USE_BAR
为ON
,那么USE_FOO
为ON
,否则为OFF
。简单来说就是依赖项为真,那么选项的值就为前面,反之为后面。
该设置不会有引用属性,也就是后续修改USE_BAR
值,USE_FOO
值并不会修改。
模板
# 设置cmake版本
cmake_minimum_required(VERSION 3.0.0)
# 设置项目名称为dome
# 这里对应变量${PROJECT_NAME}
project(dome VERSION 0.1.0)
# 设置引用目录 可省略头文件目录
include_directories(${PROJECT_SOURCE_DIR}/include/)
# 搜索src模板下的cpp文件(顶层)设置到变量SOURCE_FILES
file(GLOB_RECURSE SOURCE_FILES ${PROJECT_SOURCE_DIR}/src/*cpp)
# 打印
#message(${SOURCE_FILES})
# 这里设置两个库 dome_src dome_libsrc
# 这里有个奇异的点就是 库的名字必须是${PROJECT_NAME}这个开头 不然无法链接到
add_library(${PROJECT_NAME}_src SHARED ${SOURCE_FILES})
# 这个是直接输入源文件到库
add_library(${PROJECT_NAME}_libsrc SHARED libsrc/i.cpp)
# 添加可执行文件 编译
add_executable(${PROJECT_NAME} example/i_love_China.cpp)
# 这里把库文件链接到可执行文件中
target_link_libraries(${PROJECT_NAME}
${PROJECT_NAME}_src
${PROJECT_NAME}_libsrc)
报错
Failed to run MSBuild command:
C:/Program Files (x86)/Microsoft Visual Studio/2019/Professional/MSBuild/Current/Bin/MSBuild.exe
to get the value of VCTargetsPath:
Access violation
From VisualStudio Installer
app, click Modify
and install C++ Universal Windows Platform support for v142 build tools (ARM64)
individual component.
Notes
cmake marco
和function
的区别不大,但是前者是有点像值传递的感觉,后者有点像引用传递。
引用
https://cmake.org/cmake/help/latest/guide/tutorial/index.html