在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变量?
答案 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和生成构建系统之间有两个阶段:
在处理阶段使用get_target_property(...)
或get_property(... TARGET ...)
可能检索到的目标属性尚未完成(即使在脚本末尾调用时也是如此)。在生成阶段,CMake(递归地)遍历每个目标依赖关系树,并根据传递性用法要求附加属性值(传播标记的值PUBLIC
和INTERFACE
)。
尽管有一些变通办法,具体取决于您要达到的最终结果。这可以通过应用generator expressions来实现,该方法允许使用 final 的任何目标属性的值(在处理阶段定义)...但是稍后!
有两种通用的可能性:
foreach()
循环的复杂串联),因此它不灵活,但具有优点,即不需要采取进一步的操作,并且无需以平台无关的方式描述内容。使用file(GENERATE ...) command variant。请注意,它的行为与file (WRITE ...)
变体不同。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
)
这将在编译时打印确切的编译命令。