[笔记]Modern CMake 2:基本介绍

本文详细介绍了CMake的基本用法,包括设置最低版本、创建project、生成可执行程序和library、添加目标的包含目录、变量与缓存管理、CMake编程技巧如控制流、生成表达式、宏和函数的使用。强调了CMakeLists.txt的编写规范和最佳实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Modern CMake 2:基本介绍

最低版本

CMakeLists.txt的第一行通常都是:

cmake_minimum_required(VERSION 3.1)

请注意,cmake_minimun_required是不区分大小写的,但根据Modern CMake 1:Modern CMake简介中的正确模式所言,函数名需要使用小写。在CMake3.12中,对最低版本的要求可以是一个区间,比如VERSION 3.1…3.12,因此,在新工程中我们应该这写:

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

设置project

设置project通常看起来像这样:

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

可选语言有C,CXX,Fortran,CUDA(CMAKE3.7+)。默认的是C CXXDESCRIPTION是在CMake3.9引入的,可以对project进行描述。

生成可执行程序

add_executable(one two.cpp three.h)

one即时exe的名字,也是CMake target的名字。紧接着是源代码文件列表。

生成library

生成library,使用函数add_library,如:

add_library(one STATIC two.cpp three.h)

我们可以选择生成库的类型,如STATICSHAREDMODULE。默认选项是BUILD_SHARED_LIBS,将根据其值是否为真决定是STATIC还是SHARED

给target添加包含目录

target_include_directories(one PUBLIC include)

target_include_directories把include目录添加给target。PUBLIC关键字对exe来说没有太大的意义;对于library,可以使CMake知道任何target链接此target的时候也同样需要include目录。另一关键字是PRIVATE(仅影响到当前target,不传递给依赖项),INERFACE(仅影响依赖项)。

链接目标,我们可以这么写:

add_library(anthor STATIC anthor.cpp anthor.h)
target_link_librayies(anthor PUBLIC one)

target可以包含目录,链接库(或链接目标),编译选项,编译定义,编译特性等。

变量和缓存

局部变量

set(MY_VARIABLE "value")
set(MY_LIST1 "one" "two")
set(MY_LIST2 "one;two")

变量通常用大写字母表示,变量值紧跟变量后面。访问变量使用${},比如${MY_VARIABLE}
CMake有作用域的概念,在出作用域(比如子目录下)后,变量将不再存在。当使用${}引用变量时,要小心空格。尤其是含有空格的paths,引用时一定要用"${PATH}"而不是${PATH}.

Cache Variables

CMake提供Cache Variables用于从命令行设置变量。CMake提供了一些内置的Cache Variables,比如CMAKE_BUILD_TYPE,自定义格式:

set(MY_CACHE_VARIABLE "value" CACHE STRING "Description")

Bool变量

option(MY_OPTION "This is settable from the command line" OFF)

Bool变量值可设置为ON或者OFF

更多CMake变量参见:cmake-variables

环境变量

set(ENV{variable_name} value)
$ENV{variable_name}

注意,Modern CMake建议应尽可能的避免使用环境变量

缓存文件

CMake以纯文本的形式把设置存储在缓存文件CMakeCache.txt中,使用缓存文件可避免每次运行cmake的时候从新输入你的设置。

属性

设置属性有两种方式:

set_property(TARGET TargetName
    PROPERTY CXX_STANDARD 11)

set_target_property(TargetName PROPERTIES
    CXX_STANDARD 11)

第一种方式更常用一些,你可以一次性的设置完成targets/files/tests等。第二种用于单独给一个target设置多种属性。获取属性的方式为:

get_property(ResultVariable TARGET TargetName PROPERTY CXX_STANDARD)

更多CMake属性参见cmake-property

CMake 编程

控制流

if语句例子:

if(variable)
    # 'ON','YES','TRUE','Y',或者非0数字
else()
    # `0`, `OFF`, `NO`, `FALSE`, `N`, `IGNORE`, `NOTFOUND`, `""`, 或者以 `-NOTFOUND结尾
endif()
# 如果变量没有匹配上面任何一个,CMake将重新尝试

在CMake3.1+,你还可以这么写:

if("${variable}")
    # True
else()
    # False
endif()

生成表达式

生成表达式的功能非常强大。大部分的CMake命令发生在configure阶段,包括if控制流,但是生成表达式却可以在build阶段执行一些逻辑操作!

最简单的生成表达式是信息表达式,通常像这样$<KEYWORD>,另外一种像这样$<KEYWORD:value>,其中KEYWORD是控制流关键字,值是0或者1。

比如,你只想在DEBUG配置中加入一个编译选项,可以这么做:

target_compile_option(MyTarget PRIVATE "$<$<CONFIG:DEBUG>:--my-flag>")

这是一种比使用专有的*_DEBUG变量更新的,更好的方式。

还有一种比较常用的生成式表达式:

target_include_directories(
    MyTarget
    PUBLIC
    $<BUILD_INTERFACE:"${CMAKE_CURRENT_SOURCE_DIR}/include">
    $<INSTALL_INTERFACE:include>
    )

宏和函数

在CMake中定义宏和函数是非常简单的。宏和函数唯一的区别在于作用域,宏是没有作用域的。如果你希望函数中设置的变量可以给外部使用,那么你需要PARENT_SCOPE标记。一个函数的简单例子如下:

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

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

请注意,CMake中函数并没有返回值。所有参数都在ARGN中.

参数

我们可以通过内置函数cmake_parse_arguments使用CMake的命名变量系统。一个简单例子:

function(COMPLEX)
cmake_parse_arguments(
    COMPLEX PREFIX
    "SINGLE;ANOTHER"
    "ONE_VALUE;ALSO_ONE_VALUE"
    "MULTI_VALUES"
    ${ARGN}
    )
endfunction()

complex(SIGNLE ONE_VALUE value MULTI_VALUES some other values)

此函数调用后,各个变量如下:

COMPLEX_PREFIX_SINGLE = TRUE
COMPLEX_PREFIX_ANOTHER = FALSE
COMPLEX_PREFIX_ONE_VALUE = "value"
COMPLEX_PREFIX_ALSO_ONE_VALUE = <UNDEFINED>
COMPLEX_PREFIX_MULTI_VALUES = "some;other;values"

代码交互

CMake允许你在代码中访问参数,这是通过configure_file实现的。她是通过把.in结尾的文件的内容替换到代码中实现的。这种方法被广泛应用,一个简单例子:

Version.h.in

#pragma once

#define MY_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define MY_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define MY_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define MY_VERSION_TWEAK @PROJECT_VERSION_TWEAK@
#define MY_VERSION "@PROJECT_VERSION@"

CMake lines:

configure_file(
    "${PROJECT_SOURCE_DIR}/include/Version.h.in"
    "${PROJECT_SOURCE_DIR}/include/Version.h")

当然,构建的时候你需要把此包含目录包含在你的工程中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值