多层次目录结构的CMake工程管理

一、多层次目录结构的文件结构

我们编写程序,不可能把所有源文件都一股脑地放在顶层目录下,必然会有一个目录结构,每个目录中只放置自己相关的一些文件,甚至多层嵌套也是很常见的。

比如以下名为gtestdemo的工程目录结构, 便可以说明问题。

[hubing@192 gtestdemo]$ tree
.
├── CMakeLists.txt
├── doc
│   └── README.md
└── src
    ├── CMakeLists.txt
    ├── main.cpp
    └── utils
        ├── CMakeLists.txt
        ├── MathUtils.cpp
        ├── MathUtils.hpp
        └── ut
            └── testMathUtils.cpp

顶级工程目录下,有一个顶级的CMakeLists.txt, 外加两个子目录doc和src,doc子目录放置文档相关的文件,src子目录放置源代码。
src子目录下还有下一级目录utils,里面有若干源代码文件。

20230102211228

二、如何利用CMake组织多层次目录结构

utils 相关代码:

MathUtils.hpp

namespace utils::math
{

// Returns n! (the factorial of n).  For negative n, n! is defined to be 1.
int Factorial(int n);

// Returns true if n is a prime number.
bool IsPrime(int n);

}

MathUtils.cpp

#include "MathUtils.hpp"

namespace utils::math
{

// Returns n! (the factorial of n).  For negative n, n! is defined to be 1.
int Factorial(int n) {
    int result = 1;
    for (int i = 1; i <= n; i++) {
        result *= i;
    }
 
    return result;
}

bool IsPrime(int n) {
    // Trivial case 1: small numbers
    if (n <= 1) return false;
 
    // Trivial case 2: even numbers
    if (n % 2 == 0) return n == 2;
 
    // Now, we have that n is odd and n >= 3.
    // Try to divide n by every odd number i, starting from 3
    for (int i = 3; ; i += 2) {
        // We only have to try i up to the squre root of n
        if (i > n / i) break;
 
        // Now, we have i <= n/i < n.
        // If n is divisible by i, n is not prime.
        if (n % i == 0) return false;
    }
    // n has no integer factor in the range (1, n), and thus is prime.
    return true;
}

}

我们来看src/utils/CMakeLists.txt如何编写:

add_library(utils MathUtils.cpp)

只有一行,利用 add_library 创建一个名为utils的target库,使用的源文件是MathUtils.cpp。

为了使用这个utils库,我们需要在上一层的CMakeLists.txt(也就是src/CMakeLists.txt)中加上一行

add_subdirectory(utils)

这样一来utils库就可以被编译。

下一步为了能够链接到utils库,我们还需要使用target_link_libraries指令

add_executable(gtestdemo main.cpp)
target_link_libraries(gtestdemo PUBLIC utils)

最后,为了在main.cpp中能够使用utils库,我们在main.cpp中会写上

#include "MathUtils.hpp"

那么如何才能找到这个头文件呢?此时需要使用 target_include_directories 指令:

target_include_directories(gtestdemo PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/src/utils")

完整的 src/CMakeLists.txt 内容如下:

add_subdirectory(utils)

add_executable(gtestdemo main.cpp)

target_link_libraries(gtestdemo PUBLIC utils)

message(STATUS "this is BINARY dir " ${PROJECT_BINARY_DIR})
message(STATUS "this is SOURCE dir " ${PROJECT_SOURCE_DIR})
target_include_directories(gtestdemo PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/src/utils")

main.cpp 内容如下:

#include <iostream>
#include "MathUtils.hpp"

int main(int, char**) {
    std::cout << "Hello, world!"<< std::endl;
    std::cout << "4! = " << utils::math::Factorial(4) << std::endl;
    std::cout << "check whether 10 is a prime number: " << utils::math::IsPrime(10) << std::endl;
    std::cout << "check whether 11 is a prime number: " << utils::math::IsPrime(11) << std::endl;
}

最后到了最顶层的CMakeLists.txt,内容如下:

cmake_minimum_required(VERSION 3.0.0)
project(gtestdemo VERSION 0.1.0)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

add_subdirectory(src bin)

现在对其进行说明:

  • cmake_minimum_required(VERSION 3.0.0)
    任何工程的最顶级CMakeLists.txt,必须起始于这个cmake_minimum_required指令,用于明确CMake的最低版本号

  • project(gtestdemo VERSION 0.1.0)
    project指令设定了当前工程的名字叫 gtestdemo,版本号是 0.1.0

  • set(CMAKE_CXX_STANDARD 11)

  • set(CMAKE_CXX_STANDARD_REQUIRED True)
    这两个指令用于指示C++标准号,例如当前指定的是C++ 11标准

  • add_subdirectory(src bin)
    将子目录src添加进工程并编译,同时指定二进制目标文件的输出路径为bin目录。如果不指定bin目录,那么编译结果都将存放在build/src目录下(这个src和源码中的src目录对应)。

最后,本例中有个src/utils/ut/testMathUtils.cpp, 这个文件是用作ut demo的,此处暂未涉及。

三、构建工程

照例在工程的顶级目录中先建立一个build子目录,然后进行构建。

mkdir build
cd build
cmake ..
make

执行效果如下:
20230102214822

有了这个示例之后,对于更多层次的目录结构,便可以依葫芦画瓢,使用CMake进行管理了。

猜你喜欢

转载自blog.csdn.net/hubing_hust/article/details/128525706