CMake 集成¶
CCGO 项目中 CMake 集成的完整参考,包括构建系统架构、自定义和最佳实践。
概览¶
CCGO 使用 CMake 作为所有 C++ 跨平台构建的底层构建系统:
- 模块化 CMake - 源代码、测试、基准测试和依赖的独立模板
- 平台抽象 - 所有平台统一的构建接口
- 工具链支持 - 预配置的交叉编译工具链
- 依赖管理 - 自动集成第三方库
- 构建自定义 - 广泛的配置选项
- IDE 集成 - 生成 Visual Studio、Xcode、CodeLite 项目
CMake 结构¶
CCGO CMake 目录¶
CCGO 将所有 CMake 配置集中在包安装中:
ccgo/build_scripts/cmake/
├── CMakeLists.txt.dependencies.example # 依赖配置
├── CMakeConfig.cmake # 全局配置
├── CMakeExtraFlags.cmake # 编译器标志
├── CMakeFunctions.cmake # 辅助函数
├── CMakeUtils.cmake # 实用函数
├── FindCCGODependencies.cmake # 依赖查找器
├── CCGODependencies.cmake # 依赖解析器
├── ios.toolchain.cmake # iOS 交叉编译
├── tvos.toolchain.cmake # tvOS 交叉编译
├── watchos.toolchain.cmake # watchOS 交叉编译
├── windows-msvc.toolchain.cmake # Windows MSVC 工具链
└── template/ # CMake 模板
├── Root.CMakeLists.txt.in # 根 CMakeLists
├── Src.CMakeLists.txt.in # 源代码 CMakeLists
├── Src.SubDir.CMakeLists.txt.in # 子目录 CMakeLists
├── Tests.CMakeLists.txt.in # 测试 CMakeLists
├── Benches.CMakeLists.txt.in # 基准测试 CMakeLists
├── ThirdParty.CMakeLists.txt.in # 第三方 CMakeLists
├── External.CMakeLists.txt.in # 外部项目 CMakeLists
└── External.Download.txt.in # 下载脚本
项目 CMake 结构¶
生成的项目引用 CCGO 的 CMake 文件:
my-project/
├── CMakeLists.txt # 根 CMake 配置
├── src/
│ └── CMakeLists.txt # 源代码构建配置
├── tests/
│ └── CMakeLists.txt # 测试构建配置
├── benches/
│ └── CMakeLists.txt # 基准测试构建配置
└── cmake_build/ # 构建输出
├── android/
├── ios/
├── macos/
├── windows/
└── linux/
根 CMakeLists.txt¶
基本结构¶
cmake_minimum_required(VERSION 3.20)
# 项目定义
project(MyLib
VERSION 1.0.0
DESCRIPTION "My C++ Library"
LANGUAGES CXX
)
# C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# CCGO CMake 目录(由 ccgo build 设置)
if(NOT DEFINED CCGO_CMAKE_DIR)
message(FATAL_ERROR "CCGO_CMAKE_DIR must be set")
endif()
# 包含 CCGO 实用工具
include(${CCGO_CMAKE_DIR}/CMakeUtils.cmake)
include(${CCGO_CMAKE_DIR}/CMakeFunctions.cmake)
include(${CCGO_CMAKE_DIR}/CMakeConfig.cmake)
# 平台检测
ccgo_detect_platform()
# 构建配置
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
option(BUILD_TESTS "Build tests" OFF)
option(BUILD_BENCHES "Build benchmarks" OFF)
# 子目录
add_subdirectory(src)
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
if(BUILD_BENCHES)
add_subdirectory(benches)
endif()
版本注入¶
# 版本配置
set(PROJECT_VERSION_MAJOR 1)
set(PROJECT_VERSION_MINOR 0)
set(PROJECT_VERSION_PATCH 0)
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
# Git 信息(由 CCGO 注入)
if(DEFINED GIT_SHA)
set(PROJECT_GIT_SHA ${GIT_SHA})
else()
set(PROJECT_GIT_SHA "unknown")
endif()
if(DEFINED GIT_BRANCH)
set(PROJECT_GIT_BRANCH ${GIT_BRANCH})
else()
set(PROJECT_GIT_BRANCH "unknown")
endif()
# 生成版本头文件
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/include/${PROJECT_NAME}/version.h.in"
"${CMAKE_CURRENT_BINARY_DIR}/include/${PROJECT_NAME}/version.h"
@ONLY
)
源代码 CMakeLists.txt¶
库定义¶
# src/CMakeLists.txt
# 源文件
set(SOURCES
mylib.cpp
utils.cpp
network.cpp
)
# 公共头文件
set(PUBLIC_HEADERS
${CMAKE_SOURCE_DIR}/include/mylib/mylib.h
${CMAKE_SOURCE_DIR}/include/mylib/utils.h
${CMAKE_SOURCE_DIR}/include/mylib/network.h
)
# 私有头文件
set(PRIVATE_HEADERS
internal/config.h
internal/helpers.h
)
# 创建库
add_library(${PROJECT_NAME}
${SOURCES}
${PUBLIC_HEADERS}
${PRIVATE_HEADERS}
)
# 包含目录
target_include_directories(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
# 编译器定义
target_compile_definitions(${PROJECT_NAME}
PRIVATE
MYLIB_VERSION="${PROJECT_VERSION}"
$<$<CONFIG:Debug>:MYLIB_DEBUG>
)
# 编译器选项
target_compile_options(${PROJECT_NAME}
PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/W4>
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic>
)
# 链接库
target_link_libraries(${PROJECT_NAME}
PUBLIC
# 消费者可见的公共依赖
PRIVATE
# 消费者不可见的私有依赖
Threads::Threads
)
# 平台特定配置
ccgo_configure_platform_target(${PROJECT_NAME})
# 为共享库导出符号
if(BUILD_SHARED_LIBS)
target_compile_definitions(${PROJECT_NAME}
PRIVATE MYLIB_BUILDING_DLL
INTERFACE MYLIB_USING_DLL
)
endif()
# 安装规则
install(TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}Targets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include
)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/
DESTINATION include
FILES_MATCHING PATTERN "*.h"
)
子目录组织¶
# src/CMakeLists.txt
# 核心库
add_subdirectory(core)
# 平台特定模块
if(CCGO_PLATFORM STREQUAL "android")
add_subdirectory(jni)
elseif(CCGO_PLATFORM MATCHES "ios|macos")
add_subdirectory(objc)
elseif(CCGO_PLATFORM STREQUAL "windows")
add_subdirectory(win32)
endif()
# 可选功能
option(ENABLE_NETWORKING "Enable networking module" ON)
if(ENABLE_NETWORKING)
add_subdirectory(network)
endif()
测试 CMakeLists.txt¶
测试配置¶
# tests/CMakeLists.txt
# 查找测试框架
find_package(GTest REQUIRED)
# 测试源文件
set(TEST_SOURCES
test_main.cpp
test_calculator.cpp
test_network.cpp
)
# 创建测试可执行文件
add_executable(${PROJECT_NAME}_tests ${TEST_SOURCES})
# 链接测试框架和库
target_link_libraries(${PROJECT_NAME}_tests
PRIVATE
${PROJECT_NAME}
GTest::gtest
GTest::gtest_main
)
# 包含目录
target_include_directories(${PROJECT_NAME}_tests
PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}
)
# 发现测试
include(GoogleTest)
gtest_discover_tests(${PROJECT_NAME}_tests)
# 平台特定测试配置
ccgo_configure_platform_tests(${PROJECT_NAME}_tests)
基准测试 CMakeLists.txt¶
基准测试配置¶
# benches/CMakeLists.txt
# 查找基准测试框架
find_package(benchmark REQUIRED)
# 基准测试源文件
set(BENCH_SOURCES
bench_main.cpp
bench_calculator.cpp
bench_network.cpp
)
# 创建基准测试可执行文件
add_executable(${PROJECT_NAME}_benches ${BENCH_SOURCES})
# 链接基准测试框架和库
target_link_libraries(${PROJECT_NAME}_benches
PRIVATE
${PROJECT_NAME}
benchmark::benchmark
benchmark::benchmark_main
)
# 包含目录
target_include_directories(${PROJECT_NAME}_benches
PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}
)
# 平台特定基准测试配置
ccgo_configure_platform_benches(${PROJECT_NAME}_benches)
平台特定配置¶
Android¶
if(ANDROID)
# Android API 级别
set(ANDROID_PLATFORM android-${ANDROID_API_LEVEL})
# 架构特定标志
if(ANDROID_ABI STREQUAL "armeabi-v7a")
target_compile_options(${PROJECT_NAME} PRIVATE
-mfpu=neon
-mfloat-abi=softfp
)
elseif(ANDROID_ABI STREQUAL "arm64-v8a")
target_compile_options(${PROJECT_NAME} PRIVATE
-march=armv8-a
)
endif()
# 链接 Android 库
target_link_libraries(${PROJECT_NAME}
PUBLIC
android
log
)
# 在发布版本中剥离符号
if(CMAKE_BUILD_TYPE STREQUAL "Release")
set_target_properties(${PROJECT_NAME} PROPERTIES
LINK_FLAGS "-Wl,--strip-all"
)
endif()
endif()
iOS/macOS¶
if(APPLE)
# Framework 配置
if(IOS OR TVOS OR WATCHOS)
set_target_properties(${PROJECT_NAME} PROPERTIES
FRAMEWORK TRUE
FRAMEWORK_VERSION A
MACOSX_FRAMEWORK_IDENTIFIER com.example.${PROJECT_NAME}
PUBLIC_HEADER "${PUBLIC_HEADERS}"
)
endif()
# 部署目标
if(IOS)
set_target_properties(${PROJECT_NAME} PROPERTIES
XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET "12.0"
)
elseif(MACOS)
set_target_properties(${PROJECT_NAME} PROPERTIES
XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "10.14"
)
endif()
# 代码签名(仅 iOS)
if(IOS)
set_target_properties(${PROJECT_NAME} PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer"
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "${DEVELOPMENT_TEAM_ID}"
)
endif()
# 链接 Apple 框架
target_link_libraries(${PROJECT_NAME}
PUBLIC
"-framework Foundation"
"-framework CoreFoundation"
)
endif()
Windows¶
if(WIN32)
# MSVC 特定配置
if(MSVC)
# 运行时库
set_property(TARGET ${PROJECT_NAME} PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL"
)
# 警告级别
target_compile_options(${PROJECT_NAME} PRIVATE
/W4
/WX # 将警告视为错误
)
# 为 DLL 导出所有符号
if(BUILD_SHARED_LIBS)
set_target_properties(${PROJECT_NAME} PROPERTIES
WINDOWS_EXPORT_ALL_SYMBOLS ON
)
endif()
endif()
# MinGW 特定配置
if(MINGW)
target_compile_options(${PROJECT_NAME} PRIVATE
-Wall -Wextra -Wpedantic
)
# MinGW 运行时的静态链接
target_link_options(${PROJECT_NAME} PRIVATE
-static-libgcc
-static-libstdc++
)
endif()
# 链接 Windows 库
target_link_libraries(${PROJECT_NAME}
PUBLIC
ws2_32
bcrypt
)
endif()
Linux¶
if(UNIX AND NOT APPLE)
# 位置无关代码
set_target_properties(${PROJECT_NAME} PROPERTIES
POSITION_INDEPENDENT_CODE ON
)
# RPATH 配置
set_target_properties(${PROJECT_NAME} PROPERTIES
BUILD_RPATH_USE_ORIGIN ON
INSTALL_RPATH "$ORIGIN"
)
# 链接 Linux 库
target_link_libraries(${PROJECT_NAME}
PUBLIC
pthread
dl
)
# 在发布版本中剥离符号
if(CMAKE_BUILD_TYPE STREQUAL "Release")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_STRIP} $<TARGET_FILE:${PROJECT_NAME}>
)
endif()
endif()
依赖管理¶
查找包¶
# 查找必需的依赖
find_package(OpenSSL 1.1.1 REQUIRED)
find_package(ZLIB REQUIRED)
find_package(Protobuf REQUIRED)
# 链接依赖
target_link_libraries(${PROJECT_NAME}
PUBLIC
OpenSSL::SSL
OpenSSL::Crypto
PRIVATE
ZLIB::ZLIB
protobuf::libprotobuf
)
FetchContent¶
include(FetchContent)
# 获取 nlohmann/json
FetchContent_Declare(
nlohmann_json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.2
)
FetchContent_MakeAvailable(nlohmann_json)
# 链接获取的依赖
target_link_libraries(${PROJECT_NAME}
PUBLIC
nlohmann_json::nlohmann_json
)
ExternalProject¶
include(ExternalProject)
# 构建外部项目
ExternalProject_Add(
boost
URL https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.tar.gz
PREFIX ${CMAKE_BINARY_DIR}/external/boost
CONFIGURE_COMMAND ./bootstrap.sh
BUILD_COMMAND ./b2
INSTALL_COMMAND ""
BUILD_IN_SOURCE 1
)
# 添加依赖
add_dependencies(${PROJECT_NAME} boost)
# 包含外部项目头文件
target_include_directories(${PROJECT_NAME}
PRIVATE
${CMAKE_BINARY_DIR}/external/boost/src/boost
)
Conan 集成¶
# 包含 Conan CMake 集成
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup(TARGETS)
# 链接 Conan 依赖
target_link_libraries(${PROJECT_NAME}
PUBLIC
CONAN_PKG::openssl
CONAN_PKG::zlib
)
CCGO 辅助函数¶
ccgo_detect_platform()¶
检测目标平台:
ccgo_detect_platform()
# 检测后可用的变量:
# - CCGO_PLATFORM: android, ios, macos, windows, linux, ohos
# - CCGO_PLATFORM_ANDROID
# - CCGO_PLATFORM_IOS
# - CCGO_PLATFORM_MACOS
# - CCGO_PLATFORM_WINDOWS
# - CCGO_PLATFORM_LINUX
# - CCGO_PLATFORM_OHOS
ccgo_configure_platform_target()¶
为检测到的平台配置目标:
ccgo_add_library()¶
使用 CCGO 约定创建库:
ccgo_add_library(${PROJECT_NAME}
SOURCES ${SOURCES}
PUBLIC_HEADERS ${PUBLIC_HEADERS}
PRIVATE_HEADERS ${PRIVATE_HEADERS}
LINK_LIBRARIES ${DEPENDENCIES}
)
ccgo_configure_version()¶
配置版本信息:
ccgo_configure_version(
PROJECT_NAME ${PROJECT_NAME}
VERSION_MAJOR 1
VERSION_MINOR 0
VERSION_PATCH 0
GIT_SHA ${GIT_SHA}
GIT_BRANCH ${GIT_BRANCH}
)
构建自定义¶
编译器标志¶
# 全局编译器标志
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
add_compile_options(
-Wall
-Wextra
-Wpedantic
-Werror
$<$<CONFIG:Debug>:-O0 -g3>
$<$<CONFIG:Release>:-O3 -DNDEBUG>
)
elseif(MSVC)
add_compile_options(
/W4
/WX
$<$<CONFIG:Debug>:/Od /Zi>
$<$<CONFIG:Release>:/O2 /DNDEBUG>
)
endif()
# 目标特定标志
target_compile_options(${PROJECT_NAME} PRIVATE
-fvisibility=hidden
-ffunction-sections
-fdata-sections
)
链接器标志¶
# 删除未使用的段
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
target_link_options(${PROJECT_NAME} PRIVATE
-Wl,--gc-sections
)
endif()
# 链接时优化
if(CMAKE_BUILD_TYPE STREQUAL "Release")
include(CheckIPOSupported)
check_ipo_supported(RESULT ipo_supported)
if(ipo_supported)
set_property(TARGET ${PROJECT_NAME} PROPERTY
INTERPROCEDURAL_OPTIMIZATION TRUE
)
endif()
endif()
构建类型¶
# 自定义构建类型
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
"Build type (Debug, Release, RelWithDebInfo, MinSizeRel)"
)
# 每个配置的设置
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -DDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
IDE 项目生成¶
Xcode¶
# 生成 Xcode 项目
ccgo build ios --ide-project
# 或手动
cmake -G Xcode \
-DCMAKE_TOOLCHAIN_FILE=${CCGO_CMAKE_DIR}/ios.toolchain.cmake \
-DPLATFORM=OS64 \
..
Visual Studio¶
# 生成 Visual Studio 项目
ccgo build windows --ide-project
# 或手动
cmake -G "Visual Studio 17 2022" \
-A x64 \
..
CodeLite¶
最佳实践¶
1. 现代 CMake¶
# 好:使用基于目标的命令
target_include_directories(${PROJECT_NAME} PUBLIC include/)
target_link_libraries(${PROJECT_NAME} PUBLIC OpenSSL::SSL)
# 坏:使用基于目录的命令
include_directories(include/)
link_libraries(ssl)
2. 生成器表达式¶
# 平台特定标志
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<PLATFORM_ID:Windows>:/W4>
$<$<PLATFORM_ID:Linux>:-Wall>
)
# 构建类型特定定义
target_compile_definitions(${PROJECT_NAME} PRIVATE
$<$<CONFIG:Debug>:DEBUG_BUILD>
$<$<CONFIG:Release>:RELEASE_BUILD>
)
3. 接口库¶
# 为仅头文件库创建接口库
add_library(header_only INTERFACE)
target_include_directories(header_only INTERFACE include/)
target_compile_features(header_only INTERFACE cxx_std_17)
# 使用接口库
target_link_libraries(${PROJECT_NAME} PUBLIC header_only)
4. 导出配置¶
# 导出目标
install(EXPORT ${PROJECT_NAME}Targets
FILE ${PROJECT_NAME}Targets.cmake
NAMESPACE ${PROJECT_NAME}::
DESTINATION lib/cmake/${PROJECT_NAME}
)
# 生成配置文件
include(CMakePackageConfigHelpers)
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
INSTALL_DESTINATION lib/cmake/${PROJECT_NAME}
)
# 安装配置文件
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
DESTINATION lib/cmake/${PROJECT_NAME}
)
故障排除¶
CMake 缓存问题¶
找不到工具链¶
# 验证 CCGO_CMAKE_DIR 已设置
echo $CCGO_CMAKE_DIR
# 手动设置工具链
cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/toolchain.cmake ..