c++modules: Fix scanning with spaces in subdirectory name

If the path to a subdirectory inside the project has a space,
C++ module dependency scanning paths need quoting.

Fixes: #27647
This commit is contained in:
Brad King
2026-02-25 15:43:03 -05:00
parent 2d262efd29
commit 443014b840
8 changed files with 52 additions and 10 deletions
+6 -5
View File
@@ -148,15 +148,15 @@ std::string CxxModuleMapContentClang(CxxModuleLocations const& loc,
// extension.
mm << "-x c++-module\n";
mm << "-fmodule-output=" << bmi_loc.Location() << '\n';
mm << "-fmodule-output=\"" << bmi_loc.Location() << "\"\n";
break;
}
}
auto all_usages = GetTransitiveUsages(loc, obj.Requires, usages);
for (auto const& usage : all_usages) {
mm << "-fmodule-file=" << usage.LogicalName << '=' << usage.Location
<< '\n';
mm << "-fmodule-file=\"" << usage.LogicalName << '=' << usage.Location
<< "\"\n";
}
return mm.str();
@@ -229,7 +229,7 @@ std::string CxxModuleMapContentMsvc(CxxModuleLocations const& loc,
auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName);
if (bmi_loc.IsKnown()) {
mm << "-ifcOutput " << bmi_loc.Location() << '\n';
mm << "-ifcOutput \"" << bmi_loc.Location() << "\"\n";
}
}
@@ -237,7 +237,8 @@ std::string CxxModuleMapContentMsvc(CxxModuleLocations const& loc,
for (auto const& usage : all_usages) {
auto flag = flag_for_method(usage.Method);
mm << flag << ' ' << usage.LogicalName << '=' << usage.Location << '\n';
mm << flag << " \"" << usage.LogicalName << '=' << usage.Location
<< "\"\n";
}
return mm.str();
+10 -5
View File
@@ -1351,11 +1351,13 @@ cmNinjaBuild GetScanBuildStatement(std::string const& ruleName,
// Tell dependency scanner the object file that will result from
// compiling the source.
scanBuild.Variables["OBJ_FILE"] = objectFileName;
scanBuild.Variables["OBJ_FILE"] =
tg->ConvertToOutputFormatForShell(objectFileName);
// Tell dependency scanner where to store dyndep intermediate results.
std::string ddiFileName = cmStrCat(objectFileName, ".ddi");
scanBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFileName;
scanBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] =
tg->ConvertToOutputFormatForShell(ddiFileName);
scanBuild.RspFile = cmStrCat(ddiFileName, ".rsp");
// Outputs of the scan/preprocessor build statement.
@@ -1364,7 +1366,8 @@ cmNinjaBuild GetScanBuildStatement(std::string const& ruleName,
scanBuild.ImplicitOuts.push_back(ddiFileName);
} else {
scanBuild.Outputs.push_back(ddiFileName);
scanBuild.Variables["PREPROCESSED_OUTPUT_FILE"] = ppFileName;
scanBuild.Variables["PREPROCESSED_OUTPUT_FILE"] =
tg->ConvertToOutputFormatForShell(ppFileName);
if (!compilationPreprocesses) {
// Compilation does not preprocess and we are not compiling an
// already-preprocessed source. Make compilation depend on the scan
@@ -1650,7 +1653,8 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
// `cmNinjaTargetGenerator::ExportObjectCompileCommand` to expect the
// corresponding file path.
std::string ddModmapFile = cmStrCat(objectFileName, ".modmap");
vars["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile;
vars["DYNDEP_MODULE_MAP_FILE"] =
this->ConvertToOutputFormatForShell(ddModmapFile);
objBuild.ImplicitDeps.push_back(ddModmapFile);
scanningFiles.ModuleMapFile = std::move(ddModmapFile);
}
@@ -1877,7 +1881,8 @@ void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement(
if (!modmapFormat.empty()) {
std::string ddModmapFile = cmStrCat(bmiFileName, ".modmap");
vars["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile;
vars["DYNDEP_MODULE_MAP_FILE"] =
this->ConvertToOutputFormatForShell(ddModmapFile);
scanningFiles.ModuleMapFile = std::move(ddModmapFile);
}
@@ -13,6 +13,7 @@ endif ()
string(APPEND info "\
set(CMAKE_CXX_COMPILE_FEATURES \"${CMAKE_CXX_COMPILE_FEATURES}\")
set(CMAKE_MAKE_PROGRAM \"${CMAKE_MAKE_PROGRAM}\")
set(CMAKE_CXX_COMPILER_ID \"${CMAKE_CXX_COMPILER_ID}\")
set(CMAKE_CXX_COMPILER_VERSION \"${CMAKE_CXX_COMPILER_VERSION}\")
set(CMAKE_CXX_OUTPUT_EXTENSION \"${CMAKE_CXX_OUTPUT_EXTENSION}\")
set(CMAKE_CXX20_STANDARD_COMPILE_OPTION \"${CMAKE_CXX20_STANDARD_COMPILE_OPTION}\")
@@ -157,6 +157,10 @@ run_cxx_module_test(scan-with-pch)
# Tests which use named modules.
if ("named" IN_LIST CMake_TEST_MODULE_COMPILATION)
run_cxx_module_test(simple)
# FIXME(GCC): `g++ -c "with space.cpp" -M -fdeps-format=p1689r5` fails.
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
run_cxx_module_test(subdir)
endif ()
run_cxx_module_test(file-sets-with-dot)
run_cxx_module_test(vs-without-flags)
run_cxx_module_test(library library-static -DBUILD_SHARED_LIBS=OFF)
@@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.24...3.28)
project(cxx_modules_simple CXX)
include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
add_subdirectory("with space")
@@ -0,0 +1,13 @@
add_executable(simple)
target_sources(simple
PRIVATE
main.cxx
PRIVATE
FILE_SET CXX_MODULES
BASE_DIRS
"${CMAKE_CURRENT_SOURCE_DIR}"
FILES
importable.cxx)
target_compile_features(simple PUBLIC cxx_std_20)
add_test(NAME simple COMMAND simple)
@@ -0,0 +1,6 @@
export module importable;
export int from_import()
{
return 0;
}
@@ -0,0 +1,6 @@
import importable;
int main(int argc, char* argv[])
{
return from_import();
}