整理常用的示例。
平台依赖
主要是用于检查平台的头文件、函数等是否支持。
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)