本文最后更新于 2025年10月5日 凌晨
[toc] cmake只是一个工具而已,最本质的那些东西永远是不变的,你要理解你的需求,你要怎么样去设计整个编译框架来满足你的需求,你要用cmake的那些东西来迎合你的设计,你只拿你需要的部分,剩下的那些杂枝细节可以通通扔掉。而不是反过来因为cmake有这个功能,有那个功能,所以设计应该是这样的或者那样的
构建框架 CMake 样例 good 不错的例子
B站讲解视频,还不错
在掌握基本的cmake原理和用法的基础上,研究开源项目的cmake文件是如何编写的。
推荐的开源项目 常见的应用场景:
general usage
qt usage
cuda usage
python binding usage
更进一步掌握原理-阅读官方文档 cmake实战指南**非常好
构建(Build) Cmake比较核心的一些东西就是:
怎么去组织一个项目的编译框架
最终输出目标有哪些(可执行程序,动态库,静态库等等)
怎么为指定的输出目标指定编译参数(需要哪些源文件,需要哪些编译参数)
怎么为指定的输出目标指定链接参数(需要哪些外部库,需要哪些链接参数)
如果存在多个独立输出目标是否有执行先后顺序(比如项目有自动配置工具,用来自动生产一些源文件,那么自动配置工具输出目标就要先于其他目标比如输出可执行程序目标)
CMake一般使用流程 mkdir buildcd build && cmake .. && make
CMake配合脚本一起使用 Win
Linux
通过命令构建项目 1 2 3 4 5 6 cmake -G"Unix Makefiles" -D<var> =<value> <path-to-source>
通过命令编译项目 Build a Project
1 2 3 #统一了各平台的编译阶段,等同于在linux平台下执行make window下执行? cmake --build <dir> [<options> ] [-- <build-tool-options> ] #其中 -- 告诉cmake --build命令,后面的参数<build-tool-options> 不是cmake --build本身的参数,而是应该传递给底层构建系统(如make )的参数。
核心语法 cmake有全局作用域、目录作用域、函数作用域
全局作用域 xxx
目录作用域: 子目录的CMakeLists.txt会将父目录的所有变量拷贝到当前CMakeLists.txt中,当前CMakeLists.txt中的变量的作用域仅在当前子目录有效。 目录作用域有两个特点:向下有效,值拷贝。
函数作用域: xxx
明确作用域:在项目的不同层级清晰地设置和管理作用域,以确保路径设置正确地应用。深入理解add_subdirectory和include
头文件路径设置问题 CMAKE变量 cmake变量 官方文档
CMAKE_XXX_DIR等内置变量辨析
基础命令1 1 2 3 4 5 6 set(<variable > <value > ... [PARENT_SCOPE]) #给变量设置值。 $ {} #取值。 $<> #生成器表达式 作用为根据不同配置生成特定内容,例子add_compile_option($<$<COMPILE_LANGUAGE:C > :$ {flag} >) string(REPLACE <match-string > <replace-string > <out-var > <input > ...) #将<input > 中所有出现的<match_string > 替换为<replace_string > ,并将结果存储在<out-var > 中。若<match_string > 在<input > 中未出现,<out-var > 的值将为<input > $ENV {VAR} to read environment variable VAR,To test whether an environment variable is defined, use the signature if(DEFINED ENV {<name>} ) set(ENV {变量名} 值) 设置环境变量
基础命令2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 target_include_directories (myapp PUBLIC /path) include_directories (/path) add_link_options (option ) link_directories (/path/) add_executable (MyExe xxx)target_link_libraries (myapp <PUBLIC | INTERFACE> hellolib) target_link_libraries (MyExe PRIVATE SomePrefix::LibName)add_dependencies (<target > [<target -dependency>]...) add_library 可以生成多种类型的库,包括普通库 对象库 接口库 导入库 别名库。add_library (<name> INTERFACE) add_library (<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] [<source>...]) 构建库的源文件(.h也可)可以直接指定,**也可以后续使用target_sources ()指定**;STATIC(静态库) SHARED(动态库) MODULE(模块库)用来指定库的类型。target_sources (myapp PUBLIC hello.cc other.cc) set_target_properties (target1 PROPERTIES prop1 value1 …)get_target_property ()define_property (<GLOBAL | TARGET > PROPERTY <name>) set_propert(<GLOBAL | TARGET [<target1> ...] > [APPEND] PROPERTY <name> [<value1> ...])get_property (<variable> <GLOBAL | TARGET > PROPERTY <name>)include (<file |module> [OPTIONAL] [RESULT_VARIABLE <var>] [NO_POLICY_SCOPE])add_subdirectory (source_dir [binary_dir] [EXCLUDE_FROM_ALL]) 相对是相对当前目录的,是必选参数。add_definitions (-DMY_MACRO)add_definitions (-DFOO=${xxx} ) add_compile_definitions (<definition> ...) target_compile_definitions (<target ><INTERFACE|PUBLIC|PRIVATE>[item1…]) target_add_definitions(myapp PUBLIC MY_MACRO=1 ) add_compile_options (-fopenmp) target_compile_options (target PRIVATE "-g" ) set (CMAKE_C_FLAGS "-g" ) set_target_properties (target1 target2 PROPERTIES prop1 value1 prop2 value2)
控制流 1 2 3 foreach (<loop_var> <items>) <commands>endforeach () #<items>是一个item列表,由空格或分号分隔。
1 2 3 字符串比较STREQUAL 比较字符串是否完全相等MATCHES 检查字符串是否符合正则表达
函数 1 2 3 function (<name> [<arg1> …]) endfunction ()
函数或宏中常用专用变量:
1 2 3 $(ARGC) 函数传递参数个数 $(ARGV) 所有传递的参数 $(ARGN) 函数声明的参数之后的所有参数
进阶 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 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>...]] [...])file (GLOB <variable> [LIST_DIRECTORIES true |false ] [RELATIVE <path>] [CONFIGURE_DEPENDS] [<globbing-expressions>...])¶ file (STRINGS <filename> <variable> [<options>])execute_process (COMMAND <cmd1> [<arguments>] [COMMAND <cmd2> [<arguments>]]... [WORKING_DIRECTORY <directory>] [TIMEOUT <seconds>] [RESULT_VARIABLE <variable>] [RESULTS_VARIABLE <variable>] [OUTPUT_VARIABLE <variable>] [ERROR_VARIABLE <variable>] [INPUT_FILE <file >] [OUTPUT_FILE <file >] [ERROR_FILE <file >] [OUTPUT_QUIET] [ERROR_QUIET] [COMMAND_ECHO <where>] [OUTPUT_STRIP_TRAILING_WHITESPACE] [ERROR_STRIP_TRAILING_WHITESPACE] [ENCODING <name>] [ECHO_OUTPUT_VARIABLE] [ECHO_ERROR_VARIABLE] [COMMAND_ERROR_IS_FATAL <ANY|LAST>]) add_custom_command ( OUTPUT output1 [output2 ...] COMMAND command1 [ARGS] [args1...] [COMMAND command2 [ARGS] [args2...] ...] [MAIN_DEPENDENCY depend] [DEPENDS [depends...]] [BYPRODUCTS [files...]] [IMPLICIT_DEPENDS <lang1> depend1 [<lang2> depend2] ...] [WORKING_DIRECTORY dir] [COMMENT comment] [DEPFILE depfile] [JOB_POOL job_pool] [JOB_SERVER_AWARE <bool>] [VERBATIM] [APPEND] [USES_TERMINAL] [COMMAND_EXPAND_LISTS] [DEPENDS_EXPLICIT_ONLY] ) add_custom_command (TARGET <target > PRE_BUILD | PRE_LINK | POST_BUILD COMMAND command1 [ARGS] [args1...] [COMMAND command2 [ARGS] [args2...] ...] [DEPENDS] [depends ...] [BYPRODUCTS [files...]] [WORKING_DIRECTORY dir] [COMMENT comment] [VERBATIM] [USES_TERMINAL] [COMMAND_EXPAND_LISTS]) add_custom_target (Name [ALL] [command1 [args1...]] [COMMAND command2 [args2...] ...] [DEPENDS depend depend depend ... ] [BYPRODUCTS [files...]] [WORKING_DIRECTORY dir] [COMMENT comment] [JOB_POOL job_pool] [JOB_SERVER_AWARE <bool>] [VERBATIM] [USES_TERMINAL] [COMMAND_EXPAND_LISTS] [SOURCES src1 [src2...]])
cmake:添加自定义操作
引入第三方依赖 官方文档-Using Denpendencies Guide blog: cmake集成第三方库 总结 CMake 添加第三方库依赖方式
1.代码依赖 这种方式是把第三方库的完整代码直接添加到我们的项目中,当做项目代码的一部分进行编译,这种方式会把第三方代码和我们的代码混在一起,并不推荐使用。
2.内部工程依赖(作为子模块) 内部工程依赖会把第三方库的管理职责交给第三方库工程CMakeLists.txt文件,这种方式的好处是职责分明,是最常用的依赖方式。 纯头文件 用法:只需把他的include目录或头文件下载下来,然后include_directories(xxlib/include) or add_library(mylib INTERFACE)
3.find_library:编译库方式引入 这种方式是用来依赖已经打包好的二进制文件,这种方式也分为静态库(.a、.lib)和动态库(.so、.dll)方式引入,这种方式也可以查找本机已经安装好的库,比如 Android 的 log 库就是通过这种方式引入。
4.FetchContent 下载并构建源码 1 2 3 4 5 6 7 8 9 10 11 12 cmake_minimum_required (VERSION 3.17 )project (fetch_content_example)include (FetchContent) FetchContent_Declare(jsoncpp URL https://github.com/open-source-parsers/jsoncpp/archive/1.9 .4 .tar.gz) FetchContent_MakeAvailable(jsoncpp)add_executable (${PROJECT_NAME} main.cpp)target_link_libraries (${PROJECT_NAME} jsoncpp_lib)
5.find_package 引用系统中预安装的第三方库 可以通过find_package命令来寻找系统中的包、库这个包必须是已经安装到系统中。 find_package添加依赖库用法 find_package提供了两种主要的方式来搜索
config mode 就是你要使用的包已经给你提供了cmake所需的config file(?)
module mode 有的包不是cmake构建的,没有提供所需的文件,那就需要自己写一个module file——FindSomePackage.cmake 文件来供find_package()命令来使用。FindSomePackage.cmake文件通常放在${CMAKE_CURRENT_SOURCE_DIR}/cmake目录下。官方提供许多已经写好的不同库的 module file。
note: 如果没有module file被提供,cmake将在系统中搜索config file
标准示例:
1 2 3 4 5 6 # Make project-provided Find modules availablelist (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")find_package (SomePackage REQUIRED)add_executable (MyExe main.cpp)target_link_libraries (MyExe PRIVATE SomePrefix::LibName)
standard variable name:Xxx_INCLUDE_DIRSXxx_LIBRARIESXxx_FOUND ……
6.git submodule 这种方式是利用git的submodule实现,推荐Android使用,通过git添加另外一个仓库的依赖,可更新另外一个仓库的依赖,但是代码不会包含进来。
编译目标文件 静态库 动态库 可执行文件 版本控制 安装和打包 support CMake install and find_package() 知乎 Cmake打包
对于版本控制暂时了解有两种方法: 1.通过configure_file() 将头文件中的宏值用cmakelists中的定义替换 2.KConfig 会生产cmake文件 然后参与构建
If we write find_package(my_library ...), it will go and look for a file named my_library-config.cmake (among others) in a directory named my_library* under the ${CMAKE_INSTALL_PREFIX}/lib (among many others).
用到的变量:${CMAKE_INSTALL_PREFIX}——>/usr/local under linux,可以通过set另外赋值来指定install的目录。
测试 配置框架 Kconfig 广泛用于U-Boot等项目的开源配置框架,支持组件的参数、使能、模块化等配置,参见Kconfig Language。
mconf mconf 是 Linux 内核中的一个配置工具,用于配置和编辑内核的配置文件。它通常与 Kconfig 构建系统一起使用。以下是一些关键点:
Kconfig 文件: 内核的配置信息存储在一个名为 Kconfig 的文件中。这个文件包含了内核的各种配置选项,包括驱动、功能和系统参数。
Makefile: mconf 通过读取 Kconfig 文件,并根据用户的选择生成 内核的配置文件(通常是 .config 文件)。这个文件将被用于后续的编译过程。
菜单界面: mconf 提供了一个文本菜单界面,允许用户通过键盘导航和选择配置选项。用户可以启用或禁用特定功能、模块或驱动
qconf qconf 是配置工具,用于配置和管理项目的编译选项,主要用于CMake项目。以下是一些关键特点:
图形界面: qconf 提供了一个图形用户界面(GUI),使用户能够通过可视化的方式设置项目的配置选项,而不必手动编辑配置文件。
Kconfig配置文件(输入): 项目的配置信息通常存储在一个名为 Kconfig 的配置文件中,该文件包含了项目的各种编译选项和设置。
预设配置(输入): qconf 支持预设配置,用户可以在启动时加载特定的配置集,以简化不同构建配置之间的切换。预设配置通常是一些事先定义好的配置选项集,用户可以在启动 qconf 时加载这些预设配置。预设配置可以简化特定构建配置的选择
CMake 集成(输出): qconf 可以生成 与 CMake 构建系统兼容的配置文件,通常是 config.cmake。这样,可以使用 CMake 来进行项目的构建。
Makefile集成(输出):根据用户的选择生成 配置文件(通常是 .config 文件)。这个文件将被用于后续的编译过程。
1 2 3 4 5 6 7 qconf --fontsize xx --prefix "xx" --cmakefile xxx/config.cmake --cfgfile xxx/.config --loadcfg "xxxx" "xxxx/Kconfig"
Kconfig Kconfig 是一种配置语言,广泛用于 Linux 内核和其他一些开源项目中,用于配置和定制软件的构建选项,参见Kconfig Language。
1 2 3 4 5 6 7 8 menu "My Configuration Menu" hidden if M1 config MENU_OPTION1 bool "Option 1" config MENU_OPTION2 bool "Option 2" endmenu
MENU_OPTION1 和 MENU_OPTION2 是属于名为 “My Configuration Menu” 的菜单的配置选项。 其中,使用 hidden if 条件表达式,表示当 M1 为真时,显示该菜单,可根据其他配置项来动态控制菜单的显示隐藏。
1 2 3 4 5 6 7 8 9 10 config MY_FEATURE bool "Enable My Feature" depends on C1 || C2 default y if (D1 && (C1 || C2)) default n if D1 help_chs 开启 help Enable this option to include support for My Feature.
MY_FEATURE 是配置选项的标识符,Enable My Feature 是一个用户友好的描述。 depends on
1 2 3 4 5 6 7 8 9 10 11 choice prompt "xxxxx" default D1 if D1_CFG default D2 if D2_CFG help xxxxxxxxxxxxx config D1 bool "D1" config D2 bool "D2" endchoice
choice 是一个 Kconfig 中的关键字,用于定义一组互斥的配置选项,用户可以从中选择一个,可以添加depend on来控制
配置框架和编译框架综合
不同项目有不同的配合方式
rtos系统 PX4 https://blog.csdn.net/one__leaf/category_12140969.html
ITE linux系统 ORIN