1步
:一个基本出发点项目最顶级CMakeLists.txt
必须先使用cmake_minimum_required()
命令指定最低CMake
版本.这创建策略设置,并确保以下CMake
函数使用兼容版本的CMake
运行.
最基础
项目是基于源码
的一个可执行构建
.对简单项目
.三行CMakeLists.txt
就满足了.
在步1
路径下创建如下CMakeLists.txt
文件:
cmake_minimum_required(VERSION 3.10)
//设置项目名
project(Tutorial)
//添加可执行文件
add_executable(Tutorial tutorial.cxx)
要启动
一个项目,使用project()
命令来设置
项目名.
每一项目
都需要此调用
,且应在cmake_minimum_required()
后立即调用.
而add_executable()
命令告诉CMake
使用指定的源码文件
创建可执行文件
.
1
2
3
注意,在CMakeLists.txt
文件中,命令都使用小写.CMake
命令支持大小写
混用.tutorial.cxx
的源码在步1
目录下,可用它计算平方根.
先给项目和可执行文件
提供版本号
.尽管可在源码
中添加版本号
,但使用CMakeLists.txt
更灵活.
首先,修改CMakeLists.txt
,用project()命令来设置
项目名和版本号.
cmake_minimum_required(VERSION 3.10)
//设置项目名和版本
project(Tutorial VERSION 1.0)
首先,修改CMakeLists.txt
文件以用project()
命令设置项目名和版本号
.
调用project()
命令时,CMake
会在后台定义Tutorial_VERSION_MAJOR
和Tutorial_VERSION_MINOR
.
CMake
生成该头文件
时,会自动替换@Tutorial_VERSION_MAJOR@
和@Tutorial_VERSION_MINOR@
的值.
然后制定头文件
来传递版本号
到源码里:
使用configure_file()
复制输入文件
,并替换
了指定的CMake
变量:
configure_file(TutorialConfig.h.in TutorialConfig.h)
创建一个包含要替换
多个变量的输入文件
.这些变量
有类似@VAR@
的特殊语法.
然后,使用configure_file()
命令复制输入文件
到给定输出文件
,并替换这些变量
为CMakelists.txt
文件中VAR
的当前值
.
1
2
3
因为会把配置文件
写入项目
的二进制目录
,因此必须添加该目录
到路径列表
中,以搜索
包含文件.
注意:这里,互换
引用项目构建和项目二进制目录
.这些是相同
的,不表明引用bin/
目录.
使用target_include_directories()
来指定可执行目标
应在何处查找包含文件
,这里.
在CMakeLists.txt
文件尾写入:
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
)
有时,源码
中提供CMakelists.txt
文件中定义
的变量
会很有用.此时,如想打印项目版本
.
接着,TutorialConfig.h.in
定义接受从configure_file()
传递变量的版本号
,并创建输入头文件
:
//教程的`配置选项和设置`
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
接着,需要修改tutorial.cxx
以包含配置的TutorialConfig.h
头文件
#include "TutorialConfig.h"
最后,如下更新tutorial.cxx
来打印可执行文件名和版本号
:
if (argc < 2) {
//报告版本
std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
<< Tutorial_VERSION_MINOR << std::endl;
std::cout << "Usage: " << argv[0] << " number" << std::endl;
return 1;
}
C++
标准接着,在tutorial.cxx
中,替换atof
为std::stod
来给项目增加一些C++11
特性.同时,移除#include
.
const double inputValue = std::stod(argv[1]);
要在CMake
代码中显式
声明,以使用正确配置
.最简单方式是,在CMake
中用CMAKE_CXX_STANDARD
以启用
支持指定C++
版本标准.
这里.将CMakeLists.txt
中的CMAKE_CXX_STANDARD
设为11
,CMAKE_CXX_STANDARD_REQUIRED
设为True
.并在add_executable
前声明CMAKE_CXX_STANDARD
.
1
2
3
cmake_minimum_required(VERSION 3.10)
//设置项目名和版本
project(Tutorial VERSION 1.0)
//指定`C++`标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
运行cmake
可执行文件,或cmake-gui
来配置项目,然后用所选构建
工具构建它.
如,从命令行中,要进入Help/guide/tutorial
目录下,并创建一个build
目录:
mkdir 步1构建
之后,进入build
目录,然后运行CMake
来配置项目,并生成原生构建系统
:
cd 步1构建
cmake ../步1
然后调用该构建系统
来实际编译/链接项目
:
cmake --build .
最后,试用下述命令
来使用
新构建的Tutorial
:
Tutorial 4294967296
Tutorial 10
Tutorial
步2
:添加库现在给项目
添加一个包含计算数字平方根
的实现库
.可执行文件
就可用库
而非编译器提供的平方根函数
.
这里,在MathFunctions
子目录下放置
库.该目录已包含了一个MathFunctions.h
头文件,也包含了一个mysqrt.cxx
源文件.
源文件
中包含提供了编译器中sqrt
相近功能的叫mysqrt
的函数.
在MathFunctions
目录中,新增下面单行CMakeLists.txt
文件:
add_library(MathFunctions mysqrt.cxx)
加库
加子目录
目标包含
目标链接
项目源
为了使用新库,在顶级的CMakeLists.txt
中加入add_subdirectory()
来构建
库.
给可执行文件
加入新库
,并按包含目录
添加MathFunctions
,这样就可查询得到mqsqrt.h
头文件了.顶级CMakeLists.txt
的最后几行
应该如下:
//添加`MathFunctions`库
add_subdirectory(MathFunctions)
//添加可执行文件
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial PUBLIC MathFunctions)
//在`包含文件`的`搜索路径`中添加`二叉树`,以便找到`TutorialConfig.h`
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
接着让MathFunctions
库可作为可选项
.在大型项目
中这很常见.第一步,是在顶层CMakeLists.txt
中增加选项
:
option(USE_MYMATH "用这里实现" ON)
//配置头文件以把某些`CMake`设置传递到源码
configure_file(TutorialConfig.h.in TutorialConfig.h)
会在cmake-gui
或ccmake
中显示这一选项
,默认值为ON
,用户也可修改它.会在缓存
中存储
这一选项,用户无需每次
都设置.
下一项更改是,把MathFunctions
库的构建和链接
设置成可选
.在顶级CMakeLists.txt
尾,如下修改:
if(USE_MYMATH)
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()
//添加可执行文件
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
//在包含文件的搜索路径中添加二叉树,以便找到`TutorialConfig.h`
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
${EXTRA_INCLUDES}
)
EXTRA_LIBS
变量,收集了之后
可在可执行文件
中链接
的可选库
.EXTRA_INCLUDES
变量也相应的收集可选头文件
.
处理很多可选项
时,这是经典处理方式
.下一步会用新方式
来做.
相应源码
改动就比较直接
了.首先,在tutorial.cxx
中,如果需要则包含MathFunctions.h
:
#ifdef USE_MYMATH
//包括`"MathFunctions.h"`
#endif
然后在同一个文件
中,让USE_MYMATH
变量控制函数的选择
:
#ifdef USE_MYMATH
const double outputValue = mysqrt(inputValue);
#else
const double outputValue = sqrt(inputValue);
#endif
因为现在源码
中需要USE_MYMATH
变量,可在TutorialConfig.h.in
文件中加入:
#cmakedefine USE_MYMATH
为什么在USE_MYMATH
选项后,配置TutorialConfig.h.in
.如果交换这两行
会怎样?
运行cmake
或cmake-gui
配置项目,然后构建
,再运行构建
出的可执行文件
.
现在更新USE_MYMATH
的值.最好是使用cmake-gui
或终端中的ccmake
.或如果想在命令行
中修改这一选项
:
cmake ../步2 -DUSE_MYMATH=OFF
重构然后运行.
哪个函数结果更好,sqrt
还是mysqrt
?
步3
:对库添加使用依赖使用依赖
可更好控制库或可执行程序使用的链接和包含
.CMake
也提供了更充分控制可及属性
.控制依赖的重要命令
包括:
target_compile_definitions
target_compile_options
target_include_directories
target_link_libraries
用现代CMake
的方式重构步2
中的依赖部分.
首先除了MathFunctions
自身,明确链接到MathFunctions
的对象都需要包含当前源目录
.
所以它可作为一个INTERFACE
使用依赖.
记住INTERFACE
指的是那些消费者
需要而生产者
不需要的东西.在MathFunctions/CMakeLists.txt
尾加入:
target_include_directories(MathFunctions
INTERFACE${CMAKE_CURRENT_SOURCE_DIR}
)
现在已指定了MathFunctions
的使用依赖,就可安全地移除顶级CMakeLists.txt
文件中的EXTRA_INCLUDES
变量:
if(USE_MYMATH)
add_subdirectory(MathFunctions)
list(APPEND EXTRA_LIBS MathFunctions)
endif()
及:
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
)
完成后,运行cmake
或cmake-gui
来配置
项目,并在build
目录下cmake --build .
构建运行即可.