如何使用CMake和NMake查看底层编译器/链接器命令行?

3
默认情况下,某些工具链使用响应文件来指定编译器和链接器的命令行开关。然而,这似乎只适用于makefiles。可以说,这是为了解决在Windows上使用“NMake Makefiles”生成器时命令行长度限制为8192个字符的问题。
显然,这些设置应该受到“CMAKE__USE_RESPONSE_FILE_FOR_OBJECTS”的控制,但是就我所知,并没有完全记录。无论如何,CMake(3.15.1)发行版附带的各种CMake脚本都包含这些设置(即每种语言),它们的值为0或1。这似乎只在“cmMakefileTargetGenerator.cxx”中的“cmMakefileTargetGenerator::CheckUseResponseFileForObjects()”被读取(截至本文撰写时)。
此外,还有形式为“CMAKE__USE_RESPONSE_FILE_FOR_INCLUDES”和“CMAKE__USE_RESPONSE_FILE_FOR_LIBRARIES”的补充设置。
现在,虽然这在技术上“有效”,但它确实对我产生了一个不希望的副作用。在自动构建的日志中,我想看到正在发生的情况(是的,我也设置了 CMAKE_VERBOSE_MAKEFILE=ON)。考虑到这些响应文件是即时生成的,从日志文件中看到的命令行类似于:
C:\PROGRA~2\MICROS~1\2019\PROFES~1\VC\Tools\MSVC\1422~1.279\bin\Hostx86\x86\cl.exe @C:\Users\XA1DB~1.RLM\AppData\Local\Temp\nm2A99.tmp

这意味着我不能再看到传递的参数是什么了。对于我的目的来说,输出基本上变得毫无用处。

我该如何让CMake生成可以显示响应文件内容的makefiles,以便我可以从构建日志中获取编译器/链接器命令行参数?

注:别误解我的意思,我完全理解Windows上现有的限制。但是,即使实际的调用使用响应文件来调用编译器或链接器,我也只想看到响应文件的内容,而不是响应文件的名称。我正在寻找一种规范的方法来做到这一点,而不必想出一些容易破坏的hacky解决方案,因为下次CMake更新其内部时就会出问题。


我还发现在2019年使用了8.3(备用)路径名有点令人担忧(考虑到fsutil behavior set disable8dot3 1 / NtfsDisable8dot3NameCreation),但也许CMake的作者有我所不知道的见解。我无法在Git repo的最新源代码中找到任何配置该行为的设置。
2个回答

3

nmake有一个选项可以显示内联文件/响应文件:

/U 转储内联文件

对于递归make,您可能希望像这样在环境变量中设置它:

set MAKEFLAGS=U

这样,所有响应文件的内容都将显示在控制台上。


2
按照我一贯的做法,在我回答问题和回答自己的问答中,凡是提供有用答案的他人,我都会给予奖励。因此,我接受了你的答案。 - undefined

1

NB: TL;DR在底部。

我已经取得了一些进展,但不幸的是,乍一看应该影响所述行为的变量却没有任何作用(或者与评论中所期望的方式不同),即使强制将空值缓存给它们。摘录自Modules\Platform\Windows.cmake

# for nmake make long command lines are redirected to a file
# with the following syntax, see Windows-bcc32.cmake for use
if(CMAKE_GENERATOR MATCHES "NMake")
  set(CMAKE_START_TEMP_FILE "@<<\n")
  set(CMAKE_END_TEMP_FILE "\n<<")
endif()

include(Platform/WindowsPaths)

# uncomment these out to debug nmake and borland makefiles
#set(CMAKE_START_TEMP_FILE "")
#set(CMAKE_END_TEMP_FILE "")
#set(CMAKE_VERBOSE_MAKEFILE 1)

我在评论中找不到所引用的Windows-bcc32.cmake,但可以推断这是指Borland C编译器可执行文件的旧名称。因此,Windows-Borland-C.cmakeWindows-Borland-CXX.cmake很可能是它们的继任者,它们又分别包含Windows-Embarcadero-C.cmakeWindows-Embarcadero-CXX.cmake。但是一旦我追踪了这个线索,我发现第二次提到Windows-bcc32.cmake的引用(实际上在评论中提到了"Borland")似乎与此无关。

然而,重要的是两个变量CMAKE_START_TEMP_FILEMAKE_END_TEMP_FILE。在调用project()之后设置它们(即使强制写入缓存)也无法产生期望的效果。

这些变量在Windows.cmake中定义(上面摘录)并在以下位置使用

  • Modules\Platform\Windows-df.cmake
  • Modules\Platform\Windows-Embarcadero.cmake
  • Modules\Platform\Windows-MSVC.cmake
  • Modules\Platform\Windows-NVIDIA-CUDA.cmake
  • Modules\Platform\Windows-OpenWatcom.cmake
  • Modules\Platform\Windows-PGI.cmake
  • Tests\RunCMake\add_link_options\LINKER_expansion-list.cmake
  • Tests\RunCMake\Make\VerboseBuild.cmake
  • Tests\RunCMake\target_link_options\LINKER_expansion.cmake
  • Source\cmMakefileTargetGenerator.cxx

看起来 CMAKE_START_TEMP_FILECMAKE_END_TEMP_FILE 在使用它们的地方已经被展开,这是在 project() 完成之前。这意味着改变 Windows.cmake 将是唯一(丑陋的)可行选项... 除非...

简而言之

在调用 project() 之后添加以下内容,但请注意,如果/每当您处理大量文件/命令行参数时,这可能会达到 Windows 命令行长度限制的“上限”。但这是如何使 NMake 执行的命令再次可见。

set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 0 FORCE)
foreach(lang IN ITEMS C CXX)
    foreach(cmd IN ITEMS COMPILE_OBJECT CREATE_SHARED_LIBRARY CREATE_PREPROCESSED_SOURCE CREATE_ASSEMBLY_SOURCE LINK_EXECUTABLE)
        string(REPLACE "${CMAKE_START_TEMP_FILE}" "" CMAKE_${lang}_${cmd} "${CMAKE_${lang}_${cmd}}")
        string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_${lang}_${cmd} "${CMAKE_${lang}_${cmd}}")
    endforeach()
endforeach()

顺便说一下:替换语法是从Tests\RunCMake\Make\VerboseBuild.cmake中获取的。我只是将其转化为循环,以更简洁地处理多个变量。

这样做的目的是将NMake文件中使用的各种命令中的CMAKE_START_TEMP_FILE/CMAKE_END_TEMP_FILE实例替换为空字符串,从而禁用内联文件的特殊NMake语法。因为事实证明,导致无法看到命令的罪魁祸首并不是CMake,而是NMake。

参考资料:Makefile中的内联文件

现在这打开了其他各种可尝试的替代方案。一些想法:

将以下内容翻译成中文:
  • CMAKE_END_TEMP_FILE后追加KEEP,以保留响应文件...
  • CMAKE_START_TEMP_FILE后添加要使用的文件名(请参阅"重复使用内联文件"此答案)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接