C++ 构建系统
概述
构建系统是C++开发中的重要工具,用于自动化编译、链接、测试和部署过程。本章介绍主流的C++构建系统,包括CMake、Make、Ninja等,以及现代化的构建配置和最佳实践。
🔨 CMake基础
基本CMakeLists.txt
cmake
# 最低CMake版本要求
cmake_minimum_required(VERSION 3.16)
# 项目名称和版本
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# C++标准设置
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# 编译选项
if(MSVC)
add_compile_options(/W4)
else()
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# 创建可执行文件
add_executable(${PROJECT_NAME}
src/main.cpp
src/utils.cpp
)
# 包含目录
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
# 安装规则
install(TARGETS ${PROJECT_NAME}
DESTINATION bin
)库的创建和使用
cmake
# 创建静态库
add_library(MyLibrary STATIC
src/library.cpp
src/helper.cpp
)
# 库的包含目录
target_include_directories(MyLibrary PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
# 创建共享库
add_library(MySharedLib SHARED
src/shared.cpp
)
# 设置库版本
set_target_properties(MySharedLib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
)
# 可执行文件链接库
add_executable(MyApp src/main.cpp)
target_link_libraries(MyApp PRIVATE MyLibrary)
# 条件编译
option(BUILD_TESTS "Build test programs" ON)
option(BUILD_EXAMPLES "Build example programs" OFF)
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()查找和使用第三方库
cmake
# 查找系统包
find_package(Threads REQUIRED)
find_package(OpenSSL REQUIRED)
# 现代CMake目标链接
target_link_libraries(MyApp PRIVATE
Threads::Threads
OpenSSL::SSL
OpenSSL::Crypto
)
# pkg-config支持
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
target_link_libraries(MyApp PRIVATE ${GTK3_LIBRARIES})
target_include_directories(MyApp PRIVATE ${GTK3_INCLUDE_DIRS})
target_compile_options(MyApp PRIVATE ${GTK3_CFLAGS_OTHER})
# 自定义查找模块
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
find_package(MyCustomLib REQUIRED)
# FetchContent下载依赖
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.12.1
)
FetchContent_MakeAvailable(googletest)
# 使用下载的库
target_link_libraries(MyTests PRIVATE gtest_main)🔧 高级CMake技巧
目标属性和生成器表达式
cmake
# 设置目标属性
set_target_properties(MyLibrary PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
POSITION_INDEPENDENT_CODE ON
OUTPUT_NAME "mylib"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
)
# 生成器表达式
target_compile_definitions(MyApp PRIVATE
$<$<CONFIG:Debug>:DEBUG_BUILD>
$<$<CONFIG:Release>:NDEBUG>
$<$<PLATFORM_ID:Windows>:WINDOWS_BUILD>
$<$<PLATFORM_ID:Linux>:LINUX_BUILD>
)
# 条件编译选项
target_compile_options(MyApp PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/W4>
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra>
$<$<CXX_COMPILER_ID:Clang>:-Wall -Wextra>
)
# 接口属性传播
target_compile_features(MyLibrary PUBLIC cxx_std_17)
target_compile_definitions(MyLibrary PUBLIC
MYLIB_VERSION="${PROJECT_VERSION}"
)
# 安装和导出
install(TARGETS MyLibrary
EXPORT MyLibraryTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
install(DIRECTORY include/ DESTINATION include)
install(EXPORT MyLibraryTargets
FILE MyLibraryTargets.cmake
NAMESPACE MyLibrary::
DESTINATION lib/cmake/MyLibrary
)配置文件生成
cmake
# 生成配置头文件
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/config.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/config.h"
)
# config.h.in内容:
/*
#ifndef CONFIG_H
#define CONFIG_H
#define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define PROJECT_VERSION "@PROJECT_VERSION@"
#cmakedefine HAVE_FEATURE_X
#cmakedefine01 ENABLE_LOGGING
#endif // CONFIG_H
*/
# 包配置文件
include(CMakePackageConfigHelpers)
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/MyLibraryConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/MyLibraryConfig.cmake"
INSTALL_DESTINATION lib/cmake/MyLibrary
)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/MyLibraryConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/MyLibraryConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/MyLibraryConfigVersion.cmake"
DESTINATION lib/cmake/MyLibrary
)🚀 Make系统
Makefile基础
makefile
# 变量定义
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -O2
LDFLAGS = -pthread
TARGET = myapp
SRCDIR = src
OBJDIR = obj
SOURCES = $(wildcard $(SRCDIR)/*.cpp)
OBJECTS = $(SOURCES:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
# 默认目标
all: $(TARGET)
# 链接目标
$(TARGET): $(OBJECTS)
$(CXX) $(OBJECTS) -o $@ $(LDFLAGS)
# 编译规则
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp | $(OBJDIR)
$(CXX) $(CXXFLAGS) -c $< -o $@
# 创建目录
$(OBJDIR):
mkdir -p $(OBJDIR)
# 清理
clean:
rm -rf $(OBJDIR) $(TARGET)
# 安装
install: $(TARGET)
install -d $(DESTDIR)/usr/local/bin
install -m 755 $(TARGET) $(DESTDIR)/usr/local/bin
# 伪目标
.PHONY: all clean install
# 依赖关系
$(OBJDIR)/main.o: $(SRCDIR)/main.cpp $(SRCDIR)/utils.h
$(OBJDIR)/utils.o: $(SRCDIR)/utils.cpp $(SRCDIR)/utils.h高级Makefile技巧
makefile
# 自动依赖生成
DEPDIR = .deps
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(DEPDIR)/%.d | $(DEPDIR) $(OBJDIR)
$(CXX) $(DEPFLAGS) $(CXXFLAGS) -c $< -o $@
$(DEPDIR): ; @mkdir -p $@
DEPFILES := $(SOURCES:$(SRCDIR)/%.cpp=$(DEPDIR)/%.d)
$(DEPFILES):
include $(wildcard $(DEPFILES))
# 多配置支持
BUILD_TYPE ?= Release
ifeq ($(BUILD_TYPE),Debug)
CXXFLAGS += -g -DDEBUG
else ifeq ($(BUILD_TYPE),Release)
CXXFLAGS += -O3 -DNDEBUG
endif
# 平台检测
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
LDFLAGS += -ldl
endif
ifeq ($(UNAME_S),Darwin)
LDFLAGS += -framework CoreFoundation
endif
# 颜色输出
RED = \033[0;31m
GREEN = \033[0;32m
YELLOW = \033[0;33m
NC = \033[0m # No Color
$(TARGET): $(OBJECTS)
@echo "$(GREEN)Linking $(TARGET)...$(NC)"
$(CXX) $(OBJECTS) -o $@ $(LDFLAGS)
@echo "$(GREEN)Build successful!$(NC)"⚡ Ninja构建系统
Ninja配置
ninja
# build.ninja
# 变量定义
cxx = g++
cxxflags = -std=c++17 -Wall -Wextra -O2
ldflags = -pthread
# 规则定义
rule cxx
command = $cxx $cxxflags -MMD -MF $out.d -c $in -o $out
description = Compiling $out
depfile = $out.d
deps = gcc
rule link
command = $cxx $in -o $out $ldflags
description = Linking $out
# 构建目标
build obj/main.o: cxx src/main.cpp
build obj/utils.o: cxx src/utils.cpp
build myapp: link obj/main.o obj/utils.o
# 默认目标
default myapp
# 清理
rule clean
command = rm -rf obj myapp
description = Cleaning
build clean: clean与CMake集成
cmake
# 使用Ninja生成器
# cmake -G Ninja ..
# ninja
# 或者在CMakeLists.txt中
if(CMAKE_GENERATOR STREQUAL "Ninja")
# Ninja特定配置
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
endif()🔧 构建系统集成
持续集成配置
yaml
# .github/workflows/build.yml
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
build_type: [Debug, Release]
compiler: [gcc, clang]
exclude:
- os: windows-latest
compiler: gcc
steps:
- uses: actions/checkout@v3
- name: Install dependencies (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y cmake ninja-build
- name: Install dependencies (macOS)
if: matrix.os == 'macos-latest'
run: |
brew install cmake ninja
- name: Configure CMake
run: |
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DCMAKE_CXX_COMPILER=${{ matrix.compiler == 'clang' && 'clang++' || 'g++' }}
- name: Build
run: cmake --build build --config ${{ matrix.build_type }}
- name: Test
working-directory: build
run: ctest --output-on-failureDocker构建环境
dockerfile
# Dockerfile.build
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
cmake \
ninja-build \
g++ \
clang \
git \
pkg-config \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /workspace
COPY . .
RUN cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
RUN cmake --build build
CMD ["./build/myapp"]📦 包管理集成
vcpkg集成
cmake
# 使用vcpkg
find_package(fmt CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
target_link_libraries(MyApp PRIVATE
fmt::fmt
spdlog::spdlog
nlohmann_json::nlohmann_json
)Conan集成
cmake
# conanfile.txt
[requires]
boost/1.82.0
openssl/1.1.1
gtest/1.14.0
[generators]
CMakeDeps
CMakeToolchain
# CMakeLists.txt
find_package(Boost REQUIRED COMPONENTS system filesystem)
find_package(OpenSSL REQUIRED)
find_package(GTest REQUIRED)
target_link_libraries(MyApp PRIVATE
Boost::system
Boost::filesystem
OpenSSL::SSL
GTest::gtest_main
)🛠️ 构建优化
编译缓存
cmake
# ccache支持
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_PROGRAM}")
endif()
# 预编译头
target_precompile_headers(MyApp PRIVATE
<iostream>
<vector>
<string>
<memory>
)
# 统一构建
set_property(GLOBAL PROPERTY CMAKE_UNITY_BUILD ON)
set_property(TARGET MyApp PROPERTY UNITY_BUILD ON)并行构建
bash
# Make并行构建
make -j$(nproc)
# CMake并行构建
cmake --build build --parallel $(nproc)
# Ninja自动并行
ninja -C build总结
构建系统对比
| 特性 | CMake | Make | Ninja |
|---|---|---|---|
| 学习曲线 | 中等 | 陡峭 | 简单 |
| 跨平台 | 优秀 | 限制 | 良好 |
| 性能 | 良好 | 中等 | 优秀 |
| 生态 | 丰富 | 传统 | 新兴 |
最佳实践
- 现代CMake: 使用目标和属性,避免全局变量
- 版本控制: 设置最低版本要求
- 模块化: 使用子目录和模块组织代码
- 依赖管理: 优先使用包管理器
- CI/CD集成: 自动化构建和测试
选择建议
- 新项目: 推荐CMake + Ninja
- 传统项目: 可保持Make,逐步迁移
- 大型项目: CMake + 包管理器
- 跨平台: 必选CMake
工具链
- 构建生成: CMake, GN, Bazel
- 构建执行: Make, Ninja, MSBuild
- 包管理: vcpkg, Conan, Hunter
- 缓存加速: ccache, sccache
构建系统是C++项目成功的关键基础设施,选择合适的工具并正确配置能显著提升开发效率。