Make analyze handle targets from all toolchains

This is necessary for Fuchsia to correctly detect the need to run host
tests, which use a non-default toolchain.

Test: cases added to analyzer_unittest.cc
Bug: fuchsia:53604
Bug: gn:178
Bug: chromium:667989
Change-Id: I3e944be6fffda811ca65438f3861df19c4aef409
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/9060
Reviewed-by: Dirk Pranke <dpranke@google.com>
Commit-Queue: Scott Graham <scottmg@chromium.org>
diff --git a/src/gn/analyzer.cc b/src/gn/analyzer.cc
index b3db365..47b0a6d 100644
--- a/src/gn/analyzer.cc
+++ b/src/gn/analyzer.cc
@@ -299,11 +299,7 @@
       GetAllAffectedItems(inputs.source_files);
   std::set<const Target*> affected_targets;
   for (const Item* affected_item : affected_items) {
-    // Only handles targets in the default toolchain.
-    // TODO(crbug.com/667989): Expand analyzer to non-default toolchains when
-    // the bug is fixed.
-    if (affected_item->AsTarget() &&
-        affected_item->label().GetToolchainLabel() == default_toolchain_)
+    if (affected_item->AsTarget())
       affected_targets.insert(affected_item->AsTarget());
   }
 
diff --git a/src/gn/analyzer_unittest.cc b/src/gn/analyzer_unittest.cc
index d2e89a9..9f7d897 100644
--- a/src/gn/analyzer_unittest.cc
+++ b/src/gn/analyzer_unittest.cc
@@ -48,12 +48,20 @@
   AnalyzerTest()
       : loader_(new MockLoader),
         builder_(loader_.get()),
-        settings_(&build_settings_, std::string()) {
+        settings_(&build_settings_, std::string()),
+        other_settings_(&build_settings_, std::string()) {
     build_settings_.SetBuildDir(SourceDir("//out/"));
+
     settings_.set_toolchain_label(Label(SourceDir("//tc/"), "default"));
     settings_.set_default_toolchain_label(settings_.toolchain_label());
     tc_dir_ = settings_.toolchain_label().dir();
     tc_name_ = settings_.toolchain_label().name();
+
+    other_settings_.set_toolchain_label(Label(SourceDir("//other/"), "tc"));
+    other_settings_.set_default_toolchain_label(
+        other_settings_.toolchain_label());
+    tc_other_dir_ = other_settings_.toolchain_label().dir();
+    tc_other_name_ = other_settings_.toolchain_label().name();
   }
 
   std::unique_ptr<Target> MakeTarget(const std::string& dir,
@@ -62,6 +70,12 @@
     return std::make_unique<Target>(&settings_, label);
   }
 
+  std::unique_ptr<Target> MakeTargetOtherToolchain(const std::string& dir,
+                                                   const std::string& name) {
+    Label label(SourceDir(dir), name, tc_other_dir_, tc_other_name_);
+    return std::make_unique<Target>(&other_settings_, label);
+  }
+
   std::unique_ptr<Config> MakeConfig(const std::string& dir,
                                      const std::string& name) {
     Label label(SourceDir(dir), name, tc_dir_, tc_name_);
@@ -90,9 +104,14 @@
   scoped_refptr<MockLoader> loader_;
   Builder builder_;
   BuildSettings build_settings_;
+
   Settings settings_;
   SourceDir tc_dir_;
   std::string tc_name_;
+
+  Settings other_settings_;
+  SourceDir tc_other_dir_;
+  std::string tc_other_name_;
 };
 
 // Tests that a target is marked as affected if its sources are modified.
@@ -601,4 +620,102 @@
       "}");
 }
 
+// Tests that targets in explicitly labelled with the default toolchain are
+// included when their sources change.
+// change.
+TEST_F(AnalyzerTest, TargetToolchainSpecifiedRefersToSources) {
+  std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+  Target* t_raw = t.get();
+  builder_.ItemDefined(std::move(t));
+
+  RunAnalyzerTest(
+      R"/({
+       "files": [ "//dir/file_name.cc" ],
+       "additional_compile_targets": ["all"],
+       "test_targets": [ "//dir:target_name(//tc:default)" ]
+       })/",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"No dependency",)/"
+      R"("test_targets":[])"
+      "}");
+
+  t_raw->sources().push_back(SourceFile("//dir/file_name.cc"));
+
+  RunAnalyzerTest(
+      R"*({
+       "files": [ "//dir/file_name.cc" ],
+       "additional_compile_targets": [],
+       "test_targets": [ "//dir:target_name(//tc:default)" ]
+       })*",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"Found dependency",)/"
+      R"/("test_targets":["//dir:target_name"])/"
+      "}");
+}
+
+// Tests that targets in alternate toolchains are affected when their sources
+// change.
+TEST_F(AnalyzerTest, TargetAlternateToolchainRefersToSources) {
+  std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
+  std::unique_ptr<Target> t_alt =
+      MakeTargetOtherToolchain("//dir", "target_name");
+  Target* t_raw = t.get();
+  Target* t_alt_raw = t_alt.get();
+  builder_.ItemDefined(std::move(t));
+  builder_.ItemDefined(std::move(t_alt));
+
+  RunAnalyzerTest(
+      R"/({
+       "files": [ "//dir/file_name.cc" ],
+       "additional_compile_targets": ["all"],
+       "test_targets": [ "//dir:target_name", "//dir:target_name(//other:tc)" ]
+       })/",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"No dependency",)/"
+      R"("test_targets":[])"
+      "}");
+
+  t_raw->sources().push_back(SourceFile("//dir/file_name.cc"));
+  t_alt_raw->sources().push_back(SourceFile("//dir/alt_file_name.cc"));
+
+  RunAnalyzerTest(
+      R"*({
+       "files": [ "//dir/file_name.cc" ],
+       "additional_compile_targets": [],
+       "test_targets": [ "//dir:target_name", "//dir:target_name(//other:tc)" ]
+       })*",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"Found dependency",)/"
+      R"("test_targets":["//dir:target_name"])"
+      "}");
+
+  RunAnalyzerTest(
+      R"*({
+       "files": [ "//dir/alt_file_name.cc" ],
+       "additional_compile_targets": [],
+       "test_targets": [ "//dir:target_name", "//dir:target_name(//other:tc)" ]
+       })*",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"Found dependency",)/"
+      R"/("test_targets":["//dir:target_name(//other:tc)"])/"
+      "}");
+
+  RunAnalyzerTest(
+      R"*({
+       "files": [ "//dir/file_name.cc", "//dir/alt_file_name.cc" ],
+       "additional_compile_targets": [],
+       "test_targets": [ "//dir:target_name", "//dir:target_name(//other:tc)" ]
+       })*",
+      "{"
+      R"("compile_targets":[],)"
+      R"/("status":"Found dependency",)/"
+      R"/("test_targets":["//dir:target_name","//dir:target_name(//other:tc)"])/"
+      "}");
+}
+
 }  // namespace gn_analyzer_unittest
diff --git a/src/gn/command_analyze.cc b/src/gn/command_analyze.cc
index 3000b34..5119b14 100644
--- a/src/gn/command_analyze.cc
+++ b/src/gn/command_analyze.cc
@@ -23,7 +23,7 @@
 const char kAnalyze_HelpShort[] =
     "analyze: Analyze which targets are affected by a list of files.";
 const char kAnalyze_Help[] =
-    R"DOC(gn analyze <out_dir> <input_path> <output_path>
+    R"*(gn analyze <out_dir> <input_path> <output_path>
 
   Analyze which targets are affected by a list of files.
 
@@ -95,7 +95,7 @@
   "error" key is non-empty and a non-fatal error occurred. In other words, it
   tries really hard to always write something to the output JSON and convey
   errors that way rather than via return codes.
-)DOC";
+)*";
 
 int RunAnalyze(const std::vector<std::string>& args) {
   if (args.size() != 3) {