CMake教程 (一)
本文所有示例来源于CMake官方教程:https://cmake.org/cmake-tutorial/
CMake简介
在开源软件的世界里,使用Makefile来管理源码、定义生成规则是事实上的标准。作为工程师,Makefile的工作原理是必须要掌握的,但在实践中,却通常不需要手写Makefile。autotool系列(autoscan, autoconf等)以及本文所说的CMake,都是辅助我们生成Makefile的工具。
使用工具生成的Makefile首先都满足一定的规范,避免了手写的五花八门的Makefile造成的碎片化问题。对于工程师来说,也无需再关注Makefile的细节,只需在更高的抽象层定义生成规则即可,简化了很多不必要的工作。
CMake基础
CMake的使用方法非常简单,如下:
cmake PATH
PATH 目录是我们需要生成Makefile的目录,此目录中必须存在一个名为CMakeLists.txt的文件。cmake会分析它的内容,生成对应的Makefile文件。有了Makefile文件,那么我们执行make就可以生成目标了。
CMake教程
通过以上我们得知,学习CMake的核心就是CMakeLists.txt文件的编写。我们根据官方的7个例子,来深入浅出的学习一下CMakeLists.txt的规则。
1. 起步
首先来看最简单的场景,我们的工程里只有一个源文件 tutorial.cxx,它是一个求平方根的程序:
// A simple program that computes the square 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;
}
根据上文,我们需要在同级目录添加一个 CMakeLists.txt 文件来定义生成规则。内容非常简单,3行即可:
cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(Tutorial tutorial.cxx)
cmake_minimum_required
解析此文件需要的CMake最低版本
project
生成目标的名字
add_executable
向目标中添加源文件
它们都是CMake的命令,不区分大小写。本文为了统一,全部使用小写。
以上就是我们需要做的全部工作,接下来使用cmake命令来生成Makefile。
➜ Step1_1 cmake .
-- The C compiler identification is AppleClang 10.0.1.10010046
-- The CXX compiler identification is AppleClang 10.0.1.10010046
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/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: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/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: /Users/bairixin/Workspace/Test/CMake/Step1/Step1_1
将工程目录作为参数传给cmake,cmake就会帮我们生成相应的Makefile,目前文件列表如下:
➜ Step1_1 ls
CMakeCache.txt CMakeFiles CMakeLists.txt Makefile cmake_install.cmake tutorial.cxx
可以看到,CMake已成功生成了Makefile文件。当然,同时它还生成了一些缓存文件,这些我们暂不讨论。我们make一下:
➜ Step1_1 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。查看文件列表也证实了这一点:
➜ Step1_1 ls
CMakeCache.txt CMakeFiles CMakeLists.txt Makefile Tutorial cmake_install.cmake tutorial.cxx
运行Tutorial试一下:
➜ Step1_1 ./Tutorial 4
The square root of 4 is 2
运行成功。可以看出,CMake的使用还是很简单方便的。
接下来,我们做一下扩展,在工程里添加版本号以及config头文件。虽然我们可以把版本定义放在源码中,但是在 CMakeLists.txt 中定义的话,会有更大的灵活性。我们修改CMakeLists.txt文件,如下所示:
cmake_minimum_required (VERSION 2.6)
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)
我们来看一下相对于上一步新加入的内容,
# The version number.
这是一行注释,CMake的注释以#
开始,这之后的本行内容都被认为是注释。
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
定义了两个变量,后续我们要把它们传递到源码内。
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
"${PROJECT_BINARY_DIR}/TutorialConfig.h"
)
指定配置文件的位置,其中,TutorialConfig.h 是我们最终要生成的用于源码的配置文件。我们将它放在了${PROJECT_BINARY_DIR}
这个目录下。TutorialConfig.h.in 是生成 TutorialConfig.h 的模板文件,后面详述。由于我们把 TutorialConfig.h 放在了${PROJECT_BINARY_DIR}
这个目录下,正常情况下,编译器是找不到它的,我们还要告诉编译器,在查找头文件的时候,需要额外查找${PROJECT_BINARY_DIR}
这个目录,如下:
include_directories("${PROJECT_BINARY_DIR}")
TutorialConfig.h.in 的内容如下:
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
TutorialConfig.h.in 使用 @
@
引用 CMakeLists.txt 里定义的变量,在CMake执行时,会把它们替换为指定的变量值。
再来看修改后的 tutorial.cxx :
// A simple program that computes the square 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;
}
重新执行cmake试一下:
➜ Step1_1 cmake .
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/bairixin/Workspace/Test/CMake/Step1/Step1_1
➜ Step1_1 ls
CMakeCache.txt CMakeLists.txt TutorialConfig.h cmake_install.cmake
CMakeFiles Makefile TutorialConfig.h.in tutorial.cxx
可以看到,已经生成了 TutorialConfig.h 这个文件。make一下:
➜ Step1_1 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
➜ Step1_1 ./Tutorial
./Tutorial Version 1.0
Usage: ./Tutorial number
成功