clang: Add support for C++ modulemap files
This is a first step to support Clang modules in C++ compilation. This
change adds .modulemap as a source file extension which will be compiled
by cxx_module to a .pcm.
The subsequent patch then uses a new "module_deps" entry to refer to
these modules.
Additional test reference here:
https://fuchsia-review.googlesource.com/c/fuchsia/+/412605
Change-Id: Ic42af141b11212249dc55911a42f89268537d59a
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/9601
Reviewed-by: Petr Hosek <phosek@google.com>
Reviewed-by: Brett Wilson <brettw@chromium.org>
Commit-Queue: Scott Graham <scottmg@chromium.org>
diff --git a/src/gn/binary_target_generator.cc b/src/gn/binary_target_generator.cc
index 9811a73..6c085e6 100644
--- a/src/gn/binary_target_generator.cc
+++ b/src/gn/binary_target_generator.cc
@@ -87,6 +87,7 @@
const auto& source = target_->sources()[i];
switch (source.type()) {
case SourceFile::SOURCE_CPP:
+ case SourceFile::SOURCE_MODULEMAP:
case SourceFile::SOURCE_H:
case SourceFile::SOURCE_C:
case SourceFile::SOURCE_M:
diff --git a/src/gn/c_tool.cc b/src/gn/c_tool.cc
index 7c06364..dc391d1 100644
--- a/src/gn/c_tool.cc
+++ b/src/gn/c_tool.cc
@@ -8,6 +8,7 @@
const char* CTool::kCToolCc = "cc";
const char* CTool::kCToolCxx = "cxx";
+const char* CTool::kCToolCxxModule = "cxx_module";
const char* CTool::kCToolObjC = "objc";
const char* CTool::kCToolObjCxx = "objcxx";
const char* CTool::kCToolRc = "rc";
@@ -38,9 +39,9 @@
}
bool CTool::ValidateName(const char* name) const {
- return name == kCToolCc || name == kCToolCxx || name == kCToolObjC ||
- name == kCToolObjCxx || name == kCToolRc || name == kCToolAsm ||
- name == kCToolAlink || name == kCToolSolink ||
+ return name == kCToolCc || name == kCToolCxx || name == kCToolCxxModule ||
+ name == kCToolObjC || name == kCToolObjCxx || name == kCToolRc ||
+ name == kCToolAsm || name == kCToolAlink || name == kCToolSolink ||
name == kCToolSolinkModule || name == kCToolLink;
}
@@ -217,8 +218,9 @@
}
bool CTool::ValidateSubstitution(const Substitution* sub_type) const {
- if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolObjC ||
- name_ == kCToolObjCxx || name_ == kCToolRc || name_ == kCToolAsm)
+ if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolCxxModule ||
+ name_ == kCToolObjC || name_ == kCToolObjCxx || name_ == kCToolRc ||
+ name_ == kCToolAsm)
return IsValidCompilerSubstitution(sub_type);
else if (name_ == kCToolAlink)
return IsValidALinkSubstitution(sub_type);
@@ -230,8 +232,9 @@
}
bool CTool::ValidateOutputSubstitution(const Substitution* sub_type) const {
- if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolObjC ||
- name_ == kCToolObjCxx || name_ == kCToolRc || name_ == kCToolAsm)
+ if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolCxxModule ||
+ name_ == kCToolObjC || name_ == kCToolObjCxx || name_ == kCToolRc ||
+ name_ == kCToolAsm)
return IsValidCompilerOutputsSubstitution(sub_type);
// ALink uses the standard output file patterns as other linker tools.
else if (name_ == kCToolAlink || name_ == kCToolSolink ||
diff --git a/src/gn/c_tool.h b/src/gn/c_tool.h
index 8c5282d..01b2431 100644
--- a/src/gn/c_tool.h
+++ b/src/gn/c_tool.h
@@ -22,6 +22,7 @@
// C compiler tools
static const char* kCToolCc;
static const char* kCToolCxx;
+ static const char* kCToolCxxModule;
static const char* kCToolObjC;
static const char* kCToolObjCxx;
static const char* kCToolRc;
diff --git a/src/gn/function_toolchain.cc b/src/gn/function_toolchain.cc
index ac63c2e..a6642d1 100644
--- a/src/gn/function_toolchain.cc
+++ b/src/gn/function_toolchain.cc
@@ -284,6 +284,7 @@
Compiler tools:
"cc": C compiler
"cxx": C++ compiler
+ "cxx_module": C++ compiler used for Clang .modulemap files
"objc": Objective C compiler
"objcxx": Objective C++ compiler
"rc": Resource compiler (Windows .rc files)
diff --git a/src/gn/ninja_c_binary_target_writer_unittest.cc b/src/gn/ninja_c_binary_target_writer_unittest.cc
index bc9907a..fbd6ff4 100644
--- a/src/gn/ninja_c_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_c_binary_target_writer_unittest.cc
@@ -1477,3 +1477,39 @@
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
}
+
+TEST_F(NinjaCBinaryTargetWriterTest, ModuleMapInStaticLibrary) {
+ TestWithScope setup;
+ Err err;
+
+ std::unique_ptr<Tool> cxx_module_tool =
+ Tool::CreateTool(CTool::kCToolCxxModule);
+ cxx_module_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.pcm"));
+ setup.toolchain()->SetTool(std::move(cxx_module_tool));
+
+ TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
+ target.sources().push_back(SourceFile("//foo/bar.modulemap"));
+ target.source_types_used().Set(SourceFile::SOURCE_MODULEMAP);
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "build obj/foo/libbar.bar.pcm: cxx_module ../../foo/bar.modulemap\n"
+ "\n"
+ "build obj/foo/libbar.a: alink obj/foo/libbar.bar.pcm\n"
+ " arflags =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
diff --git a/src/gn/source_file.cc b/src/gn/source_file.cc
index 1dacbfa..0b0e8e5 100644
--- a/src/gn/source_file.cc
+++ b/src/gn/source_file.cc
@@ -35,6 +35,8 @@
return SourceFile::SOURCE_M;
if (extension == "mm")
return SourceFile::SOURCE_MM;
+ if (extension == "modulemap")
+ return SourceFile::SOURCE_MODULEMAP;
if (extension == "rc")
return SourceFile::SOURCE_RC;
if (extension == "S" || extension == "s" || extension == "asm")
@@ -105,7 +107,8 @@
}
bool SourceFileTypeSet::CSourceUsed() const {
- return empty_ || Get(SourceFile::SOURCE_CPP) || Get(SourceFile::SOURCE_H) ||
+ return empty_ || Get(SourceFile::SOURCE_CPP) ||
+ Get(SourceFile::SOURCE_MODULEMAP) || Get(SourceFile::SOURCE_H) ||
Get(SourceFile::SOURCE_C) || Get(SourceFile::SOURCE_M) ||
Get(SourceFile::SOURCE_MM) || Get(SourceFile::SOURCE_RC) ||
Get(SourceFile::SOURCE_S) || Get(SourceFile::SOURCE_O) ||
diff --git a/src/gn/source_file.h b/src/gn/source_file.h
index d7ed06e..1e156fe 100644
--- a/src/gn/source_file.h
+++ b/src/gn/source_file.h
@@ -33,6 +33,7 @@
SOURCE_H,
SOURCE_M,
SOURCE_MM,
+ SOURCE_MODULEMAP,
SOURCE_S,
SOURCE_RC,
SOURCE_O, // Object files can be inputs, too. Also counts .obj.
diff --git a/src/gn/target_unittest.cc b/src/gn/target_unittest.cc
index 09d7a1a..99ce6b0 100644
--- a/src/gn/target_unittest.cc
+++ b/src/gn/target_unittest.cc
@@ -1492,3 +1492,33 @@
EXPECT_TRUE(data_dep_present.OnResolved(&err));
EXPECT_TRUE(scheduler().GetUnknownGeneratedInputs().empty());
}
+
+// Tests that modulemap files use the cxx_module tool.
+TEST_F(TargetTest, ModuleMap) {
+ TestWithScope setup;
+
+ Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));
+
+ std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolCxxModule);
+ CTool* cxx_module = tool->AsC();
+ cxx_module->set_outputs(
+ SubstitutionList::MakeForTest("{{source_file_part}}.pcm"));
+ toolchain.SetTool(std::move(tool));
+
+ Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.SetToolchain(&toolchain);
+ Err err;
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ const char* computed_tool_type = nullptr;
+ std::vector<OutputFile> output;
+ bool result = target.GetOutputFilesForSource(
+ SourceFile("//source/input.modulemap"), &computed_tool_type, &output);
+ ASSERT_TRUE(result);
+ EXPECT_EQ(std::string("cxx_module"), std::string(computed_tool_type));
+
+ // Outputs are relative to the build directory "//out/Debug/".
+ ASSERT_EQ(1u, output.size());
+ EXPECT_EQ("input.modulemap.pcm", output[0].value()) << output[0].value();
+}
diff --git a/src/gn/tool.cc b/src/gn/tool.cc
index 99b7525..9fc10ce 100644
--- a/src/gn/tool.cc
+++ b/src/gn/tool.cc
@@ -253,6 +253,8 @@
return std::make_unique<CTool>(CTool::kCToolCc);
else if (name == CTool::kCToolCxx)
return std::make_unique<CTool>(CTool::kCToolCxx);
+ else if (name == CTool::kCToolCxxModule)
+ return std::make_unique<CTool>(CTool::kCToolCxxModule);
else if (name == CTool::kCToolObjC)
return std::make_unique<CTool>(CTool::kCToolObjC);
else if (name == CTool::kCToolObjCxx)
@@ -308,6 +310,8 @@
return CTool::kCToolCc;
case SourceFile::SOURCE_CPP:
return CTool::kCToolCxx;
+ case SourceFile::SOURCE_MODULEMAP:
+ return CTool::kCToolCxxModule;
case SourceFile::SOURCE_M:
return CTool::kCToolObjC;
case SourceFile::SOURCE_MM: