CMake教程

CMake 教程

1. 运行CMake

1.1 构建项目

在构建项目前建议创建一个build目录,以下是一个经典的Cmake构建过程:

~/package $ mkdir build
~/package $ cd build
~/package/build $ cmake ..
~/package/build $ make

make命令也可以使用cmake --build来代替

以下任何一条命令都可以执行安装:

# From the build directory (pick one)
~/package/build $ make install
~/package/build $ cmake --build . --target install
~/package/build $ cmake --install . # CMake 3.15+ only

# From the source directory (pick one)
~/package $ make -C build install
~/package $ cmake --build build --target install
~/package $ cmake --install build # CMake 3.15+ only

1.2 选择编译器

选择编译器必须在第一次运行时在空的目录中进行,以clang编译器为例:

~/package/build $ CC=clang CXX=clang++ cmake ..

1.3 指定生成器

可以选择的构建工具有很多,make工具通常是默认的。可以使用cmake --help来查看系统支持的生成器

也可以使用-G"My tool"选择构建工具,这同样需要在第一次运行Cmake之前就指定构建工具

可以使用环境变量CMAKE_GENERATOR来控制默认的生成器

1.4 标准选项

以下是一些常用的cmake选项:

  • -DCMAKE_BUILD_TYPE= 从Release、RelWithDebInfo、Debug等参数中选择
  • -DCMAKE_INSTALL_PREFIX= 安装位置,安装在UNIX系统中通常是/usr/local,用户目录通常是~/.local,或者也可以另选一个文件夹
  • -DBUILD_SHARED_LIBS= 可以设置该选项为onoff来控制共享库的默认值
  • -DBUILD_TESTING= 启用测试的常用名称

1.5 调试CMake文件

可以使用-trace选项来打印CMake运行的每一行,由于它过于冗长,CMake 3.7 添加了 --trace-source="filename" 选项,这可以打印出想看的特定文件运行时执行的每一行。

2. CMake基础知识

2.1 最低版本要求

这是每个 CMakeLists.txt 都必须包含的第一行:

cmake_minimum_required(VERSION 3.1)

从CMake 3.12开始,版本号可以声明为一个范围,如VERSION 3.1...3.15,这意味着这个工程最低可以支持3.1版本,最高可以支持3.15版本

当开始一个新的项目时,推荐这样写:

cmake_minimum_required(VERSION 3.7...3.21)

if(${CMAKE_VERSION} VERSION_LESS 3.12)
    cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
endif()

当CMake的版本低于3.12时,则CMake将会被设置为当前的版本

2.2 设置一个项目

每个顶层CMakeLists文件都应当有类似下面这行:

project(MyProject VERSION 1.0
                  DESCRIPTION "Very nice project"
                  LANGUAGES CXX)

这行表面项目的名称为MyProject,项目版本号为1.0,项目描述是"Very nice project",项目的语言为CXX(可以是C、CXX、Fortran、ASM、CUDA、SWIFT)

2.3 生成一个可执行文件

add_executable(one two.cpp three.h)

one是生成的可执行文件的名称,也是想创建的CMake目标的名称;two.cpp是源文件的列表,可以添加多个;three.h是头文件,通常可以忽略

2.4 生成一个库

add_library(one STATIC two.cpp three.h)

生成一个库时,可以选择库的类型,可以是 STATIC, SHARED或者MODULE,如果不选择,CMake将会通过BUILD_SHARED_LIBS 的值来选择构建STATIC还是SHARED类型的库

2.5 指定目标信息

target_include_directories(one PUBLIC include)

为目标one包含一个目录include,PUBLIC对于二进制目标没有什么含义,但是对于库来说,它让CMake知道,任何链接到这个目标的目标也必须包含这个目录,其他选项还有PRIVATE以(只影响当前目标,不影响依赖)及INTERFACE(只影响依赖)

将目标链接起来:

add_library(another STATIC another.cpp another.h)
# 将one库链接到目标another
target_link_libraries(another PUBLIC one)
cmake_minimum_required(VERSION 3.8)

project(Calculator LANGUAGES CXX)

add_library(calclib STATIC src/calclib.cpp include/calc/lib.hpp)
target_include_directories(calclib PUBLIC include)
target_compile_features(calclib PUBLIC cxx_std_11)

add_executable(calc apps/calc.cpp)
target_link_libraries(calc PUBLIC calclib)

3. 变量与缓存

3.1 本地变量

声明本地变量的方式如下:

set(MY_VARIBALE "value")

变量名通常全部用大写,变量值跟在其后。可以通过 ${} 来解析一个变量,例如 ${MY_VARIABLE}

CMake有作用域的概念,在声明一个变量后,只可以在它的作用域内访问这个变量。

列表就是包含一系列变量:

set(MY_LIST "one;two")

需要注意的是,在 CMake 中如果一个值没有空格,那么加和不加引号的效果是一样的。

3.2 缓存变量

CMake 提供了一个缓存变量来允许从命令行中设置变量。CMake 中已经有一些预置的变量,像 CMAKE_BUILD_TYPE 。如果一个变量还没有被定义,可以这样声明并设置它:

set(MY_CACHE_VARIABLE "VALUE" CACHE STRING "Description")
# 定义声明缓存变量时可以不使用加上FORCE

修改缓存变量:

set(MY_CACHE_VARIABLE "MODIFIED" CACHE STRING "MODIFY VARIABLE")

3.3 环境变量

可以通过 set(ENV{variable_name} value)$ENV{variable_name} 来设置和获取环境变量

3.4 缓存

缓存实际上就是个文本文件CMakeCache.txt ,当运行 CMake 构建目录时会创建它。 CMake 可以通过它来记住设置的所有东西,因此可以不必在重新运行 CMake 的时候再次列出所有的选项。

3.5 属性

CMake 也可以通过属性来存储信息。这就像是一个变量,但它被附加到一些其他的物体 ( item ) 上,像是一个目录或者是一个目标。一个全局的属性可以是一个有用的非缓存的全局变量。许多目标属性都是被以 CMAKE_ 为前缀的变量来初始化的。

set_property(TARGET TargetName
             PROPERTY CXX_STANDARD 11)

set_target_properties(TargetName PROPERTIES
                      CXX_STANDARD 11)

4. 流程控制

CMake的if语句:

if(variable)
    # If variable is `ON`, `YES`, `TRUE`, `Y`, or non zero number
else()
    # If variable is `0`, `OFF`, `NO`, `FALSE`, `N`, `IGNORE`, `NOTFOUND`, `""`, or ends in `-NOTFOUND`
endif()
# If variable does not expand to one of the above, CMake will expand it then try again

if语句中的条件既可以是变量名,也可以是使用${}进行解析

if("${variable}")  # 注意这里加了引号,防止多次解析
    # True if variable is not false-like
else()
    # Note that undefined variables would be `""` thus false
endif()

if条件还可以使用一些关键字,例如:

  • 一元的: NOT, TARGET, EXISTS (文件), DEFINED, 等。
  • 二元的: STREQUAL, AND, OR, MATCHES ( 正则表达式 ), VERSION_LESS, VERSION_LESS_EQUAL ( CMake 3.7+ ), 等。

5. 宏定义与函数

CMake可以定义自己的函数和宏 functionmacro 。函数和宏只有作用域上存在区别,宏没有作用域的限制。所以说,如果你想让函数中定义的变量对外部可见,你需要使用 PARENT_SCOPE 来改变其作用域。

一个简单函数示例:

function(SIMPLE REQUIRED_ARG)
    message(STATUS "Simple arguments: ${REQUIRED_ARG}, followed by ${ARGN}")
    set(${REQUIRED_ARG} "From SIMPLE" PARENT_SCOPE)
endfunction()

simple(This Foo Bar)
message("Output: ${This}")

一个简单宏示例:

macro(macro1)
	message(STATUS "macro1 called!")
endmacro()

macro1()

函数和宏的所有参数都会被存储在ARGN这个变量中

注意:函数名和宏名并不区分大小写!

posted @   N1rv2na  阅读(65)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
点击右上角即可分享
微信分享提示