整理常用的示例。
平台依赖
主要是用于检查平台的头文件、函数等是否支持。
CHECK_FUNCTION_EXISTS(backtrace HAVE_BACKTRACE)
CHECK_LIBRARY_EXISTS(pthread pthread_setname_np "" HAVE_PTHREAD_SETNAME_NP)
头文件检查
#----- 检查单个头文件
CHECK_INCLUDE_FILE("regex.h" HAVE_REGEX_H)
#----- 检查多个头文件
CHECK_INCLUDE_FILES("sys/prctl.h;sys/others.h" HAVE_SYS_PRCTL_H)
添加子目录
通过 ADD_SUBDIRECTORY 可以添加并构建子目录。
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
对应各个参数为:
source_dir必选参数,指定子目录,需要包含CMakeLists.txt和代码文件,可以是绝对或者相对路径。binary_dir可选参数,指定输出文件目录,默认与source_dir相同。EXCLUDE_FROM_ALL可选参数,默认不构建子目录,除非显示指定或者有其它目标依赖该对象,例如通过target_link_libraries()指定依赖。
编译参数
可以通过 ADD_COMPILE_OPTIONS(-Wall -Werror) 添加全局编译参数,或者针对某个可执行文件添加,例如 TARGET_COMPILE_OPTIONS(exec PRIVATE -O1) 。
如果针对的是 C 和 CPP 的区别,那么就可以直接更新全局参数变量。
SET(CMAKE_C_FLAGS "-fPIE -msse4.2 -mpclmul ${CMAKE_C_FLAGS}")
SET(CMAKE_CXX_FLAGS "-fPIE -msse4.2 -mpclmul ${CMAKE_CXX_FLAGS}")
或者命令行 cmake -DCMAKE_C_FLAGS="-fPIE" .. 指定。其中,编译器参数的格式为 CMAKE_<LANG>_FLAGS_<CONFIG>,比较常用的一种场景,是根据编译器设置不同的参数。
IF(CMAKE_CXX_COMPILER_ID MATCHES GNU)
LIST(APPEND CMAKE_C_FLAGS "-fno-rtti" "-fno-exceptions")
LIST(APPEND CMAKE_C_FLAGS_DEBUG "-Wsuggest-final-types" "-Wsuggest-override")
LIST(APPEND CMAKE_C_FLAGS_RELEASE "-O3" "-Wno-unused")
ENDIF()
IF(CMAKE_CXX_COMPILER_ID MATCHES Clang)
LIST(APPEND CMAKE_C_FLAGS "-fno-rtti" "-fno-exceptions" "-fcolor-diagnostics")
LIST(APPEND CMAKE_C_FLAGS_DEBUG "-Wdocumentation")
LIST(APPEND CMAKE_C_FLAGS_RELEASE "-O3" "-Wno-unused")
ENDIF()
还有就是根据某个开关配置。
SET(WITH_CONVERAGE false CACHE BOOL "Enable coverage")
IF(WITH_CONVERAGE)
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fprofile-arcs -ftest-coverage")
ENDIF()
编译时调用 cmake .. -DWITH_CONVERAGE=true 即可。
打包静态库
通过 TARGET_LINK_LIBRARIES 可以设置编译依赖的选项,不过对于静态库来说,实际不会将其打包,常规的有如下几种方式。
=====> 先将静态库拆解,再合并
ar x libfoo.a
ar x libbar.a
ar crs libfoobar.a *.o
=====> 更加简洁的命令,生成的是 Thin Archive
ar crsT libfoobar.a libfoo.a libbar.a
详细参数可以参考 编译链接,另外,上述的方法不适合 Mac 系统,需要通过如下命令合并。
libtool -static -o libfoobar.a libfoo.a libbar.a
自动生成
ADD_CUSTOM_COMMAND(OUTPUT libfoobarfat.a
#COMMAND ar crsT libfoobarfat.a $<TARGET_FILE:foo> $<TARGET_FILE:bar>
COMMAND ar x $<TARGET_FILE:foo>
COMMAND ar x $<TARGET_FILE:bar>
COMMAND ar crs libfoobarfat.a *.o
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS foo bar
)
ADD_CUSTOM_TARGET(_foobarfat ALL DEPENDS libfoobarfat.a)
上述会命令会依赖源静态库的路径,可以通过 $<TARGET_FILE:foo> 类似方式获取,而对于类似 FIND_LIBRARY(LIBBAR c HINT ${SEARCH_PATH}) 方式查找的库,可以直接通过 ${LIBBAR} 方式引用。
其中的 ADD_CUSTOM_TARGET 主要是生成一个依赖项,可以自动生成。另外,还可以根据平台进行适配。
导入静态库
上面只是生成了静态库,在 CMake 中会被标记为 GENERATED,用户可以单独使用,如果同时要在 CMake 中使用,那么就需要通过如下方式导入。
ADD_LIBRARY(foobarfat STATIC IMPORTED GLOBAL)
ADD_DEPENDENCIES(foobarfat _foobarfat)
SET_TARGET_PROPERTIES(foobarfat PROPERTIES
IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/libfoobarfat.a
)
ADD_EXECUTABLE(demo src/main.c)
TARGET_LINK_LIBRARIES(demo PRIVATE foobarfat)
库使用
关于库的使用常见有如下的操作。
----- 增加库的搜索路径
LINK_DIRECTORIES(./lib)
----- 生成库,可以是动态(SHARED)或者静态库(STATIC)
SET(SRC_LIST test.c foobar.c)
ADD_LIBRARY(hello SHARED ${SRC_LIST})
----- 指定生成对象时依赖的库
TARGET_LINK_LIBRARIES(hello A B.a C.so)
----- 自定义链接选项,单独对B.a使用--whole-archive选项
TARGET_LINK_LIBRARIES(hello A -Wl,--whole-archive B.a -Wl,--no-whole-archive C.so)
在使用 ADD_LIBRARY(foo SHARED foo.c) 时,不同平台输出有所区别,例如,foo.dll(Windows)、libfoo.so(Linux)、libfoo.dylib(Mac)。
并且各个平台下各不相同,可以通过如下方式修改前缀以及后缀:
SET_TARGET_PROPERTIES(foo PROPERTIES PREFIX "")
SET_TARGET_PROPERTIES(foo PROPERTIES SUFFIX "so")
注意,通过 ADD_LIBRARY 函数不允许同时生成静态和动态两个文件,此时需要修改某个库的生成,使用方式如下。
SET(LIB_STATIC_NAME ${PROJECT_NAME}-static)
SET(LIB_SHARED_NAME ${PROJECT_NAME}-shared)
ADD_LIBRARY(${LIB_STATIC_NAME} STATIC ${SRC_LIST})
SET_TARGET_PROPERTIES(${LIB_STATIC_NAME} PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
ADD_LIBRARY(${LIB_SHARED_NAME} SHARED ${SRC_LIST})
SET_TARGET_PROPERTIES(${LIB_SHARED_NAME} PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
安装
CMake 默认会在与源码目录相同的路径下生成二进制文件或者库文件,实际上可以通过如下方式自定义。
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
当然,对于单个可执行文件,也可以通过 SET_TARGET_PROPERTIES() 指令设置如下的变量覆盖全局的参数。
SET_TARGET_PROPERTIES(${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
SET_TARGET_PROPERTIES(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
SET_TARGET_PROPERTIES(${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
安装指令
在编译完成之后,可以通过 make install 进行安装,注意,此时通过 make clean 不会删除对应的文件,可以通过 cat install_manifest.txt | xargs rm 命令删除,需要提前指定安装规则。
INSTALL(TARGETS foobar DESTINATION bin)
INSTALL(FILES foobar.h DESTINATION include)
如上的配置会将 foobar 和 foobar.h 安装到 /usr/local/{bin,include} 目录下即可,实际上对于安装目录而言,也可以直接使用 INCLUDE(GNUInstallDirs) 指定标准的目录。
对于 TARGETS 的配置,主要有三个参数,分别为 ARCHIVE、LIBRARY、RUNTIME ,一般会类似如下的方式编写。
INSTALL(TARGETS targets...
ARCHIVE
DESTINATION <dir>
PERMISSIONS permissions...
CONFIGURATIONS [Debug|Release|...]
COMPONENT <component>
RUNTIME
DESTINATION <dir>
PERMISSIONS permissions...
CONFIGURATIONS [Debug|Release|...]
COMPONENT <component>
PUBLIC_HEADER
DESTINATION <dir>
PERMISSIONS permissions...
CONFIGURATIONS [Debug|Release|...]
COMPONENT <component>
)
其中上述的 targets 可以指定多个,而且可以是不同的类型,如二进制、动态库、静态库、头文件等。
上述指定的 DESTINATION 一般是相对路径,可以通过 CMAKE_INSTALL_PREFIX 指定其前缀,对于 Linux 默认是 /usr/local/;另外,其前面还可以指定 DESTDIR 目录。
除了使用 TARGETS 外,还可以使用 FILES 或者 DIRECTORY ,对于 DIRECTORY 使用比较灵活,常见需要注意的关键点如下。
后缀符号
需要注意其后缀的 / 符号。
#----- 会将目录复制成为 dst/src/{subdirs and files...}
INSTALL(DIRECTORY myproj/src DESTINATION dst)
#----- 会将目录复制成为 dst/{subdirs and files...}
INSTALL(DIRECTORY myproj/src/ DESTINATION dst)
文件过滤
可以通过参数 FILES_MATCHING 用于指定操作档案的条件,可以使用 PATTERN 或 REGEX 两种匹配方式,要注意 PATTERN 会比对全路径而不只是文件名。
INSTALL(DIRECTORY src/ DESTINATION include FILES_MATCHING PATTERN "*.h")
以上会把 src/ 底下所有文件后缀为 .h 的文件复制到 include 文件夹下,并且会保留原本目录树的结构。
另外,还可以在匹配条件后面通过 EXCLUDE 排除符合条件的文件或目录。
INSTALL(DIRECTORY myapp/ mylib DESTINATION myproj PATTERN ".git" EXCLUDE)