Skip to content

MSVC: Reusing precompile headers from an object library, causes the "Ninja Multi-Config" generator to generate invalid ninja configs.

Text copied from: #20161 (comment 758984)

Right now, reusing precompile headers from an object library, causes the "Ninja Multi-Config" generator to generate invalid ninja configs when compiling with cl.exe and clang-cl.exe on Windows (works on Linux).

The most important part of the CMakeFiles/impl-Debug.ninja file is:

build debug\application.exe: CXX_EXECUTABLE_LINKER__application_Debug CMakeFiles\objects.dir\Debug\cmake_pch.cxx.obj CMakeFiles\objects.dir\Debug\src\application\random.cpp.obj CMakeFiles\application.dir\Debug\src\main.cpp.obj | ..\..\src\main.manifest || objects$:Debug
  FLAGS = /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd
  LINK_FLAGS = /machine:x64 /debug /INCREMENTAL /subsystem:console  C:\Workspace\templates\application\build\windows\CMakeFiles\objects.dir\$$${CONFIGURATION}\cmake_pch.cxx.obj C:\Workspace\templates\application\build\windows\CMakeFiles\objects.dir\$$${CONFIGURATION}\cmake_pch.cxx.obj
  LINK_LIBRARIES = kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib
  MANIFESTS = ..\..\src\main.manifest
  OBJECT_DIR = CMakeFiles\application.dir\Debug
  POST_BUILD = cd .
  PRE_LINK = cd .
  TARGET_COMPILE_PDB = application.dir\Debug\objects.pdb
  TARGET_FILE = debug\application.exe
  TARGET_IMPLIB = Debug\application.lib
  TARGET_PDB = debug\application.pdb

Please notice the triple-$.

And the error message is:

LINK : fatal error LNK1104: cannot open file 'C:\Workspace\templates\application\build\windows\CMakeFiles\objects.dir\$Debug\cmake_pch.cxx.obj'

The correct path has no $ before Debug in it.

Tested with this minimal project: https://github.com/qis/cmake-ninja-multi-config

make clean all
make clean all CC=C:\llvm\bin\clang-cl.exe CXX=C:\llvm\bin\clang-cl.exe CXXFLAGS=/U__cpp_concepts

Edit 1: It works with clang.exe and clang++.exe on Windows.
Edit 2: Tested with the current CMake release and cmake-3.17.20200515-ge0dd8b9-win64-x64.msi.
Edit 3: This patch fixes the problem at hand:

--- i/Source/cmGlobalNinjaGenerator.cxx
+++ w/Source/cmGlobalNinjaGenerator.cxx
@@ -118,9 +118,12 @@ std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string& lit)
   cmSystemTools::ReplaceString(result, "$", "$$");
   cmSystemTools::ReplaceString(result, "\n", "$\n");
   if (this->IsMultiConfig()) {
-    cmSystemTools::ReplaceString(result,
-                                 cmStrCat('$', this->GetCMakeCFGIntDir()),
-                                 this->GetCMakeCFGIntDir());
+    const auto escapedCMakeCFGIntDir = cmStrCat('$', this->GetCMakeCFGIntDir());
+    while (result.find(escapedCMakeCFGIntDir) != std::string::npos) {
+      cmSystemTools::ReplaceString(result,
+                                   escapedCMakeCFGIntDir,
+                                   this->GetCMakeCFGIntDir());
+    }
   }
   return result;
 }

Sadly, I do not know how to prevent CMake from trying to link to reused precompile headers multiple times, so the following linker warning is still present:

C:\Workspace\templates\application\build\windows\CMakeFiles\objects.dir\Debug\cmake_pch.cxx.obj : warning LNK4042: object specified more than once; extras ignored
C:\Workspace\templates\application\build\windows\CMakeFiles\objects.dir\Debug\cmake_pch.cxx.obj : warning LNK4042: object specified more than once; extras ignored

Edit 4: This seems to be the root cause of the issue since it prepends more $ to the variable, than it removes.

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information