CMake Commands
Posted noexcept
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CMake Commands相关的知识,希望对你有一定的参考价值。
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_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_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
VSCode 不使用 compile_commands.json 作为库路径
【中文标题】VSCode 不使用 compile_commands.json 作为库路径【英文标题】:VSCode not using compile_commands.json for library paths 【发布时间】:2018-03-22 15:19:38 【问题描述】:我已经尝试了所有方法,但无法让 VSCode 识别 compile_commands.json
中的库路径。下面是一些图片来说明我的意思:
项目树
实际文件
如您所见,C/C++ 扩展未检测到我的库,即使它们包含在由 CMake 生成的我的 compile_commands.json
中。对此的任何帮助将不胜感激。
【问题讨论】:
您有October 2017 update 或更高版本吗? 我愿意。扩展程序、vscode 和 cmake 均已更新到最新版本。 【参考方案1】:compile_commands.json
文件的生成器正在将包含路径移动到 response files 文件中(“命令”文本中的 @file.rsp
文件:Json 的字段)。一些生成器将在 Windows 上执行此操作以限制在命令行上输入的命令的长度,目前约为 8k 个字符,而 Linux 上为 128k 个字符。从历史上看,Windows 的限制甚至更短,因此一些工具会主动将所有内容移动到 @files 中,即使不会超过行长限制。
不幸的是,vscode-cpptools
扩展目前不处理这个@file.rsp
语法。看到这个thread。
在调用 Cmake 时尝试使用 Ninja 生成器。对我来说(在 MSys2 中运行 mingw64),Ninja 创建了独立的 compile_commands.json
文件,vscode-cpptools 可以正确解析。
从源目录的根目录调用 cmake 的示例:
$ cmake -H"." -B"$output_dir" -G"Ninja" -DCMAKE_EXPORT_COMPILE_COMMANDS=1
【讨论】:
以上是关于CMake Commands的主要内容,如果未能解决你的问题,请参考以下文章
VSCode 不使用 compile_commands.json 作为库路径