贡献者: addis
参考官方教程。
ccmake
可以有 TUI。否则就用 cmake
CMakeLists.txt
的路径下,cmake .
生成 Makefile
,然后 make -j
多线程编译。make VERBOSE=1 -j
输出编译命令。cmake 会在当前路径生成 Makefile
和一些临时文件和文件夹,清理麻烦,所以还是建议输出到子文件夹。
make
命令默认 VERBOSE=1
,可以在之前用 cmake -DCMAKE_VERBOSE_MAKEFILE=ON
CMakeLists.txt
中,注释如 #[[一些注释]]
或者 #一些注释
直到行末
cmake CMakeList的路径
即可。
cmake -G 生成器 CMakeList的路径
可以指定生成器,生成器
如 Unix Makefiles
,MinGW Makefiles
,Ninja
ninja
编译(默认使用当前路径的 build.ninja
文件),ninja install
安装
cmake -L CMakeList的路径
列出编译选项,-LH
列出选项以及帮助说明。-LAH
列出所有选项(会多出很多 CMAKE_
开头的,不是作者提供的选项),用 cmake -D 选项1=值1 -D 选项2=值2 CMakeList的路径
来设置选项(-D
后面可以没有空格)。
cmake -S CMakeList的路径 -B 生成Makefile的路径
,然后再到 Makefile 的路径 make -j12
即可。cmake 会自动生成 生成Makefile的路径
。如果要清理 cmake 生成的文件以及所有编译出来的文件,直接把第二个路径清空即可。
make
,所以也可以用 cmake --build 生成Makefile的路径 [--target 可执行文件] -- -j 12
其中 --
后面的选项会传给 make
或者别的具体用于编译的程序。
命令(arg1 arg2 ...)
其中 命令
不区分大小写,arg
用空格隔开也可以换行,如果 arg
本身有空格,用双引号即可
cmake
中的变量本质上都是字符串。即使是 list,也不过是特殊的字符串 str1;str2;str3
。所谓的 bool
类型不过是 ON
和 OFF
字符串。数字也一样,并不是真正的数字。
${变量}
。注意不能用 $变量
。
set(my_var abc123)
。
message(STATUS "...")
可以输出到 stdout。如果 message(STATUS ${变量})
中的变量是 list,那么输出中 ;
会消失。应该用 message(STATUS "${变量}")
,输出的格式为 str1;str2;str3
。message(STATUS ${变量1} ${变量2} ...)
相当于把若干变量合成 list。
message(FATAL_ERROR "字符串")
会输出错误信息并立即退出。
[=[字符串]=]
等号可以有一个也可以有多个。
set
可以对变量赋值,例如 set(变量 123)
。
set
也可以把许多变量变成一个 list,如 set(Foo aaa bbb ccc)
等效于 set(Foo "aaa" "bbb" "ccc")
等效于 set(Foo "aaa;bbb;ccc")
等效于 set(Foo aaa;bbb;ccc)
。
命令()
的参数相当于多个 arg
。例如 命令(${Foo})
相当于 命令(aaa bbb ccc)
。
命令("${Foo}")
相当于 命令("aaa;bbb;ccc")
也就是说命令的第一个参数是一个 list。
$EVN{变量}
。例子:message(STATUS $ENV{PWD})
。注意 cmake 看到的环境变量未必和 bash 中的相同,可以用 cmake -E environment
检查。
separate_arguments(变量)
可以把一个含有若干空格的字符串拆分成 list(即把空格替换为分号)。
cmake_minimum_required(VERSION x.x)
要求 cmake 最小版本(必须有)
project(project_name)
指定项目名称(必须有)
set(CMAKE_CXX_COMPILER "/usr/bin/g++")
设置 compiler
set(CMAKE_CXX_FLAGS "-选项 -选项")
设置 flag,包括 C++ 标准。注意这不是一个 list。如果要在后面添加选项,就用 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -选项1 -选项2")
。
set(CMAKE_CXX_STANDARD 11)
设置 C++ 标准
set(CMAKE_CXX_STANDARD_REQUIRED ON)
(默认关)如果编译器不支持该标准就报错
set(CMAKE_CXX_EXTENSIONS OFF)
(默认开)不使用 C++ 标准拓展,例如 -std=gnu11
。后者并不完全兼容标准,一些情况会出错。
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address")
或者下面的 list(APPEND ...)
CMAKE_CXX_FLAGS_RELEASE
可以设置 release 模式下的 flag。
set(变量 str1 [str2] ...)
对变量赋值(若有多个值就生成 list)
list(APPEND 变量 str1 [str2] ...)
在变量(list)后面添加元素
list(REMOVE_ITEM 变量 str1 ...)
从 变量
的 list 中移除(如果不存在也不报错)
set(变量 "${变量} str1 str2")
等效于 list(APPEND ...)
。
file(GLOB 变量 "folder/*.cpp" "fname2.cpp")
可以列出所有 "folder/*.cpp"
文件,以及 fname2.cpp
(如果存在)赋值给 变量
。注意文件名包括绝对路径。match 的结果也会存到 CMAKE_MATCH_编号
,编号从 1 开始。
get_filename_component(变量 "文件名含路径" NAME或DIRECTORY)
会从 "文件名含路径"
中提取文件名或路径,赋值给 变量
。
file(READ 文件名 变量)
读取文件内容到变量。
string(REGEX MATCH "...(...)..." 变量 字符串变量
。在 字符串变量
中匹配正则表达式,并把 ()
中的匹配结果赋值给 变量
。
string(REPLACE "老词" "新词" 变量 "字符串")
做字符串替换。
add_definitions(-D FOO -D BAR)
给编译器添加宏定义
add_compile_options(-Wall -fopenmp)
给编译器添加选项(注意不能用 -D
定义宏),该命令不受 CMAKE_BUILD_TYPE
的影响。
CMAKE_CXX_COMPILER_ID
加不同 option
# Define flags for different compilers
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# GCC and Clang specific flags
add_compile_options(
-Wall # Enable all warnings
# Disable warning for constructor initialization order
-Wno-reorder
# Disable warning for misleading indentation
-Wno-misleading-indentation
-O3 # High-level optimization
-fopenmp # Enable OpenMP
-fmax-errors=1 # Limit the number of errors
-D NDEBUG # Disable debugging macros
)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
# MSVC specific flags
add_compile_options(
/W4 # Enable high warning level
/O2 # High-level optimization (equivalent to -O3 in g++)
/openmp # Enable OpenMP
/DNDEBUG # Disable debugging macros
)
endif()
CMAKE_SYSTEM_NAME
判断当前的系统
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
....
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
....
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
....
endif()
MINGW
,CYGWIN
, MSYS
, IOS
, 等(文档)
add_executable(目标名 源码1 源码2)
前者是可执行文件名,后者是所有需要 link 的 c/C++
文件名,不需要头文件。cmake 会自动分析代码得到哪个 cpp 调用哪个头文件,如果头文件改变了,只有调用它的 cpp 会重新编译。注意 目标名
可以是任意名字不一定是可执行文件名。
set_target_properties(exe_targ_name PROPERTIES OUTPUT_NAME 可执行文件的名字)
add_executable
指定的源码不需要包含头文件,但如果头文件需要由其他 rule 来生成,那就需要包含。所以最保险还是把所有头文件都包含。
configure_file(file_in file_out)
将文件中 @变量@
替换为变量 变量
的字符串。
include_directories(dir1 [dir2] ...)
添加头文件的搜索路径
add_library(exe_name source_name)
单独编译一个 library(在 library 路径的 CMakeLists.txt
中使用这个命令而不是 add_executable
命令)
link_directories(路径)
相当于编译器的 -L
选项,添加静态或动态 library 的搜索路径。
set(CMAKE_INSTALL_RPATH 路径)
相当于设置 rpath
(到底是 RUNPATH
还是 RPATH
?)
add_subdirectory(dir1 [dir2] ...)
执行子路径中的 CMakeLists.txt
。往往用于编译某个 library。
target_link_libraries(exe_name lib1 [lib2] ...)
link 阶段链接 library,相当于 -l lib1 -l lib2
option(opt_name description default)
定义一个 option 开关(会在 cmake 的 GUI 中显示开关)以及默认值。opt_name
可以在 CMakeLists.txt
中的 if
语句中使用例如 if(opt_name) ... endif(opt_name)
。default
可以是 ON
或 OFF
。
option
的默认值后,需要清空 cmake 的临时文件才可以生效。
set(变量 "默认值" CACHE BOOL或STRING或FILEPATH或PATH或INTEGER "描述")
。如果之前 变量
已经被赋值(例如命令行 cmake -D 变量=...
),则值不会被覆盖。这种变量叫做 cached variable。
cmake -LH .
的帮助中看到说明。
*.in
中,当 opt_name
为 ON
时,使用 configure_file
命令后 #cmakedefine opt_name
会被替换为 #define opt_name
。如果想要给宏定义一个值,用 #cmakedefine USE_FEATURE_B @USE_FEATURE_B@
target_precompile_headers(可执行文件 PRIVATE foo.h bar.h)
可以编译头文件,注意貌似不会自动包括 .h 包括的文件。如果头文件(或者依赖的文件)改了,那么将会自动重新编译头文件。
execute_process(COMMAND 命令 参数1 参数2 ...)
可以执行 shell 命令,其中命令和参数中都可以用 ${变量}
。其他功能(包括命令返回的内容,文件输入输出),详见文档。注意这个命令是在运行 cmake
时执行而不是运行 make
时。后者可以用 add_custom_target
,再用 add_dependencies(目标, target)
把 target 作为目标的依赖。
include(文件或者模块)
,详见文档。模块可以是 *.cmake
的文件,也可以是 cmake 的内建模块如 CheckCXXCompilerFlag
macro(宏名 [参数1 参数2]) 一些命令 endmacro()
定义宏,调用如 ei_add_cxx_compiler_flag(参数1 参数2)
check_cxx_source_compiles(代码变量 输出变量)
(文档) 可以判断某个源码是否可以编译成功。在调用前,可以用 CMAKE_REQUIRED_LIBRARIES
变量设置所需的库,CMAKE_REQUIRED_FLAGS
指定额外的编译器选项,等。
check_cxx_compiler_flag(编译器选项 输出变量)
可以检查某个编译器选项是否可用。输出变量
是 True 或 False。
foreach(变量 IN 列表) ... endforeach()
或者 foreach(变量 IN 变量1 变量2 ...) ... endforeach()
其中 IN
在 cmake 2.4 以后可以省略。文档。
find_program(变量 程序名 [路径1 路径2 ...])
可以在指定路径或者 $PATH
中寻找某个可执行文件,并把绝对路径写入 变量
。如果没有找到,会赋空值。
add_custom_command(OUTPUT output_files COMMAND 命令... [ARGS 参数...] [WORKING_DIRECTORY dir] [DEPENDS 依赖文件] [COMMENT 注释])
相当于 Makefile 中的一个 rule,指定依赖关系,以及使用的命令。其中 命令
可以包含完整的命令而不需要额外用 参数
。当 OUTPUT
的文件缺失时或者 依赖文件
更新时,就会运行命令。可以把 命令
和所有的参数写成一整个字符串,但是 cmake 可能会自动把空格 escape 比较恶心。可以试试用 string(REPLACE " " ";" 命令变量 ${命令变量})
然后 COMMAND ${命令变量}
把命令拆成一个 list。注释
会在该命令被运行时输出到 stdout。
命令
可以使用 ${CMAKE_COMMAND} -E 一些命令
。其中 ${CMAKE_COMMAND}
就是当前使用的 cmake
程序,一些命令
如 echo 字符串
,copy 文件 文件
,make_directory 目录
,remove_directory 目录
,remove 文件
等。
add_custom_target(目标名 [COMMAND 命令...] [ARGS arguments...] [DEPENDS depends...] [BYPRODUCTS byproducts...] [WORKING_DIRECTORY dir] [SOURCES sources...] [COMMENT 注释])
会在生成的 Makefile 中添加一个 target。
add_dependencies(目标名 依赖1 [依赖2 ...])
可以给 target(例如可执行文件)添加依赖。
check_symbol_exists("符号名字" "路径/头文件1;头文件2" 变量)
可以检查 符号名字
是否有定义,包括函数名,变量名,以及宏。
check_symbol_exists
的实现使用了 try_compile()
和 try_run()
函数,试图编译和运行一个测试程序,从运行结果来判断是否存在符号。
find_library()
的默认搜索路径如 /usr/lib
,/usr/local/lib
,/usr/[local/]share/cmake/Modules/
,/usr/lib/x86_64-linux-gnu/cmake/
以及环境变量 LD_LIBRARY_PATH
中的路径。
find_library(变量 NAMES 库名1 库名2 PATHS /usr/lib /usr/local/lib HINTS $ENV{LIBRARY_PATH} $ENV{LD_LIBRARY_PATH})
可以寻找 lib库名.so或a[.版本号]
,然后把路径写入 变量
(不做其他事情)。若要指定版本可以用例如 gfortran.3
,也可以指定 gfortran.so或a
。注意指定目录的子目录不会被搜索。
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a或.so或.lib")
可以让 find_library()
只寻找某种类型的 lib。也可以用 ".a;.so"
来指定优先级。
CMAKE_LIBRARY_PATH
变量可以指定 find_library
的更多默认搜索路径,且会优先搜索。
自定义函数如
# 计算一个数的平方
function(compute_square number result_var)
math(EXPR result "${number} * ${number}")
set(${result_var} ${result} PARENT_SCOPE)
endfunction()
# 函数调用
set(my_number 5)
compute_square(${my_number} my_result)
message(STATUS "The square of ${my_number} is ${my_result}")
find_package
是一个更高级的命令,找到某个 cmake 包的设置文件,这个文件负责把一切设置好,包括 include,如何 link 等,以及 import 一些变量。而不是像 find_library
那样简单地找到一个包所在的路径。
find_package(包名 [版本] [REQUIRED] [COMPONENTS 组件1 组件2...])
REQUIRED
:如果找不到就报错终止
包名
会在默认路径或者 CMAKE_MODULE_PATH
的路径里面搜索名为 Find包名.cmake
(Module Mode,该文件通常由 cmake 提供或用户自己写)或者 包名Config.cmake
(Config Mode,该文件通常由 package 作者提供,更高级和智能)的文件。该文件通常会设置一些变量,可以在 find_package
结束后访问。
find_package(Boost 1.71 REQUIRED COMPONENTS filesystem system)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(myapp main.cpp)
target_link_libraries(myapp ${Boost_LIBRARIES})
endif()
find_package(Boost 1.71 REQUIRED COMPONENTS filesystem)
if(Boost_FOUND)
add_executable(my_app main.cpp)
target_link_libraries(my_app Boost::filesystem)
endif()
Boost::filesystem
就是 find_package
导入的一个 imported target,::
只是名字的一部分并没有特别的 namespace 机制。Boost_FOUND
是导入的变量,显示 Boost
是否被找到.
包名Config.cmake
(Config Mode)
如果要写 Find包名.cmake
,其实只需要使用 set()
设置若干路径变量即可。一般只有库的作者没有提供 config mode 的 cmake 时才需要用户写这个文件。用 apt
安装的包如果提供了该 cmake 一般也会被搜到。
要写 包名Config.cmake
,需要给 add_library
定义要导出的 target,以及用 set_target_properties
指定库文件和头文件的路径,这样用户导入的时候就不需要再设置一次了。
# MyLibraryConfig.cmake
# Define the paths to the library and include directories
set(MyLibrary_LIBRARIES "${CMAKE_CURRENT_LIST_DIR}/lib/file.a")
set(MyLibrary_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/include")
# Create an imported target for MyLibrary
add_library(MyLibrary STATIC IMPORTED)
# Set the properties for the imported target
set_target_properties(MyLibrary PROPERTIES
# set lib files location so that user can import the target
IMPORTED_LOCATION "${MyLibrary_LIBRARIES}"
# set header files location so that user can import the target
INTERFACE_INCLUDE_DIRECTORIES "${MyLibrary_INCLUDE_DIRS}"
)
# Optionally, set the MyLibrary_FOUND variable
# to indicate the library was found
set(MyLibrary_FOUND TRUE)
# Minimum CMake version
cmake_minimum_required(VERSION 3.10)
# Project name
project(MyLibrary)
# Set the version (optional)
set(MYLIBRARY_VERSION_MAJOR 1)
set(MYLIBRARY_VERSION_MINOR 0)
# Define the library target
add_library(MyLibrary SHARED IMPORTED)
# Specify the directories for header files
target_include_directories(MyLibrary
INTERFACE
# Replace with actual path
${CMAKE_CURRENT_SOURCE_DIR}/include
)
# Link the library files (use separate statements
# for different OSes if needed)
set_target_properties(MyLibrary PROPERTIES
# Linux
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/mylibrary.so"
# Windows
IMPORTED_IMPLIB "${CMAKE_CURRENT_SOURCE_DIR}/lib/mylibrary.dll"
)
# Define macros required by the library
target_compile_definitions(MyLibrary
INTERFACE
MYLIBRARY_DEFINE_1
MYLIBRARY_DEFINE_2=VALUE # Example with a value
)
# Export the library target for use in other projects
export(TARGETS MyLibrary FILE MyLibraryConfig.cmake)
PROJECT_NAME
是 project(项目名)
设置的 项目名
PROJECT_SOURCE_DIR
是源文件的根路径,就是传给 cmake
的路径
PROJECT_BINARY_DIR
是 Cmake 的输出路径,临时文件,Makefile 等都在这个路径。这就是运行 cmake
命令时的当前路径
CMAKE_CURRENT_LIST_DIR
当前在处理的 CMakeLists.txt
的路径。
CMAKE_SOURCE_DIR
和 CMAKE_BINARY_DIR
和上面两个有什么区别?
CMAKE_BUILD_TYPE
可以被用户设置为 Release
,Debug
等。例如 cmake -DCMAKE_BUILD_TYPE=Debug
用 debug 模式编译。Debug
模式下,编译器优化会被自动关闭,-g
选项会自动加上,NDEBUG
不会。在两种模式下,CMAKE_CXX_FLAGS_DEBUG
和 CMAKE_CXX_FLAGS_RELEASE
中的选项会分别传给编译器。任何模式下 CMAKE_CXX_FLAGS
的选项都会传给编译器。
WIN32
可以判断是否在 windows 上(包括 32 和 64 位)
MSVC
判断是否用 Visual C++ 编译器
参考这里。
if(1 或 ON 或 YES 或 TRUE 或 Y)
……
elseif(0 或 OFF 或 NO 或 FALSE 或 N 或 IGNORE 或 NOTFOUND)
这里永远不会执行
elseif(条件 AND (条件 OR 条件) OR NOT 条件)
……
elseif(("bar" IN_LIST 变量) OR (file1 IS_NEWER_THAN file2))
……
elseif(变量)
当变量有定义且不是 false constant
if(ENV{变量})
这里永远不会执行
elseif (DEFINED <name>|CACHE{<name>}|ENV{<name>})
若定义了变量
elseif (变量1 STREQUAL 变量2)
比较字符串
elseif (IS_DIRECTORY 某路径)
判断某路径是否存在
else()
……
endif()
*.lib
,只需要在 add_executable()
命令前面插入(必须在之前)abc.lib
的路径 link_directories(path/to/lib)
然后再 add_executable()
之后插入 target_link_libraries(exe_name abs)
即可
if(MSVC)...end(MSVC)
可以专门给 Visual Studio 执行一些命令
ZERO_CHECK
会重新运行/更新 CMakeLists.txt
,ALL_BUILD
会编译所有工程。如果看着不爽的话也可以把这两个 project 删掉。如果直接 run without debug 的话,会提示 ALL_BUILD
不能 run,所以要 run 只能右键某个 project 然后 run(所以还是把两个多余的 project 删掉好些)。
sudo apt install cmake-curses-gui
安装
ccmake
,这个路径可以是源文件路径
ccmake ../
h
帮助,q
退出,c
configure,G
生成 Makefile