CMake Commands

发布时间 2023-05-28 14:48:58作者: zhaowenwei

cmake_minimum_required

cmake_minimum_required(VERSION <min>[...<policy_max>] [FATAL_ERROR])
#限制CMake的版本支持范围

cmake_minimum_required(VERSION 3.16.3) #限制CMake最低版本3.16.3
cmake_minimum_required(VERSION 3.16.3...3.20.0) #限制CMake版本最低3.16.3,只要CMake的版本大于3.16.3就行了,如果CMake高于3.20.0也是可以的 policy_max 限制的是CMake的行为,高版本兼容低版本
cmake_minimum_required(VERSION 3.16.3 "cmake version error") #限制CMake最低版本3.16.3 [FATAL_ERROR] 就是一个自定义提示而已 只有小于3.12的版本支持 新版本CMake忽略掉这个参数(因为确实没啥用)

project

project(<PROJECT-NAME>
        [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
        [DESCRIPTION <project-description-string>]
        [HOMEPAGE_URL <url-string>]
        [LANGUAGES <language-name>...])
#定义项目名字
#<PROJECT-NAME> PROJECT_NAME变量会被设置
PROJECT_NAME
#[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]] 下面变量会被设置
PROJECT_VERSION, <PROJECT-NAME>_VERSION
PROJECT_VERSION_MAJOR, <PROJECT-NAME>_VERSION_MAJOR
PROJECT_VERSION_MINOR, <PROJECT-NAME>_VERSION_MINOR
PROJECT_VERSION_PATCH, <PROJECT-NAME>_VERSION_PATCH
PROJECT_VERSION_TWEAK, <PROJECT-NAME>_VERSION_TWEAK
#LANGUAGES 支持这些参数:C CXX CUDA OBJC OBJCXX Fortran HIP ISPC ASM。一般就用C CXX,不指定默认也是C CXX

CMAKE_CXX_STANDARD

# specify the C++ standard
set(CMAKE_CXX_STANDARD 11) # 设置C++标准 整个cmake工程
#数值 支持
# 98 (C++98)
# 11 (C++11)
# 14 (C++14)
# 17 (New in version 3.8. C++17)
# 20 (New in version 3.12.C++20)
# 23 (New in version 3.20.C++23)

set_property(TARGET tgt PROPERTY CXX_STANDARD 11) #针对特定的项目设置 tgt 一般就是${PROJECT_NAME}

include

include(<file|module> [OPTIONAL] [RESULT_VARIABLE <var>]
                      [NO_POLICY_SCOPE])
#加载并执行file或module
#先在cmake的安装目录下搜索,如果没有在在CMAKE_MODULE_PATH下查找

include(CheckTypeSize) #和cpp的include也差不多
check_type_size("long" CMAKE_SIZEOF_LONG) #include完后可以跑check_type_size这个功能

find_package

find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
             [REQUIRED] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [NO_POLICY_SCOPE])
#搜索系统是否存在相关的包或组件 有两种搜索机制Moudle mode和Config mode

find_package(Qt6 REQUIRED COMPONENTS Widgets)
#搜索系统中是否存在Qt6 及其Widgets组件

Module mode

#这个模式是匹配Find<PackageName>.cmake 文件来 执行查找
CMAKE_MODULE_PATH #这个变量定义了搜索的模块 所在的路径 以;号分隔
find_package(Qt5 REQUIRED COMPONENTS Widgets)
#如果在CMAKE_MODULE_PATH等路径下找到Find<PackageName>.cmake 匹配的cmake文件 将执行这个文件的内容来进行查找
#例如FindQt5.cmake 存在就通过这个来找包

Config mode

#匹配<lowercasePackageName>-config.cmake或<PackageName>Config.cmake 文件来 执行查找
Qt6Config.cmake #例如Qt6的安装目录下就有这个文件
#会搜下面的变量路径
CMAKE_PREFIX_PATH #这个最常用
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
PATH
CMAKE_SYSTEM_PREFIX_PATH
CMAKE_SYSTEM_FRAMEWORK_PATH
CMAKE_SYSTEM_APPBUNDLE_PATH

add_executable

add_executable(<name> [WIN32] [MACOSX_BUNDLE]
               [EXCLUDE_FROM_ALL]
               [source1] [source2 ...])
#[WIN32] 创建一个windows的可执行文件 主要区别就是main函数的入口是winmain 还是 main 也是区分Gui程序的标记
#[MACOSX_BUNDLE] 创建mac Gui程序的标记
#Linux不需要这种区分,默认就是main了无论是gui还是非gui
#这种平台标记一般是不需要的

#EXCLUDE_FROM_ALL 排除在工程之外,默认不会被编译,如果需要编译需要可以手动执行 例如name是xxx,make xxx

add_executable(<name> ALIAS <target>)
#创建别名

add_library

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            [<source>...])
#添加一个library项目
#name 是要唯一的名称的,不能和其它项目有重复
#STATIC 生成静态库 例如windows下的xxx.lib
#SHARED 生成动态库 例如window下的xxx.lib + xxx.dll
#MODULE 也是生成动态库 不过这个动态库 不能被其它项目直接引用 是通过动态加载的 例如Windows下LoadLibrary("xxx.dll")
#如果没有指定 [STATIC | SHARED | MODULE],全局变量BUILD_SHARED_LIBS(ON/OFF)可以控制是否生成动态库
#EXCLUDE_FROM_ALL 排除在工程之外,默认不会被编译,如果需要编译需要可以手动执行 例如name是xxx,make xxx

add_library(<name> INTERFACE [<source>...] [EXCLUDE_FROM_ALL])
#创建一个INTERFACE库,简单来说就是源码不会被编译的,可以用来包装一些接口的头文件之类的

add_library(<name> ALIAS <target>)
#创建一个库的别名 看到很多开源库用了 例如 add_library(fmt ALIAS fmt::fmt) 为了降低库名冲突

target_compile_definitions

target_compile_definitions(<target>
  <INTERFACE|PUBLIC|PRIVATE> [items1...]
  [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
#添加宏定义 就是C++里面的 #define WIN32 之类的 用于编译控制编译哪些代码
#<target> 不能是ALIAS的别名 一定要是原名

#下面都是一样的效果(-D估计是以前的兼容处理,现在没啥用不用写)
target_compile_definitions(foo PUBLIC FOO) #定义宏FOO
target_compile_definitions(foo PUBLIC -DFOO)  # -D removed 也是定义FOO -D这个会被忽略
target_compile_definitions(foo PUBLIC "" FOO) # "" ignored,忽略“”
target_compile_definitions(foo PUBLIC -D FOO) # -D becomes "", then ignored(没用,只是说明-D会被忽略掉),定义FOO


target_compile_options

target_compile_options(<target> [BEFORE]
  <INTERFACE|PUBLIC|PRIVATE> [items1...]
  [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
#添加编译选项
#[BEFORE] 意思是选项添加到前面 例如本来就有 -Wall 加 [BEFORE] -Werror 就是 -Werror -Wall
#不同的编译器参数可能是不一样的

#例如添加warning
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
	target_compile_options(app PUBLIC /W4 /WX) #最高warning、warning当error处理
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
	target_compile_options(app PUBLIC -Wall -Werror) 
elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") #Clang
	target_compile_options(app PUBLIC -Wall -Werror) 
endif()

#COMPILE_OPTIONS / INTERFACE_COMPILE_OPTIONS 获取目标的编译选项
target_compile_options(app PUBLIC -Wall -Werror)
get_target_property(APP_COMPLIE_OPTIONS app COMPILE_OPTIONS)
message(STATUS "complie option ${APP_COMPLIE_OPTIONS}")
#-- complie option -Wall;-Werror 以;分隔

target_include_directories

target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
  <INTERFACE|PUBLIC|PRIVATE> [items1...]
  [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
#添加头文件include目录
#<target> 不能是ALIAS的别名 一定要是原名

#这个设置 影响目标的两个 属性变量参考target_compile_options的属性获取方法
#INCLUDE_DIRECTORIES
#INTERFACE_INCLUDE_DIRECTORIES

target_link_libraries(<target> ... <item>... ...)
target_link_libraries(<target>
                      <PRIVATE|PUBLIC|INTERFACE> <item>...
                     [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
#链接一个库
#<target> 不能是ALIAS的别名 一定要是原名 item可以是别名
#item 可以是
#1. A library target name
#2. A full path to a library file
#3. A plain library name
#4. 其它不常用的 A link flag、A generator expression
target_link_options(<target> [BEFORE]
  <INTERFACE|PUBLIC|PRIVATE> [items1...]
  [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
#修改目标的链接选项 -lpthread

target_link_options(app PUBLIC -pthread)
#例如Linux平台下链接pthread库

install

install(TARGETS <target>... [...])
install(IMPORTED_RUNTIME_ARTIFACTS <target>... [...])
install({FILES | PROGRAMS} <file>... [...])
install(DIRECTORY <dir>... [...])
install(SCRIPT <file> [...])
install(CODE <code> [...])
install(EXPORT <export-name> [...])
install(RUNTIME_DEPENDENCY_SET <set-name> [...])
#安装 到某个位置 实际和copy的效果差不多

install(TARGETS app DESTINATION bin)
#将app安装到bin目录
#跑cmake的可以修改一下 安装的前缀CMAKE_INSTALL_PREFIX
cmake -DCMAKE_INSTALL_PREFIX=/home/noexcept/testinstall ..
#或(两步走)
cmake .. #生成工程
cmake --install . --prefix "/home/noexcept/testinstall" #在工程目录下 生成install配置
#然后
make install
#效果就是copy app到/home/noexcept/testinstall/bin/app

testing

enable_testing() #开启测试
add_test(NAME <name> COMMAND <command> [<arg>...]
         [CONFIGURATIONS <config>...]
         [WORKING_DIRECTORY <dir>]
         [COMMAND_EXPAND_LISTS])
#添加测试用例 看起来是只能跑cmd的 感觉用处不是很大

#例子
enable_testing() 
add_test(NAME test1 COMMAND app) #添加测试用例COMMAND就是要跑的命令
set_tests_properties(test1 PROPERTIES PASS_REGULAR_EXPRESSION "no_test_def") #test1的命令输出结果和正则字符串比较是否匹配,匹配就通过测试

cmake ..
make test #生成工程后可跑测试

#set_tests_properties一些常用的属性
# PASS_REGULAR_EXPRESSION 测试通过的正则 可以用;分隔多个正则
# FAIL_REGULAR_EXPRESSION 测试失败的正则 可以用;分隔多个正则
# TIMEOUT 超时执行时间 单位是秒 超过秒数 就是测试失败
# WORKING_DIRECTORY 工作目录

cpack

#cpack是一个独立的打包工具 支持生成各种安装包

#CMakeLists.txt
set(CPACK_PACKAGE_NAME "app") #需要打的包名
set(CPACK_INSTALL_PREFIX "/home/noexcept/testcpack") #打包的目标安装目录
set(CPACK_PACKAGE_VERSION "0.0.1") #包的版本
include(CPack) #includeCPack 对上参数解析

#sh
cpack #执行cpack就可以打包 deb、rpm、nsis、zip等格式都可以打 基本包括各种平台

file

读取文件

file(READ <filename> <variable>
     [OFFSET <offset>] [LIMIT <max-in>] [HEX])
#读取文件
file(STRINGS <filename> <variable> [<options>...])
#读取文件 不过都是当字符串处理 如果有多行 variable 就是一个list变量了

file(READ readme.md README_CONTENT)
message(STATUS ${README_CONTENT})
#读取readme.md的内容到变量README_CONTENT 然后通过message输出

写入文件

file(WRITE <filename> <content>...)
#覆写文件
file(APPEND <filename> <content>...)
#在文件未添加内容
file(TOUCH [<files>...])
#files...的access/modification时间会被更新,如果files...不存在则创建文件 
file(TOUCH_NOCREATE [<files>...])
#files...的access/modification时间会被更新,如果files...不存在神马都不做

file(GENERATE OUTPUT output-file
     <INPUT input-file|CONTENT content>
     [CONDITION expression] [TARGET target]
     [NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS |
      FILE_PERMISSIONS <permissions>...]
     [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
#生成文件 看起来比WRITE功能更多
file(GENERATE OUTPUT gen_file.md CONTENT "i my a gen_file")
#在构建目录下生成gen_file.md内容是"i my a gen_file"

HASH文件

file(<HASH> <filename> <variable>)
#计算文件的hash值保存到variable里面
#HASH的值可以是: MD5 SHA1 SHA224 SHA256 SHA384 SHA512 SHA3_224 SHA3_256 SHA3_384 SHA3_512

file(MD5 ${CMAKE_SOURCE_DIR}/readme.md README_HASH_MD5) #文件的路径要写好才行 貌似不支持相对路径
message(STATUS "readme.md-md5:" ${README_HASH_MD5})
#计算readme.md的md5 并输出
file(SHA256 ${CMAKE_SOURCE_DIR}/readme.md README_HASH_SHA256)
message(STATUS "readme.md-sha256" ${README_HASH_SHA256})
#计算readme.md的sha256 并输出

文件目录

file(GLOB <variable>
     [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS]
     [<globbing-expressions>...])
file(GLOB_RECURSE <variable> [FOLLOW_SYMLINKS]
     [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS]
     [<globbing-expressions>...])
#匹配目录下的文件,globbing-expressions 参考bash的glob就行了
     
file(GLOB PNG_FILES *.png) #匹配目录下的*.png文件保存到PNG_FILES GLOB是非递归的 就处理当前目录 GLOB_RECURSE是递归目录的

file(MAKE_DIRECTORY [<directories>...])
#创建目录
file(MAKE_DIRECTORY dir1 dir2 dir3)
#在CMakeLists.txt目录下创建dir1 dir2 dir3

file(REMOVE [<files>...])
file(REMOVE_RECURSE [<files>...])
#删除文件REMOVE非递归 REMOVE_RECURSE递归删除子目录下的文件

file(RENAME <oldname> <newname>
     [RESULT <result>]
     [NO_REPLACE])
#重命名文件 3.21

file(COPY_FILE <oldname> <newname>
     [RESULT <result>]
     [ONLY_IF_DIFFERENT])
#复制文件 3.21

file(<COPY|INSTALL> <files>... DESTINATION <dir>
     [NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS]
     [FILE_PERMISSIONS <permissions>...]
     [DIRECTORY_PERMISSIONS <permissions>...]
     [FOLLOW_SYMLINK_CHAIN]
     [FILES_MATCHING]
     [[PATTERN <pattern> | REGEX <regex>]
      [EXCLUDE] [PERMISSIONS <permissions>...]] [...])
#也是复制文件 功能更多 COPY和INSTALL的区别是 INSTALL会输出路径

file(SIZE <filename> <variable>)
#获取文件的大小
file(SIZE ${CMAKE_SOURCE_DIR}/readme.md README_SIZE) 
#获取readme.md的大小 单位看起来是 字节

上传下载

file(DOWNLOAD <url> [<file>] [<options>...])
file(UPLOAD   <file> <url> [<options>...])

string