获取完整的C ++编译器命令行

时间:2016-03-04 23:55:55

标签: cmake

在CMake中,C ++编译器的标志可以通过多种方式受到影响:手动设置CMAKE_CXX_FLAGS,使用add_definitions(),强制使用某个C ++标准,等等。

为了使用不同的规则(在我的情况下是预编译的头)在同一个项目中编译目标,我需要重现用于编译由add_executable()之类的命令添加的文件的确切命令。这个目录。

读取CMAKE_CXX_FLAGS只返回显式设置的值CMAKE_CXX_FLAGS_DEBUG,兄弟姐妹只列出默认的Debug / Release选项。有一个特殊的函数可以从add_definitions()add_compiler_options()检索标志,但似乎没有一个能够返回最后的命令行。

如何将传递给编译器的所有标志都放入CMake变量?

4 个答案:

答案 0 :(得分:3)

最简单的方法是在编译时使用make VERBOSE=1

cd my-build-dir
cmake path-to-my-sources
make VERBOSE=1

这将执行单线程构建,make将在它运行之前打印它运行的每个shell命令。所以你会看到如下输出:

[  0%] Building CXX object Whatever.cpp.o
<huge scary build command it used to build Whatever.cpp>

答案 1 :(得分:3)

回答我自己的问题:似乎获取所有编译器标志的唯一方法是从各种源重建它们。我现在使用的代码如下(对于GCC):

macro (GET_COMPILER_FLAGS TARGET VAR)
    if (CMAKE_COMPILER_IS_GNUCXX)
        set(COMPILER_FLAGS "")

        # Get flags form add_definitions, re-escape quotes
        get_target_property(TARGET_DEFS ${TARGET} COMPILE_DEFINITIONS)
        get_directory_property(DIRECTORY_DEFS COMPILE_DEFINITIONS)
        foreach (DEF ${TARGET_DEFS} ${DIRECTORY_DEFS})
            if (DEF)
                string(REPLACE "\"" "\\\"" DEF "${DEF}")
                list(APPEND COMPILER_FLAGS "-D${DEF}")
            endif ()
        endforeach ()

        # Get flags form include_directories()
        get_target_property(TARGET_INCLUDEDIRS ${TARGET} INCLUDE_DIRECTORIES)
        foreach (DIR ${TARGET_INCLUDEDIRS})
            if (DIR)
                list(APPEND COMPILER_FLAGS "-I${DIR}")
            endif ()
        endforeach ()

        # Get build-type specific flags
        string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE_SUFFIX)
        separate_arguments(GLOBAL_FLAGS UNIX_COMMAND
                "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BUILD_TYPE_SUFFIX}}")
        list(APPEND COMPILER_FLAGS ${GLOBAL_FLAGS})

        # Add -std= flag if appropriate
        get_target_property(STANDARD ${TARGET} CXX_STANDARD)
        if ((NOT "${STANDARD}" STREQUAL NOTFOUND) AND (NOT "${STANDARD}" STREQUAL ""))
            list(APPEND COMPILER_FLAGS "-std=gnu++${STANDARD}")
        endif ()
    endif ()
    set(${VAR} "${COMPILER_FLAGS}")
endmacro ()

这可以扩展为包括由add_compiler_options()等引起的选项。

答案 2 :(得分:0)

简短答案

在所有用例中都无法使用CMake脚本中的变量来分配编译器命令行的最终值。

长答案

不幸的是,即使解决方案被接受为答案仍然不能获得所有编译器标志。如注释中所述,有Transitive Usage Requirements。这是写CMake文件的一种现代而恰当的方法,越来越受欢迎。另外,您可能会使用生成器表达式定义一些编译选项(它们看起来像变量引用,但在需要时不会扩展)。

考虑以下示例:

add_executable(myexe ...);
target_compile_definitions(myexe PRIVATE "PLATFORM_$<PLATFORM_ID>");
add_library(mylib ...);
target_compile_definitions(mylib INTERFACE USING_MY_LIB);
target_link_libraries(myexe PUBLIC mylib);

如果您尝试使用myexe目标调用建议的GET_COMPILER_FLAGS宏,则会得到结果输出-DPLATFORM_$<PLATFORM_ID>而不是预期的-DPLATFORM_Linux -DUSING_MY_LIB

这是因为调用CMake和生成构建系统之间有两个阶段:

  1. 处理中。在此阶段,CMake从cmake脚本读取并执行命令,尤其是评估和分配变量值。此时,CMake只是收集所有必需的信息,并准备生成构建系统(makefile)。
  2. 正在生成。 CMake使用值special variablesproperties(留在已处理脚本的末尾)最终决定并形成生成的输出。它是在此处根据其内部算法为编译器构造最终命令行的地方,而脚本不可用。

在处理阶段使用get_target_property(...)get_property(... TARGET ...)可能检索到的目标属性尚未完成(即使在脚本末尾调用时也是如此)。在生成阶段,CMake(递归地)遍历每个目标依赖关系树,并根据传递性用法要求附加属性值(传播标记的值PUBLICINTERFACE)。

尽管有一些变通办法,具体取决于您要达到的最终结果。这可以通过应用generator expressions来实现,该方法允许使用 final 的任何目标属性的值(在处理阶段定义)...但是稍后!

有两种通用的可能性:

  1. 基于模板生成任何输出文件,该模板的内容包含变量引用和/或生成器表达式,并定义为字符串变量值或输入文件。由于对条件逻辑的支持非常有限(因此,您不能使用仅适用于嵌套foreach()循环的复杂串联),因此它不灵活,但具有优点,即不需要采取进一步的操作,并且无需以平台无关的方式描述内容。使用file(GENERATE ...) command variant。请注意,它的行为与file (WRITE ...)变体不同。
  2. 添加自定义目标(和/或自定义命令),以实现扩展值的进一步使用。它依赖于平台,并且要求用户另外调用make(通过某些特殊目标或包含到all目标),但是它具有足够的灵活性,因为您可以实现shell脚本(但没有可执行文件)位)。

结合以下选项演示解决方案的示例:

set(target_name "myexe")
file(GENERATE OUTPUT script.sh CONTENT "#!/bin/sh\n echo \"${target_name} compile definitions: $<TARGET_PROPERTY:${target_name},COMPILE_DEFINITIONS>\"")
add_custom_target(mycustomtarget
  COMMAND echo "\"Platform: $<PLATFORM_ID>\""
  COMMAND /bin/sh -s < script.sh
  )

调用CMake之后,构建目录将包含文件script.sh,调用make mycustomtarget将打印到控制台:

Platform: Linux
myexe compile definitions: PLATFORM_Linux USING_MY_LIB

答案 3 :(得分:0)

实际上,在编译时使用CXX_COMPILER_LAUNCHER可以做到这一点:

如果您有脚本print_args.py

#!/usr/bin/env python
import sys
import argparse

print(" ".join(sys.argv[1:]))
# we need to produce an output file so that the link step does not fail
p = argparse.ArgumentParser()
p.add_argument("-o")
args, _ = p.parse_known_args()
with open(args.o, "w") as f:
    f.write("")

您可以如下设置目标的属性:

add_library(${TARGET_NAME} ${SOURCES})
set_target_properties(${TARGET_NAME} PROPERTIES
      CXX_COMPILER_LAUNCHER
        ${CMAKE_CURRENT_SOURCE_DIR}/print_args.py
)
# this tells the linker to not actually link. Which would fail because output file is empty
set_target_properties(${TARGET_NAME} PROPERTIES
      LINK_FLAGS
        -E
)

这将在编译时打印确切的编译命令。