clang: Fixes to module support

1. -fmodules-embed-all-files is a cc1 flag, and should have been passed
   to clang++ via -Xclang.

2. Don't require a .cc in the target to emit the module_deps variables
   (this is to be able to compile a target that contains only a
   .modulemap, e.g. the C++ std module).

3. Add {{module_deps_no_self}}, which is the same as {{module_deps}}
   except that it excludes the flags to include the pcm for the current
   target.

   That is, for A depending on B, and both using modules, when building
   for A, {{module_deps}} will reference both A.pcm and B.pcm, and so
   can be used to build .cc files in A. By contrast,
   {{module_deps_no_self}} for A will only reference B.pcm, and so can
   be used to build A.pcm.

Bug: fuchsia:27276
Change-Id: I239d1c5cd4d5d167251801b1347b02e8d89caeee
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/9680
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Scott Graham <scottmg@chromium.org>
diff --git a/docs/reference.md b/docs/reference.md
index 257f474..f8c2bd4 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -3718,6 +3718,13 @@
         prefixed by "-I" (these work with Posix tools as well as Microsoft
         ones).
 
+    {{module_deps}}
+    {{module_deps_no_self}}
+        Strings that correspond to the flags necessary to depend upon the Clang
+        modules referenced by the current target. The "_no_self" version doesn't
+        include the module for the current target, and can be used to compile
+        the pcm itself.
+
     {{source}}
         The relative path and name of the current input file.
         Example: "../../base/my_file.cc"
diff --git a/src/gn/c_substitution_type.cc b/src/gn/c_substitution_type.cc
index 618e846..ea47ce9 100644
--- a/src/gn/c_substitution_type.cc
+++ b/src/gn/c_substitution_type.cc
@@ -10,22 +10,33 @@
 #include "gn/err.h"
 
 const SubstitutionTypes CSubstitutions = {
-    &CSubstitutionAsmFlags,        &CSubstitutionCFlags,
-    &CSubstitutionCFlagsC,         &CSubstitutionCFlagsCc,
-    &CSubstitutionCFlagsObjC,      &CSubstitutionCFlagsObjCc,
-    &CSubstitutionDefines,         &CSubstitutionFrameworkDirs,
-    &CSubstitutionIncludeDirs,     &CSubstitutionModuleDeps,
+    &CSubstitutionAsmFlags,
+    &CSubstitutionCFlags,
+    &CSubstitutionCFlagsC,
+    &CSubstitutionCFlagsCc,
+    &CSubstitutionCFlagsObjC,
+    &CSubstitutionCFlagsObjCc,
+    &CSubstitutionDefines,
+    &CSubstitutionFrameworkDirs,
+    &CSubstitutionIncludeDirs,
+    &CSubstitutionModuleDeps,
+    &CSubstitutionModuleDepsNoSelf,
     &CSubstitutionSwiftModules,
 
-    &CSubstitutionLinkerInputs,    &CSubstitutionLinkerInputsNewline,
-    &CSubstitutionLdFlags,         &CSubstitutionLibs,
-    &CSubstitutionSoLibs,          &CSubstitutionFrameworks,
+    &CSubstitutionLinkerInputs,
+    &CSubstitutionLinkerInputsNewline,
+    &CSubstitutionLdFlags,
+    &CSubstitutionLibs,
+    &CSubstitutionSoLibs,
+    &CSubstitutionFrameworks,
     &CSubstitutionRlibs,
 
     &CSubstitutionArFlags,
 
-    &CSubstitutionSwiftModuleName, &CSubstitutionSwiftBridgeHeader,
-    &CSubstitutionSwiftModuleDirs, &CSubstitutionSwiftFlags,
+    &CSubstitutionSwiftModuleName,
+    &CSubstitutionSwiftBridgeHeader,
+    &CSubstitutionSwiftModuleDirs,
+    &CSubstitutionSwiftFlags,
 };
 
 // Valid for compiler tools.
@@ -42,6 +53,8 @@
 const Substitution CSubstitutionIncludeDirs = {"{{include_dirs}}",
                                               "include_dirs"};
 const Substitution CSubstitutionModuleDeps = {"{{module_deps}}", "module_deps"};
+const Substitution CSubstitutionModuleDepsNoSelf = {"{{module_deps_no_self}}",
+                                                    "module_deps_no_self"};
 
 // Valid for linker tools.
 const Substitution CSubstitutionLinkerInputs = {"{{inputs}}", "in"};
@@ -74,7 +87,9 @@
          type == &CSubstitutionCFlagsCc || type == &CSubstitutionCFlagsObjC ||
          type == &CSubstitutionCFlagsObjCc || type == &CSubstitutionDefines ||
          type == &CSubstitutionFrameworkDirs ||
-         type == &CSubstitutionIncludeDirs || type == &CSubstitutionModuleDeps;
+         type == &CSubstitutionIncludeDirs ||
+         type == &CSubstitutionModuleDeps ||
+         type == &CSubstitutionModuleDepsNoSelf;
 }
 
 bool IsValidCompilerOutputsSubstitution(const Substitution* type) {
diff --git a/src/gn/c_substitution_type.h b/src/gn/c_substitution_type.h
index e4255fd..1f4e90e 100644
--- a/src/gn/c_substitution_type.h
+++ b/src/gn/c_substitution_type.h
@@ -24,6 +24,7 @@
 extern const Substitution CSubstitutionFrameworkDirs;
 extern const Substitution CSubstitutionIncludeDirs;
 extern const Substitution CSubstitutionModuleDeps;
+extern const Substitution CSubstitutionModuleDepsNoSelf;
 
 // Valid for linker tools.
 extern const Substitution CSubstitutionLinkerInputs;
diff --git a/src/gn/function_toolchain.cc b/src/gn/function_toolchain.cc
index 52ac8f7..267198e 100644
--- a/src/gn/function_toolchain.cc
+++ b/src/gn/function_toolchain.cc
@@ -647,6 +647,13 @@
         prefixed by "-I" (these work with Posix tools as well as Microsoft
         ones).
 
+    {{module_deps}}
+    {{module_deps_no_self}}
+        Strings that correspond to the flags necessary to depend upon the Clang
+        modules referenced by the current target. The "_no_self" version doesn't
+        include the module for the current target, and can be used to compile
+        the pcm itself.
+
     {{source}}
         The relative path and name of the current input file.
         Example: "../../base/my_file.cc"
diff --git a/src/gn/ninja_c_binary_target_writer.cc b/src/gn/ninja_c_binary_target_writer.cc
index 5f4a14b..17f9c08 100644
--- a/src/gn/ninja_c_binary_target_writer.cc
+++ b/src/gn/ninja_c_binary_target_writer.cc
@@ -31,8 +31,12 @@
 struct ModuleDep {
   ModuleDep(const SourceFile* modulemap,
             const std::string& module_name,
-            const OutputFile& pcm)
-      : modulemap(modulemap), module_name(module_name), pcm(pcm) {}
+            const OutputFile& pcm,
+            bool is_self)
+      : modulemap(modulemap),
+        module_name(module_name),
+        pcm(pcm),
+        is_self(is_self) {}
 
   // The input module.modulemap source file.
   const SourceFile* modulemap;
@@ -42,6 +46,9 @@
 
   // The compiled version of the module.
   OutputFile pcm;
+
+  // Is this the module for the current target.
+  bool is_self;
 };
 
 namespace {
@@ -80,7 +87,7 @@
 std::vector<ModuleDep> GetModuleDepsInformation(const Target* target) {
   std::vector<ModuleDep> ret;
 
-  auto add = [&ret](const Target* t) {
+  auto add = [&ret](const Target* t, bool is_self) {
     const SourceFile* modulemap = GetModuleMapFromTargetSources(t);
     CHECK(modulemap);
 
@@ -94,17 +101,17 @@
         t->GetOutputFilesForSource(*modulemap, &tool_type, &modulemap_outputs));
     // Must be only one .pcm from .modulemap.
     CHECK(modulemap_outputs.size() == 1u);
-    ret.emplace_back(modulemap, label, modulemap_outputs[0]);
+    ret.emplace_back(modulemap, label, modulemap_outputs[0], is_self);
   };
 
   if (target->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
-    add(target);
+    add(target, true);
   }
 
   for (const auto& pair: target->GetDeps(Target::DEPS_LINKED)) {
     // Having a .modulemap source means that the dependency is modularized.
     if (pair.ptr->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
-      add(pair.ptr);
+      add(pair.ptr, false);
     }
   }
 
@@ -256,25 +263,13 @@
   }
 
   if (!module_dep_info.empty()) {
-    // TODO(scottmg): Currently clang modules only supported for C++.
-    if (target_->source_types_used().Get(SourceFile::SOURCE_CPP)) {
-      if (target_->toolchain()->substitution_bits().used.count(
-              &CSubstitutionModuleDeps)) {
-        EscapeOptions options;
-        options.mode = ESCAPE_NINJA_COMMAND;
-
-        out_ << CSubstitutionModuleDeps.ninja_name << " = ";
-        EscapeStringToStream(out_, "-fmodules-embed-all-files", options);
-
-        for (const auto& module_dep : module_dep_info) {
-          out_ << " ";
-          EscapeStringToStream(
-              out_, "-fmodule-file=" + module_dep.module_name + "=", options);
-          path_output_.WriteFile(out_, module_dep.pcm);
-        }
-
-        out_ << std::endl;
-      }
+    // TODO(scottmg): Currently clang modules only working for C++.
+    if (target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
+        target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
+      WriteModuleDepsSubstitution(&CSubstitutionModuleDeps, module_dep_info,
+                                  true);
+      WriteModuleDepsSubstitution(&CSubstitutionModuleDepsNoSelf,
+                                  module_dep_info, false);
     }
   }
 
@@ -290,7 +285,8 @@
   if (target_->source_types_used().Get(SourceFile::SOURCE_C) ||
       target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
       target_->source_types_used().Get(SourceFile::SOURCE_M) ||
-      target_->source_types_used().Get(SourceFile::SOURCE_MM)) {
+      target_->source_types_used().Get(SourceFile::SOURCE_MM) ||
+      target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
     WriteOneFlag(target_, &CSubstitutionCFlags, false, Tool::kToolNone,
                  &ConfigValues::cflags, opts, path_output_, out_);
   }
@@ -299,7 +295,8 @@
                  CTool::kCToolCc, &ConfigValues::cflags_c, opts, path_output_,
                  out_);
   }
-  if (target_->source_types_used().Get(SourceFile::SOURCE_CPP)) {
+  if (target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
+      target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
     WriteOneFlag(target_, &CSubstitutionCFlagsCc, has_precompiled_headers,
                  CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output_,
                  out_);
@@ -356,6 +353,30 @@
   WriteSharedVars(subst);
 }
 
+void NinjaCBinaryTargetWriter::WriteModuleDepsSubstitution(
+    const Substitution* substitution,
+    const std::vector<ModuleDep>& module_dep_info,
+    bool include_self) {
+  if (target_->toolchain()->substitution_bits().used.count(
+          substitution)) {
+    EscapeOptions options;
+    options.mode = ESCAPE_NINJA_COMMAND;
+
+    out_ << substitution->ninja_name << " = -Xclang ";
+    EscapeStringToStream(out_, "-fmodules-embed-all-files", options);
+
+    for (const auto& module_dep : module_dep_info) {
+      if (!module_dep.is_self || include_self) {
+        out_ << " ";
+        EscapeStringToStream(out_, "-fmodule-file=", options);
+        path_output_.WriteFile(out_, module_dep.pcm);
+      }
+    }
+
+    out_ << std::endl;
+  }
+}
+
 void NinjaCBinaryTargetWriter::WritePCHCommands(
     const std::vector<OutputFile>& input_deps,
     const std::vector<OutputFile>& order_only_deps,
diff --git a/src/gn/ninja_c_binary_target_writer.h b/src/gn/ninja_c_binary_target_writer.h
index c2e63e4..bbc71c5 100644
--- a/src/gn/ninja_c_binary_target_writer.h
+++ b/src/gn/ninja_c_binary_target_writer.h
@@ -29,6 +29,12 @@
   // Writes all flags for the compiler: includes, defines, cflags, etc.
   void WriteCompilerVars(const std::vector<ModuleDep>& module_dep_info);
 
+  // Write module_deps or module_deps_no_self flags for clang modulemaps.
+  void WriteModuleDepsSubstitution(
+      const Substitution* substitution,
+      const std::vector<ModuleDep>& module_dep_info,
+      bool include_self);
+
   // Writes build lines required for precompiled headers. Any generated
   // object files will be appended to the |object_files|. Any generated
   // non-object files (for instance, .gch files from a GCC toolchain, are
diff --git a/src/gn/ninja_c_binary_target_writer_unittest.cc b/src/gn/ninja_c_binary_target_writer_unittest.cc
index 542221a..6290301 100644
--- a/src/gn/ninja_c_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_c_binary_target_writer_unittest.cc
@@ -1724,8 +1724,9 @@
   std::unique_ptr<Tool> cxx_module_tool =
       Tool::CreateTool(CTool::kCToolCxxModule);
   TestWithScope::SetCommandForTool(
-      "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
-      "-fmodule-name={{label}} -c -x c++ -Xclang -emit-module -o {{output}}",
+      "c++ {{source}} {{cflags}} {{cflags_cc}} {{module_deps_no_self}} "
+      "{{defines}} {{include_dirs}} -fmodule-name={{label}} -c -x c++ "
+      "-Xclang -emit-module -o {{output}}",
       cxx_module_tool.get());
   cxx_module_tool->set_outputs(SubstitutionList::MakeForTest(
       "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.pcm"));
@@ -1774,7 +1775,8 @@
 
     const char expected[] = R"(defines =
 include_dirs =
-module_deps = -fmodules-embed-all-files -fmodule-file=//blah$:a=obj/blah/liba.a.pcm
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files
 cflags =
 cflags_cc =
 label = //blah$:a
@@ -1814,7 +1816,8 @@
 
     const char expected[] = R"(defines =
 include_dirs =
-module_deps = -fmodules-embed-all-files -fmodule-file=//stuff$:b=obj/stuff/libb.b.pcm
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/stuff/libb.b.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files
 cflags =
 cflags_cc =
 label = //stuff$:b
@@ -1835,6 +1838,45 @@
     EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
   }
 
+  Target target3(&module_settings, Label(SourceDir("//things/"), "c"));
+  target3.set_output_type(Target::STATIC_LIBRARY);
+  target3.visibility().SetPublic();
+  target3.sources().push_back(SourceFile("//stuff/c.modulemap"));
+  target3.source_types_used().Set(SourceFile::SOURCE_MODULEMAP);
+  target3.private_deps().push_back(LabelTargetPair(&target));
+  target3.SetToolchain(&module_toolchain);
+  ASSERT_TRUE(target3.OnResolved(&err));
+
+  // A third library that depends on one of the previous static libraries, to
+  // check module_deps_no_self.
+  {
+    std::ostringstream out;
+    NinjaCBinaryTargetWriter writer(&target3, out);
+    writer.Run();
+
+    const char expected[] = R"(defines =
+include_dirs =
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/stuff/libc.c.pcm -fmodule-file=obj/blah/liba.a.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm
+cflags =
+cflags_cc =
+label = //things$:c
+root_out_dir = withmodules
+target_out_dir = obj/things
+target_output_name = libc
+
+build obj/stuff/libc.c.pcm: cxx_module ../../stuff/c.modulemap | obj/blah/liba.a.pcm
+
+build obj/things/libc.a: alink || obj/blah/liba.a
+  arflags =
+  output_extension = 
+  output_dir = 
+)";
+
+    std::string out_str = out.str();
+    EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+  }
+
   Target depender(&module_settings, Label(SourceDir("//zap/"), "c"));
   depender.set_output_type(Target::EXECUTABLE);
   depender.sources().push_back(SourceFile("//zap/x.cc"));
@@ -1853,7 +1895,8 @@
 
     const char expected[] = R"(defines =
 include_dirs =
-module_deps = -fmodules-embed-all-files -fmodule-file=//blah$:a=obj/blah/liba.a.pcm -fmodule-file=//stuff$:b=obj/stuff/libb.b.pcm
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm -fmodule-file=obj/stuff/libb.b.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm -fmodule-file=obj/stuff/libb.b.pcm
 cflags =
 cflags_cc =
 label = //zap$:c