From d2fa56772f290c61925a70161c77be5f4334fd77 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Fri, 17 Nov 2023 22:53:45 -0500 Subject: [PATCH] Ninja: support "forwarding" modules from other targets When a target uses objects from another target which provides modules as sources, the modules provided by the referenced target must also be treated as if they were provided by the referencing target. Add the concept of "forwarding" modules so that consumers can use modules created by these sources as well. Note that this is only sensible for Fortran where module usages are implicit as far as CMake's visibility model is concerned. C++ modules have their own concept of visibility which does not require or support such `$` reuse in this way. --- Source/cmGlobalNinjaGenerator.cxx | 59 ++++++++++++++++++- Source/cmGlobalNinjaGenerator.h | 1 + Source/cmNinjaTargetGenerator.cxx | 9 +++ .../NinjaDependInfoBMIInstall-private.json | 1 + .../NinjaDependInfoBMIInstall-public.json | 1 + .../expect/NinjaDependInfoExport-private.json | 1 + .../expect/NinjaDependInfoExport-public.json | 1 + .../NinjaDependInfoFileSet-private.json | 1 + .../expect/NinjaDependInfoFileSet-public.json | 1 + 9 files changed, 74 insertions(+), 1 deletion(-) diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index d691d8884c..cba48f4e39 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -2527,6 +2527,7 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( std::string const& arg_dd, std::vector const& arg_ddis, std::string const& module_dir, std::vector const& linked_target_dirs, + std::vector const& forward_modules_from_target_dirs, std::string const& arg_lang, std::string const& arg_modmapfmt, cmCxxModuleExportInfo const& export_info) { @@ -2804,6 +2805,51 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile( // use by dependents that reference this target in linked-target-dirs. std::string const target_mods_file = cmStrCat( cmSystemTools::GetFilenamePath(arg_dd), '/', arg_lang, "Modules.json"); + + // Populate the module map with those provided by linked targets first. + for (std::string const& forward_modules_from_target_dir : + forward_modules_from_target_dirs) { + std::string const fmftn = + cmStrCat(forward_modules_from_target_dir, '/', arg_lang, "Modules.json"); + Json::Value fmft; + cmsys::ifstream fmftf(fmftn.c_str(), std::ios::in | std::ios::binary); + if (!fmftf) { + cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to open ", + fmftn, " for module information")); + return false; + } + Json::Reader reader; + if (!reader.parse(fmftf, fmft, false)) { + cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ", + forward_modules_from_target_dir, + reader.getFormattedErrorMessages())); + return false; + } + if (!fmft.isObject()) { + continue; + } + + auto forward_info = [](Json::Value& target, Json::Value const& source) { + if (!source.isObject()) { + return; + } + + for (auto i = source.begin(); i != source.end(); ++i) { + std::string const key = i.key().asString(); + if (target.isMember(key)) { + continue; + } + target[key] = *i; + } + }; + + // Forward info from forwarding targets into our collation. + Json::Value& tmi_target_modules = target_module_info["modules"]; + forward_info(tmi_target_modules, fmft["modules"]); + forward_info(target_references, fmft["references"]); + forward_info(target_usages, fmft["usages"]); + } + cmGeneratedFileStream tmf(target_mods_file); tmf << target_module_info; @@ -2891,6 +2937,16 @@ int cmcmd_cmake_ninja_dyndep(std::vector::const_iterator argBeg, linked_target_dirs.push_back(tdi_linked_target_dir.asString()); } } + std::vector forward_modules_from_target_dirs; + Json::Value const& tdi_forward_modules_from_target_dirs = + tdi["forward-modules-from-target-dirs"]; + if (tdi_forward_modules_from_target_dirs.isArray()) { + for (auto const& tdi_forward_modules_from_target_dir : + tdi_forward_modules_from_target_dirs) { + forward_modules_from_target_dirs.push_back( + tdi_forward_modules_from_target_dir.asString()); + } + } std::string const compilerId = tdi["compiler-id"].asString(); std::string const simulateId = tdi["compiler-simulate-id"].asString(); std::string const compilerFrontendVariant = @@ -2914,7 +2970,8 @@ int cmcmd_cmake_ninja_dyndep(std::vector::const_iterator argBeg, # endif return gg.WriteDyndepFile(dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, arg_dd, arg_ddis, module_dir, linked_target_dirs, - arg_lang, arg_modmapfmt, *export_info) + forward_modules_from_target_dirs, arg_lang, + arg_modmapfmt, *export_info) ? 0 : 1; } diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index c5d69010bd..220d393566 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -430,6 +430,7 @@ public: std::string const& arg_dd, std::vector const& arg_ddis, std::string const& module_dir, std::vector const& linked_target_dirs, + std::vector const& forward_modules_from_target_dirs, std::string const& arg_lang, std::string const& arg_modmapfmt, cmCxxModuleExportInfo const& export_info); diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 1adcaadcea..22839233cc 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx @@ -1211,6 +1211,9 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements( for (std::string const& l : linked_directories.Direct) { build.ImplicitDeps.push_back(cmStrCat(l, '/', language, "Modules.json")); } + for (std::string const& l : linked_directories.Forward) { + build.ImplicitDeps.push_back(cmStrCat(l, '/', language, "Modules.json")); + } this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), build); @@ -1915,6 +1918,12 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, tdi_linked_target_dirs.append(l); } + Json::Value& tdi_forward_modules_from_target_dirs = + tdi["forward-modules-from-target-dirs"] = Json::arrayValue; + for (std::string const& l : linked_directories.Forward) { + tdi_forward_modules_from_target_dirs.append(l); + } + cmDyndepGeneratorCallbacks cb; cb.ObjectFilePath = [this](cmSourceFile const* sf, std::string const& cnf) { return this->GetObjectFilePath(sf, cnf); diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json index 45b039677b..78f7928670 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json @@ -45,6 +45,7 @@ "exports": [], "include-dirs": [], "language": "CXX", + "forward-modules-from-target-dirs": [], "linked-target-dirs": [], "module-dir": "/CMakeFiles/ninja-bmi-install-private.dir" } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json index 30b55e3f0b..6c23354188 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json @@ -45,6 +45,7 @@ "exports": [], "include-dirs": [], "language": "CXX", + "forward-modules-from-target-dirs": [], "linked-target-dirs": [], "module-dir": "/CMakeFiles/ninja-bmi-install-public.dir" } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json index f06a846a46..61f8f6476f 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json @@ -73,6 +73,7 @@ ], "include-dirs": [], "language": "CXX", + "forward-modules-from-target-dirs": [], "linked-target-dirs": [], "module-dir": "/CMakeFiles/ninja-exports-private.dir" } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json index 938481c0e5..d0263b0b92 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json @@ -73,6 +73,7 @@ ], "include-dirs": [], "language": "CXX", + "forward-modules-from-target-dirs": [], "linked-target-dirs": [], "module-dir": "/CMakeFiles/ninja-exports-public.dir" } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json index 3a66a9446c..ed61e0e276 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json @@ -40,6 +40,7 @@ "exports": [], "include-dirs": [], "language": "CXX", + "forward-modules-from-target-dirs": [], "linked-target-dirs": [], "module-dir": "/CMakeFiles/ninja-file-sets-private.dir" } diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json index ac06c0fb83..171935f267 100644 --- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json +++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json @@ -40,6 +40,7 @@ "exports": [], "include-dirs": [], "language": "CXX", + "forward-modules-from-target-dirs": [], "linked-target-dirs": [], "module-dir": "/CMakeFiles/ninja-file-sets-public.dir" }