CMake工程管理实战

本文详细介绍了CMake作为跨平台构建系统在工程管理中的应用,从基础工程到高级特性,涵盖添加配置头文件、库、编译选项、安装支持、测试和系统模块自检等。通过实例演示如何编写CMakeLists.txt文件,实现项目编译、安装、测试等流程,帮助开发者快速上手CMake。
摘要由CSDN通过智能技术生成

本文翻译自cmake tutorial:https://cmake.org/cmake-tutorial/

本文不介绍cmake命令使用方法,也不讲CMakeLists.txt的语法,有需要的读者可以看我另外相关的文章即可(此为实战篇)。废话少说,直接上主菜(复杂的解释也略过了)。

有关常见的问题,可上官网Wiki查看:https://gitlab.kitware.com/cmake/community/wikis/FAQ。

一、简介

1.1 简介

你或许听过好几种Make工具,如GNU Make、QT的qmake、微软的MS nmake、BSD Make(pmake)等,遵循着不同的规范和标准,所执行的Makefile格式也不同。如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用Make工具,必须为不同的Make工具编写不同的Makefile。

CMake是一个比Make工具更高级的编译配置工具,是一个跨平台的、开源的构建系统(BuildSystem)。它可以根据不同平台、不同的编译器,生成相应的Makefile或者vcproj项目。通过编写CMakeLists.txt,可以控制生成的Makefile,从而控制编译过程。CMake自动生成的Makefile不仅可以通过make命令构建项目生成目标文件,还支持安装(make install)、测试安装的程序是否能正确执行(make test,或者ctest)、生成当前平台的安装包(make package)、生成源码包(make package_source)、产生Dashboard显示数据并上传等高级功能,只要在CMakeLists.txt中简单配置,就可以完成很多复杂的功能,包括写测试用例。如果有嵌套目录,子目录下可以有自己的CMakeLists.txt。

CMake允许开发者编写一种平台无关的CMakeList.txt文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化Makefile和工程文件,如:为Unix平台生成Makefile文件(使用GCC编译),为Windows MSVC生成projects/workspaces(使用VS IDE编译)或Makefile文件(使用nmake编译)。使用CMake作为项目架构系统的知名开源项目有VTK、ITK、KDE、OpenCV、OSG等。

下面是一篇非常好的介绍:https://blog.51cto.com/9291927/2115399

1.2 CMake管理工程

在Linux平台下使用CMake生成Makefile并编译的流程如下:

  1. 编写CMake配置文件CMakeLists.txt;
  2. 执行ccmake PATH配置编译选项,如安装目录、编译工具、编译选项等,PATH是CMakeLists.txt所在的目录;
  3. 执行命令cmake PATH生成Makefile,PATH是CMakeLists.txt所在的目录;
  4. 使用make命令进行编译。

CMake管理工程就是这么简单,其难点在于如何编写CMakeLists.txt文件。

下面从简单的工程实例到复杂的工程实例带你快速上手CMake。

二、基本工程

最简单的工程是编译一个源文件,并生成一个可执行文件。CMakeLists.txt仅仅只需要3行:

cmake_minimum_required(VERSION 2.8)
project(Tutorial)
add_executable(Tutorial tutorial.cxx)

对应的tutorial.cxx源码用于计算输入数据的平方根:

// A simple program that computes the suqare root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(int argc, char *argv[])
{
	if (argc < 2)
	{
		fprintf(stdout, "Usage: %s number\n", argv[0]);
		return 1;
	}

	double inputValue = atof(argv[1]);
	double outputValue = sqrt(inputValue);
	fprintf(stdout, "The square root of %g is %g\n", inputValue, outputValue);

	return 0;
}

将tutorial.cxx与CMakeLists.txt放在同一目录,创建build目录,并编译、运行,结果如下:

$ mkdir build
$ ls
build  CMakeLists.txt  tutorial.cxx
$ cd build/
$ cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /work/study/Studied_Module/Module/cmake/basic_point/build
$ make
Scanning dependencies of target Tutorial
[ 50%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.o
[100%] Linking CXX executable Tutorial
[100%] Built target Tutorial
$ ./Tutorial 
Usage: ./Tutorial number
$ ./Tutorial 9
The square root of 9 is 3

三、添加配置头文件

有时候我们需要为我们的代码添加版本信息,或者其他相关的配置信息。我们当然可以通过直接添加配置的头文件来实现,不过通过CMakeLists.txt来实现会更加的灵活。下面是基于前面例子实现添加版本信息的CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)
project(Tutorial)
# The version number
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)

# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
 "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
 "${PROJECT_BINARY_DIR}/TutorialConfig.h"
)

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")

# add the executable
add_executable(Tutorial tutorial.cxx)

上面指定TutorialConfig.h由TutorialConfig.h.in文件生成。TutorialConfig.h.in的内容如下:

// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

当CMake配置这个头文件时,@Tutorial_VERSION_MAJOR@和@Tutorial_VERSION_MINOR@的值将被CMakeLists.txt文件中的值替换(注意两边的名字要一致)。

接下来我们修改tutorial.cxx源码,看是如何使用CMakeLists.txt定义的版本的:

// A simple program that computes the suqare root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "TutorialConfig.h"

int main(int argc, char *argv[])
{
	if (argc < 2)
	{
		fprintf(stdout, "%s Version %d.%d\n", argv[0],
			Tutorial_VERSION_MAJOR, Tutorial_VERSION_MINOR);
		fprintf(stdout, "Usage: %s number\n", argv[0]);
		return 1;
	}

	double inputValue = atof(argv[1]);
	double outputValue = sqrt(inputValue);
	fprintf(stdout, "The square root of %g is %g\n", inputValue, outputValue);

	return 0;
}

建立build目录,再次编译结果如下:

$ mkdir build
$ ls
build  CMakeLists.txt  TutorialConfig.h.in  tutorial.cxx
$ cd build/
$ cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /work/study/Studied_Module/Module/cmake/basic_version/build
$ make
Scanning dependencies of target Tutorial
[ 50%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.o
[100%] Linking CXX executable Tutorial
[100%] Built target Tutorial
$ ./Tutorial 
./Tutorial Version 1.0
Usage: ./Tutorial number
$ ./Tutorial 16
The square root of 16 is 4

总结:添加配置头文件的流程如下:

  1. 在CMakeLists.txt中通过set命令设置自己命名的变量;
  2. 添加配置文件说明:
configure_file (
 "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
 "${PROJECT_BINARY_DIR}/TutorialConfig.h"
)
  1. 创建配置文件*.h.in,并使用@variable@来访问第一步在CMakeLists.txt通过set定义的变量,使用C/C++的标准#define来定义;
  2. 在C/C++源码中通过include包含编译生成的配置头文件;
  3. 在C/C++源码中直接使用#define宏定义的名称。

四、添加一个库

继续基于前面的实例,我们将计算平方根的方法提取出来,做成一个mysqrt.cxx源文件库放在MathFunctions目录,我们提供的算法名称为mysqrt()。源码如下:

$ cd MathFunctions/
$ cat mysqrt.h 
#ifndef MYSQRT_H__
#define MYSQRT_H__

double mysqrt(double x);

#endif

$ cat mysqrt.cxx 
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

double mysqrt(double x)
{
	double g = x;
	while(fabs(g*g - x) > 0.000001)
	{
		g = (g+x/g)/2;
	}
	return g;
}

在MathFunctions目录中添加子CMakeLists.txt,编译生成库文件:

$ cat CMakeLists.txt 
cmake_minimum_required(VERSION 2.8)
project(MathFunctions)
add_library(MathFunctions SHARED mysqrt.cxx)

接下来修改顶层CMakeLists.txt,添加子目录MathFunctions的编译支持,添加头文件依赖路径,并添加依赖的库说明:

include_directories("${PROJECT_SOURCE_DIR}/MathFunctions")
add_library(MathFuncions)

# add the executable
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial MathFunctions)

最后,修改tutorial.cxx源码,添加代码调用我们自己的mysqrt()函数(这里同时调用,还可以验证我们的算法是否正确):

    double inputValue = atof(argv[1]);
	double outputValue = sqrt(inputValue);
	fprintf(stdout, "The square root of %g is %g\n", inputValue, outputValue);
	outputValue = mysqrt(inputValue);
	fprintf(stdout, "The square root of %g is %g\n", inputValue, outputValue);

五、添加自定义编译选项

继续接着前面的实例,我们添加了自己的mysqrt()函数,但是我们并不一定都是使用自己的mysqrt()函数,当系统提供了sqrt()时,我们肯定希望调用系统标准的函数。也就是说,我们需要根据系统情况,动态选择调用方法。

我们当然可以通过在头文件中添加编译选项来实现,不过通过CMakeLists.txt来实现将更加灵活有趣。

首先,我们定义一个编译宏USE_MYMATH(在顶层CMakeLists.txt):

# should we use our own math functions ?
option(USE_MYMATH
    "Use tutorial provided math implementation" ON)

这将告诉CMake默认打开USE_MYMATH,当然也可以通过ccmake或者编译选项来重新配置。接下来我们在顶层CMakeLists.txt同时添加依赖库的说明:

# add the MathFunctions library ?
#
if (USE_MYMATH)
    include_directories("${PROJECT_SOURCE_DIR}/MathFunctions")
    add_subdirectory(MathFunctions)
    set(EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
#endif(USE_MYMATH)

# add the executable
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial ${EXTRA_LIBS})

注意:上面添加的USE_MYMATH选项的option命令,必须在configure_file命令之前,否则其默认ON的配置将会无效

接下来,修改tutorial.cxx源代码,添加条件编译代码:

// A simple program that computes the suqare root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "TutorialConfig.h"

#ifdef USE_MYMATH
#include "mysqrt.h"
#endif

int main(int argc, char *argv[
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值