规范设置编译参数
CMake 中有许多关于编译器和链接器的设置。当你需要添加一些特殊的需求,你应该首先检查 CMake 是否支持这个需求,如果支持的话,你就可以不用关心编译器的版本,一切交给 CMake 来做即可。 更棒的是,你可以在 CMakeLists.txt
表明你的意图,而不是通过开启一系列标志 (flag) 。
优化参数(-O0/-O1/-O2/-O3/-Ofast/-Os) 和 debug 参数 (-g)
CMAKE_BUILD_TYPE 包括 Debug
, Release
, RelWithDebInfo
和 MinSizeRe
4 个选项
它们对应参数是(不同编译平台可能有少许区别):
- Release:
-O3 -DNDEBUG
- Debug:
-O0 -g
- RelWithDebInfo:
-O2 -g -DNDEBUG
- MinSizeRel:
-Os -DNDEBUG
这些参数可以通过 CMAKE_CXX_FLAGS_DEBUG,CMAKE_CXX_FLAGS_RELEASE… 查看,并不直接体现在 CMAKE_CXX_FLAGS 中。
CMAKE_BUILD_TYPE 的默认值不是Release,也不属于上面4种,而是空值,即不附加任何参数。
全局(或非debug模式)设置 -O3:
建议改为将 CMAKE_BUILD_TYPE 设置默认 Release
Release 模式加 O3, Debug 模式加 -g 等等
不用加,这些都是无用功
Release 模式加 -g
如果软件发行版本需要 debug 符号,请用 RelWithDebInfo 模式
Debug 加 -O3
高级别的优化会严重影响 debug 调试, 可以改用 -Og,它允许不影响调试的优化
同时有 -O1 -O3
-O0/-O1/-O2/-O3/-Ofast 开启的优化参数前依次递增,是完全的子集关系,不是多多益善的。 -Os 是在 -O2 的基础上,尽可能优化程序大小。
Release 模式使用 -Ofast 或其他优化级别。
最好是 string(REPLACE "-O3" "-Ofast" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
,这样就没有原来的-O3
了
如果是是降低优化基本,则必须这样替换。
参考资料:
de
参考修改:
c++标准版本 (比如 -std=c++11)
直接使用 set(CMAKE_CXX_STANDARD 11)
部分项目设置了 CMAKE_CXX_STANDARD 为 14/17, 又加了参数 -std=c++11,这是冲突的,请检查。 比如: https://github.com/linuxdeepin/deepin-movie-reborn/blob/0a08e29e78c4f35f787b999d857f696f804f8641/CMakeLists.txt#L18
Hardening
DEB_BUILD_HARDENING_FORMAT (gcc/g++ -Wformat -Wformat-security -Werror=format-security) DEB_BUILD_HARDENING_FORTIFY (gcc/g++ -D_FORTIFY_SOURCE=2) DEB_BUILD_HARDENING_STACKPROTECTOR (gcc/g++ -fstack-protector-strong) DEB_BUILD_HARDENING_PIE (gcc/g++ -fPIE -pie) DEB_BUILD_HARDENING_RELRO (ld -z relro) DEB_BUILD_HARDENING_BINDNOW (ld -z now)
- https://wiki.debian.org/Hardening#Using_Hardening_Options
- https://nixos.org/manual/nixpkgs/stable/#sec-hardening-in-nixpkgs
- https://wiki.archlinux.org/title/Security
- https://wiki.ubuntu.com/Security/Features
- https://wiki.gentoo.org/wiki/Project:Hardened
as-need 参数
已知 as-need 参数 break 了 mold 连接器,可用 as-needed 代替。 相关:https://github.com/linuxdeepin/developer-center/issues/3345
dl 库
如果你需要链接到 dl
库,在 Linux 上可以使用 -ldl
标志,不过在 CMake 中只需要在 target_link_libraries
命令中使用内置的 CMake 变量 ${CMAKE_DL_LIBS}
。这里不需要模组或者使用 find_package
来寻找它。(这个命令包含了调用 dlopen
与 dlclose
的一切依赖)
程序间优化(Interprocedural optimization)
INTERPROCEDURAL_OPTIMIZATION
,最有名的是 链接时间优化 以及 -flto
标志,这在最新的 CMake 版本中可用。你可以通过变量 CMAKE_INTERPROCEDURAL_OPTIMIZATION
( CMake 3.9+ 可用)或对目标指定 INTERPROCEDURAL_OPTIMIZATION
属性来打开它。在 CMake 3.8 中添加了对 GCC 及 Clang 的支持。如果你设置了 cmake_minimum_required(VERSION 3.9)
或者更高的版本(参考 CMP0069),当在编译器不支持 INTERPROCEDURAL_OPTIMIZATION
时,通过变量或属性启用该优化会产生报错。你可以使用内置模块 CheckIPOSupported 中的 check_ipo_supported()
来检查编译器是否支持 IPO 。下面是基于 CMake 3.9 的一个例子:
include(CheckIPOSupported)
check_ipo_supported(RESULT result)
if(result)
set_target_properties(foo PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
位置无关的代码
在 g++ 中,对编译生成动态库是 -fPIC/-fpic,对生成可执行文件是 -fPIE/-fpie。
如果要把静态库链接到动态库,这一选项是必须的。其他情况也建议开启,可以防范一些内存攻击。
在 cmake 中,对于动态库(SHARED and MODULE library),这一选项是默认开启的。对于静态库,在 cmake 3.14 版本之后(CMP0083 新标准),默认值是 POSITION_INDEPENDENT_CODE,而该值默认为假。
可以通过 set(POSITION_INDEPENDENT_CODE True)
让静态库默认增加 “-fpic” 编译。
最好通过 set_property 设置此选项:set_property(TARGET foo PROPERTY POSITION_INDEPENDENT_CODE TRUE)
,foo 为对应静态库或者可执行程序。
此外,推荐使用 check_pie_supported(),检查一下编译器是否支持。
参考资料:
- https://cmake.org/cmake/help/latest/prop_tgt/POSITION_INDEPENDENT_CODE.html
- https://cmake.org/cmake/help/latest/policy/CMP0083.html
-lpthread
POSIX thread 是基于 C/C++ 的标准线程库。在 cmake 3.1 以上的版本提供了 FindThreads 模块, 如果设置 THREADS_PREFER_PTHREAD_FLAG 变量,会优先使用 pthread。
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(my_app PRIVATE Threads::Threads)
- https://cmake.org/cmake/help/latest/module/FindThreads.html
- https://stackoverflow.com/questions/1620918/cmake-and-libpthread/29871891
参考文档:
https://modern-cmake-cn.github.io/Modern-CMake-zh_CN/chapters/features/small.html
设置时不要覆盖之前的选项
错误示例:set(CMAKE_CXX_FLAGS “-Wall”) 正确示例:set(CMAKE_CXX_FLAGS “${CMAKE_CXX_FLAGS} -Wall”)
如果确实有覆盖的要求,请在源码注释说明,因为一般出现覆盖 flag 很可能是失误。
判断架构
遗留代码有使用 dpkg-architecture 判断的,改用 CMAKE_SYSTEM_PROCESSOR。
dpkg-architecture 是 debian 系特有的,并不通用。 除了 deb 安装器等不考虑像其他发行版移植的应用,不要在 CMakeList 里使用 dpkg 系列命令。
CMAKE_L_FLAGS
部分项目 使用了 CMAKE_L_FLAGS,这不是 cmake 标准的变量,这是想用 CMAKE_EXE_LINKER_FLAGS ?
glib-compile-schemas
gsettings schemas 安装后需要编译一下(一般目录是 /usr/share/glib-2.0/schemas)才能使用,这个应该是安装后进行的,用对应目录所有 gschema.xml 再编译出一个 gschemas.compiled 文件。
像下面这样在 CMakeList.txt 编译是不需要的:
install(CODE "execute_process(COMMAND glib-compile-schemas ${CMAKE_INSTALL_PREFIX}/share/glib-2.0/schemas)")
相关修改