CMake更进一步

CMake更进一步

以前翻译过 CMake 的官方文档,之后自己整理了一份入门级别的个人心得,随着很长一段时间的积累和沉淀,现在准备写项目管理中经常用到的一些比较好用的 CMake 用法。

跨平台

参考一些开源项目,摘取其中一部分比较简洁的,如下

# 如果没有指定 PROJECT_ARCH,尝试去猜一下
if(NOT DEFINED PROJECT_ARCH)
  if(OS_WINDOWS AND "${CMAKE_GENERATOR_PLATFORM}" STREQUAL "arm64")
    set(PROJECT_ARCH "arm64")
  elseif(CMAKE_SIZEOF_VOID_P MATCHES 8)
    set(PROJECT_ARCH "x86_64")
  else()
    set(PROJECT_ARCH "x86")
  endif()
endif()

# 暂时以 linux 和 windows 平台为例
# 定义宏 OS_LINUX,OS_WINDOWS,以及编译参数
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
  set(OS_LINUX 1)
  set(OS_POSIX 1)
  add_definitions(-DOS_LINUX=1 -DOS_POSIX=1)

  if(PROJECT_ARCH STREQUAL "x86_64")
    add_compile_options(-m64 -march=x86-64)
    add_link_options(-m64)
  elseif(PROJECT_ARCH STREQUAL "x86")
    add_compile_options(-m32)
    add_link_options(-m32)
  endif()
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
  set(OS_WINDOWS 1)
  add_definitions(-DOS_WINDOWS=1)
endif()

版本号的玩法

之前在翻译官方文档的时候,有一节是 添加版本号和配置头文件, 通过 configure_file() 进行一些可配置性的信息设置,在 CMakeLists.txt 中定义或从系统中获取,然后生成头文件加入到项目中

windows

project(test VERSION 1.0.0)

if(MSVC)
    set(MY_VERSIONINFO_RC "${CMAKE_CURRENT_BINARY_DIR}/version.rc")
    configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in"
                   "${MY_VERSIONINFO_RC}")
endif()

这里有个小细节,通过 project() 命令设置版本号的时候会给以下四个变量赋值

  • <PROJECT-NAME>_VERSION_MAJOR : 版本号第一位
  • <PROJECT-NAME>_VERSION_MINOR : 版本号第二位
  • <PROJECT-NAME>_VERSION_PATCH : 版本号第三位
  • <PROJECT-NAME>_VERSION_TWEAK : 版本号第四位

这里的 version.rc.in 内容如下,这是windows下一个 dll 库的版本信息

1 VERSIONINFO
 FILEVERSION ${PROJECT_VERSION_MAJOR}, ${PROJECT_VERSION_MINOR}, ${PROJECT_VERSION_PATCH}
 PRODUCTVERSION ${PROJECT_VERSION_MAJOR}, ${PROJECT_VERSION_MINOR}, ${PROJECT_VERSION_PATCH}
 FILEFLAGSMASK 0x17L
#ifdef _DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS 0x4L
 FILETYPE 0x0L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904b0"
        BEGIN
            VALUE "FileDescription", "${PROJECT_NAME} Binary"
            VALUE "FileVersion", "${PROJECT_VERSION_MAJOR}, ${PROJECT_VERSION_MINOR}, ${PROJECT_VERSION_PATCH}"
            VALUE "InternalName", "${PROJECT_NAME}.dll"
            VALUE "LegalCopyright", "Copyright (C) 2023"
            VALUE "OriginalFilename", "${PROJECT_NAME}.dll"
            VALUE "ProductName", "${PROJECT_NAME}"
            VALUE "ProductVersion", "${PROJECT_VERSION_MAJOR}, ${PROJECT_VERSION_MINOR}, ${PROJECT_VERSION_PATCH}"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1200
    END
END

linux

linux 下一般就是给程序添加一个版本号字符串,编译成库后使用 strings 命令查看,类似下面这样

const char *LIB_INFO = "version: 1.0.0";

或者提供一个类似 getVersion 的返回字符串作为版本的接口,其实大同小异,我们就可以创建一个 VersionDefine.h.in 的文件

#pragma once
#define APP_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}"

cmake 里面只要简单的如下编写

set(COMMON_INFO "${CMAKE_CURRENT_SOURCE_DIR}/VersionDefine.h")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/VersionDefine.h.in"
               "${COMMON_INFO}")

提供的接口也很简单

#include "VersionDefine.h"

std::string getVersion()
{
    return APP_VERSION;
}

版本号的更改就很优雅,只需要更改 CMakeLists.txt 的 project(test VERSION 1.0.0) 一处就可以了

产物输出地址

$<config> 区分 debug, release 版本,动态库静态库可执行程序分开放,方便管理以及之后后处理等

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/output/$<CONFIG>/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/output/$<CONFIG>/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/output/$<CONFIG>/lib)
  • CMAKE_ARCHIVE_OUTPUT_DIRECTORY :默认存放静态库的文件夹位置;
  • CMAKE_LIBRARY_OUTPUT_DIRECTORY :默认存放动态库的文件夹位置;
  • LIBRARY_OUTPUT_PATH :默认存放库文件的位置,如果产生的是静态库并且没有指定 CMAKE_ARCHIVE_OUTPUT_DIRECTORY 则存放在该目录下,动态库也类似;
  • CMAKE_RUNTIME_OUTPUT_DIRECTORY :存放可执行软件的目录;

多模块的管理

比如我现在 Project 里面有 2 个模块 A(动态库) 和 B(可执行程序),项目的层级结构我一般会如下组织

Project
├─ CMakeLists.txt
└─ src
       ├─ A
       │    └─ CMakeLists.txt
       ├─ B
       │    ├─ CMakeLists.txt
       │    └─ main.cpp
       └─ common
              └─ version.h.in

此时我会把 AB 公共的配置提取到最外层的 CMakeLists.txt 里面

# 公共配置
cmake_minimum_required(VERSION 3.4)
...

# 编译参数,option等
...

# 统一的导出位置
...

# 跨平台宏等
...

# target
add_subdirectory(A)

add_subdirectory(B)

需要先编译 A 模块,然后编译 B 模块,我就只需要在 B 模块的 CMakeLists.txt 使用以下命令

add_dependencies(${PROJECT_NAME} A)

这样就能保证多模块的话,在编译 B 的时候,一定会先编译 A

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会偷懒的程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值