Skip to content

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-failure

Docker构建环境

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

总结

构建系统对比

特性CMakeMakeNinja
学习曲线中等陡峭简单
跨平台优秀限制良好
性能良好中等优秀
生态丰富传统新兴

最佳实践

  • 现代CMake: 使用目标和属性,避免全局变量
  • 版本控制: 设置最低版本要求
  • 模块化: 使用子目录和模块组织代码
  • 依赖管理: 优先使用包管理器
  • CI/CD集成: 自动化构建和测试

选择建议

  • 新项目: 推荐CMake + Ninja
  • 传统项目: 可保持Make,逐步迁移
  • 大型项目: CMake + 包管理器
  • 跨平台: 必选CMake

工具链

  • 构建生成: CMake, GN, Bazel
  • 构建执行: Make, Ninja, MSBuild
  • 包管理: vcpkg, Conan, Hunter
  • 缓存加速: ccache, sccache

构建系统是C++项目成功的关键基础设施,选择合适的工具并正确配置能显著提升开发效率。

本站内容仅供学习和研究使用。