当前位置: 首页 > news >正文

CMake 实战:创建并使用静态库(MinGW + clangd)

本文通过两个完全独立的 CMake 项目,演示如何在 Windows + PowerShell 7 + MinGW 环境下创建、安装、导出并消费静态库,同时配置 clangd 的代码补全与跳转支持。

示例已经完成实际构建和运行验证。生产端是cmake-static-lib,消费端是cmake-use-static-lib;两个项目使用独立包名、命名空间和安装前缀,不会与动态库示例混淆。

一、创建并导出静态库

本文说明cmake-static-lib如何生成、安装并导出静态库,使其他 CMake
项目能够通过find_package()使用它们。

1. 项目概览

cmake-static-lib使用 C++17,生成两个彼此独立的静态库:

CMake 目标职责公开接口
message_utils构造示例提示信息std::string build_message()
sort_utils返回排序后的整数副本bubble_sortselection_sortinsertion_sortmerge_sortquick_sort

排序接口位于sort_algorithms命名空间中,参数和返回值均为
std::vector<int>

本项目与动态库示例完全独立:

  • 包名:cmake-static-lib
  • 默认安装前缀:C:/install/cmake-static-lib
  • 安装头文件目录:include/cmake-static-lib
  • 导入目标:cmake-static-lib::message_utils
    cmake-static-lib::sort_utils

因此它可以与动态库包安装在不同前缀中,不会让
find_package(cmake-static-lib)错误加载动态库包。

项目的主要结构如下:

cmake-static-lib/ ├── CMakeLists.txt ├── CMakePresets.json ├── cmake-static-lib-config.cmake.in ├── create_static_lib.md ├── .vscode/ │ └── tasks.json └── src/ ├── CMakeLists.txt └── utils/ ├── CMakeLists.txt ├── message/ │ ├── CMakeLists.txt │ ├── message.h │ └── message.cpp └── sort/ ├── CMakeLists.txt ├── sort_algorithms.h └── sort_algorithms.cpp

2. 环境要求

  • Windows 与 PowerShell 7(pwsh
  • CMake 3.25 或更高版本
  • MinGW 的g++mingw32-make已加入PATH

CMakeLists.txt声明的最低版本是 3.16,但
CMakePresets.json使用 schema version 6;通过本文的 preset 命令构建时,
实际需要 CMake 3.25 或更高版本。

项目还提供 Ninja/Clang preset。无论选择哪套工具链,生产静态库和消费静态库
的工程都应使用 ABI 兼容的编译器、C++ 标准库、目标架构和构建配置。

3. CMake 如何生成静态库

3.1 根目录与三级 CMake 结构

CMakeLists.txt定义项目版本和 C++17 标准,并在进入源码目录前加载
GNUInstallDirs

cmake_minimum_required(VERSION 3.16) project(cmake-static-lib VERSION 1.0.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) include(GNUInstallDirs) add_subdirectory(src)

提前加载GNUInstallDirs,可以保证叶子目录配置
${CMAKE_INSTALL_INCLUDEDIR}时,该变量已经有确定值。

src/CMakeLists.txtsrc/utils/CMakeLists.txt负责进入下一级目录;真正
定义目标的是message/CMakeLists.txtsort/CMakeLists.txt。这形成
“根工程 → 功能集合 → 具体库目标”的三级结构。

3.2 显式创建STATIC目标

两个叶子目录都显式使用STATIC,因此目标类型不受
BUILD_SHARED_LIBS的值影响:

add_library(message_utils STATIC message.cpp message.h ) add_library(sort_utils STATIC sort_algorithms.cpp sort_algorithms.h )

目标的公开头文件路径同时覆盖构建树和安装树:

target_include_directories(message_utils PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> )
  • BUILD_INTERFACE让同一构建树中的调用方从源码目录找到头文件。
  • INSTALL_INTERFACE让安装后的导入目标公开<prefix>/include
  • sort_utils使用相同配置。

4. 安装与 CMake 包导出

4.1 安装头文件

公开头文件安装到:

<prefix>/include/cmake-static-lib/message.h <prefix>/include/cmake-static-lib/sort_algorithms.h

消费端因此使用:

#include<cmake-static-lib/message.h>#include<cmake-static-lib/sort_algorithms.h>
4.2 只安装静态归档

两个静态目标加入cmake-static-lib-targets导出集,并通过
ARCHIVE DESTINATION安装:

install( TARGETS message_utils sort_utils EXPORT cmake-static-lib-targets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} )

在 MinGW 下,安装产物通常是:

lib/libmessage_utils.a lib/libsort_utils.a

这里的.a包含库的目标代码,是静态归档;它不是动态库对应的
.dll.a导入库。使用 MSVC 时,对应静态归档通常使用.lib扩展名。

本项目的安装规则不需要为两个库设置RUNTIME目的地,也不会生成或安装
项目 DLL。静态库代码会在链接阶段按需合入最终程序。

4.3 导出带命名空间的目标

导出集安装到<prefix>/lib/cmake/cmake-static-lib,并添加独立命名空间:

install( EXPORT cmake-static-lib-targets FILE cmake-static-lib-targets.cmake NAMESPACE cmake-static-lib:: DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cmake-static-lib )

这会为消费端创建:

cmake-static-lib::message_utils cmake-static-lib::sort_utils

导入目标携带静态归档位置和公开 include 路径。消费端无需硬编码.a
.lib文件的绝对路径。

4.4 包配置和版本文件

cmake-static-lib-config.cmake.in加载导出的 targets 文件:

@PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/cmake-static-lib-targets.cmake") check_required_components(cmake-static-lib)

安装时还会生成cmake-static-lib-config-version.cmake。消费端通过下面一行
同时加载包配置和两个导入目标:

find_package(cmake-static-lib REQUIRED)

5. 使用 MinGW 构建和安装

cmake-static-lib目录中运行:

cmake--preset mingw cmake--build--preset mingwbuild cmake--install build/mingw--prefix C:/install/cmake-static-lib

三条命令分别完成:

  1. 使用MinGW Makefiles配置 Debug 构建,并生成
    build/mingw/compile_commands.json
  2. 编译message_utilssort_utils静态库。
  3. 安装静态归档、头文件和 CMake package 文件。

MinGW 构建树中的库通常位于:

build/mingw/src/utils/message/libmessage_utils.a build/mingw/src/utils/sort/libsort_utils.a

安装后的典型结构如下:

C:/install/cmake-static-lib/ ├── include/ │ └── cmake-static-lib/ │ ├── message.h │ └── sort_algorithms.h └── lib/ ├── libmessage_utils.a ├── libsort_utils.a └── cmake/ └── cmake-static-lib/ ├── cmake-static-lib-config.cmake ├── cmake-static-lib-config-version.cmake ├── cmake-static-lib-targets.cmake └── cmake-static-lib-targets-debug.cmake

该安装树不需要保存本项目的bin目录,也不应包含
libmessage_utils.dlllibsort_utils.dll或相应的.dll.a导入库。

6. 使用 Ninja/Clang 构建

生产端还提供ninja-clangclangbuild

cmake--preset ninja-clang cmake--build--preset clangbuild cmake--install build/ninja-clang--prefix C:/install/cmake-static-lib-clang

该流程要求clang++ninja已加入PATH。当前消费示例以
MinGW 为主,因此不要直接把 Clang 生成的静态归档交给 MinGW 消费。示例使用
独立安装前缀,避免覆盖 MinGW 产物。

7. 静态库与动态库的区别

项目静态库动态库
MinGW 链接输入.a静态归档.dll.a导入库
项目运行时文件库代码已合入可执行文件还需加载.dll
项目 DLL 搜索路径不需要为这两个库配置PATH通常需把安装目录的bin加入PATH
更新库重新链接程序后生效可在 ABI 兼容时替换 DLL

“链接本项目的静态库”不等于“生成完全静态的可执行文件”。本工程没有添加
MinGW 的-static选项;程序仍可能依赖libstdc++libgcc、线程库和
Windows 系统 DLL。将程序复制到其他机器时,应使用依赖检查工具确认实际运行
时依赖。

两个项目内部仍使用通用目标名message_utilssort_utils。不要把共享版
和静态版同时通过add_subdirectory()加入同一顶层构建,否则会发生目标
重名;安装后应分别通过各自的包名和命名空间消费。

8. 常见问题

现象检查方法
找不到g++mingw32-make确认 MinGW 工具目录已加入PATH
preset 无法读取使用 CMake 3.25 或更高版本
安装目录没有.a先成功构建,再执行cmake --install
安装结果出现.dll.dll.a检查叶子目标是否确实使用STATIC,并确认安装的是本项目的构建目录
消费端找不到包检查<prefix>/lib/cmake/cmake-static-lib/cmake-static-lib-config.cmake是否存在
链接时报未定义符号或文件格式错误确认生产端和消费端的编译器、C++ 标准库、位数及构建配置兼容
程序在其他机器上仍提示缺少运行库 DLL这是工具链运行时依赖,不是message_utilssort_utils项目 DLL;按部署环境处理 MinGW 运行库

二、在另一个 CMake 项目中使用静态库

本文说明cmake-use-static-lib如何查找并链接cmake-static-lib安装的两个
静态库,以及如何通过compile_commands.json为 clangd 提供语法、补全和
跳转支持。

1. 消费关系概览

消费流程分为三个阶段:

  1. cmake-static-lib构建并安装头文件、静态归档和 CMake 包配置。
  2. cmake-use-static-lib通过find_package(cmake-static-lib)加载导入目标。
  3. 链接器从两个静态归档中按需取出目标代码,写入最终可执行文件。

项目使用以下两个导入目标:

导入目标提供的功能
cmake-static-lib::message_utilsbuild_message()
cmake-static-lib::sort_utils五种返回排序副本的排序函数

默认安装前缀是C:/install/cmake-static-lib。包名、命名空间、头文件目录和
安装前缀均与动态库示例分开。

2. 环境要求

  • Windows 与 PowerShell 7(pwsh
  • CMake 3.25 或更高版本
  • MinGW 的g++mingw32-make已加入PATH
  • 使用兼容 MinGW 工具链构建并安装的cmake-static-lib
  • 如需代码补全和跳转:clangd 可执行文件及 VS Code clangd 扩展

两个项目使用 C++17。其CMakePresets.json使用 schema version 6,因此
本文的 preset 命令实际要求 CMake 3.25 或更高版本。

3. 完整构建和运行流程

以下命令从仓库根目录开始执行。

3.1 构建并安装静态库
Set-Location.\cmake-static-lib cmake--preset mingw cmake--build--preset mingwbuild cmake--install build/mingw--prefix C:/install/cmake-static-lib

安装完成后,至少应存在:

C:/install/cmake-static-lib/ ├── include/ │ └── cmake-static-lib/ │ ├── message.h │ └── sort_algorithms.h └── lib/ ├── libmessage_utils.a ├── libsort_utils.a └── cmake/ └── cmake-static-lib/ ├── cmake-static-lib-config.cmake ├── cmake-static-lib-config-version.cmake ├── cmake-static-lib-targets.cmake └── cmake-static-lib-targets-debug.cmake

MinGW 下的.a是包含目标代码的静态归档,不是动态库使用的.dll.a
导入库。

3.2 配置并构建消费程序
Set-Location..\cmake-use-static-lib cmake--fresh--preset mingw cmake--build--preset mingwbuild

mingwpreset 默认设置:

"CMAKE_PREFIX_PATH":"C:/install/cmake-static-lib"

因此配置阶段会在该前缀中查找cmake-static-lib包。这里使用--fresh
重新生成 CMake 缓存,避免之前的cmake-static-lib_DIR或自定义安装前缀
继续生效。

3.3 直接运行
.\build\mingw\cmake-use-static-lib.exe

运行前不需要把C:/install/cmake-static-lib/bin加入PATH,因为
message_utilssort_utils的代码已经在链接时写入可执行文件。

程序的预期输出为:

Hello from a CMake subdirectory project built with MinGW. 原始数组: 5 3 8 1 9 2 7 4 6 快速排序: 1 2 3 4 5 6 7 8 9 冒泡排序: 1 2 3 4 5 6 7 8 9 归并排序: 1 2 3 4 5 6 7 8 9

不需要本项目的 DLL,不代表可执行文件完全静态。项目没有添加 MinGW 的
-static选项,程序仍可能依赖 MinGW C++ 运行库、线程库和 Windows 系统
DLL。

4. CMake 如何找到并链接静态库

4.1CMAKE_PREFIX_PATH指向安装前缀

CMAKE_PREFIX_PATH应指向安装根目录,而不是includelib或具体
.cmake文件:

CMAKE_PREFIX_PATH └── C:/install/cmake-static-lib └── lib/cmake/cmake-static-lib/cmake-static-lib-config.cmake
4.2find_package()加载导入目标

消费端调用:

find_package(cmake-static-lib REQUIRED)

包配置会加载cmake-static-lib-targets.cmake,从而创建:

cmake-static-lib::message_utils cmake-static-lib::sort_utils
4.3 使用PRIVATE链接

消费端直接链接带命名空间的导入目标:

add_executable(cmake-use-static-lib src/main.cpp) target_link_libraries(cmake-use-static-lib PRIVATE cmake-static-lib::message_utils cmake-static-lib::sort_utils )

导入目标把安装前缀下的 include 目录和.a文件位置传递给链接目标。由于
最终目标是可执行程序,使用PRIVATE即可,不需要把依赖继续传播给下游。

源码使用安装后的公开头文件:

#include<cmake-static-lib/message.h>#include<cmake-static-lib/sort_algorithms.h>

可以在生成的链接命令或安装导出文件中确认,两个导入目标的实际位置指向
libmessage_utils.alibsort_utils.a,而不是.dll.a

5. clangd 语法支持

5.1 生成编译数据库

消费端 preset 开启:

"CMAKE_EXPORT_COMPILE_COMMANDS":"ON"

成功执行配置命令后:

cmake--fresh--preset mingw

CMake 会生成:

cmake-use-static-lib/build/mingw/compile_commands.json

该文件记录真实编译器、C++17 参数和导入目标提供的 include 路径。更换安装
前缀、编译器或 CMake 配置后,应重新运行配置命令。

5.2.clangd配置

项目根目录的.clangd指定编译数据库,并为默认安装头文件提供补充路径:

CompileFlags:CompilationDatabase:build/mingwAdd:-"-IC:/install/cmake-static-lib/include"Index:Background:Build
  • CompilationDatabase让 clangd 读取build/mingw/compile_commands.json
  • Add是默认安装目录的兜底 include 路径。
  • 成功加载导入目标后,正确的 include 路径也会出现在编译数据库中。

本项目不抑制pp_file_not_found。如果依赖没有安装、CMake 配置失败或路径
错误,clangd 应保留头文件缺失诊断,便于及时发现问题。

.vscode/settings.json只禁用 Microsoft C/C++ 扩展的重复 IntelliSense:

{"C_Cpp.intelliSenseEngine":"disabled"}

该设置不会安装 clangd,也不会配置 clangd 可执行文件路径;这些仍属于本机
前置条件。

6. 使用自定义安装前缀

自定义前缀时,需要同步更新三个位置:

  1. 安装静态库时的--prefix
  2. 消费端配置时的CMAKE_PREFIX_PATH
  3. .clangd中附加的-I<prefix>/include

例如使用D:/sdk/cmake-static-lib,以下命令从仓库根目录开始执行:

$staticLibPrefix="D:/sdk/cmake-static-lib"Set-Location.\cmake-static-lib cmake--preset mingw cmake--build--preset mingwbuild cmake--install build/mingw--prefix$staticLibPrefixSet-Location..\cmake-use-static-lib cmake--fresh--preset mingw"-DCMAKE_PREFIX_PATH=$staticLibPrefix"cmake--build--preset mingwbuild.\build\mingw\cmake-use-static-lib.exe

同时把.clangd更新为:

CompileFlags:CompilationDatabase:build/mingwAdd:-"-ID:/sdk/cmake-static-lib/include"

--fresh很重要:CMake 会缓存找到的包目录。若只修改 preset 或命令行前缀
而沿用旧缓存,find_package()仍可能加载之前安装的静态库。自定义前缀不
需要加入运行时PATH,因为本项目不安装运行时 DLL。

7. 常见问题

现象原因与处理
CMake 提示找不到cmake-static-libConfig.cmake先安装静态库,确认CMAKE_PREFIX_PATH指向安装根目录,并用--fresh清除旧的包目录缓存
#include <cmake-static-lib/...>飘红确认头文件已安装,重新配置 CMake,并核对.clangd-I路径
没有compile_commands.jsonCMake 配置尚未成功,或查看了错误的build/mingw目录
修改前缀后 clangd 仍跳转到旧头文件--fresh重新配置、同步更新.clangd,然后重启 clangd language server
链接命令出现.dll.a很可能加载了动态库包或旧缓存;核对包名、命名空间、安装前缀并重新配置
链接时报未定义符号或文件格式错误确认静态库和程序使用兼容的 MinGW 工具链、架构及构建配置
运行时提示缺少libmessage_utils.dlllibsort_utils.dll当前程序可能链接了动态版或使用了旧构建缓存;静态版不需要这两个 DLL
运行时提示缺少libstdc++libgcc或线程库 DLL这属于 MinGW 运行时依赖;链接项目静态库不等于完全静态链接工具链运行库

三、验证结果

消费程序可以在不配置项目库运行时PATH的情况下直接启动,三种排序算法均得到相同结果:

Hello from a CMake subdirectory project built with MinGW. 原始数组: 5 3 8 1 9 2 7 4 6 快速排序: 1 2 3 4 5 6 7 8 9 冒泡排序: 1 2 3 4 5 6 7 8 9 归并排序: 1 2 3 4 5 6 7 8 9

需要特别注意:链接本项目静态库不等于把整个程序做成完全静态程序。本文没有添加 MinGW 运行库的-static选项,程序仍可能依赖系统或工具链运行库。

http://www.cnnetsun.cn/news/3628506.html

相关文章:

  • 如何在Windows上创建完美虚拟显示器:Parsec VDD终极指南
  • 揭秘信创合规深水区:如何用流式脱敏+国密SM2防篡改,把国产库审计验收从“踩雷”变“满分”的4个血泪教训!
  • Formality:使用机器学习驱动的分布式处理(DPX)
  • ncmdumpGUI:Windows下一键解密网易云音乐NCM文件的完整指南
  • 终极简单指南:3分钟让Figma界面全中文化,设计师效率翻倍
  • 宝塔+雷池WAF部署
  • 页面路由导航:Router与Navigation组件的跳转传参(7)
  • 【claude code实践】Hooks 调试方法:让自动化流程稳定可靠
  • 高并发内存池 - central cache 结构设计
  • Cpp2IL完整指南:如何分析和理解Unity IL2CPP编译后的应用
  • 英雄联盟智能助手Seraphine:免费开源的终极战绩查询与BP辅助工具
  • Betaflight Configurator终极指南:5步打造完美无人机飞控系统
  • HarmonyOS开发实战:小分享-@ohos.net.http 网络请求封装进阶
  • F429-HAL-I2C读取AT24C02(2026/7/24)
  • ADC12DJ3200 JESD204B接口实战:报警寄存器与高速PCB布局设计
  • Claude Code 安装、配置、依赖与使用说明书
  • 具身智能如何才能更快走出实验室(2)
  • 具身智能如何才能更快走出实验室(7)
  • Modula-3编程语言全记录:诞生、发展、多版本实现与发行情况揭秘
  • Atmosphere大气层系统:Nintendo Switch定制固件技术解析与实战部署指南
  • Atmosphere系统架构解析:Nintendo Switch定制固件的安全实现与技术创新
  • 移动POS终端工控主板怎么选?安全加密与移动支付接口要点
  • Windows Defender彻底移除方案:三模式深度优化与安全风险管控
  • 关于4G/5G网络光路中断或者RRU/AAS故障远程控制中断的问题深度分析与系统性解决方案
  • 临床预测+医学RAG=结构化EHR建模能力+医学大模型应用能力(三)
  • HSTracker:macOS炉石传说玩家的终极对战助手完全指南
  • Linux入门攻坚——83、kvm虚拟化-3
  • 终极3D模型转换指南:5分钟将专业设计变成Minecraft建筑
  • 终极跨平台串口调试助手:COMTool一站式通信解决方案
  • AI工具套装部署失败率高达68%?:20年DevOps专家手把手教你构建零故障、可审计、合规的程序员AI工作流