Add support for module-definition files to gn

BUG=

Review URL: https://codereview.chromium.org/1160773002

Cr-Original-Commit-Position: refs/heads/master@{#332355}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: b73b2de6a23e3220c79c6bb9e9f3a7b4dd20d2dd
diff --git a/tools/gn/ninja_binary_target_writer.cc b/tools/gn/ninja_binary_target_writer.cc
index 88a6eb2..7a611db 100644
--- a/tools/gn/ninja_binary_target_writer.cc
+++ b/tools/gn/ninja_binary_target_writer.cc
@@ -80,12 +80,13 @@
   WriteCompilerVars();
 
   std::vector<OutputFile> obj_files;
-  WriteSources(&obj_files);
+  std::vector<SourceFile> other_files;
+  WriteSources(&obj_files, &other_files);
 
   if (target_->output_type() == Target::SOURCE_SET)
     WriteSourceSetStamp(obj_files);
   else
-    WriteLinkerStuff(obj_files);
+    WriteLinkerStuff(obj_files, other_files);
 }
 
 void NinjaBinaryTargetWriter::WriteCompilerVars() {
@@ -134,7 +135,8 @@
 }
 
 void NinjaBinaryTargetWriter::WriteSources(
-    std::vector<OutputFile>* object_files) {
+    std::vector<OutputFile>* object_files,
+    std::vector<SourceFile>* other_files) {
   object_files->reserve(target_->sources().size());
 
   OutputFile input_dep =
@@ -145,9 +147,11 @@
   std::vector<OutputFile> tool_outputs;  // Prevent reallocation in loop.
   for (const auto& source : target_->sources()) {
     Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
-    if (!GetOutputFilesForSource(target_, source,
-                                 &tool_type, &tool_outputs))
+    if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) {
+      if (GetSourceFileType(source) == SOURCE_DEF)
+        other_files->push_back(source);
       continue;  // No output for this source.
+    }
 
     if (tool_type != Toolchain::TYPE_NONE) {
       out_ << "build";
@@ -195,7 +199,8 @@
 }
 
 void NinjaBinaryTargetWriter::WriteLinkerStuff(
-    const std::vector<OutputFile>& object_files) {
+    const std::vector<OutputFile>& object_files,
+    const std::vector<SourceFile>& other_files) {
   std::vector<OutputFile> output_files;
   SubstitutionWriter::ApplyListToLinkerAsOutputFile(
       target_, tool_, tool_->outputs(), &output_files);
@@ -245,6 +250,18 @@
     }
   }
 
+  const SourceFile* optional_def_file = nullptr;
+  if (!other_files.empty()) {
+    for (const SourceFile& src_file : other_files) {
+      if (GetSourceFileType(src_file) == SOURCE_DEF) {
+        optional_def_file = &src_file;
+        implicit_deps.push_back(
+            OutputFile(settings_->build_settings(), src_file));
+        break;  // Only one def file is allowed.
+      }
+    }
+  }
+
   // Append implicit dependencies collected above.
   if (!implicit_deps.empty()) {
     out_ << " |";
@@ -270,13 +287,15 @@
   out_ << std::endl;
 
   // These go in the inner scope of the link line.
-  WriteLinkerFlags();
+  WriteLinkerFlags(optional_def_file);
+
   WriteLibs();
   WriteOutputExtension();
   WriteSolibs(solibs);
 }
 
-void NinjaBinaryTargetWriter::WriteLinkerFlags() {
+void NinjaBinaryTargetWriter::WriteLinkerFlags(
+    const SourceFile* optional_def_file) {
   out_ << "  ldflags =";
 
   // First the ldflags from the target and its config.
@@ -299,6 +318,12 @@
                                PathOutput::DIR_NO_LAST_SLASH);
     }
   }
+
+  if (optional_def_file) {
+    out_ << " /DEF:";
+    path_output_.WriteFile(out_, *optional_def_file);
+  }
+
   out_ << std::endl;
 }
 
diff --git a/tools/gn/ninja_binary_target_writer.h b/tools/gn/ninja_binary_target_writer.h
index 22d2332..977e982 100644
--- a/tools/gn/ninja_binary_target_writer.h
+++ b/tools/gn/ninja_binary_target_writer.h
@@ -26,9 +26,11 @@
   typedef std::set<OutputFile> OutputFileSet;
 
   void WriteCompilerVars();
-  void WriteSources(std::vector<OutputFile>* object_files);
-  void WriteLinkerStuff(const std::vector<OutputFile>& object_files);
-  void WriteLinkerFlags();
+  void WriteSources(std::vector<OutputFile>* object_files,
+                    std::vector<SourceFile>* other_files);
+  void WriteLinkerStuff(const std::vector<OutputFile>& object_files,
+                        const std::vector<SourceFile>& other_files);
+  void WriteLinkerFlags(const SourceFile* optional_def_file);
   void WriteLibs();
   void WriteOutputExtension();
   void WriteSolibs(const std::vector<OutputFile>& solibs);
diff --git a/tools/gn/ninja_binary_target_writer_unittest.cc b/tools/gn/ninja_binary_target_writer_unittest.cc
index df5f5a7..f4e4065 100644
--- a/tools/gn/ninja_binary_target_writer_unittest.cc
+++ b/tools/gn/ninja_binary_target_writer_unittest.cc
@@ -357,3 +357,42 @@
       "  output_extension = \n";
   EXPECT_EQ(final_expected, final_out.str());
 }
+
+TEST(NinjaBinaryTargetWriter, SharedLibraryModuleDefinitionFile) {
+  TestWithScope setup;
+  setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
+  setup.settings()->set_target_os(Settings::WIN);
+
+  Target shared_lib(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+  shared_lib.set_output_type(Target::SHARED_LIBRARY);
+  shared_lib.SetToolchain(setup.toolchain());
+  shared_lib.sources().push_back(SourceFile("//foo/sources.cc"));
+  shared_lib.sources().push_back(SourceFile("//foo/bar.def"));
+
+  Err err;
+  ASSERT_TRUE(shared_lib.OnResolved(&err));
+
+  std::ostringstream out;
+  NinjaBinaryTargetWriter writer(&shared_lib, out);
+  writer.Run();
+
+  const char expected[] =
+      "defines =\n"
+      "include_dirs =\n"
+      "cflags =\n"
+      "cflags_c =\n"
+      "cflags_cc =\n"
+      "cflags_objc =\n"
+      "cflags_objcc =\n"
+      "root_out_dir = .\n"
+      "target_out_dir = obj/foo\n"
+      "target_output_name = libbar\n"
+      "\n"
+      "build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
+      "\n"
+      "build ./libbar.so: solink obj/foo/libbar.sources.o | ../../foo/bar.def\n"
+      "  ldflags = /DEF:../../foo/bar.def\n"
+      "  libs =\n"
+      "  output_extension = .so\n";
+  EXPECT_EQ(expected, out.str());
+}
diff --git a/tools/gn/source_file_type.cc b/tools/gn/source_file_type.cc
index 35c8709..2b34125 100644
--- a/tools/gn/source_file_type.cc
+++ b/tools/gn/source_file_type.cc
@@ -25,6 +25,8 @@
     return SOURCE_S;
   if (extension == "o" || extension == "obj")
     return SOURCE_O;
+  if (extension == "def")
+    return SOURCE_DEF;
 
   return SOURCE_UNKNOWN;
 }
diff --git a/tools/gn/source_file_type.h b/tools/gn/source_file_type.h
index f8d9a09..f738daa 100644
--- a/tools/gn/source_file_type.h
+++ b/tools/gn/source_file_type.h
@@ -18,6 +18,7 @@
   SOURCE_S,
   SOURCE_RC,
   SOURCE_O,  // Object files can be inputs, too. Also counts .obj.
+  SOURCE_DEF,
 };
 
 SourceFileType GetSourceFileType(const SourceFile& file);
diff --git a/tools/gn/toolchain.cc b/tools/gn/toolchain.cc
index dbc1173..8ec2c2d 100644
--- a/tools/gn/toolchain.cc
+++ b/tools/gn/toolchain.cc
@@ -116,6 +116,7 @@
     case SOURCE_UNKNOWN:
     case SOURCE_H:
     case SOURCE_O:
+    case SOURCE_DEF:
       return TYPE_NONE;
     default:
       NOTREACHED();