2401cmake,学习cmake1

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_MAJORTutorial_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中,替换atofstd::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-guiccmake中显示这一选项,默认值为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.如果交换这两行会怎样?

运行cmakecmake-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}"
)

完成后,运行cmakecmake-gui配置项目,并在build目录下cmake --build .构建运行即可.

你可能感兴趣的:(cmake,c++,cmake)