banner
NEWS LETTER

Linux CMake 工程实践:目录结构、库、测试与安装

Scroll down

一、为什么要规范工程结构

小 demo 可以一个 main.c 加一条 gcc 命令解决,但真实项目会有多个模块、多个库、测试、安装和部署。工程结构不清晰,后期会很难维护。

一个比较常见的 C/C++ 项目结构:

1
2
3
4
5
6
7
8
9
10
11
my_app/
├── CMakeLists.txt
├── include/
│ └── my_app/
│ └── version.h
├── src/
│ ├── main.c
│ └── version.c
├── tests/
│ └── test_version.c
└── README.md

二、顶层 CMakeLists

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
cmake_minimum_required(VERSION 3.16)

project(my_app C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

add_library(my_app_core
src/version.c
)

target_include_directories(my_app_core
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)

target_compile_options(my_app_core PRIVATE
-Wall
-Wextra
)

add_executable(my_app
src/main.c
)

target_link_libraries(my_app PRIVATE my_app_core)

这样把核心逻辑放进库,主程序只负责入口。

三、头文件和源文件

include/my_app/version.h

1
2
3
4
5
6
#ifndef MY_APP_VERSION_H
#define MY_APP_VERSION_H

const char *my_app_version(void);

#endif

src/version.c

1
2
3
4
5
#include "my_app/version.h"

const char *my_app_version(void) {
return "1.0.0";
}

src/main.c

1
2
3
4
5
6
7
#include <stdio.h>
#include "my_app/version.h"

int main(void) {
printf("my_app version %s\n", my_app_version());
return 0;
}

四、构建

1
2
cmake -S . -B build
cmake --build build

运行:

1
./build/my_app

清理:

1
rm -rf build

五、Debug 和 Release

Debug:

1
2
cmake -S . -B build-debug -DCMAKE_BUILD_TYPE=Debug
cmake --build build-debug

Release:

1
2
cmake -S . -B build-release -DCMAKE_BUILD_TYPE=Release
cmake --build build-release

开发时用 Debug,发布时用 Release。

六、添加测试

最简单的测试可以先不用测试框架。

tests/test_version.c

1
2
3
4
5
6
#include <string.h>
#include "my_app/version.h"

int main(void) {
return strcmp(my_app_version(), "1.0.0") == 0 ? 0 : 1;
}

CMake:

1
2
3
4
5
6
7
8
9
enable_testing()

add_executable(test_version
tests/test_version.c
)

target_link_libraries(test_version PRIVATE my_app_core)

add_test(NAME test_version COMMAND test_version)

运行:

1
ctest --test-dir build --output-on-failure

七、安装规则

1
2
3
4
5
6
7
install(TARGETS my_app
RUNTIME DESTINATION bin
)

install(DIRECTORY include/
DESTINATION include
)

安装到指定目录:

1
cmake --install build --prefix /tmp/my_app_install

结果:

1
2
/tmp/my_app_install/bin/my_app
/tmp/my_app_install/include/...

八、常用脚本

scripts/build.sh

1
2
3
4
5
6
#!/usr/bin/env bash
set -euo pipefail

cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build
ctest --test-dir build --output-on-failure

这样新同事不用记一堆命令。

九、建议

  • 不要把构建产物放进源码目录。
  • 公共头文件放 include
  • 内部实现放 src
  • 可测试逻辑放 library,入口程序尽量薄。
  • 每次提交前至少跑一次构建和测试。
  • 嵌入式项目也可以复用这套结构,只是换 toolchain file。

十、总结

CMake 的价值不只是替代 Makefile,而是把目标、依赖、测试和安装规则清晰表达出来。项目越大,规范工程结构越重要。

其他文章
目录导航 置顶
  1. 1. 一、为什么要规范工程结构
  2. 2. 二、顶层 CMakeLists
  3. 3. 三、头文件和源文件
  4. 4. 四、构建
  5. 5. 五、Debug 和 Release
  6. 6. 六、添加测试
  7. 7. 七、安装规则
  8. 8. 八、常用脚本
  9. 9. 九、建议
  10. 10. 十、总结
请输入关键词进行搜索