CMake add_library 命令完全指南:从普通/对象/接口库到导入库与别名目标的实战详解

发布时间:2026/10/3 2:30:38
CMake add_library 命令完全指南:从普通/对象/接口库到导入库与别名目标的实战详解 构建工具开发工具CLI【免费下载链接】CMakeMirror of CMake upstream repository项目地址https://gitcode.com/gh_mirrors/cm/CMake点击查看免费下载add_library是 CMake 中创建库目标的核心命令用于将指定源文件编译为静态库、动态库、模块库或对象库并支持通过INTERFACE、IMPORTED、ALIAS等关键字构造纯接口库、外部导入库与别名目标。本文以 CMake 官方文档 Help/command/add_library.rst 为骨架结合源码 Source/cmAddLibraryCommand.cxx 与仓库内真实测试用例完整讲解每一种库类型的语法、默认行为、目标属性与平台差异帮助你写出可复制、可运行、可维护的构建脚本。概述一条命令五种库形态add_library的完整签名体系覆盖五种目标形态各自服务于不同的构建需求普通库Normal Librariesadd_library(name [type] [EXCLUDE_FROM_ALL] sources...)产出磁盘上的.a/.so/.dll等库文件对象库Object Librariesadd_library(name OBJECT sources...)只编译不归档供其他目标按需引用对象文件接口库Interface Librariesadd_library(name INTERFACE ...)不编译任何源码、不产出工件只传播使用要求导入库Imported Librariesadd_library(name type IMPORTED [GLOBAL])引用项目外部已存在的库文件别名库Alias Librariesadd_library(name ALIAS target)为已有目标提供第二个名字。命令解析入口位于 cmAddLibraryCommand.cxx其中用一个循环扫描STATIC/SHARED/MODULE/OBJECT/UNKNOWN/ALIAS/INTERFACE/EXCLUDE_FROM_ALL/SYMBOLIC/IMPORTED/GLOBAL等关键字遇到第一个无法识别的参数即视为源文件列表的开始随后调用cmMakefile::AddLibrary或AddImportedTarget完成目标创建。普通库STATIC / SHARED / MODULE三种库类型的语义type参数决定库的形态三者的本质区别在于“能否被链接”与“如何被加载”类型产物用途STATIC对象文件归档如libname.a、name.lib链接其他目标时直接合并进可执行文件SHARED动态库如libname.so、name.dll可被其他目标链接也可在运行时被加载MODULE插件式动态库如.so、.dll不可被其他目标链接只能通过dlopen类机制在运行时动态加载当type被省略时默认值由全局变量BUILD_SHARED_LIBS决定该变量开启则默认SHARED否则默认STATIC。这一逻辑在源码中可见——cmAddLibraryCommand.cxx 先将类型初始化为SHARED_LIBRARY若BUILD_SHARED_LIBS为关IsOff()则改为STATIC_LIBRARYcmake_minimum_required(VERSION 3.16) project(mylib) # 未指定类型时BUILD_SHARED_LIBS 为 ON 则构建共享库否则构建静态库 # 配置阶段用 -DBUILD_SHARED_LIBSON 可全局切换 add_library(mylib src/mylib.c) # 显式指定类型不受 BUILD_SHARED_LIBS 影响 add_library(myutils STATIC utils.c) add_library(mycore SHARED core.c) add_library(myplugin MODULE plugin.c)目标名称与文件名规则name是逻辑目标名必须在整个项目中全局唯一实际产出的文件名由平台约定决定例如 Unix 上的libname.aWindows 上的name.lib。源码对名称做了三重校验cmAddLibraryCommand.cxx必须是合法的目标名、不能是 CMake 保留目标名如all、clean、非导入非别名目标的名字中不能包含:。如需修改最终文件名可通过目标属性定制OUTPUT_NAME替换文件名中的name部分ARCHIVE_OUTPUT_DIRECTORY控制归档文件静态库、导入库输出目录LIBRARY_OUTPUT_DIRECTORY控制运行时加载的动态库.so/.dylib输出目录RUNTIME_OUTPUT_DIRECTORY控制可执行运行时文件Windows 上的.dll输出目录。默认情况下库文件生成在“调用该命令的源目录对应的构建树目录”中。生成器表达式与源文件灵活性从 3.1 版本起add_library的源参数支持$...形式的生成器表达式详见 cmake-generator-expressions从 3.11 版本起源文件甚至可以完全省略稍后用target_sources补充add_library(foo SHARED) # 稍后补源文件可配合条件分支按平台/配置加入不同源 if(WIN32) target_sources(foo PRIVATE win/foo_win.c) else() target_sources(foo PRIVATE unix/foo_unix.c) endif()SHARED / MODULE 的自动属性对于SHARED和MODULE库CMake 会自动将目标属性POSITION_INDEPENDENT_CODE置为ON位置无关代码。此外SHARED库可设置FRAMEWORK目标属性生成 macOS Framework3.8 起STATIC库也可设置生成静态 Framework没有导出任何符号的库不得声明为SHARED。例如 Windows 资源 DLL、不导出非托管符号的 C/CLI 托管 DLL都必须声明为MODULE库——因为 CMake 假定SHARED库在 Windows 上总是带有对应的导入库import library。若某些源文件只是预处理产物希望原始源文件在 IDE 中仍然可见可参考HEADER_FILE_ONLY源文件属性。平台不支持动态链接时的行为CMP0164 策略从 3.30 版本起在TARGET_SUPPORTS_SHARED_LIBS为假平台不支持共享库的平台上add_library创建SHARED库时会直接失败而不再像 3.29 及以前那样自动降级为静态库。该行为由策略 CMP0164 控制OLD静默创建静态库源码中对应case cmPolicies::OLD: type cm::TargetType::STATIC_LIBRARY;见 cmAddLibraryCommand.cxxNEW默认发出致命错误并终止配置cmAddLibraryCommand.cxxWARN发出开发者警告后仍降级为静态库。需要兼容旧行为的老项目可在cmake_minimum_required之前显式设置cmake_policy(SET CMP0164 OLD)对象库OBJECT编译但不归档对象库只负责把源文件编译成.o目标文件不进行归档或链接。其他由add_library或add_executable创建的目标可以通过$TARGET_OBJECTS:objlib表达式把对象文件当作“源”引用add_library(objlib OBJECT a.c b.c) add_library(mylib SHARED $TARGET_OBJECTS:objlib) add_executable(myapp main.c $TARGET_OBJECTS:objlib)上述代码会把objlib的对象文件同时并入mylib与myapp。仓库测试 Tests/ObjectLibrary/CMakeLists.txt 中就有大量此类用法例如add_library(Cstatic STATIC c.c $TARGET_OBJECTS:A $TARGET_OBJECTS:B) add_executable(UseCstatic main.c) target_link_libraries(UseCstatic Cstatic)对象库的使用约束只能包含可编译的源文件、头文件以及不影响普通库链接的其他文件如.txt可以包含生成这些源文件的自定义命令但不能包含PRE_BUILD、PRE_LINK、POST_BUILD自定义命令某些原生构建系统如 Xcode 生成器不喜欢只有对象文件的目标因此建议给任何引用$TARGET_OBJECTS:objlib的目标至少添加一个真实源文件——测试 Tests/ObjectLibrary/CMakeLists.txt 中即通过set(dummy dummy.c)来处理 Xcode 场景自 3.12 起对象库可以直接被target_link_libraries链接从而传播其INTERFACE_*使用要求。接口库INTERFACE不编译、不产物的纯约定目标基础形态add_library(name INTERFACE)创建一个接口库目标它不编译源文件、不产生磁盘上的库文件只用于为依赖方传递使用要求。没有源文件的接口库不会出现在生成的构建系统中但仍可设置属性、被安装和导出。典型的接口库填充方式是通过INTERFACE_*属性族配合以下命令set_property/set_target_propertiestarget_link_libraries(INTERFACE ...)target_link_options(INTERFACE ...)target_include_directories(INTERFACE ...)target_compile_options(INTERFACE ...)target_compile_definitions(INTERFACE ...)target_sources(INTERFACE ...)随后它像任何普通目标一样作为target_link_libraries的参数使用。一个经典场景是聚合一组头文件搜索路径测试 Tests/InterfaceLibrary/CMakeLists.txt 中有大量接口库与IMPORTED接口库的组合用例add_library(myheaders INTERFACE) target_include_directories(myheaders INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) target_compile_definitions(myheaders INTERFACE MY_FEATURE1) add_executable(app main.c) target_link_libraries(app PRIVATE myheaders)带源文件的接口库3.193.19 起支持add_library(name INTERFACE [EXCLUDE_FROM_ALL] sources...)形式接口库可以拥有源文件列表或通过target_sources以PRIVATE/PUBLIC关键字追加。需要注意关键字语义差异此签名中的INTERFACE仅表示库类型紧随其后的源文件是接口库自己的PRIVATE源不会出现在其INTERFACE_SOURCES属性中。当接口库设置了SOURCES或HEADER_SETS目标属性时它就会像add_custom_target创建的目标一样出现在构建系统中虽然不编译任何源但会承载add_custom_command为它创建的构建规则例如用于生成头文件的自定义命令。3.15 起接口库还可以设置PUBLIC_HEADER与PRIVATE_HEADER属性配合install(TARGETS)安装这些头文件。符号接口库SYMBOLIC4.2add_library(name INTERFACE SYMBOLIC)创建符号接口库对应目标属性SYMBOLIC为真。它没有使用要求、不编译源、不产出工件但可以被导出、安装并能用if(TARGET)检测存在性。符号接口库的典型用途是表达可选组件例如库libgui可能提供、也可能不提供widget特性消费方通过链接widget符号目标来声明“我必须依赖该组件”从而让find_package声明的必需组件通过链接对应符号目标得到校验。测试 Tests/InterfaceLibrary/CMakeLists.txt 中的add_library(imp::iface INTERFACE IMPORTED)与符号目标理念一致均用于在包中表达接口约定。导入库IMPORTED引用项目外的库基本用法与可见性add_library(name type IMPORTED [GLOBAL])创建一个导入库目标用于引用项目外部如系统库、第三方预编译库的库文件。导入目标可以像项目内目标一样被引用但默认只在创建它的目录及其子目录中可见加上GLOBAL选项后全局可见。导入目标不会生成任何构建规则其IMPORTED目标属性为True通常配合target_link_libraries便捷引用。源码对导入库的约束cmAddLibraryCommand.cxxIMPORTED签名必须显式指定库类型否则报错called with IMPORTED argument but no library type目标名不能与已有目标重复GLOBAL选项只允许与IMPORTED连用。add_library(extfoo SHARED IMPORTED) set_target_properties(extfoo PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/libfoo.so INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/include ) add_executable(app main.c) target_link_libraries(app PRIVATE extfoo)各类型的属性约定导入库的类型决定 CMake 如何理解磁盘上的文件详情通过IMPORTED_*与INTERFACE_*前缀属性描述STATIC/SHARED/MODULE/UNKNOWN引用项目外部的一个库文件主文件位置由IMPORTED_LOCATION或按配置变体IMPORTED_LOCATION_CONFIG指定。非 Windows 平台上的SHARED库主文件就是链接器和动态加载器共用的.so/.dylib文件。若该文件带SONAMEmacOS 上为以rpath/开头的LC_ID_DYLIB应把该值写入IMPORTED_SONAME若没有SONAME且平台支持应设置IMPORTED_NO_SONAME。Windows 上的SHARED库IMPORTED_IMPLIB或IMPORTED_IMPLIB_CONFIG指定 DLL 导入库文件.lib或.dll.a的位置IMPORTED_LOCATION则指向.dll运行时文件可选但TARGET_RUNTIME_DLLS生成器表达式需要它。UNKNOWN类型通常只在 Find 模块实现中使用参见 Find Modules 相关章节允许把find_library找到的路径直接交给导入库而无需事先知道库的类型——这在 Windows 上尤为有用因为静态库和 DLL 的导入库扩展名相同都是.lib。OBJECT引用项目外部的一组对象文件位置由IMPORTED_OBJECTS或IMPORTED_OBJECTS_CONFIG指定。INTERFACE不引用磁盘上的任何库或对象文件仅通过INTERFACE_*属性携带使用要求。# Find 模块风格先 find_library 再建导入库 find_library(FOO_LIB foo) add_library(foo UNKNOWN IMPORTED) set_target_properties(foo PROPERTIES IMPORTED_LOCATION ${FOO_LIB})别名库ALIAS给目标起第二个名字add_library(name ALIAS target)创建别名目标使name在后续命令中可等价引用target。别名不会出现在生成的构建系统中不是 make 目标且target本身不能是另一个ALIAS。源码中的别名校验cmAddLibraryCommand.cxx包括名称必须合法EXCLUDE_FROM_ALL与IMPORTED与ALIAS连用无意义直接报错ALIAS恰好需要一个目标参数被指向的目标必须已存在且是库类型SHARED/STATIC/MODULE/OBJECT/INTERFACE或导入的UNKNOWN库。add_library(foo SHARED foo.cpp) add_library(MyNS::Foo ALIAS foo) # 命名空间风格别名 add_executable(app main.cpp) target_link_libraries(app PRIVATE MyNS::Foo)版本演进要点3.11ALIAS可以指向GLOBAL导入目标3.18ALIAS可以指向非GLOBAL的导入目标此时别名作用域限定在创建目录及其子目录可用ALIAS_GLOBAL目标属性查询别名是否全局4.5name可作为set_property、set_target_properties、target_link_libraries等的操作数来修改target的属性将ALIAS传给install或export时实际安装/导出的是其指向的目标。CMake 4.4 及更早版本不允许这些用法。别名目标的典型用法包括为库提供带命名空间Foo::Foo的公开名称、为同一目标提供长短两个名字、以及在add_custom_command与if(TARGET)中引用。仓库测试 Tests/AliasTarget/CMakeLists.txt 全面覆盖了这些场景定义PREFIX::Foo、Another::Alias别名用ALIASED_TARGET属性校验别名指向并用get_property/get_target_property验证别名的存在性与指向。实战要点小结类型选择需要被其他目标链接选STATIC/SHARED仅作运行时插件选MODULE只需编译选OBJECT只传使用要求选INTERFACE引用外部库选IMPORTED复用已有目标名字选ALIAS默认类型不写type时由BUILD_SHARED_LIBS决定全局开关便于一键切换静态/动态构建命名规范目标名全局唯一、避开 CMake 保留名接口库/别名目标常使用Namespace::Name形式注意普通库名不能含:平台差异Windows 无符号导出 DLL 用MODULE而非SHARED不支持共享库的平台从 3.30 起直接报错受 CMP0164 控制作用域非GLOBAL的导入目标只在创建目录及子目录可见跨目录使用需加GLOBAL或改用ALIAS3.18 可别名非全局导入目标。进一步阅读cmake-buildsystem 手册可了解构建系统属性全貌add_executable 命令与add_library共享同一套目标模型而 cmake-generator-expressions 手册则覆盖$TARGET_OBJECTS:...等表达式的完整语法。赞分享构建工具开发工具CLI【免费下载链接】CMakeMirror of CMake upstream repository项目地址https://gitcode.com/gh_mirrors/cm/CMake点击查看免费下载相关推荐Mailu 命令行完全指南域名、用户、别名管理与配置导入导出实战Mailu 命令行完全指南域名、用户、别名管理与配置导入导出实战 本文以 Mailu 邮件服务器Docker 化部署的开源邮件系统的 CLI 为核心系统后端通信CMake接口库终极指南现代CMake接口设计模式详解CMake接口库终极指南现代CMake接口设计模式详解 想要掌握现代CMake接口库设计模式吗 作为C项目构建的核心工具CMake的接口库功能能够示例工程教程构建工具TEN Framework 仓库内 googletest 的构建与集成指南从编译命令到 GN/CMake 接入TEN Framework 仓库内 googletest 的构建与集成指南从编译命令到 GN/CMake 接入 Google Testgoogletest人工智能AI Agent多模态语音AI 应用上一篇SeaTunnel RocketMQ Sink 连接器实战参数解析、消息分区与精确一次写入下一篇Tinycast 系统动作指南31 个内置 macOS 快捷操作的完整实现与配置创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询