Cmake构建模版

🕒 2024-12-01 📁 调试 👤 laumy 🔥 289 热度

基础示例

# 最低 CMake 版本要求
cmake_minimum_required(VERSION 3.16)

# 定义工程名称和语言
project(mlink_device VERSION 1.0.0 LANGUAGES C)

# ============================================
# 收集源码文件
# ============================================
# 只收集 src/ 目录下的实现文件
file(GLOB_RECURSE MLINK_DEVICE_SOURCES
    ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c
)

# ============================================
# 创建库目标(可选静态 / 可选共享)
# ============================================
option(MLINK_DEVICE_BUILD_SHARED "Build mlink_device as shared library" ON)

if(MLINK_DEVICE_BUILD_SHARED)
    add_library(mlink_device SHARED ${MLINK_DEVICE_SOURCES})
else()
    add_library(mlink_device STATIC ${MLINK_DEVICE_SOURCES})
endif()

# 给库取一个 namespace 别名,让别人可以用 components::mlink_device
add_library(components::mlink_device ALIAS mlink_device)

# ============================================
# 设置 C 标准
# ============================================
target_compile_features(mlink_device PUBLIC c_std_99)

# ============================================
# PRIVATE 宏定义(不会影响其他 target)
# ============================================
target_compile_definitions(mlink_device
    PRIVATE
        _GNU_SOURCE
)

# ============================================
# 头文件路径设置(现代写法)
# ============================================
# PUBLIC:构建时 + 安装后供使用者使用
# PRIVATE:仅本库自己使用,不导出
target_include_directories(mlink_device
    PUBLIC
        # 本地构建时使用的 include 路径
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>

        # 安装后,头文件会被安装到 ${CMAKE_INSTALL_PREFIX}/include
        # 所以使用相对路径 include(自动加上 install prefix)
        $<INSTALL_INTERFACE:include>

    PRIVATE
        # 库内部使用的私有头文件,不暴露给外部项目
        ${CMAKE_CURRENT_SOURCE_DIR}/private
)

# ============================================
# 链接依赖(示例)
# ============================================
# target_link_libraries(mlink_device
#     PUBLIC some_other_lib
#     PRIVATE pthread
# )

# ============================================
# 安装规则(核心)
# ============================================

# 安装库文件
install(
    TARGETS mlink_device
    EXPORT mlink_deviceTargets            # 导出 target 列表
    ARCHIVE DESTINATION lib               # 静态库 .a 安装位置
    LIBRARY DESTINATION lib               # 共享库 .so 安装位置
    RUNTIME DESTINATION bin               # 可执行文件(如果是 EXE)
)

# 安装公共头文件到 include/
install(
    DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/
    DESTINATION include
)

# ============================================
# 导出 CMake package(下游使用 find_package)
# ============================================

# 生成 mlink_deviceTargets.cmake
install(
    EXPORT mlink_deviceTargets
    NAMESPACE components::                # 别名前缀
    DESTINATION lib/cmake/mlink_device    # 包的安装位置
)

# 生成 mlink_deviceConfig.cmake & version 文件
include(CMakePackageConfigHelpers)

# 自动生成版本文件,包含 version 检查逻辑
write_basic_package_version_file(
    ${CMAKE_CURRENT_BINARY_DIR}/mlink_deviceConfigVersion.cmake
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY ExactVersion
)

# 生成 Config 文件(下游项目 find_package 用)
configure_package_config_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/mlink_deviceConfig.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/mlink_deviceConfig.cmake
    INSTALL_DESTINATION lib/cmake/mlink_device
)

# 安装 config 与 version 文件
install(
    FILES
        ${CMAKE_CURRENT_BINARY_DIR}/mlink_deviceConfig.cmake
        ${CMAKE_CURRENT_BINARY_DIR}/mlink_deviceConfigVersion.cmake
    DESTINATION lib/cmake/mlink_device
)

收集源代码

file(GLOB_RECURSE MLINK_DEVICE_SOURCES
    "${CMAKE_CURRENT_SOURCE_DIR}/core/*.c"
)

自动递归扫描 core/ 目录下所有 .c 文件,并把结果放到变量 MLINK_DEVICE_SOURCES 中。

创建库目标

option(MLINK_DEVICE_BUILD_SHARED "Build mlink_device as shared library" ON)

if(MLINK_DEVICE_BUILD_SHARED)
    add_library(mlink_device SHARED ${MLINK_DEVICE_SOURCES})
else()
    add_library(mlink_device STATIC ${MLINK_DEVICE_SOURCES})
endif()

# 给库取一个 namespace 别名,让别人可以用 components::mlink_device
add_library(components::mlink_device ALIAS mlink_device)
  • option:添加一个Bool的开关,决定是编译动态库还是静态库。
  • add_library:创建一个库,语法add_library(name [STATIC | SHARED | MODULE] [sources…])

ALIAS是给库创建一个新的别名,方便其他模块调用。

编译参数

target_compile_features(mlink_device PUBLIC c_std_99)

# ========
# PRIVATE 宏定义(不会影响其他 target)
# ========
target_compile_definitions(mlink_device
    PRIVATE
        _GNU_SOURCE
)
  • target_compile_features:如上示例,gcc -std=c99
  • target_compile_definitions:添加编译宏,-Dxxx,如上-D_GNU_SOURCE,但PRIVATE设置了作用于只对taget自己游侠,下游不继承。

作用域

设置方式 A 本身用 A 的用户(B)用
PRIVATE
PUBLIC ✔(传播)
INTERFACE ✔(只传播)

头文件搜索

target_include_directories(mlink_device
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:include>
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/include/mcp
        ${CMAKE_CURRENT_SOURCE_DIR}/include/transport
        ${CMAKE_CURRENT_SOURCE_DIR}/include/utils
)
  • target_include_directories:给目标mlink_device配置编译时要搜索的头文件目录,类似加上-I….。
  • PRIVATE:在编译mlink_device模块编译自己这个库时寻找的头文件路径,不会传给它的依赖目标。
  • PUBLIC:这是mlink_device对外提供API的头文件目录,其他模块链接mlink_device库时也会自动继承这些include目录。

PRIVATE搜索的头文件路径不会传播,只会影响当前的mlink_device模块这个编译目标。而PUBLIC会传播给他一来的target,然后这个传播又分为两类一个是构建期和安装后。

  • PUBLIC + BUILD_INTERFACE:构建阶段,适用于同一个构建Cmake,提供的头文件路径为源码路径,${CMAKE_CURRENT_SOURCE_DIR}/include就是srobot/components/mlink/include。
  • PUBLIC + INSTALL_INTERFACE:安装阶段,适用于不同的构建Cmake,给其他模块find_package() 后使用的 include 路径。:include就会变成 ${PREFIX}/include,如srobot/outpu/sdk/include。

库搜索

find_package+target_link_libraries组合。

find_package(OpenSSL REQUIRED)

Cmake就会自动搜索

/usr/lib/cmake/openssl/...
/usr/local/lib/cmake/openssl/...
/opt/sdk/lib/cmake/openssl/...

然后只需要:

target_link_libraries(myapp PRIVATE OpenSSL::SSL OpenSSL::Crypto)

库和头文件导出

#导出编译产物如库、可执行文件
install(
    TARGETS mlink_device
    EXPORT ComponentsTargets
    ARCHIVE DESTINATION lib
    LIBRARY DESTINATION lib
    RUNTIME DESTINATION bin
)

#导出普通文件,如头文件
install(
    FILES
        ${CMAKE_CURRENT_SOURCE_DIR}/include/mlink.h
    DESTINATION include
)

  • EXPORT:mlink_device 这个 target 加入一个“targets 导出列表”,后面install(EXPORT…) 把它写入 CMake 模块文件,让外部项目 find_package 时导入 components::mlink_device。
  • ARCHIVE DESTINATION lib:静态库(.a)安装到哪里?——> prefix/lib。编译cmake指定的-DCMAKE_INSTALL_PREFIX
  • LIBRARY DESTINATION lib:共享库(.so / .dylib)安装到哪里?——>prefix/lib。
  • RUNTIME DESTINATION bin:可执行文件安装到哪里?——>prefix/bin。
  • FILES:导出头文件到——>prefix/include

发表你的看法

\t