本文很大程度参考了小彭老师的课程,倒不如说就是 小彭老师课程的笔记 虽然以往都会补充,但 本次因为一些原因并没有补充额外的东西,更多信息可以参考cmake的官网。
[ 注 ] CMake是区分大小写的,更准确地说,指令不区分大小但是其他的所有内容是区分大小写的。
我在之前的笔记中记录了程序是怎么从.cpp文件变为可执行文件,同时也知道了所谓build其实就是就是把别人已经写好的子模块源代码经过编译成库文件,然后为自己所使用。也许有人会问,那为什么我们不直接把别人的代码直接加到我们的项目里面?这样做也是可以的,但是这往往取决于你所使用的第三方库,如果说你的引入的库是“Head Only”的也就是只有头文件(.h .hpp),那你就只需要include进来相关的头文件即可,一般这种库,会有一个总的入口头文件,include这一个就行。如果说一个库既有.h 也有.cpp 那么此时你就应该将其编译成库文件。这主要是取决于你所使用的第三方库。
CMake2.x和CMake3.x
mkdir -p build
cd build
cmake .. _DCMAKE_BUILD_TYPE=Release // 设置Build Type为Release
make -j4 // 用4个进程并行的构建
make install // 让本地的构建系统执行安装
cd ..
[ Release 和 Debug 版本的区别 ]:
Debug 是所谓的调试版本,也就是程序中带有调试信息,即你可以打断点查内存。g++ -g 就是生成具有调试信息的文件。同时 Debug版本编译器是不会使用优化的。也就是你的程序就是他原本的样子
Release是发行版本,是交付给用户的版本,编译器会对Release版本进行各种优化,以至于代码底层执行顺序已经被改变。而C++的重要课题就是“如何让编译器更好的为你的程序提供优化”,毕竟不开优化就不同用C++
cmake -B build -_DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel 4
cmake --build build --target install
CMake程序构建的步骤:
-
cmake -B build, 配置阶段(configure):这个阶段检测环境并生成构建规则 也就是makefile。使用VS,它会生成sln vcxproj的文件,这就是相当于Win下的makefile,你如果不想使用VS打开,你可以找到Win下的msbuild(记得添加msbuild到环境变量.\bin)
-
cmake --build build, 构建阶段(build):此时编译器通过-D来设hi在缓存变量。同时-D的配置依旧会被保留:
cmake -B build -DCMAKE_INSTALL_PREFIX= …/xxx/xxx
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake -B build 即便 你没有对 -DCMAKE_INSTALL_PREFIX,-DCMAKE_BUILD_TYPE这两个缓存变量进行配置,它也会默认之前的选择
还有一种相对常用的参数 -G : cmake -B build -G Ninja
实际上 -G 的选项很多,Linux平台下默认生成的是 Unix Makefiles。
[ Ninja ]: Ninja 是Google的一名程序员推出的注重速度的构建工具,一般在Unix/Linux上的程序通过make/makefile来构建编译,而Ninja通过将编译任务并行组织,大大提高了构建速度。Ninja > Makefile > msbuild
添加源文件
方法一:
add_executable(main main.cpp) // 第一个参数是名字,后续的参数是文件列表
方法二:
add_executable(main)
target_sources(main PUBLIC test.cpp) // 后续往main中添加
方法三:
add_executable(main)
set(sources main.cpp other.cpp other.h) // 创建一个变量, .h不加也行 最好加上
target_sources(main PUBLIC ${sources}) // 访问一个变量
方法二和三,如果文件太对一个一个打就很麻烦
方法四:
add_executable(main)
file(GLOB source CONFIGURE_DEPENDS *.cpp *.h) // 让source包括所有.cpp .h
target_sources(main PUBLIC ${sources})
如果没有CONFIGURE_DEPENDS 则添加或删除新文件cmake不会更新
如果代码在子文件里面则需要把子文件夹都写进去
add_executable(main)
file(GLOB source CONFIGURE_DEPENDS *.cpp *.h subfile/*.cpp subfile/*.h)
target_sources(main PUBLIC ${sources})
还是老问题一个一个手打很烦
可以把GLOB换成GLOB_RECURSE,这样会自动包含所有的.cpp .h文件, 但是这个GLOB_RECURSE 会把build下的临时文件也加入进来
[ 注 ]: cmake为了测试编译器会生成临时文件
此时 建议把源码都放在 src 文件夹中,意思是src和build是平级的文件夹
方法五:
add_executable(main)
aux_source_directory(. source)
aux_source_directory(subfile source)
target_sources(main PUBLIC ${sources})
aux_source_directory(. source) 中的 . 表示:当前目录
项目配置变量
-DCMAKE_BUILD_TYPE 除了能选择 Debug还是Release以外还可以有其他选择, 默认Debug
- Debug : -O0 -g
- Release : -O3 -DNDEBUG
- MinSizeRel :最小体积发布,按照最小体积进行优化,所以体积比Release更小 : -Os DNDEBUG
- RelWithDebInfo : 带有调式信息的Release,生成的体积>Release。方便后续返回 :-O2 -g -DNDEBUG
若定义了NDEBUG会使得assert被除去
如果希望默认值为 Release
if(NOT CMAKE_BUILD_TYPE) // 若CMAKE_BUILD_TYPE未定义
set(CMAKE_BUILD_TYPE Release) // 则CMAKE_BUILD_TYPE定义为 Release
endif()
project:初始化项目信息
project:会初始化项目信息,并且把当前CMakeList.txt所在位置作为根目录
简单来说 project(xxxx) 就是给项目起名字
PROJECT_NAME:当前项目名
CMAKE_PROJECT_NAME:根项目名
PROJECT_SOURCE_DIR:当前项目的源码位置 -存main.cpp的地方
PROJECT_BINARY_DIR:当前项目的输出位置 -存main.exe的地方
CMAKE_CURRENT_SOURCE_DIR:表示当前源码目录的位置 -存main.cpp的地方
CMAKE_CURRENT_BINARY_DIR:表示输出目录的位置 -存main.exe的地方
PROJECT_IS_TOP_LEVEL:BOOL类型,表示当前目录是否是项目的最顶层 => 可以用来检测是否作为子模块
PROJECT_SOURCE_DIR,PROJECT_BINARY_DIR 和 CMAKE_CURRENT_SOURCE_DIR,CMAKE_CURRENT_BINARY_DIR的区别在于:当作为子模块时,PROJUECT_ 是表示最近一次调用projectde的CMakeLists.txt的位置,而CMAKE_ 则表示最外层的CMakeLists.txt的目录。如果使用CMAKE_CURRENT_SOURCE_DIR 会让你的程序无法作为子模块使用
LANGUAGES 字段
project(testcmakeproject LANGUAGES C CXX)
project(testcmakeproject LANGUAGES NONE)
enable_language(CXX)
设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) // 当编译器不支持C++17时报错
set(CMAKE_CXX_EXTENSIONS OFF) // 不会启用GCC的特性,保证可移植性
project(testcmakeproject LANGUAGES C CXX)
虽然可以手动手动输入命令来启用C++17 : -std=c++17 但是这个编译器flag只对g++有效,对于MSVC则不支持
VERSION 字段
VERSION字段 表示版本号:project(name VERSION x.y.z) ,其中 x 是主版本号, y 是次版本号, z是补丁版本号
- PROJECT_VERSION_MAJOR:获取主版本号
- PROJECT_VERSION_MINOR:获取次版本号
- PROJECT_VERSION_PATCH:获取补丁版本号
CMake的 ${ } 可以嵌套
projectname_VERSION = ${${PROJECT_NAME}_VERSION}
projectname_SOURCE_DIR = ${${PROJECT_NAME}_SOURCE_DIR}
projectname_BINARY_DIR = ${${PROJECT_NAME}_BINARY_DIR}
cmake_minimum_required 指定最低CMake版本
cmake_minimum_required(VERSION 3.15)
CMAKE_VERSION: 查询cmake版本
CMAKE_MINIMUM_REQUIRED_VERSION: 查询cmake最低版本
一个标准的CMakeLists.txt模板
cmake_minimum_required(VERSION 3.15)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project(projectname LANGUAGE C CXX)
if(PROJECT_BINARY_DIR STREQUAL PROJECT_SOURCE_DIR)
message(WARNING "The binary directory of CMake cannot be the same as source directory")
endif()
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
if(WIN32)
add_definitions(-DNOMINMAX -D_USE_MATH_DEFINES)
endif()
if(NOT MSVC)
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
message(STATUS "Found CCache: ${CCACHE_PROGRAM}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_PROGRAM})
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_PROGRAM})
endif()
endif()
链接库
add_executable(main main.cpp libtest.cpp) // 直接链接
add_library(libtest STATIC libtest.cpp) // 指定为静态链接库
add_library(libtest SHARED libtest.cpp) // 指定为动态库或者叫共享库
add_library(libtest OBJECT libtest.cpp) // 指定为对象链接库
add_executable(main main.cpp)
target_link_libraries(main PUBLIC libtest) // 链接静态链接库
所谓对象库:不生成.a文件 只是由CMake记住该库生成了哪些对象文件。同时能绕开编译器,从而实现跨平台。但是对象库仅仅作为代码组织的方式,而实际生成的可执行文件只有一个,减轻部署的困难
例如:
//CMakeLists
add_library(libtest STATIC libtest.cpp)
// libtest.cpp:
#include <cstdio>
static int a = printf("静态初始化!");
// main.cpp:
#include <cstdio>
int main(){
printf("main");
}
以上代码libtest.cpp会先于main执行,此时gcc看到没有文件引用a 于是gcc就会删除libtest.o 但是我们需要这个静态初始化。所以此时使用对象库就能避开gcc的这个特性。
默认情况下CMake会根据 BUILD_SHARED_LIBS变量来确定是共享库还是静态库,ON是SHARED,OFF是STATIC。BUILD_SHARED_LIBS默认是OFF
动态库无法链接静态库
add_library(libstatic STATIC libstatic.cpp)
add_library(libshared SHARED libshared.cpp)
target_link_libraries(libshared PUBLIC libstatic)
因为CMake以为你的静态库要插入程序中,但是你插入共享库中,共享库是运行时链接,静态库插入的时候共享库还没有分配内存。静态库不想修改地址,而动态需要修改地址。更具体地来说,因为共享库中由fpic这个字段用来表示相对地址,如果你要把动态库链接静态库,要么将静态库改变为对象库,要么给静态库也生成表示相对地址的代码
- set(CMAKE_POSITION_INDEPENDENT_CODE ON) 全局修改
- set_property(TARGET libstatic PROPERTY POSITION_INDEPENDENT_CODE ON) 只针对 libstatic 这一个库
对象属性
set_property(TARGET name PROJECT xxx)
set_property(TARGET mian PROPECT CXX_STANDARD 17) # 采用C++17编译(默认11)
set_property(TARGET mian PROPECT CXX_STANDARD_REQUIRED ON) # 若编译器不支持C++17 则报错(默认OFF)
set_property(TARGET mian PROPECT WIN32_EXECUTABLE ON) # 在Windows系统中,运行时不启动控制台窗口,只有GUI界面(默认OFF)
set_property(TARGET mian PROPECT LINK_WHAT_YOU_USE ON) # 告诉编译器不要自动剔除没有引用符号的链接库(默认OFF)
set_property(TARGET mian PROPECT LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib) # 设置动态链接库输出路径(默认${CMAKE_SOURCE_DIR})
set_property(TARGET mian PROPECT ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib) # 设置静态链接库输出路径(默认${CMAKE_SOURCE_DIR})
set_property(TARGET mian PROPECT RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib) # 设置可执行文件输出路径(默认${CMAKE_SOURCE_DIR})
set_target_properties(TARGET name PROJECTIES xxx xxx xxx)
set_target_properties(TARGET name PROJECTIES
PROPECT CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
WIN32_EXECUTABLE ON
LINK_WHAT_YOU_USE ON
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib
)
全局设置
set_property(CXX_STANDARD 17) # 采用C++17编译(默认11)
set_property(CXX_STANDARD_REQUIRED ON) # 若编译器不支持C++17 则报错(默认OFF)
set_property(WIN32_EXECUTABLE ON) # 在Windows系统中,运行时不启动控制台窗口,只有GUI界面(默认OFF)
set_property(LINK_WHAT_YOU_USE ON) # 告诉编译器不要自动剔除没有引用符号的链接库(默认OFF)
set_property(LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib) # 设置动态链接库输出路径(默认${CMAKE_SOURCE_DIR})
set_property(ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib) # 设置静态链接库输出路径(默认${CMAKE_SOURCE_DIR})
set_property(RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib) # 设置可执行文件输出路径(默认${CMAKE_SOURCE_DIR})
add_executable(main main.cpp)
Windows下的动态链接库
// .cpp
#include <cstdio>
#ifdef _MSC_VER
__declspec(dllexport)
#endif
void hello(){ printf("hello"); }
// .h
#pragma once
#ifdef _MSC_VER
__declspec(dllimport)
#endif
void hello();
在Windows下,如果exe和dll不在同一目录下且不在PATH中,则会出错,因为Win不会递归的找,只会查找exe的当前路径和PATH
在CMake中,我们可以设置
set_property(TARGET libtest PROPERTY RUN_OUT_DIRECTORY ${PROJECT_DINARY_DIR})
set_property(TARGET libtest PROPERTY ARCHIVE_OUT_DIRECTORY ${PROJECT_DINARY_DIR})
set_property(TARGET libtest PROPERTY LIBRARY_OUT_DIRECTORY ${PROJECT_DINARY_DIR})
set_property(TARGET libtest PROPERTY RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_DINARY_DIR})
set_property(TARGET libtest PROPERTY ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${PROJECT_DINARY_DIR})
set_property(TARGET libtest PROPERTY LIBRARY_OUTPUT_DIRECTORY_DEBUG ${PROJECT_DINARY_DIR})
set_property(TARGET libtest PROPERTY RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_DINARY_DIR})
set_property(TARGET libtest PROPERTY ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${PROJECT_DINARY_DIR})
set_property(TARGET libtest PROPERTY LIBRARY_OUTPUT_DIRECTORY_DEBUG ${PROJECT_DINARY_DIR})
链接第三方库
需要注意一个问题:在Linux下有一个专门存放库文件的路径。但是在Windows下并没有。所以在Windows下链接可能是需要进一步指定地址。
在CMake中统一使用‘ / ’即便是Windows下也是‘ / ’,CMake会自动转换。
target_link_libraries(main PUBLIC lib_path) # lib_path 即链接库的路径
以上方式不能跨平台,更通用使用find_package,以TBB库为例:
find_package(TBB REQUIRED) # 查找./user/lib/cmake/TBB/TBBConfig.cmake这个配置文件,创建TBB::tbb这伪对象
target_link_libraries(main PUBLIC TBB::tbb) # PUBLIC属性会给TBB::tbb指向的libtbb.so链接它对象的带上一些flag
# 具体来说,TBB::tbb的头文件,包括tbb本身使用的别的库,也会自动链接上不用手动加
实际上,find_package()还可以增加CONFIG选项。添加CONFIG选项后 会优先查找 TBBConfig.cmake,而不是Find TBB.cmake 。
这个Findxxx.cmake一般是项目作者写的,并把它放进cmake/ 目录里面并添加到CMAKE_MOUDEL_PATH。
而xxxConfig.cmake一般是操作系统自带的
有的老库是没有xxxConfig.cmake,只有Findxxx.cmake
find_package( ) 会生成 包名::组件名 的伪对象,可以在find_package( )中使用 COMPOENENTS选项,后面跟随一个列表表示需要调用的组件
find_package(TBB COMPOENENTS tbb tbbmalloc tbbmalloc_proxy REQUIRED)
target_link_libraries(main PUBLIC TBB::tbb TBB::tbbmalloc TBB::tbbmalloc_proxy)
由于Windows没有固定的路径去安装 lib (Linux有user/lib) 所以可能出现 系统找不到xxxConfig.cmake文件。此时,你找到你的下载路径。在这个安装的路径中找到xxxConfig.cmake这个文件
- 自己设置CMAKE_MOUDEL_PATH: set(CMAKE_MOUDEL_PATH ${CMAKE_MOUDEL_PATH} your_path) 注意在Windows下也要用’ / ’
- 法1的一种全局的方法,所有的库都会去这个路径下找xxxConfig.cmake。我们针对不同的库专门设置一个路径:set(XXX_DIR your_path)。
- 也可以通过命令行 -Dxx_DIR=”your path”来指定 : cmake -B build -Dxx_DIR=”your path”
- 通过设置环境变量 xxx_DIR : export xxx_DIR=”your path” 这样对Windows用户比较困难
如果不指定REQUIRED,找不到也不会报错,仅仅是将TBB_FOUND 设置为FALSE:
find_package(TBB)
if(TBB_FOUND)
message(STATUS "TBB found at: ${TBB_DIR}")
target_link_libraries(main PUBLIC TBB::tbb)
target_compile_definitions(main PUBLIC WITH_TBB)
else()
message(WARNING "TBB not found!")
endif()
在.cpp中可以判断WITH_TBB宏,找不到就....(自己处理)
#include <cstdio>
#ifdef WITH_TBB
#include <tbb/parallel_for.h>
#endif
int main(){
#ifdef WITH_TBB
tbb::parallel_for(0, 4, [&](int i){
#else
for(int i=0; i<4; i++){
#endif
printf("!!");
#ifdef WITH_TBB
})
#else
}
#endif
return 0;
}
或者,我们可以通过检测受否有TBB::tbb这个伪对象来实现同样的效果
find_package(TBB)
if(TARGET TBB::tbb) # 这里面可以使用各种布尔运算: NOT TARGET TBB::tbb AND TARGET Eigen3::eigen
message(STATUS "TBB found at: ${TBB_DIR}")
target_link_libraries(main PUBLIC TBB::tbb)
target_compile_definitions(main PUBLIC WITH_TBB)
else()
message(WARNING "TBB not found!")
endif()
输出与变量
cmake -B build 打印字符串
message(”string”) : 在控制台直接输出,用于调试信息
message(STATUS ”string”) : 在控制台输出时增加 -- 前缀,表示状态信息
message(WARNING ”string”) : 在控制台输出时变为黄色字体,表示警告信息
message(AUTHOR_WARNING ”string”) : 在控制台输出时变为黄色字体,表示警告信息,但仅仅给项目作者才能看到,可以通过-Wno-dev关闭
message(FATAL_ERROR ”string”) : 表示错误信息(红字),会终止CMake运行
message(SEND_ERROR ”string”) : 表示错误信息(红字),不会终止CMake运行
针对”string”也可以单独设置一个变量:
set(msg “msg”)
message(”string : ${msg}”)
[ 补充 ] set(msg “a;b”) 等价于 set(msg a b) 没有引号和分号
变量与缓存
cmake会将检测编译器等信息放入一个缓存文件中,第二次在此检测时cmake就会去查找这个缓存文件,以便加速。但这里面有一个问题:当你外部的环境变化了,但cmake仍然还在使用缓存文件。 “cmake 99%的问题都可以通过删除 build 来解决”(rm -rf build)。
如果删除 build 那么会将很多中间结果删除,导致我们编译变慢。我们其实只需要清除缓存, 找CMakeCache.txt删除即可
设置缓存变量
set(var “hello” CACHE STRING “This is the docsrting”) # CACHE是选项, STRING是类型
message(”var is : ${var}”)
更新缓存变量
-
cmake -B build -Dxxxx=xxxx
-
ccmake -B build 在可视化界面中更新
Windows下使用 cmake-gui -B build
-
直接用编辑器打开build/CMakeCache.txt修改保存
案例:添加一个BOOL值,控制某种特性的开启与关闭
add_executable(main main.cpp)
set(WITH_TBB ON CACHE BOOL "set to ON to enable TBB, OFF to disable TBB")
if(WITH_TBB)
target_compile_definitions(main PUBLIC WITH_TBB)
fing_package(TBB REQUIRED)
target_link_libraries(main PUBLIC TBB::tbb)
endif()
如果是设置BOOL值,则可以用option(变量名 “描述” 变量值)
option(WITH_TBB "set to ON to enable TBB, OFF to disable TBB" ON)
修改需要用上述方式修改cmake -B build -DWITH_TBB=ON
跨平台与编译器
在CMake中给.cpp定义宏
#include <cstdio>
int main(){
#ifdef MY_MACRO
#else
#endif
}
target_compile_definitions(main PUBLIC MY_MACRO=1)
根据不同系统修改宏
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
target_compile_definitions(main PUBLIC MY_MACRO="Bill Gates")
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
target_compile_definitions(main PUBLIC MY_MACRO="Linus Torvalds")
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin") # APPLE 叫这个
target_compile_definitions(main PUBLIC MY_MACRO="Steve Jobs")
endif()
简写:
if(WIN32) # WIN32 WIN64都可以
target_compile_definitions(main PUBLIC MY_MACRO="Bill Gates")
elseif(UNIX AND NOT APPLE) # 类Unix系统,所以要出去APPLE
target_compile_definitions(main PUBLIC MY_MACRO="Linus Torvalds")
elseif(APPLE)
target_compile_definitions(main PUBLIC MY_MACRO="Steve Jobs")
endif()
使用生成器表达式,简化:
target_compile_definitions(main PUBLIC
$<$<PLATFROM_ID:Windows>:MY_MACRO="Bill Gates"> # 只有当PLATFROM_ID=Windows时生效
$<$<PLATFROM_ID:Linux>:MY_MACRO="Linus Torvalds">
$<$<PLATFROM_ID:Darwin>:MY_MACRO="Steve Jobs">
)
target_compile_definitions(main PUBLIC
$<$<PLATFROM_ID:Windows>:MY_MACRO="Bill Gates"> # 只有当PLATFROM_ID=Windows时生效
$<$<PLATFROM_ID:Linux,Darwin>:MY_MACRO="Unix-like">
)
类似的,你也可以用CMAKE_CXX_COMPILER_ID来检测编译器
if(CMAKE_CXX_COMPILER_ID MATCHES "GUN")
target_compile_definitions(main PUBLIC MY_MACRO="gcc")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "NVIDIA")
target_compile_definitions(main PUBLIC MY_MACRO="nvcc")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_definitions(main PUBLIC MY_MACRO="clang")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
target_compile_definitions(main PUBLIC MY_MACRO="msvc")
endif()
可以在官网(cmake-generator-expressions(7) — CMake 3.23.2 Documentation)上查找更多
在命令行设置编译器
cmake -B build DCMAKE_CXX_COMPILER=”your_path”
分支与判断
我们知道想访问一个变量 需要使用,但是则不需要。可以直接写这是因为的出现比{}还要早,所以你不用,会自动帮你替换。你如果在里加了{}会出现一个问题:
set(hello World)
set(var hello)
if(${var} MATCHES "hello")
message("YES")
else()
message("NO")
endif()
这里我们希望它能显示YES 但是它却显示NO。这是因为var通过展开之后,变成,但是又会用的语法再次替换一次变成,这就和我们的希望不一样。那我不希望它替换,但是想用{},那你可以加”” : if(”${var}” MATCHES "hello")
变量与作用域
变量传播规则:父传子,子不传父。父模块的变量能给子模块,但是子模块定义的变量不能给父模块去用
同时 如果父子模块中有变量重名,父模块也不会感觉到。如果想让父模块察觉,则需要在子模块的变量中使用PARENT_SCOPE选项,让其往上传播。set(var ON PARENT_SCOPE)
include中的 XXX.cmake没有独立作用域
add_subdirectory的CMakeLists.txt有独立作用域
macro没有独立作用域
function有独立作用域
变量的访问
$ENV{PATH} 获取PATH的值
$CACHE{xx} 获取缓存中的变量
环境变量和缓存变量是父子模块共有的,所有没有作用域的概念
if(DEFINED var) 判断var是否有被定义 [ 注 ] : 空字符串不等于没有定义
if(DEFINED var)
message("YES")
else()
message("NO")
endif()
[ 注 ] : 在if和set里面的使用ENV不要加$ Ex: set(ENV{var} “hello”) , if(DEFINED ENV{var})
- if 之前说过是历史原因
- set 是因为 本来就是要设置变量var,你再使用$去展开也没用
CACHE也是同理
CCache 编译加速
gcc -c main.cpp -o main.cpp ⇒ ccache -c main.cpp -o main.cpp 即可
在CMake启用ccache加速:
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM ccache)
message(STATUS "Found CCache: ${CCACHE_PROGRAM}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_PROGRAM})
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_PROGRAM})
endif()
伪目标的添加
run伪目标
add_executable(main main.cpp)
add_custom_target(run COMMAND $<TARGET_FILE:main>) # 会让run自动依赖于main
当你生成好伪目标时,可以在命令行运行 cmake --build build --target run 来启动main.exe。这样就会用根据平台手动的写 build/main or build\main.exe
configure伪目标
add_custom_target(run COMMAND $<TARGET_FILE:main>)
if(CMAKE_EDIT_COMMAND)
add_custom_target(configure COMMAND ${CMAKE_EDIT_COMMAND} -B ${CMAKE_BINARY_DIR})
cmake --build build --target configure 来启动ccmake修改缓存,用于可视化地修改缓存变量
在Linux上相当于 : ccmake -B build
在Windows上相当于 : cmake-gui -B build