CMake 指令、技巧与错误 | C & C++
本文最后更新于:2023年3月5日
指令
find_package
find_package 的作用就是寻找第三方模块的头文件目录和库文件路径,并将其设为变量,返回提供给 CMakeLists.txt 其他部分使用。
find_package 首先会寻找并执行模块相关的
.cmake
文件。寻找顺序如下:- 在寻找模块的时候显式指定了模块目录:
find_package(${module_name} REQUIRED PATHS ${module_dir})
- 在
${module_name}_DIR
变量中指定了模块目录 - 查找路径的根目录,默认查找目录如下:
${module_name}_DIR
MAKE_PREFIX_PATH
CMAKE_FRAMEWORK_PATH
CMAKE_APPBUNDLE_PATH
PATH
:如果以 bin 或 sbin 结尾,则自动回退到上一级目录
- 检查上述目录下的这些目录:
(lib/${arch}|lib|share)/cmake/${module_name}*/
(lib/${arch}|lib|share)/${module_name}*/
(lib/${arch}|lib|share)/${module_name}*/(cmake|CMake)/
- 寻找并执行
${module_name}Config.cmake
或Find${module_name}.cmake
脚本
- 在寻找模块的时候显式指定了模块目录:
find_package 然后会设置以下几个变量:
${module_name}_FOUND
:是否找到该模块${module_name}_INCLUDE_DIR
:模块头文件目录${module_name}_LIBRARY
或${module_name}_LIBRARIES
:模块库文件路径
以 LibTorch 为例展示一个例子
find_package(Torch REQUIRED) # REQUIRED 表明这个模块是必需的,如果找不到就报错
add_executable(torch_test "torch_test.cpp")
include_directories(${TORCH_INCLUDE_DIRS})
target_link_libraries(torch_test ${TORCH_LIBRARIES})
参考
file
查找某些文件并构成一个文件列表变量
file(GLOB LIB_CPP_SRCS ${CMAKE_SOURCE_DIR}/src/*.cpp)
file(GLOB_RECURSE LIB_CPP_SRCS ${CMAKE_SOURCE_DIR}/src/*.cpp)
list
list(LENGTH <list><output variable>)
list(GET <list> <elementindex> [<element index> ...]<output variable>)
list(APPEND <list><element> [<element> ...])
list(FIND <list> <value><output variable>)
list(INSERT <list><element_index> <element> [<element> ...])
list(REMOVE_ITEM <list> <value>[<value> ...])
list(REMOVE_AT <list><index> [<index> ...])
list(REMOVE_DUPLICATES <list>)
list(REVERSE <list>)
list(SORT <list>)
- LENGTH:返回 list 的长度
- GET:返回 list 中 index 的 element 到 value 中
- APPEND:添加新 element 到 list中
- FIND:返回 list 中 element 的 index,没有找到返回 -1
- INSERT:将新 element 插入到 list 中 index 的位置
- REMOVE_ITEM:从 list 中删除某个 element
- REMOVE_AT:从 list 中删除指定 index 的 element
- REMOVE_DUPLICATES:从 list 中删除重复的 element
- REVERSE:将 list 的内容反转
- SORT:将 list 按字母顺序排序
include_directories
将指定目录添加到编译器的头文件搜索路径之下,指定的目录被解释成当前源码路径的相对路径
include_directories(${CMAKE_SOURCE_DIR}/src)
该命令不会进行递归查找,正常情况下只需要 include src 目录即可,引用头文件时相对于 src 路径填写头文件路径
include
用于包含其他 cmake 文件
include_directories(${CMAKE_SOURCE_DIR}/third_path.cmake)
add_subdirectory
添加子目录,子目录中需要也有 cmake 文件,使用该命令即运行子目录的 cmake 文件。子目录中的 set 命令默认有效范围仅子目录和子目录的子目录,如果想令子目录的 set 在上层目录生效,需要加上 PARENT_SCOPE 参数,但是这样在子目录就无法生效了
add_subdirectory(src/proto)
add_library
将指定的源文件生成库文件
# SHARED,动态库
# STATIC,静态库
# MODULE,在使用 dyld 的系统有效,如果不支持 dyld,则被当作 SHARED 对待。
SET(LIBHELLO_SRC hello.c)
add_library(hello_shared SHARED ${LIBHELLO_SRC})
set_target_properties(hello_shared PROPERTIES OUTPUT_NAME "hello")
add_library(hello_static STATIC ${LIBHELLO_SRC})
set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")
这里 hello_shared 和 hello_static 名字必须不同,否则会忽略掉第二个库,所以使用 set_target_properties 命令重命名
add_executable
和 add_library 类似,将指定的源文件生成可执行文件
set_target_properties
set_target_properties(hello PROPERTIES VERSION 1.6.0. SOVERSION 1)
这条命令会生成三个文件:
libhello.so => libhello.so.1*
libhello.so.1 => libhello.so.1.6.0*
libhello.so.1.6.0
其中,libhello.so.1.6.0 为动态库的文件名(realname),libhello.so.1 为动态库的别名(soname),libhello.so 为动态库的链接名(linkname)
target_link_libraries
为 target 添加需要链接的共享库
target_link_libraries(${PROJECT_NAME} opencv_imgcodecs)
option
命令行参数
project(hello)
option(USE_XXX "option for use xxx" OFF)
if (USE_XXX)
add_definitions(-DUSE_XXX)
...
endif()
cmake -DUSE_XXX=ON ..
install
CMake之install方法的使用
【CMake】cmake的install指令
add_custom_command
和 install 相比,add_custom_command 更加灵活,下面展示一个用 add_custom_command 做拷贝的示例:
add_custom_command(TARGET ${target_name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${target_name}>
${path/to/dst})
target_sources
target_sources 用于向 target 中追加源文件,例如:
add_library(my_lib "") # 这里的 "" 不能省略
target_sources(my_lib
PRIVATE
${CMAKE_CURRENT_DIR}/foo.cpp
${CMAKE_CURRENT_DIR}/bar.cpp
PUBLIC
${CMAKE_CURRENT_DIR}/foo.h
${CMAKE_CURRENT_DIR}/bar.h
)
注:
- 如果在根目录通过 add_library 定义了一个 target,也可以在子目录中用 target_sources 命令往这个 target 中追加源文件
- C++ 的源文件指定为 PRIVATE,是因为源文件只是在构建库文件时使用,头文件指定为 PUBLIC 是因为构建库文件和使用库文件时都会使用
参考
CMake - 使用 target_sources() 提高源文件处理能力
execute_process
execute_process(
COMMAND <command>
WORKING_DIRECTORY <directory>
RESULT_VARIABLE res_var # 子进程返回码或者错误描述字符串
OUTPUT_VARIABLE out_var # 标准输出
ERROR_VARIABLE err_var # 标准错误
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE)
如果 OUTPUT_VARIABLE 和 ERROR_VARIABLE 变量名相同,它们的输出将按照产生顺序被合并。
技巧
target_**() 中的 PRIVATE、INTERFACE 和 PUBLIC
以 target_link_libraries
为例:
target_link_libraries(A PRIVATE/INTERFACE/PUBLIC B)
target_link_libraries(C A)
PRIVATE
:依赖项 B 仅链接到目标 A,不链接到 CINTERFACE
:依赖项 B 仅链接到目标 C,不链接到 APUBLIC
:依赖项 B 链接到目标 A 和 目标 C
target_include_directories
同理
参考
CMake 中的 PUBLIC,PRIVATE,INTERFACE
macro 形参获取
cmake_minimum_required (VERSION 3.5)
project(test)
macro(testarg var1 var2)
message("ARGC=${ARGC}")
message("ARGV=${ARGV}")
message("ARGN=${ARGN}")
message("ARGV0=${ARGV0}")
message("ARGV1=${ARGV1}")
message("ARGV2=${ARGV2}")
message("ARGV3=${ARGV3}")
message("ARGV4=${ARGV4}")
endmacro()
message("\n")
testarg(a b c d e)
message("\n")
testarg(a b)
ARGC=5
ARGV=a;b;c;d;e
ARGN=c;d;e
ARGV0=a
ARGV1=b
ARGV2=c
ARGV3=d
ARGV4=e
ARGC=2
ARGV=a;b
ARGN=
ARGV0=a
ARGV1=b
ARGV2=
ARGV3=
ARGV4=
参考
错误
Unknown CMake command “cuda_add_library”
在这之前使用 find_package(CUDA)
指令即可。
评论系统采用 utterances ,加载有延迟,请稍等片刻。