【CMake】CMake 项目打包与 find_package 使用流程:从 A 到 B 的完整范例
目录
- 1. 项目概述
- 2. 构建前目录结构
- 3. 项目 A:源代码与构建文件
- 3.1 头文件 `A/include/a_lib.h`
- 3.2 库实现文件 `A/src/a_lib.c`
- 3.3 测试程序 `A/src/main.c`
- 3.4 CMake 配置文件 `A/CMakeLists.txt`
- 3.5 AConfig 模板 `A/AConfig.cmake.in`
- 4. 项目 B:源代码与构建文件
- 4.1 主程序 `B/main.c`
- 4.2 CMake 配置文件 `B/CMakeLists.txt`
- 5. 构建步骤
- 5.1 A 项目测试构建
- 5.2 A 项目生成库并安装
- 5.3 B 项目构建(使用 A)
- 6. 构建后目录结构
- 6.1 项目 A
- 测试构建 (`A/build_test/`)
- 安装导出 (`A_export/`)
- 6.2 项目 B
- 7. 使用总结
- 相关文章:
1. 项目概述
本示例展示了如何使用 CMake 创建一个 C 语言静态库项目(A),并在 另一个完全独立的项目(B) 中通过 find_package()
使用该库。
特性:
-
项目 A
- 提供
a_lib
库 - 自带
main.c
测试程序 - 控制是否生成库(通过
BUILD_LIB_A
开关) - 安装时导出头文件、库文件和 CMake 配置文件
- 提供
-
项目 B
- 无需 A 的源码
- 通过
find_package(A)
自动找到 A 的头文件和库 - 直接调用 A 提供的接口函数
2. 构建前目录结构
project_root/
├── A/ # 项目 A:开发者 A 创建的库
│ ├── include/
│ │ └── a_lib.h # 库的头文件
│ ├── src/
│ │ ├── a_lib.c # 库的实现
│ │ └── main.c # A 的测试用程序
│ ├── CMakeLists.txt
│ └── AConfig.cmake.in
├── B/ # 项目 B:开发者 B 使用库
│ ├── main.c
│ └── CMakeLists.txt
3. 项目 A:源代码与构建文件
3.1 头文件 A/include/a_lib.h
#ifndef A_LIB_H
#define A_LIB_H// 输出一个测试消息
void a_print(void);#endif
3.2 库实现文件 A/src/a_lib.c
#include <stdio.h>
#include "a_lib.h"void a_print(void) {printf("Hello from A library!\n");
}
3.3 测试程序 A/src/main.c
#include "a_lib.h"int main() {a_print(); // 测试 A 库函数return 0;
}
3.4 CMake 配置文件 A/CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(A LANGUAGES C)# === 变量管理 ===
set(A_PROJECT_NAME A)
set(A_TARGET_NAME a_lib)
set(A_TEST_TARGET a_test)
set(A_VERSION 1.0.0)
set(A_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(A_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
set(A_HEADERS ${A_HEADER_DIR}/a_lib.h)
set(A_SOURCES ${A_SRC_DIR}/a_lib.c)
set(A_TEST_SOURCE ${A_SRC_DIR}/main.c)# 控制是否生成库
option(BUILD_LIB_A "Build A as library" OFF)# 测试可执行文件
add_executable(${A_TEST_TARGET} ${A_TEST_SOURCE} ${A_SOURCES})
target_include_directories(${A_TEST_TARGET} PRIVATE ${A_HEADER_DIR})# 库构建与安装
if(BUILD_LIB_A)add_library(${A_TARGET_NAME} STATIC ${A_SOURCES})target_include_directories(${A_TARGET_NAME}PUBLIC$<BUILD_INTERFACE:${A_HEADER_DIR}>$<INSTALL_INTERFACE:include>)include(GNUInstallDirs)install(TARGETS ${A_TARGET_NAME}EXPORT ${A_PROJECT_NAME}TargetsARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})install(DIRECTORY ${A_HEADER_DIR}/DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})install(EXPORT ${A_PROJECT_NAME}TargetsFILE ${A_PROJECT_NAME}Targets.cmakeNAMESPACE ${A_PROJECT_NAME}::DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${A_PROJECT_NAME})include(CMakePackageConfigHelpers)write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/${A_PROJECT_NAME}ConfigVersion.cmake"VERSION ${A_VERSION}COMPATIBILITY AnyNewerVersion)configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/${A_PROJECT_NAME}Config.cmake.in"${CMAKE_CURRENT_BINARY_DIR}/${A_PROJECT_NAME}Config.cmake"INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${A_PROJECT_NAME})install(FILES"${CMAKE_CURRENT_BINARY_DIR}/${A_PROJECT_NAME}Config.cmake""${CMAKE_CURRENT_BINARY_DIR}/${A_PROJECT_NAME}ConfigVersion.cmake"DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${A_PROJECT_NAME})
endif()
3.5 AConfig 模板 A/AConfig.cmake.in
@PACKAGE_INIT@include("${CMAKE_CURRENT_LIST_DIR}/@A_PROJECT_NAME@Targets.cmake")
4. 项目 B:源代码与构建文件
4.1 主程序 B/main.c
#include "a_lib.h"int main() {a_print(); // 调用 A 库的函数return 0;
}
4.2 CMake 配置文件 B/CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(B LANGUAGES C)# === 变量定义 ===
set(B_EXECUTABLE_NAME B_exec)
set(B_SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/main.c)
set(A_PROJECT_NAME A)
set(A_TARGET_NAME a_lib)
set(A_INSTALL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../A_export)# 告诉 CMake 去哪里找 A
set(CMAKE_PREFIX_PATH "${A_INSTALL_DIR}")find_package(${A_PROJECT_NAME} REQUIRED)add_executable(${B_EXECUTABLE_NAME} ${B_SOURCE_FILE})
target_link_libraries(${B_EXECUTABLE_NAME} PRIVATE ${A_PROJECT_NAME}::${A_TARGET_NAME})
5. 构建步骤
5.1 A 项目测试构建
cd A
mkdir build_test && cd build_test
cmake .. -DBUILD_LIB_A=OFF
cmake --build .
./a_test # 输出: Hello from A library!
5.2 A 项目生成库并安装
cd ..
mkdir build_install && cd build_install
cmake .. -DBUILD_LIB_A=ON -DCMAKE_INSTALL_PREFIX=../../A_export
cmake --build .
cmake --install .
5.3 B 项目构建(使用 A)
cd ../../B
mkdir build && cd build
cmake ..
cmake --build .
./B_exec # 输出: Hello from A library!
6. 构建后目录结构
6.1 项目 A
测试构建 (A/build_test/
)
build_test/
├── a_test
├── CMakeFiles/...
└── Makefile / ninja.build
安装导出 (A_export/
)
A_export/
├── include/
│ └── a_lib.h
├── lib/
│ ├── liba_lib.a (或 a_lib.lib/.so/.dll,视平台而定)
│ └── cmake/
│ └── A/
│ ├── AConfig.cmake
│ ├── AConfigVersion.cmake
│ └── ATargets.cmake
6.2 项目 B
B/build/
├── B_exec
├── CMakeFiles/...
└── Makefile / ninja.build
7. 使用总结
-
开发者 A
- 开发和测试 A
- 在测试通过后,用
cmake --install
导出A_export/
- 将
A_export/
提供给 B(打包 zip 或共享)
-
开发者 B
- 不需要 A 源码
- 只要设置
CMAKE_PREFIX_PATH
指向A_export/
- 使用
find_package(A)
即可自动引入头文件和库
- 本节内容已经全部介绍完毕,希望通过这篇文章,大家对
CMake
有了更深入的理解和认识。- 感谢各位的阅读和支持,如果觉得这篇文章对你有帮助,请不要吝惜你的点赞和评论,这对我们非常重要。再次感谢大家的关注和支持!点我关注❤️
相关文章:
- 指针的神秘探险:从入门到精通的奇幻之旅 !