多层次目录结构的CMake工程管理
- 一、多层次目录结构的文件结构
- 二、如何利用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,里面有若干源代码文件。
二、如何利用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
执行效果如下:
有了这个示例之后,对于更多层次的目录结构,便可以依葫芦画瓢,使用CMake进行管理了。