1. 第1例
工程目标与目录结构
本案例构建一个名为 mathkit 的多目标 C++ 工程,包含三个构建目标:静态库 mathkit_static、动态库 mathkit_shared 和可执行程序 calc_app。静态库与动态库共享同一份源码,可执行程序链接静态库并调用其接口。工程还需导出 CMake 包配置,使外部工程能通过 find_package(mathkit) 引用。
目录结构如下:
mathkit/
├── CMakeLists.txt
├── include/
│ └── mathkit/
│ └── mathkit.h
├── src/
│ └── mathkit.cpp
├── app/
│ └── main.cpp
└── cmake/
└── mathkitConfig.cmake.in
核心 CMakeLists.txt 实现
顶层 CMakeLists.txt 使用现代 CMake 的目标级命令组织依赖。关键点:target_include_directories 使用 PUBLIC 让链接者自动获得头文件路径;target_compile_features 声明 C++17 要求;install(EXPORT) 生成包配置文件。
cmake_minimum_required(VERSION 3.20)
project(mathkit VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 公共接口头文件目录
set(MATHKIT_PUBLIC_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/include)
# 静态库目标
add_library(mathkit_static STATIC src/mathkit.cpp)
target_include_directories(mathkit_static PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_compile_features(mathkit_static PUBLIC cxx_std_17)
set_target_properties(mathkit_static PROPERTIES
OUTPUT_NAME mathkit
POSITION_INDEPENDENT_CODE ON)
# 动态库目标(复用同一源码)
add_library(mathkit_shared SHARED src/mathkit.cpp)
target_include_directories(mathkit_shared PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_compile_features(mathkit_shared PUBLIC cxx_std_17)
set_target_properties(mathkit_shared PROPERTIES
OUTPUT_NAME mathkit
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR})
# 可执行程序:链接静态库
add_executable(calc_app app/main.cpp)
target_link_libraries(calc_app PRIVATE mathkit_static)
# 安装规则
include(GNUInstallDirs)
install(TARGETS mathkit_static mathkit_shared calc_app
EXPORT mathkitTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# 导出包配置
install(EXPORT mathkitTargets
FILE mathkitTargets.cmake
NAMESPACE mathkit::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mathkit)
include(CMakePackageConfigHelpers)
configure_package_config_file(
cmake/mathkitConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/mathkitConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mathkit)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/mathkitConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/mathkitConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/mathkitConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mathkit)
源码与包配置文件
include/mathkit/mathkit.h 声明接口,src/mathkit.cpp 实现,app/main.cpp 调用。包配置模板 cmake/mathkitConfig.cmake.in 内容为:
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/mathkitTargets.cmake")
check_required_components(mathkit)
头文件示例:
#pragma once
namespace mathkit {
int add(int a, int b);
int multiply(int a, int b);
}
实现文件示例:
#include "mathkit/mathkit.h"
namespace mathkit {
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
}
主程序示例:
#include <cstdio>
#include "mathkit/mathkit.h"
int main() {
std::printf("add=%d\n", mathkit::add(2, 3));
std::printf("mul=%d\n", mathkit::multiply(4, 5));
return 0;
}
构建与验证
构建并安装到本地前缀:
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=./install
cmake --build build
cmake --install build
验证产物:install/lib/libmathkit.a、install/lib/libmathkit.so、install/bin/calc_app、install/lib/cmake/mathkit/ 下的配置文件。运行 ./install/bin/calc_app 应输出 add=5 与 mul=20。
外部工程验证 find_package:新建 consumer/CMakeLists.txt:
cmake_minimum_required(VERSION 3.20)
project(consumer LANGUAGES CXX)
find_package(mathkit 1.0 REQUIRED)
add_executable(use_mathkit main.cpp)
target_link_libraries(use_mathkit PRIVATE mathkit::mathkit_static)
配置时指定 -DCMAKE_PREFIX_PATH=../mathkit/install,若配置成功且链接运行正常,说明包导出与目标命名空间均正确。
OUTPUT_NAME 时,在 Windows 上会因导入库与静态库同名冲突,建议为动态库设置不同 OUTPUT_NAME 或使用 ARCHIVE_OUTPUT_NAME 区分。关键配置要点
| 配置项 | 作用 | 本案例取值 |
|---|---|---|
target_include_directories | 传递头文件搜索路径 | PUBLIC + 生成器表达式区分构建/安装接口 |
target_compile_features | 声明语言标准要求 | PUBLIC cxx_std_17 |
POSITION_INDEPENDENT_CODE | 静态库可被动态库链接 | ON |
install(EXPORT) | 导出目标供外部引用 | 命名空间 mathkit:: |
configure_package_config_file | 生成包配置入口 | 模板含 @PACKAGE_INIT@ |
find_package 配置成功 → 链接运行输出正确结果。任一环节失败,优先检查 INSTALL_INTERFACE 路径与导出目标命名空间是否匹配。