cmake:设置编译选项

1059 篇文章 274 订阅

此文为:轻松入门cmake系列教程

常用

1、 cmake debug和release设置

# default is "Debug"
#set(CMAKE_BUILD_TYPE "Release")


if (!CMAKE_BUILD_TYPE STREQUAL "RELEASE")
		add_definitions("-g")
endif()

2、启用Makefile版本中的详细输出。

# set this to see the compilation commands
# set(CMAKE_VERBOSE_MAKEFILE 1)

3、根据cmake的debug和release设置编译选项

IF("${CMAKE_BUILD_TYPE}" MATCHES "Debug")
    message(STATUS "building for: debugging")

    ## unfortunately these produce errors
    #include(CheckCXXCompilerFlag)
    #CHECK_CXX_COMPILER_FLAG("-Wformat-signedness" CXX_FORMAT_SIGNEDNESS)
    #CHECK_CXX_COMPILER_FLAG("-Werror=format-security" CXX_FORMAT_SECURITY)
    #CHECK_CXX_COMPILER_FLAG("-fstack-protector-all" CXX_STACK_PROTECTOR)
    set(CXX_FORMAT_SIGNEDNESS "-Wformat-signedness")
    set(CXX_FORMAT_SECURITY "-Werror=format-security")
    set(CXX_STACK_PROTECTOR "-fstack-protector-all")
    set(CXX_FLAGS_DEBUG "-O0")
    set(CMAKE_C_STANDARD 99)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O1 -ggdb -Wall -Wextra -DNETDATA_INTERNAL_CHECKS=1 -DNETDATA_VERIFY_LOCKS=1 ${CXX_FORMAT_SIGNEDNESS} ${CXX_FORMAT_SECURITY} ${CXX_STACK_PROTECTOR} ${CXX_FLAGS_DEBUG}")
ELSE()
    message(STATUS "building for: release")
    cmake_policy(SET CMP0069 "NEW")
    include(CheckIPOSupported)
    check_ipo_supported(RESULT ipo_supported OUTPUT error)
    IF(${ipo_supported})
        message(STATUS "link time optimization: supported")
        set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
    ELSE()
        message(STATUS "link time optimization: not supported")
    ENDIF()
ENDIF()

设置CMAKE_CXX_FLAGS

# Use C++11.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")  #禁用运行时类型信息
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")  # 禁用异常机制

理论

GCC编译选项

  • -E:只进行预处理,不编译
  • -S:只编译,不汇编
  • -c:只编译、汇编,不链接
  • -g:包含调试信息
  • -I:指定include包含文件的搜索目录
  • -o:输出成指定文件名

常用选项

优化选项

  • -O0:关闭所有优化选项
  • -O1:第一级别优化,使用此选项可使可执行文件更小、运行更快,并不会增加太多编译时间,可以简写为-O
  • -O2:第二级别优化,采用了几乎所有的优化技术,使用此选项会延长编译时间
  • -O3:第三级别优化,在-O2的基础上增加了产生inline函数、使用寄存器等优化技术
  • -Os:此选项类似于-O2,作用是优化所占用的空间,但不会进行性能优化,常用于生成最终版本

高级选项

  • -ggdb:在可执行文件中包含可供 GDB使用的调试信息
  • -v:详细输出编译过程中所采用的每一个选项
  • -C:预处理时保留注释信息
  • -fverbose-asm:在编译成汇编语言时,把C变量的名称作为汇编语言中的注释
  • -save-temps::自动输出预处理文件、汇编文件、对象文件,编译正常进行
  • -fsyntax-only::只测试源文件语法是否正确,不会进行任何编译操作
  • -ffreestanding:编译成独立程序,而非宿主程序

出错提示

当GCC在编译过程中检查出错误的话,它就会中止编译;但检测到警告时却能继续编译生成可执行程序,因为警告只是针对程序结构的诊断信息,它不能说明程序一定有错误,而是存在风险,或者可能存在错误。虽然GCC提供了非常丰富的警告,但前提是你已经启用了它们,否则它不会报告这些检测到的警告。

  • -Wall:开启大部分警告提示
  • -Wextra:对所有合法但值得怀疑的表达式发出警告
  • -w:忽略所有警告
  • -Werror:不区分警告和错误,遇到任何警告都停止编译
  • -Wshadow:一旦某个局部变量屏蔽了另一个局部变量,编译器就发出警告.(此警告未包含在-Wall选项中,需单独开启)
  • -Wlong-long
    • 如果使用了long long 类型就发出警告.
    • 该警告是缺省项.
    • 使用-Wno-long-long选项能够防止这个警告.
    • -Wlong-long-Wno-long-long仅在 -pedantic之下才起作用.
  • -Waggregate-return
    • 如果定义或调用了返回结构或联合的函数,编译器就发出警告
    • (从语言角度你可以返回一个数组,然而同样会 导致警告.)
  • -Wstrict-prototypes
    • 如果函数的声明或定义没有指出参数类型,编译器就发出警告.
    • (如果函数的前向引用说明指出了参数类型,则允许后面 使用旧式风格的函数定义,而不会产生警告.)
  • -Wmissing-prototypes
    • 如果没有预先声明函数原形就定义了全局函数,编译器就发出警告
    • 即使函数定义自身提供了函数原形也会产生这个警告.
    • 他的目的是检查没有在头文件中声明的全局函数.
使用示例:-Wall选项

在众多的警告选项之中,最常用的就是-Wall选项。该选项能发现程序中一系列的常见错误警告,该选项用法举例如下:

  • 命令行:
$ gcc -Wall test.c -o test
  • cmakelist.txt:
add_definitions(
		"-W"
		"-Wall"
		"-Werror"
		"-Wshadow"
)

该选项相当于同时使用了下列所有的选项:

  • unused-function:遇到仅声明过但尚未定义的静态函数时发出警告。
  • unused-label:遇到声明过但不使用的标号的警告。
  • unused-parameter:从未用过的函数参数的警告。
  • unused-variable:在本地声明但从未用过的变量的警告。
  • unused-value:仅计算但从未用过的值得警告。
  • Format:检查对printf和scanf等函数的调用,确认各个参数类型和格式串中的一致。
  • implicit-int:警告没有规定类型的声明。
  • implicit-function-:在函数在未经声明就使用时给予警告。
  • char-subscripts:警告把char类型作为数组下标。这是常见错误,程序员经常忘记在某些机器上char有符号。
  • missing-braces:聚合初始化两边缺少大括号。
  • Parentheses:在某些情况下如果忽略了括号,编译器就发出警告。
  • return-type:如果函数定义了返回类型,而默认类型是int型,编译器就发出警告。同时警告那些不带返回值的 return语句,如果他们所属的函数并非void类型。
  • sequence-point:出现可疑的代码元素时,发出报警。
  • Switch:如果某条switch语句的参数属于枚举类型,但是没有对应的case语句使用枚举元素,编译器就发出警告(在switch语句中使用default分支能够防止这个警告)。超出枚举范围的case语句同样会导致这个警告。
  • strict-aliasing:对变量别名进行最严格的检查。
  • unknown-pragmas:使用了不允许的#pragma。
  • Uninitialized:在初始化之前就使用自动变量。

需要注意的是,各警告选项既然能使之生效,当然也能使之关闭。比如假设我们想要使用-Wall来启用个选项,同时又要关闭unused警告,利益通过下面的命令来达到目的:

 gcc -Wall -Wno-unused test.c -o test
使用示例:-Wpointer-arith
  • 任何语句如果依赖于函数类型的大小(size)或者void类型的大小,编译器就发出警告.
  • GNU C为了 便于计算void *指针和函数指针,就把这些类型的大小定义为1.
  • 在C++中,当算术运算涉及"NULL"时也要发出警告.
#include <stdio.h>
int main() {
    void *test = NULL;
    int value_int[] = {12, 34, 56, 67, 27};
    char value_char[] = {'H', 'e', 'I', 'I', 'O'};

    test = (void *) value_char;
    printf("%c\n", *((char*)(test + 1)));
    return 0;
}

在这里插入图片描述

-Waggregate-return
  • -Waggregate-return: 当返回结构、联合或数组时给出警告

引发警告的小例子:

class foo{};
foo f(void){return foo{};}
int main(){}
$ g ++ -std = c ++ 0x -Waggregate-return -o main main.cpp
main.cpp:在函数'foo f()':
main.cpp:2:5:warning:函数返回一个聚合[-Waggregate-返回]

另一个不引发警告的小例子:

#include <string>
std::string f(void){return "test";}
int main(){}

使用-Waggregate-return可以获得什么好处?
为什么有人想要对此发出警告?
另外,std :: string不是一个类吗? - 为什么我不警告第二个例子中的’返回聚合’?

回答:
主要有两种可能的解释:

  • 1)警告用户返回聚合对象使他意识到如果返回聚合对象(在堆栈上分配),堆栈可能会溢出.
  • 2)显然,一些旧的C编译器不支持返回aggrregates(你必须返回一个指针).

讨论请参见这里

-Wmissing-prototypes

这个选项仅适用于C编译,如果是C++,会警告:

在这里插入图片描述

讨论请参见这里

语言标准

  • -ansi:ANSI标准
  • -std=c99:C99标准
  • -std=gnu89:ISO/IEC 9899:1990 以及GNU扩充
  • -std=gnu99:ISO/IEC 9899:1999 以及GNU扩充
  • -trigraphs:支持ISO C三字符组

其他

-D_REENTRANT(很重要)

在一个多线程程序中,默认情况下,只有一个errno变量供所有的线程共享。在一个线程准备获取刚才的错误代码时,该变量很容易被另一个线程中的函数调用所改变。类似的问题还存在于fputs之类的函数中,这些函数通常用一个单独的全局性区域来缓存输出数据

为解决这个问题,需要使用可重入的例程。可重入代码可以被多次调用而仍然工作正常。编写的多线程程序,通过定义宏_REENTRANT来告诉编译器我们需要可重入功能,这个宏的定义必须出现于程序中的任何#include语句之前。

_REENTRANT为我们做三件事情,并且做的非常优雅:

  • 它会对部分函数重新定义它们的可安全重入的版本,这些函数名字一般不会发生改变,只是会在函数名后面添加_r字符串,如函数名gethostbyname变成gethostbyname_r。
  • stdio.h中原来以宏的形式实现的一些函数将变成可安全重入的函数
  • 在error.h中定义的变量error现在将成为一个函数调用,它能够以一种安全的多线程方式来获取真正的errno的值

设置编译器标志

为每个目标设置编译标志

CMake支持以多种不同方式设置编译标志:

  • 使用target_compile_definitions()函数
  • 使用CMAKE_C_FLAGS和CMAKE_CXX_FLAGS变量。
target_compile_options
target_compile_options(<target> [BEFORE]
  <INTERFACE|PUBLIC|PRIVATE> [items1...]
  [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

编译选项可以添加三个级别的可见性: INTERFACE 、 PUBLIC 和 PRIVATE 。可见性的含义如下:

  • PRIVATE,编译选项会应用于给定的目标,不会传递给与目标相关的目标。比如,即使exe将链接到A库,exe也不会继承A目标上设置的编译器选项。
  • INTERFACE,给定的编译选项将只应用于指定目标,并传递给与目标相关的目标。
  • PUBLIC,编译选项将应用于指定目标和使用它的目标。

可以为库、可执行文件设置编译选项。

target_compile_options(lib PRIVATE -O3)
#-O3:编译器优化级别编译库 。
check_cxx_compiler_flag
check_cxx_compiler_flag(<flag> <var>)

检查C++编译器是否支持某个/某些flag标志,检测结果存放在var变量中。

统一设置所有目标的编译标志

使用 -D CLI标志直接修改CMAKE_<LANG>_FLAGS_<CONFIG>变量,比如:

SET(CMAKE_CXX_FLAGS_RELEASE "-g -DNDEBUG -O3")

实验

实验一

编写代码

工程结构

在这里插入图片描述

  • CMakeLists.txt内容:
cmake_minimum_required(VERSION 3.5)

# Set a default C++ compile flag
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DEX2" CACHE STRING "Set C++ Compiler Flags" FORCE)

# Set the project name
project (compile_flags)

# Add an executable
add_executable(${PROJECT_NAME} main.cpp)

target_compile_definitions(${PROJECT_NAME}
        PRIVATE EX3
        )

  • main.cpp
#include <iostream>

int main(int argc, char *argv[])
{
   std::cout << "Hello Compile Flags!" << std::endl;

   // only print if compile flag set
#ifdef EX2
  std::cout << "Hello Compile Flag EX2!" << std::endl;
#endif

#ifdef EX3
  std::cout << "Hello Compile Flag EX3!" << std::endl;
#endif

   return 0;
}

理论

(1)设置每个目标的C++标志

  • 在现代CMake中设置C++标志的推荐方式是使用每个目标的标志,这些标志可以通过target_compile_definitions()函数的作用域(或者说接口范围)递到其他目标(INTERFACE或PUBLIC)。
  • 这将填充库的INTERFACE_COMPILE_DEFINITIONS,并根据作用域将定义传递到链接的目标。
target_compile_definitions(${PROJECT_NAME}
        PRIVATE EX3
        )

  • 这将导致编译器在编译目标时添加定义 -DEX3。
  • 如果目标是库,并且已经选择了作用域PUBLIC或者INTERFACE,则该定义也将包含在链接该目标的任何可执行文件中。
  • 对于编译器选项,你还可以使用target_compile_options()函数。

(2)设置默认C++标志

CMAKE_CXX_FLAGS的默认值为空或包含生成类型的相应标志。

  • 可以通过下面获取当前编译器标志
message("C++ compiler flags:${CMAKE_CXX_FLAGS}")
  • 要设置其他默认编译标志,可以将以下内容添加到顶级CMakeLists.txt。(不推荐的写法)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DEX2" CACHE STRING "Set C++ Compiler Flags" FORCE)
    • 上述命令中的值 CACHE STRING “Set C++ Compiler Flags” FORCE 用于强制在CMakeCache.txt文件中设置此变量

与CMAKE_CXX_FLAGS类似的其他选项包括:

  • 使用CMAKE_C_FLAGS设置 C 编译器标志
  • 使用CMAKE_LINKER_FLAGS设置链接器标志

一旦设置,CMAKE_C_FLAGS和CMAKE_CXX_FLAGS将为该目录或任何包含的子目录中的所有目标全局设置编译器标志/定义。现在不建议将此方法用于一般用途,最好使用target_compile_definitions函数

(3)设置CMake标志

与构建类型类似,可以使用以下方法设置全局C++编译器标志。

  • 使用GUI工具,如ccmake/cmake-gui
  • 传递到cmake
cmake .. -DCMAKE_CXX_FLAGS="-DEX3"

在这里插入图片描述

参考

  • 25
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值