CMake学习1 - 初出茅庐

0

一、基本概念

1.1 构建方式

CMake 构建方式可分为两种:内部构建和外部构建。应优先选择外部构建,此方式产生的中间文件和最终文件不会污染原工程文件。

内部构建(in-source build)
是指产生的中间文件及最终目标产出物均生成至当前工作目录。由于 cmake 没有类似 clean 之类可清空生成的中间文件指令,需手动删除。因此,不推荐此方式。OpenWrt 采用此方式构建工程。

clipboard.png

外部构建(out-of-source build)
通过建立一个临时文件夹,在此文件夹内执行构建操作,将生成的中间文件和最终的目标文件均生成至此文件夹来消除对原有工程文件的污染。

clipboard.png

1.2 目录树

通过目录树变量可轻松引用项目源代码目录路径和构建后的二进制文件目录路径。

源代码树(Source Tree)包含

  • CMake 输入文件(CMakeLists.txt)
  • 程序源文件(hello.cpp)
  • 程序头文件(hello.h)

二进制树(Binary Tree)包含

  • 本地构建系统文件(Makefiles)
  • 构建过程中产生的文件(Libraries、Executables、Any other build generated files)

二者关系

  • in-source build 时,二者指向同一目录。
  • out-of-source build,二者指向不同目录。

1.3 目标对象

Target 是 CMake 中非常重要的一个概念,很多高级操作都需要提供一个 Target 对象。

Target 对象

  • add_executable 创建可执行程序对象
  • add_library 创建共享库或静态库对象

二、基本规则

2.1 工程结构

CMake 工程由 CMakeLists.txt 文件和 .h .hpp .c .cpp 等文件组成。每层目录必须包含一个 CMakeLists.txt 文件。
  • 一层目录结构
    clipboard.png
  • 多层目录结构
    clipboard.png
  • 标准目录结构
    clipboard.png

2.2 书写方式

CMake 语法支持大小写两种方式,建议统一采用指令小写参数大写的编码方式。vscode 指令小写可智能提示。
  • 指令小写

    # 所需 cmake 工具的最低版本
    cmake_minimum_required(VERSION 3.1)
    
    # 工程名称
    project(hello-world)
    
    # 生成可执行程序
    add_executable(hello-world main main.c )
  • 指令大写

    # 所需 cmake 工具的最低版本
    CMAKE_MINIMUM_REQUIRED(VERSION 3.1)
    
    # 工程名称
    PROJECT(hello-world)
    
    # 生成可执行程序
    ADD_EXECUTABLE(hello-world main main.c )

三、常用变量

3.1 系统变量

CMAKE_SOURCE_DIR
执行 cmake 命令时操作的目标目录,一般为顶层 CMakeLists.txt 文件所在目录。

CMAKE_BINARY_DIR
执行 cmake 命令时所在的执行目录。

CMAKE_CURRENT_SOURCE_DIR
当前 CMakeLists.txt 文件所在目录。

3.2 工程变量

PROJECT_SOURCE_DIR
当前工程的顶层源码所在目录,一般为 project 所在 CMakeLists.txt 文件的目录。

PROJECT_BINARY_DIR
该工程在构建后的二进制目录中对应的目录。

3.3 构建方式

CMAKE_BUILD_TYPE
指定工程构建类型,取值包括 Debug, Release, RelWithDebInfo, MinSizeRel。

BUILD_SHARED_LIBS
通过设置该全局变量为on或off,可自动生成共享库或静态库。如果 add_library 中已明确指定库类型,则无效。

3.4 搜索路径

CMAKE_MODULE_PATH
以";"分割的目录列表,指定要由其加载的 CMake 模块的 find、find_package 搜索路径。默认情况下是空的,它由项目设置。

3.5 安装变量

CMAKE_INSTALL_PREFIX
执行安装操作时,此目录将预先添加至所有安装目录之前,拼接成新的安装路径。

3.5 运行时路径

CMAKE_SKIP_BUILD_RPATH
执行构建操作时,生成的可执行程序或库是否添加 'rpath' 信息(默认不添加)。

CMAKE_BUILD_RPATH
执行构建操作时,在 “CMAKE_SKIP_BUILD_RPATH” 为 FALSE 的情况下,附加的 'rpath' 路径。

CMAKE_SKIP_INSTALL_RPATH
执行安装操作时,拷贝的可执行程序或库是否添加 'rpath' 信息(默认不添加)。

CMAKE_INSTALL_RPATH
执行安装操作时,在 “CMAKE_SKIP_INSTALL_RPATH” 为 FALSE 的情况下,附加的 'rpath' 路径。

四、常用指令

4.1 工程选项

cmake_minimum_required
指定工程所需 cmake 工具的最低版本

语法
cmake_minimum_required(VERSION <min>[...<max>] [FATAL_ERROR])

示例
cmake_minimum_required(VERSION 3.1)

project
指定工程名称,可通过 ${PROJECT_NAME} 引用工程名。工程名大小写均可。

语法
project(<PROJECT-NAME>
        [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
        [DESCRIPTION <project-description-string>]
        [HOMEPAGE_URL <url-string>]
        [LANGUAGES <language-name>...])
示例
project(SampleApp)

4.2 构建可执行程序或标准库

add_executable
构建可执行程序

语法
add_executable(<name> [WIN32] [MACOSX_BUNDLE]
           [EXCLUDE_FROM_ALL]
           [source1] [source2 ...])
示例
add_executable(${PROJECT_NAME} main.cpp utils.cpp parse.cpp)

add_library

语法
add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            [source1] [source2 ...])
示例
# 默认静态库
add_library(${PROJECT_NAME} button.cpp edit.cpp)

# 静态库
add_library(${PROJECT_NAME STATIC} button.cpp edit.cpp)

# 动态库
add_library(${PROJECT_NAME SHARED} button.cpp edit.cpp)

4.3 文件包含

target_include_directories
指定编译时查找所需头文件的目录集合

语法
target_include_directories(<target> [SYSTEM] [BEFORE]
                           <INTERFACE|PUBLIC|PRIVATE> [items1...]
                          [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
示例
target_include_directories(${PROJECT_NAME} PUBLIC
    ${App_SOURCE_DIR}/include
    ${Math_SOURCE_DIR}/include
    ${Ui_SOURCE_DIR}/include
)
说明
BEFORE 选项可将当前包含的目录附加在所有头文件包含目录的前面。

target_link_libraries
指定可执行程序或库程序链接时所需链接的库文件(此指令有多种重载方式)

语法
target_link_libraries(<target> ... <item>... ...)

示例
target_link_libraries(${PROJECT_NAME}
    Math    # 子工程
    Ui      # 子工程
    pthread # 系统库
)

include
从一个文件或模块中加载并运行 CMake 代码。

语法
include(<file|module> [OPTIONAL] [RESULT_VARIABLE <var>]
                      [NO_POLICY_SCOPE]
示例
include("../macro.cmake")

4.4 使用 pkg-config

find_package
使用 pkg-config 组件查找相关库文件的配置信息,包括头文件路径、库文件路径、版本号等信息。

语法
find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]
             [REQUIRED] [[COMPONENTS] [components...]]
             [OPTIONAL_COMPONENTS components...]
             [NO_POLICY_SCOPE])
示例
find_package(PkgConfig)

pkg_check_modules
查找验证模块并返回符合验证要求的库信息。

语法
pkg_check_modules(<prefix>
                  [REQUIRED] [QUIET]
                  [NO_CMAKE_PATH]
                  [NO_CMAKE_ENVIRONMENT_PATH]
                  [IMPORTED_TARGET [GLOBAL]]
                  <moduleSpec> [<moduleSpec>...])
示例
pkg_check_modules(GST REQUIRED gstreamer-1.0>=1.8 gstreamer-app-1.0>=1.8)

返回对象

语法
# 头文件目录
<Pacakgename>_INCLUDE_DIRS

# 库文件目录
<Packagename>_LIBRARIES

示例
$(GST_INCLUDE_DIRS)
${GST_LIBRARIES})

4.5 设置选项

add_definitions
用于实现编译器中需要-D选项的编译参数设置

语法
add_definitions(-DFOO -DBAR ...)

示例
add_definitions(-DRAPIDJSON_HAS_STDSTRING)

add_compile_options
作为 add_definitions 指令的替代者,可支持所有编译参数设置。

语法
add_compile_options(<option> ...)

示例
if (MSVC)
    # warning level 4 and all warnings as errors
    add_compile_options(/W4 /WX)
else()
    # lots of warnings and all warnings as errors
    add_compile_options(-Wall -Wextra -pedantic -Werror)
endif()

option
可视化配置界面中提供一个可选择“ON”与"OFF"的选项,默认值为“OFF”。

语法
option(<variable> "<help_text>" [value])

示例
option(LIB_TYPE "Build shared library or static library." ON)

4.6 工程安装

install target

语法
install(TARGETS targets... [EXPORT <export-name>]
        [[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
          PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE]
         [DESTINATION <dir>]
         [PERMISSIONS permissions...]
         [CONFIGURATIONS [Debug|Release|...]]
         [COMPONENT <component>]
         [NAMELINK_COMPONENT <component>]
         [OPTIONAL] [EXCLUDE_FROM_ALL]
         [NAMELINK_ONLY|NAMELINK_SKIP]
        ] [...]
        [INCLUDES DESTINATION [<dir> ...]]
        )
示例
# 安装可执行程序
install(TARGETS ${PROJECT_NAME} DESTINATION "/usr/bin")

# 安装库文件
install(TARGETS ${PROJECT_NAME} DESTINATION "/usr/lib")

install directory

语法
install(DIRECTORY dirs...
        TYPE <type> | DESTINATION <dir>
        [FILE_PERMISSIONS permissions...]
        [DIRECTORY_PERMISSIONS permissions...]
        [USE_SOURCE_PERMISSIONS] [OPTIONAL] [MESSAGE_NEVER]
        [CONFIGURATIONS [Debug|Release|...]]
        [COMPONENT <component>] [EXCLUDE_FROM_ALL]
        [FILES_MATCHING]
        [[PATTERN <pattern> | REGEX <regex>]
         [EXCLUDE] [PERMISSIONS permissions...]] [...])
示例
# 安装头文件
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/" DESTINATION "/usr/include")

说明
前面的 include/ 斜杠必须带上,不然会形成 /usr/include/include 的情况。
结尾带斜杠时只拷贝其下的所有文件和目录,不带斜杠否则包含当前目录。

五、基本语法

5.1 注释

CMake 中使用 “#” 表示注释。
# 所需 cmake 工具的最低版本
cmake_minimum_required(VERSION 3.1)

5.2 输出

message 指令用于输出正常信息、警告信息和错误信息。
# 正常信息
message(STATUS "normal message.")

# 警告信息
message(WARNING "warning message.")

# 错误信息
message(FATAL_ERROR "fatal error message.")

...

5.1 变量

可通过 set 和 ${} 分别设置和读取变量。CMake 变量也存在作用于和值拷贝与引用的关系,此处不展开。
# 设置变量
set(MY_VARIABLE "system variable or custom variable.")

# 读取变量
message("MY_VARIABLE = ${MY_VARIABLE}")

# 设置环境变量
set(ENV{JAVA_HOME} /opt/java/bin)

# 读取环境变量
message("LANG=$ENV{LANG}")

# 判断环境变量是否存在
if(NOT DEFINED ENV{JAVA_HOME})
    <command>
endif

5.2 列表

list 可用于保存多个值
# 设置列表
set(APP_SOURCE)
list(APPEND APP_SOURCE main.cpp math.cpp button.cpp)

# 查看列表
message("${APP_SOURCES}")

# 遍历列表
foreach(SOURCE ${APP_SOURCES})
    message("src = ${SOURCE}")
endforeach

5.4 函数

通过 function() 与 endfunction() 定义函数,函数参数列表、参数个数和具体参数由 ARGC、ARGV、ARGVN 表示。
# 定义函数
function(foo)
    message("there are ${ARGC} arguments: ${ARGV}")
    message("argument 0 is ${ARGV0}")
endfunction()

# 调用函数
foo(first second third fourth)

5.5 条件

Condition 用于决定判断和循环流程的走向。
if(<constant>)
True: 1, ON, YES, TRUE, Y, 非零值
False:0, OFF, NO, FALSE,N, IGNORE, NOTFOUND, 空字符串,以 -NOTFOUND 结尾。

if(<variable|string>)
if(NOT <condition>)
if(<cond1> AND <cond2>)
if(<cond1> OR <cond2>)
if(COMMAND command-name)
if(POLICY policy-id)
if(TARGET target-name)
if(<variable|string> IN_LIST <variable|string>)
if(DEFINED <name>|CACHE{<name>}|ENV{<name>})
if(TEST test-name)
if(EXISTS path-to-file-or-dirctory)
if(file1 IS_NEWER_THAN file2)
if(IS_DIRECTORY path-to-directory)
if(IS_SYMLINK file-name)
if(IS_ABSOLUTE path)
if(<variable|string> MATCHES regex)
if(<variable|string> LESS <variable|string>)
if(<variable|string> GREATER <variable|string>)
if(<variable|string> EQUAL <variable|string>)
if(<variable|string> LESS_EQUAL <variable|string>)
if(<variable|string> GREATER_EQUAL <variable|string>)
if(<variable|string> STRLESS <variable|string>)
if(<variable|string> STRGREATER <variable|string>)
if(<variable|string> STREQUAL <variable|string>)
if(<variable|string> STRLESS_EQUAL <variable|string>)
if(<variable|string> STRGREATER_EQUAL <variable|string>)
if(<variable|string> VERSION_LESS <variable|string>)
if(<variable|string> VERSION_GREATER <variable|string>)
if(<variable|string> VERSION_EQUAL <variable|string>)
if(<variable|string> VERSION_LESS_EQUAL <variable|string>)
if(<variable|string> VERSION_GREATER_EQUAL <variable|string>)
if((condition) AND (condition OR (condition)))

5.6 判断

CMake 语法中只有 if 语句没有 switch 语句。
if(<condition>)
    <commands>
elseif(<condition>) # optional block, can be repeated
    <commands>
else()              # optional block
    <commands>
endif()

5.7 循环

CMake 语法仅包含 while 语句,没有 do while、while do、until 等其它循环语句。
while(<condition>)
    <commands>
    <break>        # 跳出循环
    <continue>     # 进入下一个循环
endwhile()

参考资料

你可能感兴趣的

nikoladi · 1 天前

学习一下,很系统,多谢博主

回复

载入中...