cmake入门到入土

发布时间 2023-09-11 18:06:20作者: ydqun

介绍

CMakeLists.txt 的语法比较简单,由命令、注释和空格组成,其中命令是不区分大小写的。符号 # 后面的内容被认为是注释。命令由命令名称、小括号和参数组成,参数之间使用空格进行间隔。

最简单的例程

单个源文件

假设我们的项目中只有一个源文件,且该源文件是一个指数幂函数的实现和运用。

/*************************************************************************
        > File Name: main.c
        > Author:
        > Mail:
        > Created Time: Mon 28 Aug 2023 11:16:11 AM CST
 ************************************************************************/
#include <stdio.h>
#include <stdlib.h>

double power(double base, int exponent) {
    int result = base;

    if (exponent == 0) {
        return 1;
    }

    for (int i = 1; i < exponent; i++) {
        result = result * base;
    }

    return result;
}

int main(int argc, char *argv[]) {
    if (argc < 3) {
        printf("Usage: %s base exponent\n", argv[0]);
        return 1;
    }
    double base = atof(argv[1]);
    int exponent = atoi(argv[2]);
    double result = power(base, exponent);
    printf("%g ^ %d is %g\n", base, exponent, result);
    return 0;
}

编写CMakeLists.txt

编写CMakeLists.txt文件,并保存在与main.c源文件同个目录下:

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)

# 项目信息
project (Demo1)

# 指定生成目标
add_executable(Demo main.c)

这个CMakeLists.txt文件,依次出现了几个命令:
1.cmake_minimum_required: 指运行此配置文件所需的CMake的最低版本;
2.project:参数值是Demo1,表示项目的名称是Demo1;
3.add_executable: 将main.c的源文件编译成一个名称为Demo的可执行文件.

编译项目

在当前目录执行cmake .,得到Makefile后再使用make命令编译得到Demo1可执行文件.

ydqun@ydqhost 01 % cmake .                                                                                                                                                                                       [0]
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- 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: /home/ydqun/workspace/study/cppnetwork/cmake/01
ydqun@ydqhost 01 % make                                                                                                                                                                                          [0]
Scanning dependencies of target Demo
[ 50%] Building C object CMakeFiles/Demo.dir/main.c.o
[100%] Linking C executable Demo
[100%] Built target Demo

多个源文件

多个源文件在同一目录

加入我们把pow函数单独写进一个名为MathFunctions.c的源文件里,使得这个工程变成如下的形式:

.
├── CMakeLists.txt
├── main.c
├── MathFunctions.c
└── MathFunctions.h

main函数所在源文件代码

#include <stdio.h>
#include <stdlib.h>
#include "MathFunctions.h"

int main(int argc, char *argv[]) {
    if (argc < 3) {
        printf("Usage: %s base exponent\n", argv[0]);
        return 1;
    }
    double base = atof(argv[1]);
    int exponent = atoi(argv[2]);
    double result = power(base, exponent);
    printf("%g ^ %d is %g\n", base, exponent, result);
    return 0;
}

MathFunctions.c和MathFunctions.h。

/*************************************************************************
        > File Name: MathFunctions.c
        > Author:
        > Mail:
        > Created Time: Mon 28 Aug 2023 11:30:03 AM CST
 ************************************************************************/
#include <stdio.h>

double power(double base, int exponent) {
    int result = base;
    int i;

    if (exponent == 0) {
        return 1;
    }

    for (i = 1; i < exponent; i++) {
        result = result * base;
    }

    return result;
}
/*************************************************************************
        > File Name: MathFunctions.h
        > Author:
        > Mail:
        > Created Time: Mon 28 Aug 2023 11:32:16 AM CST
 ************************************************************************/
#ifndef _MATHFUNCTIONS_H
#define _MATHFUNCTIONS_H

double power(double base, int exponent);

#endif

这时候,CMakeLists.txt可以改成如下的形式:

ydqun@ydqhost 02 % cat CMakeLists.txt                                                                                                                                                                            [0]
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)

# 项目信息
project (Demo2)

# 指定生成目标
add_executable (Demo ${DIR_SRCS})

唯一的改动只是在add_executable命令中增加了一个MathFunctions.c源文件。这样写没什么问题,但是如果源文件的数量很多,把所有源文件的
名字都加进去将是一件很烦人的工作。更省事的方式是使用aux_source_directory命令,该命令会查找指定目录下的所有源文件,然后将结果存进指
变量名。其语法如下:

aux_source_directory(<dir> <variable>)

因此,可以修改CMakeLists.txt如下:

# cmake 最低版本号要求
cmake_minimum_required (VERSION 2.8)

# 项目信息
project (Demo2)

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)

# 指定生成目标
add_executable(Demo ${DIR_SRCS})

aux_source_directory(. DIR_SRCS)会将当前目录所有源文件的文件名赋值给变量DIR_SRCS,再指示变量DIR_SRCS中的源文件需要编译成一个
名称为Demo的可执行文件。

多个目录,多个源文件

现在进一步将MathFunctions.hMathFunctions.c文件移动到math目录下:

ydqun@ydqhost 03 % tree                                                                                                                                                                                          [0]
.
├── CMakeLists.txt
├── main.c
└── math
    ├── CMakeLists.txt
    ├── MathFunctions.c
    └── MathFunctions.h

1 directory, 5 files  

对于这种情况,需要分别在项目根目录Demo3和math目录各编写一个CMakeLists.txt文件。为了方便,我们可以先将math目录里的文件编译成静态库
再有main函数调用。根目录中的CMakeList.txt:

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)

# 项目信息
project (Demo3)

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)

# 添加 math 子目录
add_subdirectory(math)

# 指定生成目标
add_executable(Demo main.c)

# 添加链接库
target_link_libraries(Demo MathFunctions)

在CMakeLists.txt中,使用命令add_subdirectory指明本项目包含一个子目录math,这样math目录下的CMakeLists.txt文件和源代码也会被处理。
第6行,使用命令target_link_libraries指明可执行文件main需要连接一个名为MathFunctions的链接库。子目录中的CMakeLists.txt:

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS变量
aux_source_directory(. DIR_LIB_SRCS)

# 生成链接库
add_library (MathFunctions ${DIR_LIB_SRCS})

在子目录中使用命令add_library将src目录中的源文件编译为静态链接库。

自定义编译选项

CMake允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。例如,可以将MathFunctions库设为一个可选的库,如果该选项
on,就使用该库定义的数据函数来进来运算。否则就调用标准库中的数学函数库。其工程结构为:

ydqun@ydqhost 04 % tree                                                                                                                                                                                          [0]
.
├── CMakeLists.txt
├── config.h
├── config.h.in
├── main.c
└── math
    ├── CMakeLists.txt
    ├── MathFunctions.c
    └── MathFunctions.h

1 directory, 7 files

修改CMakeLists文件

我们要做的第一步是在顶层的CMakeLists.txt文件中添加该选项:

# CMake 最低版本号要求
cmake_minumum_required (VERSION 2.8)

# 项目信息
project (Demo4)

# 加入一个配置文件,用于处理CMake对源码的设置
configure_file (
  "${PROJECT_SOURCE_DIR}/config.h.in"
  "${PROJECT_BINARY_DIR}/config.h"
)

# 是否使用自已的MathFunctions 库
option (USE_MYMATH
       "Use provided math implementation" ON)

# 是否加入MathFunctions 库
if (USE_MYMATH)
  include_directories ("${PROJECT_SOURCE_DIR}/math")
  add_subdirectory(math)
  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif(USE_MYMATH)

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)

# 指定生成目标
add_executable(Demo ${DIR_SRCS})
target_link_libraries (Demo  ${EXTRA_LIBS})

1.configure_file命令用于加入一个配置头文件config.h,这个文件有CMake从config.h.in生成,通过这样的机制,将可以通过预定义一些参数和变
量来控制代码的生成。
2.option命令添加了一个USE_MYMATH选项,并默认值为ON
3.根据USE_MYMATH变量的值来决定是否使用我们编写的MathFunctions库。

修改main.c文件

修改main.c文件,让其根据USE_MYMATH的预定义来决定是否调用标准库还是MathFunctions库:

/*************************************************************************
        > File Name: main.c
        > Author:
        > Mail:
        > Created Time: Tue 29 Aug 2023 04:06:23 PM CST
 ************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "config.h"

#ifdef USE_MYMATH
    #include "math/MathFunctions.h"
#else
    #include <math.h>
#endif

int main(int argc, char *argv[])
{
    if (argc < 3) {
        printf("Usage: %s base exponent\n", argv[0]);
        return 1;
    }
    double base = atof(argv[1]);
    int exponent = atoi(argv[2]);

#ifdef USE_MYMATH
    printf("Now we use our own Math library.\n");
    double result = power(base, exponent);
#else
    printf("Now we use the standard library.\n");
    double result = pow(base, exponent);
#endif
    printf("%g ^ %d is %g\n", base, exponent, result);
    return 0;
}

编写config.h.in文件

main.c引用了一个config.h文件,这个文件预定义了USE_MYMATH的值。但我们并不直接编写这个文件,为了方便从CMakeLists.txt中导入编制,我们
编写一个config.h.in文件,内容如下:
#cmakedefine USE_MYMATH
有了config.h.in文件后,cmake会自动根据CMakeLists.txt编制文件中的设置自动生成config.h文件。

编译项目

为了便于交互式的选择该变量的值,我们可以使用ccmake命令。

ccmake

1.安装ccmake
我使用的操作系统是ubuntu20.04.3 LTS,安装ccmake使用的是apt命令安装,安装过程如下:

ydqun@ydqhost 04 % sudo apt search ccmake                                                                                                                                                                      [100]
Sorting... Done
Full Text Search... Done
cmake-curses-gui/focal-updates 3.16.3-1ubuntu1.20.04.1 amd64
  curses based user interface for CMake (ccmake)

ydqun@ydqhost 04 % sudo apt install cmake-curses-gui/focal-updates                                                                                                                                             [130]
Reading package lists... Done
Building dependency tree
Reading state information... Done
Selected version '3.16.3-1ubuntu1.20.04.1' (Ubuntu:20.04/focal-updates [amd64]) for 'cmake-curses-gui'
Selected version '3.16.3-1ubuntu1.20.04.1' (Ubuntu:20.04/focal-updates [amd64]) for 'cmake' because of 'cmake-curses-gui'
The following additional packages will be installed:
  cmake cmake-data
Suggested packages:
  cmake-doc ninja-build
The following NEW packages will be installed:
  cmake-curses-gui
The following packages will be upgraded:
  cmake cmake-data
2 upgraded, 1 newly installed, 0 to remove and 293 not upgraded.
Need to get 1,768 kB/7,049 kB of archives.
After this operation, 5,597 kB of additional disk space will be used.
Do you want to continue? [Y/n] Y
Get:1 http://mirrors.cloud.aliyuncs.com/ubuntu focal-updates/universe amd64 cmake-curses-gui amd64 3.16.3-1ubuntu1.20.04.1 [1,768 kB]
Fetched 1,768 kB in 0s (10.8 MB/s)
(Reading database ... 122942 files and directories currently installed.)
Preparing to unpack .../cmake_3.16.3-1ubuntu1.20.04.1_amd64.deb ...
Unpacking cmake (3.16.3-1ubuntu1.20.04.1) over (3.16.3-1ubuntu1) ...
Preparing to unpack .../cmake-data_3.16.3-1ubuntu1.20.04.1_all.deb ...
Unpacking cmake-data (3.16.3-1ubuntu1.20.04.1) over (3.16.3-1ubuntu1) ...
Selecting previously unselected package cmake-curses-gui.
Preparing to unpack .../cmake-curses-gui_3.16.3-1ubuntu1.20.04.1_amd64.deb ...
Unpacking cmake-curses-gui (3.16.3-1ubuntu1.20.04.1) ...
Setting up cmake-data (3.16.3-1ubuntu1.20.04.1) ...
Setting up cmake (3.16.3-1ubuntu1.20.04.1) ...
Setting up cmake-curses-gui (3.16.3-1ubuntu1.20.04.1) ...
Processing triggers for man-db (2.9.1-1) ...
ydqun@ydqhost 04 %                                

使用ccmake
安装完成后,在工程项目的目录下(主CMakeLists.txt)使用ccmake .打开会话式的交互配置界面,

在CMake的交互式配置界面中,我们可以找到CMakeLists.txt定义的USE_MYMATH选项,通过使用上下方向键在不同的配置项上跳转,在选中配置项后
按下enter按键后可以修改该选项,修改完成后可以按下c按键完成配置,之后再按下g按键确认生成Makefile文件并退出。在这里我们可以尝试
分别将USE_MYMATH设置为ONOFF进行测试:
USE_MYMATH为ON
编译并运行:

ydqun@ydqhost 04 % make                                                                                                                                                                                          [0]
[ 50%] Built target MathFunctions
[100%] Built target Demo
ydqun@ydqhost 04 % ./Demo 2 4                                                                                                                                                                                    [0]
Now we use our own Math library.
2 ^ 4 is 16
ydqun@ydqhost 04 % 

此时config.h的内容为:

ydqun@ydqhost 04 % cat config.h                                                                                                                                                                                  [0]
#define USE_MYMATH
ydqun@ydqhost 04 % 

USE_MYMATH为OFF

ydqun@ydqhost 04 % make                                                                                                                                                                                          [0]
Scanning dependencies of target Demo
[ 50%] Building C object CMakeFiles/Demo.dir/main.c.o
[100%] Linking C executable Demo
[100%] Built target Demo
ydqun@ydqhost 04 % ./Demo 2 4                                                                                                                                                                                    [0]
Now we use the standard library.
2 ^ 4 is 16
ydqun@ydqhost 04 % 

此时config.h的内容为:

ydqun@ydqhost 04 % cat config.h                                                                                                                                                                                  [0]
/* #undef USE_MYMATH */
ydqun@ydqhost 04 % 

安装和测试

CMake也可以指定安装规则,以及添加测试。这两个功能分别可以通过在产生Makefile后使用make installmake test来执行。在以前的GNU
Makefile里,你可能需要为此编写installtest两个伪目标和相应的规则,但在CMake里,这样的工作同样只需要简单的调用几条命令。
工程目录如下:

ydqun@ydqhost 05 % tree                                                                                                                                                                                                             [0]
.
├── install
│   ├── CMakeLists.txt
│   ├── config.h.in
│   ├── main.c
│   └── math
│       ├── CMakeLists.txt
│       ├── MathFunctions.c
│       └── MathFunctions.h
└── test
    ├── CMakeLists.txt
    ├── config.h.in
    ├── main.c
    └── math
        ├── CMakeLists.txt
        ├── MathFunctions.c
        └── MathFunctions.h

4 directories, 12 files

定制安装规则

首先在math/CMakeLists.txt文件里添加下面两行:

# 指定MathFunctions 库的安装路径
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)

这样完整的math/CMakeLists.txt文件代码如下:

ydqun@ydqhost install % cat math/CMakeLists.txt                                                                                                                                                                                     [0]
# 查找当前目录下的所有源文件
# 并将名称报春到DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)

# 生成链接库
add_library (MathFunctions ${DIR_LIB_SRCS})

# 指定MathFunctions 库的安装路径
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)

整个工程的主CMakeLists.txt如下

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)

# 项目信息
project (Demo5)

set (CMAKE_INCLUDE_CURRENT_DIR ON)

# 是否使用自己的 MathFunctions 库
option (USE_MYMATH
           "Use provided math implementation" ON)

# 加入一个配置头文件,用于处理 CMake 对源码的设置
configure_file (
  "${PROJECT_SOURCE_DIR}/config.h.in"
  "${PROJECT_BINARY_DIR}/config.h"
  )

# 是否加入 MathFunctions 库
if (USE_MYMATH)
    # 将math添加到include路径中
    # 这样cmake在编译过程中就能直接找到math中的头文件
    # 编写main的时候就不需要include相对路径了
    include_directories ("${PROJECT_SOURCE_DIR}/math")
    # 将 EXTRA_LIBS 的值与字符串 "MathFunctions" 连接,重新复制给EXTRA_LIBS
    add_subdirectory (math)
    set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
else()
    # 不链接math库会报错,因为linux中默认没有math库
    LINK_LIBRARIES(m)
endif (USE_MYMATH)

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)

# 指定生成目标
add_executable(Demo ${DIR_SRCS})
target_link_libraries (Demo  ${EXTRA_LIBS})

# 指定安装路径
install (TARGETS Demo DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/config.h"
        DESTINATION include)
ydqun@ydqhost install % cat math/CMakeLists.txt                                                                                                                                                                                     [0]
# 查找当前目录下的所有源文件
# 并将名称报春到DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)

# 生成链接库
add_library (MathFunctions ${DIR_LIB_SRCS})

# 指定MathFunctions 库的安装路径
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)

编译和安装。

ydqun@ydqhost install % cmake .                                                                                                                                                                                                     [0]
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- 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: /home/ydqun/workspace/study/cppnetwork/cmake/05/install
ydqun@ydqhost install % sudo make install                                                                                                                                                                                           [0]
[sudo] password for ydqun:
Scanning dependencies of target MathFunctions
[ 25%] Building C object math/CMakeFiles/MathFunctions.dir/MathFunctions.c.o
[ 50%] Linking C static library libMathFunctions.a
[ 50%] Built target MathFunctions
Scanning dependencies of target Demo
[ 75%] Building C object CMakeFiles/Demo.dir/main.c.o
[100%] Linking C executable Demo
[100%] Built target Demo
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/bin/Demo
-- Installing: /usr/local/include/config.h
-- Installing: /usr/local/bin/libMathFunctions.a
-- Installing: /usr/local/include/MathFunctions.h

通过上面的定制,生成的Demo文件和MathFunctions函数库libMathFunctions.o文件将会被赋值到/usr/local/bin中,而MathFunctions.h和
生成的config.h文件则会被复制到/usr/local/include中。我们可以验证一下(顺带一提的是,这里的/usr/local是默认安装的根目录,可以
过修改CMAKE_INTALL_PREFIX变量的值来指定这些文件应该拷贝到哪个根目录,修改方式可以通过之前讲的ccmake进行修改):

为工程添加测试

在CMake里添加测试也同样很简单,它提供了一个称为CTest的测试工具,我们只需在项目根目录的CMakeLists.txt文件中调用一系列的add_test
命令即可以添加测试。

# 启用测试
enable_testing()

# 测试程序是否成功运行
add_test (test_run Demo 5 2)

# 测试帮助信息是否可以正常提示
add_test (test_usage Demo)
set_tests_properties (test_usage
  PROPERTIES PASS_REGULAR_EXPRESSION "Usage: .* base exponent")

# 测试 5 的平方
add_test (test_5_2 Demo 5 2)

set_tests_properties (test_5_2
 PROPERTIES PASS_REGULAR_EXPRESSION "is 25")

# 测试 10 的 5 次方
add_test (test_10_5 Demo 10 5)

set_tests_properties (test_10_5
 PROPERTIES PASS_REGULAR_EXPRESSION "is 100000")

# 测试 2 的 10 次方
add_test (test_2_10 Demo 2 10)

set_tests_properties (test_2_10
 PROPERTIES PASS_REGULAR_EXPRESSION "is 1024")

上述代码包含了四个测试。第一个测试test_run用来测试程序是否成功运行并返回0值。其余三个分别用来测试5的平方、10的5次方、2的10次方
是否都能得到正确的结果。其中PASS_REGULAR_EXPRESSION用来测试输出是否包含后面跟着的字符串。测试结果如下:

ydqun@ydqhost test % cmake .                                                                                                                                                                                                        [0]
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- 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: /home/ydqun/workspace/study/cppnetwork/cmake/05/test
ydqun@ydqhost test % make test                                                                                                                                                                                                      [0]
Running tests...
Test project /home/ydqun/workspace/study/cppnetwork/cmake/05/test
    Start 1: test_run
1/5 Test #1: test_run .........................   Passed    0.00 sec
    Start 2: test_usage
2/5 Test #2: test_usage .......................   Passed    0.00 sec
    Start 3: test_5_2
3/5 Test #3: test_5_2 .........................   Passed    0.00 sec
    Start 4: test_10_5
4/5 Test #4: test_10_5 ........................   Passed    0.00 sec
    Start 5: test_2_10
5/5 Test #5: test_2_10 ........................   Passed    0.00 sec

100% tests passed, 0 tests failed out of 5

Total Test time (real) =   0.01 sec

但是如果要测试更多的输入数据,向上面这种一个个写测试用例太繁琐,这时候可以通过编写宏来实现:

# 启用测试
enable_testing()

macro (do_test arg1 arg2 result)
    add_test (test_${arg1}_${arg2} Demo ${arg1} ${arg2})
    set_tests_properties (test_${arg1}_${arg2})
        PROPERTIES PASS_REGULAR_EXPRESSION ${result})
endmacro (do_test)

# 使用该宏进行一系列的数据测试
do_test (5 2 "is 25")
do_test (10 5 "is 100000")
do_test (2 10 "is 1024")

如果CTest的其他详细用法可以通过man 1 ctest参考CTest的文档。

支持gdb

CMake支持gdb,只需要在指定Debug模式下开启-g选项:

set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

工程目录如下:

ydqun@ydqhost debug % tree                                                                                                                                                                                    [0]
.
├── CMakeLists.txt
├── config.h.in
├── main.c
└── math
    ├── CMakeLists.txt
    ├── MathFunctions.c
    └── MathFunctions.h

1 directory, 6 files

工程主CMakeLists.txt

ydqun@ydqhost debug % cat CMakeLists.txt                                                                                                                                                                      [0]
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)

# 项目信息
project (Demo4)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
# 指定Debug模式,并开启gdb
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

# 是否使用自己的MathFunctions 库
option (USE_MYMATH
       "Use provided math implementation" ON)

# 加入一个配置头文件,用于处理CMake对源码的设置
configure_file (
    "${PROJECT_SOURCE_DIR}/config.h.in"
    "${PROJECT_BINARY_DIR}/config.h"
    )

# 是否加入 MathFunctions
if(USE_MYMATH)
    include_directories("${PROJECT_SOURCE_DIR}/math")
    add_subdirectory (math)
    set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
else()
    # 不连接math库会报错,因为Linux中默认没有math库
    LINK_LIBRARIES(m)
endif(USE_MYMATH)

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)

# 指定生成目标
add_executable(Demo ${DIR_SRCS})
target_link_libraries (Demo ${EXTRA_LIBS})

通过上述CMakeLists.txt编译出来的可执行程序即可进行gdb调试。

添加版本号

本节对应的源代码结构如下:

ydqun@ydqhost 07 % tree                                                                                 [0]
.
├── CMakeLists.txt
├── config.h.in
├── main.c
└── math
    ├── CMakeLists.txt
    ├── MathFunctions.c
    └── MathFunctions.h

1 directory, 6 files

给项目添加和维护版本号是一个好习惯,这样有利于用户了解每个版本的维护情况,并及时了解当前所用的版本是否过时,或是否可能出现不兼容的
情况。首先修改顶层CMakeLists文件,在project命令之后加入如下两行:

set (Demo_VERSION_MAJOR 1)
set (Demo_VERSION_MINOR 0)

分别指定当前的项目的主版本号和副版本号。之后,为了可以在代码中获取版本信息,我们可以修改config.h.in文件,添加两个预定义变量:

// the configured options and settings for Tutorial
#define Demo_VERSION_MAJOR @Demo_VERSION_MAJOR@
#define Demo_VERSION_MINOR @Demo_VERSION_MINOR@

此时,主目录的CMakeLists.txt如下:

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)

# 项目信息
project (Demo4)

set (Demo_VERSION_MAJOR 1)
set (Demo_VERSION_MINOR 0)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

# 是否使用自己的 MathFunctions 库
option (USE_MYMATH
        "Use provided math implementation" ON)

# 是否使用自己的MathFunctions 库
configure_file (
    "${PROJECT_SOURCE_DIR}/config.h.in"
    "${PROJECT_BINARY_DIR}/config.h"
    )

# 是否加入 MathFunctions 库
if(USE_MYMATH)
    # 将math添加到include路径中
    # 这样cmake在编译过程中就能直接找到math中的头文件
    # 编写main的时候就不需要include相对路径了
    include_directories("${PROJECT_SOURCE_DIR}/math")
    add_subdirectory(math)
    # 将 EXTRA_LIBS 的值与字符串 "MathFunctions" 连接,重新复制给EXTRA_LIBS
    set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif(USE_MYMATH)

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)

# 指定生成目标
add_executable (Demo ${DIR_SRCS})
target_link_libraries (Demo  ${EXTRA_LIBS})

这样就可以直接在代码中打印版本信息了:

/*************************************************************************
        > File Name: main.c
        > Author:
        > Mail:
        > Created Time: Tue 29 Aug 2023 04:06:23 PM CST
 ************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "config.h"

#ifdef USE_MYMATH
    #include "math/MathFunctions.h"
#else
    #include <math.h>
#endif

int main(int argc, char *argv[])
{
    if (argc < 3) {
        printf("%s Version %d.%d\n",
              argv[0],
              Demo_VERSION_MAJOR,
              Demo_VERSION_MINOR);
        printf("Usage: %s base exponent\n", argv[0]);
        return 1;
    }
    double base = atof(argv[1]);
    int exponent = atoi(argv[2]);

#ifdef USE_MYMATH
    printf("Now we use our own Math library.\n");
    double result = power(base, exponent);
#else
    printf("Now we use the standard library.\n");
    double result = pow(base, exponent);
#endif
    printf("%g ^ %d is %g\n", base, exponent, result);
    return 0;
}