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.