本文通过两个完全独立的 CMake 项目演示如何在 Windows PowerShell 7 MinGW 环境下创建、安装、导出并消费静态库同时配置 clangd 的代码补全与跳转支持。示例已经完成实际构建和运行验证。生产端是cmake-static-lib消费端是cmake-use-static-lib两个项目使用独立包名、命名空间和安装前缀不会与动态库示例混淆。一、创建并导出静态库本文说明cmake-static-lib如何生成、安装并导出静态库使其他 CMake项目能够通过find_package()使用它们。1. 项目概览cmake-static-lib使用 C17生成两个彼此独立的静态库CMake 目标职责公开接口message_utils构造示例提示信息std::string build_message()sort_utils返回排序后的整数副本bubble_sort、selection_sort、insertion_sort、merge_sort、quick_sort排序接口位于sort_algorithms命名空间中参数和返回值均为std::vectorint。本项目与动态库示例完全独立包名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.cpp2. 环境要求Windows 与 PowerShell 7pwshCMake 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定义项目版本和 C17 标准并在进入源码目录前加载GNUInstallDirscmake_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.txt和src/utils/CMakeLists.txt负责进入下一级目录真正定义目标的是message/CMakeLists.txt和sort/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消费端因此使用#includecmake-static-lib/message.h#includecmake-static-lib/sort_algorithms.h4.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三条命令分别完成使用MinGW Makefiles配置 Debug 构建并生成build/mingw/compile_commands.json。编译message_utils和sort_utils静态库。安装静态归档、头文件和 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.dll、libsort_utils.dll或相应的.dll.a导入库。6. 使用 Ninja/Clang 构建生产端还提供ninja-clang和clangbuildcmake--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_utils和sort_utils。不要把共享版和静态版同时通过add_subdirectory()加入同一顶层构建否则会发生目标重名安装后应分别通过各自的包名和命名空间消费。8. 常见问题现象检查方法找不到g或mingw32-make确认 MinGW 工具目录已加入PATHpreset 无法读取使用 CMake 3.25 或更高版本安装目录没有.a先成功构建再执行cmake --install安装结果出现.dll或.dll.a检查叶子目标是否确实使用STATIC并确认安装的是本项目的构建目录消费端找不到包检查prefix/lib/cmake/cmake-static-lib/cmake-static-lib-config.cmake是否存在链接时报未定义符号或文件格式错误确认生产端和消费端的编译器、C 标准库、位数及构建配置兼容程序在其他机器上仍提示缺少运行库 DLL这是工具链运行时依赖不是message_utils或sort_utils项目 DLL按部署环境处理 MinGW 运行库二、在另一个 CMake 项目中使用静态库本文说明cmake-use-static-lib如何查找并链接cmake-static-lib安装的两个静态库以及如何通过compile_commands.json为 clangd 提供语法、补全和跳转支持。1. 消费关系概览消费流程分为三个阶段cmake-static-lib构建并安装头文件、静态归档和 CMake 包配置。cmake-use-static-lib通过find_package(cmake-static-lib)加载导入目标。链接器从两个静态归档中按需取出目标代码写入最终可执行文件。项目使用以下两个导入目标导入目标提供的功能cmake-static-lib::message_utilsbuild_message()cmake-static-lib::sort_utils五种返回排序副本的排序函数默认安装前缀是C:/install/cmake-static-lib。包名、命名空间、头文件目录和安装前缀均与动态库示例分开。2. 环境要求Windows 与 PowerShell 7pwshCMake 3.25 或更高版本MinGW 的g和mingw32-make已加入PATH使用兼容 MinGW 工具链构建并安装的cmake-static-lib如需代码补全和跳转clangd 可执行文件及 VS Code clangd 扩展两个项目使用 C17。其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.cmakeMinGW 下的.a是包含目标代码的静态归档不是动态库使用的.dll.a导入库。3.2 配置并构建消费程序Set-Location..\cmake-use-static-lib cmake--fresh--preset mingw cmake--build--preset mingwbuildmingwpreset 默认设置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_utils和sort_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应指向安装根目录而不是include、lib或具体.cmake文件CMAKE_PREFIX_PATH └── C:/install/cmake-static-lib └── lib/cmake/cmake-static-lib/cmake-static-lib-config.cmake4.2find_package()加载导入目标消费端调用find_package(cmake-static-lib REQUIRED)包配置会加载cmake-static-lib-targets.cmake从而创建cmake-static-lib::message_utils cmake-static-lib::sort_utils4.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即可不需要把依赖继续传播给下游。源码使用安装后的公开头文件#includecmake-static-lib/message.h#includecmake-static-lib/sort_algorithms.h可以在生成的链接命令或安装导出文件中确认两个导入目标的实际位置指向libmessage_utils.a和libsort_utils.a而不是.dll.a。5. clangd 语法支持5.1 生成编译数据库消费端 preset 开启CMAKE_EXPORT_COMPILE_COMMANDS:ON成功执行配置命令后cmake--fresh--preset mingwCMake 会生成cmake-use-static-lib/build/mingw/compile_commands.json该文件记录真实编译器、C17 参数和导入目标提供的 include 路径。更换安装前缀、编译器或 CMake 配置后应重新运行配置命令。5.2.clangd配置项目根目录的.clangd指定编译数据库并为默认安装头文件提供补充路径CompileFlags:CompilationDatabase:build/mingwAdd:--IC:/install/cmake-static-lib/includeIndex:Background:BuildCompilationDatabase让 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. 使用自定义安装前缀自定义前缀时需要同步更新三个位置安装静态库时的--prefix消费端配置时的CMAKE_PREFIX_PATH.clangd中附加的-Iprefix/include例如使用D:/sdk/cmake-static-lib以下命令从仓库根目录开始执行$staticLibPrefixD:/sdk/cmake-static-libSet-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$staticLibPrefixcmake--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.dll或libsort_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选项程序仍可能依赖系统或工具链运行库。