CMake 教程
CMake 教程

CMake 教程

Created
Sep 17, 2021 10:58 AM
Tags
CMake
Category
Coding
Last Edited
Last updated July 16, 2022
Abstract
本文记录了 CMake 的常用功能、如何处理依赖以及如何打包
Related to Reading List (Column)

一、CMake 常用变量

1. CMAKE_BINARY_DIR

PROJECT_BINARY_DIR <projectname>_BINARY_DIR一样,这三个变量指代的内容是一致的,如果是 in-source 编译,指的就是工程顶层目录,如果是 out-of-source 编译,则指的是工程编译发生的目录。PROJECT_BINARY_DIR 跟其他指令稍有区别,现在,你可以理解为他们是一致的。

2. CMAKE_SOURCE_DIR

PROJECT_SOURCE_DIR <projectname>_SOURCE_DIR 三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。也就是在 in source 编译时,他跟 CMAKE_BINARY_DIR 等变量一致。PROJECT_SOURCE_DIR 跟其他指令稍有区别,现在,你可以理解为他们是一致的。

3. CMAKE_CURRENT_SOURCE_DIR

指的是当前处理的 CMakeLists.txt 所在的路径,比如上面我们提到的 src 子目录。

4. CMAKE_CURRRENT_BINARY_DIR

如果是 in-source 编译,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-of-source 编译,他指的是 target 编译目录。使用我们上面提到的 ADD_SUBDIRECTORY(src bin)可以更改这个变量的值。使用 SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径。

5.CMAKE_CURRENT_LIST_FILE

输出调用这个变量的 CMakeLists.txt 的完整路径

6. CMAKE_CURRENT_LIST_LINE

输出这个变量所在的行

7. CMAKE_MODULE_PATH

这个变量用来定义自己的 cmake 模块所在的路径。如果你的工程比较复杂,有可能会自己编写一些 cmake 模块,这些 cmake 模块是随你的工程发布的,为了让 cmake 在处理 CMakeLists.txt 时找到这些模块,你需要通过 SET 指令,将自己的 cmake 模块路径设置一下。 比如 SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) 这时候你就可以通过 INCLUDE 指令来调用自己的模块了,类似 add_subdirectory

8. EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH

分别用来重新定义最终结果的存放目录,前面我们已经提到了这两个变量。

9. PROJECT_NAME

返回通过 PROJECT 指令定义的项目名称。

二、CMake 环境变量

使用 $ENV{NAME} 指令就可以调用系统的环境变量了。 比如:MESSAGE(STATUS “HOME dir: $ENV{HOME}”) 设置环境变量的方式是: SET(ENV{变量名}值)

三、基本指令

1. ADD_DEFINITIONS

向 C/C++编译器添加 -D 定义,比如:ADD_DEFINITIONS(-DENABLE_DEBUG -DABC),参数之间用空格分割。如果你的代码中定义了#ifdef ENABLE_DEBUG #endif,这个代码块就会生效。 如果要添加其他的编译器开关,可以通过 CMAKE_C_FLAGS 变量和 CMAKE_CXX_FLAGS 变量设置。

2. ADD_DEPENDENCIES

定义 target 依赖的其他 target,确保在编译本 target 之前,其他的 target 已经被构建。 ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...)
让一个顶层目标依赖于其他的顶层目标。一个顶层目标是由命令 ADD_EXECUTABLEADD_LIBRARY,或者 ADD_CUSTOM_TARGET 产生的目标。为这些命令的输出引入依赖性可以保证某个目标在其他的目标之前被构建。

3. ADD_EXECUTABLE、ADD_LIBRARY、ADD_SUBDIRECTORY

ADD_EXECUTABLE(可执行文件名 生成该可执行文件的源文件)
源文件需要编译出的可执行文件名。 例:ADD_EXECUTABLE(hello ${SRC_LIST})
 
ADD_LIBRARY(name [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
name 属性必须全局唯一,生成的 library 名会根据 STATIC 或 SHARED 成为 name.a 或 name.lib。 这里的 STATIC 和 SHARED 可不设置,通过全局的 BUILD_SHARED_LIBS 的 FALSE 或 TRUE 来指定。 例:ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
 
ADD_SUBDIRECTORY(src_dir [binary_dir] [EXCLUDE_FROM_ALL]) 向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制的存放位置 EXCLUDE_FROM_ALL含义:将这个目录从编译过程中排除
 

4. INCLUDE

include(${CMAKE_SOURCE_DIR}/vendor/CMakeLists.txt)
引入一个新的CMake项目,ADD_SUBDIRECTORY的区别在于其不会创建一个新的变量域。

四、第三方依赖

1. 使用 ExternalProject

参考链接

基础配置

# Dependencies' .cmake include(ExternalProject) set(VENDOR_ROOT ${CMAKE_BINARY_DIR}/vendor) set(VENDOR_BUILD ${CMAKE_BINARY_DIR}/vendor/vbuild) ExternalProject_Add(ZLOG PREFIX ${VENDOR_ROOT}/zlog URL https://github.com/HardySimpson/zlog/archive/refs/tags/1.2.15.tar.gz URL_MD5 155f841543f947bf21112ae4328e7152 CONFIGURE_COMMAND pwd BUILD_IN_SOURCE 1 BUILD_COMMAND make PREFIX=${VENDOR_BUILD} INSTALL_COMMAND make PREFIX=${VENDOR_BUILD} install INSTALL_DIR ${VENDOR_BUILD} ) add_library(zlog SHARED IMPORTED) add_dependencies(zlog ZLOG) set_target_properties(zlog PROPERTIES IMPORTED_LOCATION ${VENDOR_BUILD}/lib/libzlog.a INCLUDE_DIRECTORIES ${VENDOR_BUILD}/include) # Origin .cmake # build third-party package include(${CMAKE_SOURCE_DIR}/vendor/CMakeLists.txt) add_executable(rdarm rdarm.c) target_link_libraries(rdarm zlog) # 告知依赖顺序 add_dependencies(rdarm zlog)
notion image
notion image

下载

  • URL
notion image
  • Git
notion image

将依赖的静态库打包进产出的静态库中

add_custom_target(combined ALL COMMAND rm librdarm.a | ${CMAKE_AR} crsT librdarm.a $<TARGET_FILE:rdarm-lib> $<TARGET_FILE:zlog> $<TARGET_FILE:xxhash> $<TARGET_FILE:sds>) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/librdarm.a DESTINATION lib/)

2. 使用 PKG

其实质上是通过检测系统中的 pkg-config 是否存在指定的 .pc 文件,来寻找软件的。
include(FindPkgConfig) pkg_check_modules(LIBNETFILTER_QUEUE REQUIRED libnetfilter_queue) # 会生成三个环境变量 LIBNETFILTER_QUEUE_FOUND LIBNETFILTER_QUEUE_LIBRARIES LIBRARY_DIRS LIBNETFILTER_QUEUE_INCLUDE_DIRS add_dependencies(nat_experiment LIBNETFILTER_QUEUE) link_directories(${LIBDPDK_LIBRARY_DIRS}) target_link_libraries(nat_experiment ${LIBNETFILTER_QUEUE_LIBRARIES}) target_include_directories(nat_experiment PRIVATE ${LIBNETFILTER_QUEUE_INCLUDE_DIRS})

五、CPack

set(CMAKE_INSTALL_PREFIX "/opt/nat_experiment") set(CPACK_SET_DESTDIR ON) #以下为RPM信息的设置,包名,概述,供应者,版本,分组等信息,通过其变量名称可以知道意思 set(CPACK_PACKAGE_NAME "nat-experiment") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A NAT application") set(CPACK_PACKAGE_VENDOR "MESALAB") set(CPACK_PACKAGE_VERSION "0.0.1") set(CPACK_PACKAGE_VERSION_MAJOR "0") set(CPACK_PACKAGE_VERSION_MINOR "0") set(CPACK_PACKAGE_VERSION_PATCH "1") set(CPACK_RPM_PACKAGE_URL "https://git.mesalab.cn/Chenming/nat_experiment") set(CPACK_RPM_PACKAGE_DESCRIPTION "A NAT application") set(CPACK_RPM_PACKAGE_LICENSE "MIT") set(CPACK_RPM_PACKAGE_EPOCH 1) # [可选] release 默认为 1 set(CPACK_RPM_PACKAGE_RELEASE 1) # [可选] 指定生成的 rpm 文件名称 set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_RPM_PACKAGE_RELEASE}.el7.centos.${CMAKE_SYSTEM_PROCESSOR}.rpm") set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") # 设置默认生成器,RPM生成器会构建RPM安装包,其它还有TGZ/ZIP等 set(CPACK_GENERATOR "RPM") # 配置依赖检测 # set(CPACK_RPM_PACKAGE_REQUIRES_PRE "libmnl, libnfnetlink-devel, libnetfilter_queue-devel") # 引入CPack模块,放在根目录的 CMake 配置文件最后,不能少 include(CPack)