【问题标题】:How do I add a linker or compile flag in a CMake file?如何在 CMake 文件中添加链接器或编译标志?
【发布时间】:2020-03-12 15:02:18
【问题描述】:

我正在使用arm-linux-androideabi-g++ 编译器。当我尝试编译一个简单的“Hello, World!”程序编译得很好。当我通过在该代码中添加一个简单的异常处理来测试它时,它也可以工作(添加-fexceptions .. 我猜它默认是禁用的)。

这是针对 Android 设备的,我只想使用 CMake,而不是 ndk-build

例如 - first.cpp

#include <iostream>

using namespace std;

int main()
{
   try
   {
   }
   catch (...)
   {
   }
   return 0;
}

./arm-linux-androideadi-g++ -o first-test first.cpp -fexceptions

没有问题...

问题 ...我正在尝试使用 CMake 文件编译该文件。

我想将-fexceptions 添加为标志。我试过了

set (CMAKE_EXE_LINKER_FLAGS -fexceptions ) or set (CMAKE_EXE_LINKER_FLAGS "fexceptions" )

set ( CMAKE_C_FLAGS "fexceptions")

它仍然显示错误。

【问题讨论】:

标签: c++ cmake


【解决方案1】:

注意:鉴于编写此答案后 CMake 的演变,这里的大多数建议现在都已过时/不推荐使用,并且有更好的替代方案


假设您要添加这些标志(最好在常量中声明它们):

SET(GCC_COVERAGE_COMPILE_FLAGS "-fprofile-arcs -ftest-coverage")
SET(GCC_COVERAGE_LINK_FLAGS    "-lgcov")

有几种添加方式:

  1. 最简单的一个(不干净,但简单方便,只适用于编译标志,C & C++ 一次):

     add_definitions(${GCC_COVERAGE_COMPILE_FLAGS})
    
  2. 附加到相应的 CMake 变量:

     SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
     SET(CMAKE_EXE_LINKER_FLAGS  "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
    
  3. 使用目标属性,参见。 doc CMake compile flag target property 并且需要知道目标名称。

     get_target_property(TEMP ${THE_TARGET} COMPILE_FLAGS)
     if(TEMP STREQUAL "TEMP-NOTFOUND")
       SET(TEMP "") # Set to empty string
     else()
       SET(TEMP "${TEMP} ") # A space to cleanly separate from existing content
     endif()
     # Append our values
     SET(TEMP "${TEMP}${GCC_COVERAGE_COMPILE_FLAGS}" )
     set_target_properties(${THE_TARGET} PROPERTIES COMPILE_FLAGS ${TEMP} )
    

现在我使用方法2。

【讨论】:

  • 为什么 add_definitions() 不干净?
  • @leinaD_natipaC:official documentation 说:此命令可用于添加任何标志,但它旨在添加预处理器定义。我想这就是原因。
  • 虽然这是公认的答案,但它确实显示了非常老式的 CMAKE,请参阅@vitaut 的答案,了解如何根据编译时参数构建任何新的 CMAKE 代码
  • string(APPEND CMAKE_EXE_LINKER_FLAGS "new_value")set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} new_value") 更短更干净
  • 过时的建议
【解决方案2】:

在较新版本的 CMake 中,您可以分别使用 target_compile_optionstarget_link_libraries 为单个目标设置编译器和链接器标志(是的,后者也设置链接器选项):

target_compile_options(first-test PRIVATE -fexceptions)

这种方法的优点是您可以通过PUBLICPRIVATE 控制将选项传播到依赖此选项的其他目标。

从 CMake 3.13 开始,您还可以使用 target_link_options 添加链接器选项,使意图更加清晰。

【讨论】:

  • 我试过用它:target_compile_options(main_static PRIVATE --static) 但它似乎不起作用,知道为什么吗?
  • -static 可能是链接器,而不是编译器选项。尝试将其传递给target_link_libraries
  • 哦,target_link_libraries 我错过了文档的那一部分:“指定链接给定目标时要使用的库或标志。”。谢谢。
  • 最近添加的内容:CMake 3.13 引入了target_link_options 作为一种更简洁的方式来指定链接器标志。您以后应该避免将target_link_libraries 用作链接器标志,而改用target_link_options
【解决方案3】:

尝试设置变量CMAKE_CXX_FLAGS 而不是CMAKE_C_FLAGS

set (CMAKE_CXX_FLAGS "-fexceptions")

变量CMAKE_C_FLAGS 只影响C 编译器,但您正在编译C++ 代码。

将标志添加到CMAKE_EXE_LINKER_FLAGS 是多余的。

【讨论】:

  • 我试过了,但仍然报错。设置(CMAKE_CXX_FLAGS "-fexceptions") 是指定编译器标志的唯一方法。
  • 我解决了这个问题,但不是很好,它是一个糟糕的解决方法.. 我在命令行中制作了 -DCMAKE_CXX_FLAGS="-fexceptions"。由于某种原因,cmake 没有从 .cmake 文件中读取标志。 :( .. 谢谢 sakra 的回答 ..
  • -DCMAKE_CXX_FLAGS="-fexceptions" = 和"之间不能有空格
  • 关于你所说的:“由于某种原因,cmake 没有从 .cmake 文件中读取标志。”确保清除现有缓存。这可以通过在再次 cmake 之前从构建目录中删除所有内容来完成。
【解决方案4】:

指定工具链特定选项的首选方法是使用 CMake 的 toolchain facility。这确保了以下之间的清晰划分:

  • 关于如何将源文件组织成目标的说明——以 CMakeLists.txt 文件表示,完全与工具链无关;和
  • 应如何配置某些工具链的详细信息 - 分为 CMake 脚本文件,可由项目的未来用户扩展,可扩展。

理想情况下,您的 CMakeLists.txt 文件中不应有编译器/链接器标志——即使在 if/endif 块中也是如此。并且您的程序应该使用默认工具链(例如 GNU/Linux 上的 GCC 或 Windows 上的 MSVC)为本地平台构建,而不需要任何额外的标志。

添加工具链的步骤:

  1. 创建一个文件,例如arm-linux-androideadi-gcc.cmake 带有全局工具链设置:

    set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
    set(CMAKE_CXX_FLAGS_INIT "-fexceptions")
    

    (您可以找到一个示例 Linux 交叉编译工具链文件here。)

  2. 当您想使用此工具链生成构建系统时,请在命令行中指定CMAKE_TOOLCHAIN_FILE 参数:

    mkdir android-arm-build && cd android-arm-build
    cmake -DCMAKE_TOOLCHAIN_FILE=$(pwd)/../arm-linux-androideadi-gcc.cmake ..
    

    (注意:不能使用相对路径。)

  3. 正常构建:

    cmake --build .
    

工具链文件使交叉编译更容易,但它们还有其他用途:

  • 为您的单元测试提供强化诊断。

    set(CMAKE_CXX_FLAGS_INIT "-Werror -Wall -Wextra -Wpedantic")
    
  • 难以配置的开发工具。

    # toolchain file for use with gcov
    set(CMAKE_CXX_FLAGS_INIT "--coverage -fno-exceptions -g")
    
  • 加强安全检查。

    # toolchain file for use with gdb
    set(CMAKE_CXX_FLAGS_DEBUG_INIT "-fsanitize=address,undefined -fsanitize-undefined-trap-on-error")
    set(CMAKE_EXE_LINKER_FLAGS_INIT "-fsanitize=address,undefined -static-libasan")
    

【讨论】:

  • 不要在工具链中设置CMAKE_&lt;LANG&gt;_FLAGS。而且没有 CMAKE_CXX_DEBUG_FLAGS 这样的东西。这是CMAKE_CXX_FLAGS_DEBUG。你也不应该使用 that 。仅在工具链中设置CMAKE_&lt;LANG&gt;_FLAGS[_&lt;CONFIG&gt;]_INIT
  • 一般来说,在 CMake 中,变量CMAKE_&lt;LANG&gt;_FLAGS[_&lt;CONFIG&gt;] 是由构建您的应用程序/库的客户端设置的。它们不应该以编程方式设置。附加到,很少。
  • @AlexReinking 更新,加上CMAKE_EXE_LINKER_FLAGS_INIT
  • 这是答案,经过大量搜索,谢谢@JohnMcFarlane!
【解决方案5】:

您还可以使用LINK_FLAGS 属性将链接器标志添加到特定目标:

set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS " ${flag}")

如果您想将此更改传播到其他目标,您可以创建一个虚拟目标以链接到。

【讨论】:

    【解决方案6】:

    当我需要一个名为“NO_DEBUG”的预编译定义时,这对我有用:

    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -DNO_DEBUG")
    

    然后从代码

    #ifdef NO_DEBUG
    .....
    

    【讨论】:

    • 这就是 add_definitions 的用途。
    猜你喜欢
    • 2019-02-02
    • 2010-12-13
    • 1970-01-01
    相关资源
    最近更新 更多