CMake 实战练习
本文将演示如何使用 CMake 管理一个中等复杂度的项目,从创建项目到编译和运行的整个过程,涵盖了从基本配置到高级特性的实际应用。
实战内容如下:
- 创建
CMakeLists.txt
文件:定义项目、库、可执行文件和测试。 - 编写源代码和测试:编写代码和测试文件。
- 创建构建目录:保持源代码目录整洁。
- 配置项目:生成构建系统文件。
- 编译项目:生成目标文件。
- 运行可执行文件:执行程序。
- 运行测试:验证功能正确性。
- 使用自定义命令和目标:执行额外操作。
- 跨平台和交叉编译:支持不同平台和架构。
构建一个简单的 C++ 项目
假设我们有一个项目,包含一个主程序和一个库,库中有两个不同的功能模块。
项目结构如下:
MyProject/ ├── CMakeLists.txt ├── src/ │ ├── main.cpp │ ├── lib/ │ │ ├── module1.cpp │ │ ├── module2.cpp │ ├── include/ │ └── mylib.h └── tests/ ├── test_main.cpp └── CMakeLists.txt
1、创建 CMakeLists.txt 文件
1.1 根目录 CMakeLists.txt 文件
在 MyProject 根目录下创建一个 CMakeLists.txt 文件:
实例
cmake_minimum_required(VERSION 3.10) # 指定最低 CMake 版本
project(MyProject VERSION 1.0) # 定义项目名称和版本
# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 包含头文件路径
include_directories(${PROJECT_SOURCE_DIR}/src/include)
# 添加子目录
add_subdirectory(src)
add_subdirectory(tests)
project(MyProject VERSION 1.0) # 定义项目名称和版本
# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 包含头文件路径
include_directories(${PROJECT_SOURCE_DIR}/src/include)
# 添加子目录
add_subdirectory(src)
add_subdirectory(tests)
1.2 src 目录 CMakeLists.txt 文件
在 src 目录下创建一个 CMakeLists.txt 文件:
实例
# 创建库目标
add_library(MyLib STATIC
lib/module1.cpp
lib/module2.cpp
)
# 指定库的头文件
target_include_directories(MyLib PUBLIC ${CMAKE_SOURCE_DIR}/src/include)
# 创建可执行文件目标
add_executable(MyExecutable main.cpp)
# 链接库到可执行文件
target_link_libraries(MyExecutable PRIVATE MyLib)
add_library(MyLib STATIC
lib/module1.cpp
lib/module2.cpp
)
# 指定库的头文件
target_include_directories(MyLib PUBLIC ${CMAKE_SOURCE_DIR}/src/include)
# 创建可执行文件目标
add_executable(MyExecutable main.cpp)
# 链接库到可执行文件
target_link_libraries(MyExecutable PRIVATE MyLib)
1.3 tests 目录 CMakeLists.txt 文件
在 tests 目录下创建一个 CMakeLists.txt 文件:
实例
# 查找 GTest 包
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
# 创建测试目标
add_executable(TestMyLib test_main.cpp)
# 链接库和 GTest 到测试目标
target_link_libraries(TestMyLib PRIVATE MyLib ${GTEST_LIBRARIES})
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
# 创建测试目标
add_executable(TestMyLib test_main.cpp)
# 链接库和 GTest 到测试目标
target_link_libraries(TestMyLib PRIVATE MyLib ${GTEST_LIBRARIES})
2、编写源代码和测试
以下是各个文件的代码:
2.1 src/main.cpp 文件代码
实例
#include <iostream>
#include "mylib.h"
int main() {
std::cout << "Hello, CMake!" << std::endl;
return 0;
}
#include "mylib.h"
int main() {
std::cout << "Hello, CMake!" << std::endl;
return 0;
}
2.2 src/lib/module1.cpp 文件代码
实例
#include "mylib.h"
// Implementation of module1
// Implementation of module1
2.3 src/lib/module2.cpp 文件代码
实例
#include "mylib.h"
// Implementation of module2
// Implementation of module2
2.4 src/include/mylib.h 文件代码
实例
#ifndef MYLIB_H
#define MYLIB_H
// Declarations of module functions
#endif // MYLIB_H
#define MYLIB_H
// Declarations of module functions
#endif // MYLIB_H
2.5 tests/test_main.cpp 文件代码
实例
#include <gtest/gtest.h>
// Test cases for MyLib
TEST(MyLibTest, BasicTest) {
EXPECT_EQ(1, 1);
}
// Test cases for MyLib
TEST(MyLibTest, BasicTest) {
EXPECT_EQ(1, 1);
}
3、创建构建目录
在项目根目录下创建一个构建目录:
mkdir build cd build
4、配置项目
在构建目录中运行 CMake 以配置项目:
cmake ..
5、编译项目
使用生成的构建系统文件进行编译,假设生成了 Makefile:
make
6、运行可执行文件
编译完成后,可以运行生成的可执行文件:
./MyExecutable
7、运行测试
使用生成的测试目标进行测试:
./TestMyLib
8、使用自定义命令和目标
8.1 自定义命令
在 src/CMakeLists.txt 文件中添加自定义命令:
add_custom_command( TARGET MyExecutable POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo "Build complete!" )
8.2 自定义目标
在 src/CMakeLists.txt 文件中添加自定义目标:
add_custom_target(run COMMAND ${CMAKE_BINARY_DIR}/MyExecutable DEPENDS MyExecutable )
运行自定义目标:
make run
9、跨平台和交叉编译
9.1 指定平台
如果需要指定平台进行构建,可以在运行 CMake 时指定平台:
cmake -DCMAKE_SYSTEM_NAME=Linux ..
9.2 使用工具链文件
创建一个工具链文件 toolchain.cmake:
set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm)
使用工具链文件进行构建:
cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake ..