CMakelist专题
参考文献
内置变量
- CMAKE_CURRENT_LIST_FILE: 当前处理的 CMakeLists.txt 文件的完整路径。
- CMAKE_CURRENT_LIST_DIR: 当前处理的 CMakeLists.txt 文件所在的目录路径。
- CMAKE_CURRENT_FUNCTION: 当前调用的函数名。
- PROJECT_NAME: 项目名称,由
project()
命令设置。 - CMAKE_MODULE_PATH: 模块查找路径,由
set(CMAKE_MODULE_PATH ...)
命令设置。 - CMAKE_PREFIX_PATH: 前缀路径,由
set(CMAKE_PREFIX_PATH ...)
命令设置,指定项目依赖库的安装路径。 - CMAKE_BUILD_TYPE: 构建类型,可选项为 Debug 或 Release,由
set(CMAKE_BUILD_TYPE ...)
命令设置。 - CMAKE_INSTALL_PREFIX: 安装路径,由
set(CMAKE_INSTALL_PREFIX ...)
命令设置,指定项目安装路径。
常用API
- set 设置参数,包含一般/缓存/环境变量。
- PARENT_SCOPE
【不加:子目录修改父目录变量效果只能作用域子目录,父目录读取该变量值不变;父目录读不到子目录的变量值】
- PARENT_SCOPE
- list 针对列表操作,比如针对文件列表/参数列表/编译列表的增加删除这些。
- file 针对文件操作,如收集文件列表,读写文件等。
- string 针对字符串的操作,如大小写,查找,正则表达式匹配等。
- message 打印消息,可以跟踪测试修改。message(“${aa}”)
- mark_as_advanced(COMPILER_BIN_PATH) 没有$变量!!标记已命名的缓冲变量为advanced,advanced变量不会在cmake图形界面中显示除非advanced选项开启
- foreach 循环
- target_compile_options():指定目标的编译选项
- target_link_libraries:将已知库链接到一个目标上(add_executable()或add_library()之类的命令创建)
- target_include_directories:指定目标包含的头文件路径。
- 上面3个带有PUBLIC,PRIVATE,INTERFACE
- PRIVATE:私有的,ab为2个a库,a私有依赖b;prj【a(私有b)】project看不到b的h文件,b只给a
- PUBLIC:就是指定搜索头文件路径是可见的,这意味着对于调用这个库的prj只需要
- include_directories: 添加包含头文件路径。全局包含,向下传递;即如果某个目录的 CMakeLists.txt 中使用了该指令,当前cmakelist及其下所有子cmakelist的目标默认也包含了该目录。
- add_library:添加h和c文件,生成(静态/动态)库so或者.a文件,默认a库。如果只添加c文件,那么也会添加当前文件夹下的h文件。
动态库libhello.so:add_library( hello SHARED ${libhello_src})
静态库libhello_static.a:ADD_LIBRARY (hello_static STATIC ${LIBHELLO_SRC}
注意:- target不能重复,但如果静态库也想是libhello.a,那么就要设置属性get_target_property();
- 已有同名库时(比如生成静态库libhello.a发现有同名动态库libhello.so那么会删除先前的同名库,可以同上设置属性来避免)
- add_executable:生成执行文件。
- add_subdirectory:添加一个子目录并构建该子目录,用于多个Cmakelist构建;
- aux_source_directory:查找指定目录下的所有源文件(只能找到c),然后将结果存进指定变量名
- get_filename_component:获取完整文件名路径的特定部分。
- add_dependencies:被依赖的项目总是最先构建,这样就不会出现找不到库而报错;
- add_custom_command :用户自定义命令
- target_sources:新的构建子cmakelist方式,更优秀。
编译器相关设置 - set_source_files_properties:指定源文件一些具体编辑器里的属性,给源文件设置一些属性。
- set_target_properties:给可执行文件或者库,不能是虚构的变量等设置属性。使用get_property()或get_target_property()命令提取它。结合add_library看例子
- 希望 “hello_static” 在输出时,不是"hello_static",而是以"hello"的名字显示
SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME “hello”)
GET_TARGET_PROPERTY (OUTPUT_VALUE hello_static OUTPUT_NAME) 最终结果是OUTPUT_NAME - 生成同名库避免删除以前的库,使用SET_TARGET_PROPERTIES定义 CLEAN_DIRECT_OUTPUT属性。
SET_TARGET_PROPERTIES (hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES (hello PROPERTIES CLEAN_DIRECT_OUTPUT 1) - 按照规则,动态库是应该包含一个版本号的, VERSION指代动态库版本,SOVERSION指代API版本。
SET_TARGET_PROPERTIES (hello PROPERTIES VERSION 1.2 SOVERSION 1)
- 希望 “hello_static” 在输出时,不是"hello_static",而是以"hello"的名字显示
- get_target_property: 获取可执行文件或者库中的属性值,并用一个变量接收。
- set_property:也是给文件、可执行文件设置属性。
构建子cmakelist
- 在顶层CmakeList中,使用add_subdirectory,这个文件内一般会包含add_library或add_executable
- 用各目录的函数在cmakelsit怎么写,看加入三方静态库
引入第三方库
-
find_package(LibaryName):用于查找包(通常是使用三方库),并返回关于包的细节(使用包所依赖的头文件、库文件、编译选项、链接选项等),是一个.cmake文件
- find_package(OpenCV REQUIRED)
REQUIRED表示一定要找到这个库,找不到这个库,编译就直接结束,不再往下进行 - 如果前面使用了find_package查找到了opencv库【if(OpenCV_FOUND)判断】,那么就可以使用
- 引入头文件目录:include_directories(${OpenCV_INCLUDE_DIRS})
- 链接库文件 :target_link_libraries(main ${OpenCV_LIBRARIES})
- find_package(OpenCV REQUIRED)
-
加入三方静态库
无论是加入外部三方库,还是各目录互相调用,都是要加以下两句:target_link_libraries(${PROJECT_NAME} "${CMAKE_CURRENT_LIST_DIR}/../../lib/rf_control.a")
target_include_directories( ${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/../../inc/rf_control")
- 使用外部三方库,除了本身的程序需要链接到静态库,还需要知道改库的接口函数,也就是要include头文件。其中 “PRIVATE“ 还是“”public“都没影响。
- 当构建子目录时, 需要注意 “PRIVATE“ 和 “PUBLIC”的含义,当子目录add_library生成静态库时,如果试用PUBLIC,target_include_directories( 库 PUBLIC 头文件路径 ), 那么该路径对于别的目录是可见的,其他目录调用该库时只要link静态库即可。如果使用PRIVATE或者不写target_include_directories,那么需要在调用处的cmaklist做target_include_directories。
- 因此,构建子目录建议生成库后要么都使用PUBLIC添加头文件路径,要么都不写。
- c文件中,引用头文件为什么有路径"/":
构建子目录时,在生成库时target_include_directories( 库 PRIVATE 头文件路径 )
或者不做该操作,那么该目录对于别的目录不可见,是添加这个头文件的c文件,和这个头文件的相对路径#include "../aa.h"
;
如果使用 PUBLIC,那么直接#include "aa.h"
;