静态库
当同一份实现要被多个程序使用时,每次复制源码会带来重复维护。本章把 greeting 构建成静态库,再链接进 app。
认识静态库
静态库在 Linux 上通常使用 .a 扩展名,保存已经编译好的库代码,不能直接运行。链接 app 时,链接器会把程序实际使用的库代码放进最终可执行文件。
工程目录
static-library/
├── CMakeLists.txt
├── main.cpp
└── greeting/
├── CMakeLists.txt
├── include/
│ └── greeting.h
└── src/
└── greeting.cpp
查看构建规则
顶层文件不直接编译 greeting.cpp:add_subdirectory(greeting) 会让构建系统进入库目录执行其中的规则。
CMakeLists.txt
# 构建 StaticLibrary 工程:根 main.cpp 使用 greeting/ 提供的静态库。
cmake_minimum_required(VERSION 3.31)
project(StaticLibrary LANGUAGES CXX)
# 使用 C++20 作为语言标准。
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 可执行文件输出到 build/bin。
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# 静态库输出到 build/lib。
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
# 编译数据库,记录每个源文件的实际编译参数。
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# 加载 greeting/CMakeLists.txt 中的静态库目标。
add_subdirectory(greeting)
# 创建 app 可执行目标。
add_executable(app main.cpp)
# 链接 greeting 静态库。
target_link_libraries(app PRIVATE greeting)
# 编译时启用 libc++ 与常用警告。
target_compile_options(app PRIVATE -stdlib=libc++ -Wall -Wextra -Werror)
# 链接时也使用 libc++。
target_link_options(app PRIVATE -stdlib=libc++)库目录自己负责创建 greeting 目标。
greeting/CMakeLists.txt
# 构建 greeting 静态库。
# 声明 greeting 静态库目标:由 src/greeting.cpp 编译生成。
add_library(greeting STATIC src/greeting.cpp)
# include/ 加入头文件搜索路径,供使用方 include。
target_include_directories(greeting PUBLIC include)
# 编译时启用 libc++ 与常用警告。
target_compile_options(greeting PRIVATE -stdlib=libc++ -Wall -Wextra -Werror)STATIC 生成的就是静态库产物 .a 文件,构建产物为 libgreeting.a。PUBLIC 表示使用这个库的程序也能找到 include/。target_link_libraries(app PRIVATE greeting) 告诉 CMake:构建 app 时使用这个库。
构建并运行
Ubuntu Bash(工程根目录)
# 配置工程,并生成 Ninja 使用的构建规则
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=clang++
# 编译库和 app,并完成链接
cmake --build build
# 运行程序
./build/bin/app输出如下:
输出
Hello, World!
构建后会得到 build/lib/libgreeting.a 和 build/bin/app。
静态库产物
Ubuntu Bash(static-library 工程根目录)
ldd build/bin/app输出中不会出现 libgreeting.a。静态库只参与链接,需要的代码已经进入 app,运行时不再读取 .a 文件。
选择静态库
静态库能让程序少带一个运行时库文件,适合希望简化部署的场景。代价是库改变后,所有使用它的程序都要重新链接。
常见错误
链接器找不到 MakeGreeting()
先确认顶层 CMakeLists.txt 包含 target_link_libraries(app PRIVATE greeting)。补回后重新配置并构建:
Ubuntu Bash(工程根目录)
# 重新配置,让 CMake 重新读取链接关系
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=clang++
# 编译库和 app,并完成链接
cmake --build build自测问题
先不要展开答案,试着自己把回答完整说出来,再展开对照。说不出来的部分,回到对应小节对照正文。
为什么
ldd build/bin/app不会列出libgreeting.a?查看答案
静态库只在链接阶段提供目标代码,链接器会把被使用的代码复制进
app。程序运行时不再依赖原来的.a文件。
本章回顾
你已经能构建并链接静态库。下一章会改成动态库,观察程序运行时如何查找库文件。