Browse Source

cmCxxModuleMapper: support the `clang` module map format

Ben Boeckel 3 years ago
parent
commit
21b9fb1e8c

+ 6 - 3
Help/dev/experimental.rst

@@ -77,9 +77,9 @@ For compilers that generate module maps, tell CMake as follows:
   set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG
     "${compiler_flags_for_module_map} -fmodule-mapper=<MODULE_MAP_FILE>")
 
-Currently, the only supported formats are ``gcc`` and ``msvc``.  The ``gcc``
-format is described in the GCC documentation, but the relevant section for the
-purposes of CMake is:
+Currently, the only supported formats are, ``clang``, ``gcc``, and ``msvc``.
+The ``gcc`` format is described in the GCC documentation, but the relevant
+section for the purposes of CMake is:
 
     A mapping file consisting of space-separated module-name, filename
     pairs, one per line.  Only the mappings for the direct imports and any
@@ -94,6 +94,9 @@ The ``msvc`` format is a response file containing flags required to compile
 any module interfaces properly as well as find any required files to satisfy
 ``import`` statements as required for Microsoft's Visual Studio toolchains.
 
+Similarly, the ``clang`` format is a response file containing flags using
+Clang's module flags.
+
 .. _`D1483r1`: https://mathstuf.fedorapeople.org/fortran-modules/fortran-modules.html
 .. _`P1689r5`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html
 .. _`cxx-modules-sandbox`: https://github.com/mathstuf/cxx-modules-sandbox

+ 36 - 0
Source/cmCxxModuleMapper.cxx

@@ -28,6 +28,38 @@ cm::optional<std::string> CxxModuleLocations::BmiGeneratorPathForModule(
 
 namespace {
 
+std::string CxxModuleMapContentClang(CxxModuleLocations const& loc,
+                                     cmScanDepInfo const& obj)
+{
+  std::stringstream mm;
+
+  // Clang's command line only supports a single output. If more than one is
+  // expected, we cannot make a useful module map file.
+  if (obj.Provides.size() > 1) {
+    return {};
+  }
+
+  // A series of flags which tell the compiler where to look for modules.
+
+  for (auto const& p : obj.Provides) {
+    if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
+      // Force the TU to be considered a C++ module source file regardless of
+      // extension.
+      mm << "-x c++-module\n";
+
+      mm << "-fsave-std-c++-module-file=" << *bmi_loc << '\n';
+      break;
+    }
+  }
+  for (auto const& r : obj.Requires) {
+    if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) {
+      mm << "-fmodule-file=" << *bmi_loc << '\n';
+    }
+  }
+
+  return mm.str();
+}
+
 std::string CxxModuleMapContentGcc(CxxModuleLocations const& loc,
                                    cmScanDepInfo const& obj)
 {
@@ -179,6 +211,8 @@ cm::static_string_view CxxModuleMapExtension(
 {
   if (format) {
     switch (*format) {
+      case CxxModuleMapFormat::Clang:
+        return ".pcm"_s;
       case CxxModuleMapFormat::Gcc:
         return ".gcm"_s;
       case CxxModuleMapFormat::Msvc:
@@ -297,6 +331,8 @@ std::string CxxModuleMapContent(CxxModuleMapFormat format,
                                 CxxModuleUsage const& usages)
 {
   switch (format) {
+    case CxxModuleMapFormat::Clang:
+      return CxxModuleMapContentClang(loc, obj);
     case CxxModuleMapFormat::Gcc:
       return CxxModuleMapContentGcc(loc, obj);
     case CxxModuleMapFormat::Msvc:

+ 1 - 0
Source/cmCxxModuleMapper.h

@@ -17,6 +17,7 @@
 
 enum class CxxModuleMapFormat
 {
+  Clang,
   Gcc,
   Msvc,
 };

+ 2 - 0
Source/cmGlobalNinjaGenerator.cxx

@@ -2567,6 +2567,8 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
   cm::optional<CxxModuleMapFormat> modmap_fmt;
   if (arg_modmapfmt.empty()) {
     // nothing to do.
+  } else if (arg_modmapfmt == "clang") {
+    modmap_fmt = CxxModuleMapFormat::Clang;
   } else if (arg_modmapfmt == "gcc") {
     modmap_fmt = CxxModuleMapFormat::Gcc;
   } else if (arg_modmapfmt == "msvc") {

+ 1 - 1
Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi.cmake

@@ -1,6 +1,6 @@
 function (check_for_bmi prefix destination name)
   set(found 0)
-  foreach (ext IN ITEMS gcm ifc)
+  foreach (ext IN ITEMS gcm ifc pcm)
     if (EXISTS "${prefix}/${destination}/${name}.${ext}")
       set(found 1)
       break ()