本示例展示了如何使用 CMake 创建一个 C 语言静态库项目(A),并在 另一个完全独立的项目(B) 中通过 find_package()
使用该库。
特性:
a_lib
库main.c
测试程序BUILD_LIB_A
开关)find_package(A)
自动找到 A 的头文件和库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
A/include/a_lib.h
#ifndef A_LIB_H
#define A_LIB_H
// 输出一个测试消息
void a_print(void);
#endif
A/src/a_lib.c
#include <stdio.h>
#include "a_lib.h"
void a_print(void) {
printf("Hello from A library!\n");
}
A/src/main.c
#include "a_lib.h"
int main() {
a_print(); // 测试 A 库函数
return 0;
}
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}Targets
ARCHIVE 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}Targets
FILE ${A_PROJECT_NAME}Targets.cmake
NAMESPACE ${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()
A/AConfig.cmake.in
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@A_PROJECT_NAME@Targets.cmake")
B/main.c
#include "a_lib.h"
int main() {
a_print(); // 调用 A 库的函数
return 0;
}
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})
cd A
mkdir build_test && cd build_test
cmake .. -DBUILD_LIB_A=OFF
cmake --build .
./a_test # 输出: Hello from A library!
cd ..
mkdir build_install && cd build_install
cmake .. -DBUILD_LIB_A=ON -DCMAKE_INSTALL_PREFIX=../../A_export
cmake --build .
cmake --install .
cd ../../B
mkdir build && cd build
cmake ..
cmake --build .
./B_exec # 输出: Hello from A library!
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
B/build/
├── B_exec
├── CMakeFiles/...
└── Makefile / ninja.build
cmake --install
导出 A_export/
A_export/
提供给 B(打包 zip 或共享)CMAKE_PREFIX_PATH
指向 A_export/
find_package(A)
即可自动引入头文件和库CMake
有了更深入的理解和认识。