1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "gn/commands.h"
6 
7 #include <optional>
8 
9 #include "base/command_line.h"
10 #include "base/environment.h"
11 #include "base/strings/string_split.h"
12 #include "base/strings/string_util.h"
13 #include "base/values.h"
14 #include "gn/builder.h"
15 #include "gn/config_values_extractors.h"
16 #include "gn/filesystem_utils.h"
17 #include "gn/item.h"
18 #include "gn/label.h"
19 #include "gn/label_pattern.h"
20 #include "gn/setup.h"
21 #include "gn/standard_out.h"
22 #include "gn/target.h"
23 #include "util/build_config.h"
24 
25 namespace commands {
26 
27 namespace {
28 
29 // Like above but the input string can be a pattern that matches multiple
30 // targets. If the input does not parse as a pattern, prints and error and
31 // returns false. If the pattern is valid, fills the vector (which might be
32 // empty if there are no matches) and returns true.
33 //
34 // If default_toolchain_only is true, a pattern with an unspecified toolchain
35 // will match the default toolchain only. If true, all toolchains will be
36 // matched.
ResolveTargetsFromCommandLinePattern(Setup * setup,const std::string & label_pattern,bool default_toolchain_only,std::vector<const Target * > * matches)37 bool ResolveTargetsFromCommandLinePattern(Setup* setup,
38                                           const std::string& label_pattern,
39                                           bool default_toolchain_only,
40                                           std::vector<const Target*>* matches) {
41   Value pattern_value(nullptr, label_pattern);
42 
43   Err err;
44   LabelPattern pattern = LabelPattern::GetPattern(
45       SourceDirForCurrentDirectory(setup->build_settings().root_path()),
46       setup->build_settings().root_path_utf8(), pattern_value, &err);
47   if (err.has_error()) {
48     err.PrintToStdout();
49     return false;
50   }
51 
52   if (default_toolchain_only) {
53     // By default a pattern with an empty toolchain will match all toolchains.
54     // If the caller wants to default to the main toolchain only, set it
55     // explicitly.
56     if (pattern.toolchain().is_null()) {
57       // No explicit toolchain set.
58       pattern.set_toolchain(setup->loader()->default_toolchain_label());
59     }
60   }
61 
62   std::vector<LabelPattern> pattern_vector;
63   pattern_vector.push_back(pattern);
64   FilterTargetsByPatterns(setup->builder().GetAllResolvedTargets(),
65                           pattern_vector, matches);
66   return true;
67 }
68 
69 // If there's an error, it will be printed and false will be returned.
ResolveStringFromCommandLineInput(Setup * setup,const SourceDir & current_dir,const std::string & input,bool default_toolchain_only,UniqueVector<const Target * > * target_matches,UniqueVector<const Config * > * config_matches,UniqueVector<const Toolchain * > * toolchain_matches,UniqueVector<SourceFile> * file_matches)70 bool ResolveStringFromCommandLineInput(
71     Setup* setup,
72     const SourceDir& current_dir,
73     const std::string& input,
74     bool default_toolchain_only,
75     UniqueVector<const Target*>* target_matches,
76     UniqueVector<const Config*>* config_matches,
77     UniqueVector<const Toolchain*>* toolchain_matches,
78     UniqueVector<SourceFile>* file_matches) {
79   if (LabelPattern::HasWildcard(input)) {
80     // For now, only match patterns against targets. It might be nice in the
81     // future to allow the user to specify which types of things they want to
82     // match, but it should probably only match targets by default.
83     std::vector<const Target*> target_match_vector;
84     if (!ResolveTargetsFromCommandLinePattern(
85             setup, input, default_toolchain_only, &target_match_vector))
86       return false;
87     for (const Target* target : target_match_vector)
88       target_matches->push_back(target);
89     return true;
90   }
91 
92   // Try to figure out what this thing is.
93   Err err;
94   Label label = Label::Resolve(
95       current_dir, setup->build_settings().root_path_utf8(),
96       setup->loader()->default_toolchain_label(), Value(nullptr, input), &err);
97   if (err.has_error()) {
98     // Not a valid label, assume this must be a file.
99     err = Err();
100     file_matches->push_back(current_dir.ResolveRelativeFile(
101         Value(nullptr, input), &err, setup->build_settings().root_path_utf8()));
102     if (err.has_error()) {
103       err.PrintToStdout();
104       return false;
105     }
106     return true;
107   }
108 
109   const Item* item = setup->builder().GetItem(label);
110   if (item) {
111     if (const Config* as_config = item->AsConfig())
112       config_matches->push_back(as_config);
113     else if (const Target* as_target = item->AsTarget())
114       target_matches->push_back(as_target);
115     else if (const Toolchain* as_toolchain = item->AsToolchain())
116       toolchain_matches->push_back(as_toolchain);
117   } else {
118     // Not an item, assume this must be a file.
119     file_matches->push_back(current_dir.ResolveRelativeFile(
120         Value(nullptr, input), &err, setup->build_settings().root_path_utf8()));
121     if (err.has_error()) {
122       err.PrintToStdout();
123       return false;
124     }
125   }
126 
127   return true;
128 }
129 
130 enum TargetPrintingMode {
131   TARGET_PRINT_BUILDFILE,
132   TARGET_PRINT_LABEL,
133   TARGET_PRINT_OUTPUT,
134 };
135 
136 // Retrieves the target printing mode based on the command line flags for the
137 // current process. Returns true on success. On error, prints a message to the
138 // console and returns false.
GetTargetPrintingMode(TargetPrintingMode * mode)139 bool GetTargetPrintingMode(TargetPrintingMode* mode) {
140   std::string switch_key = "as";
141   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
142 
143   if (!cmdline->HasSwitch(switch_key)) {
144     // Default to labels.
145     *mode = TARGET_PRINT_LABEL;
146     return true;
147   }
148 
149   std::string value = cmdline->GetSwitchValueASCII(switch_key);
150   if (value == "buildfile") {
151     *mode = TARGET_PRINT_BUILDFILE;
152     return true;
153   }
154   if (value == "label") {
155     *mode = TARGET_PRINT_LABEL;
156     return true;
157   }
158   if (value == "output") {
159     *mode = TARGET_PRINT_OUTPUT;
160     return true;
161   }
162 
163   Err(Location(), "Invalid value for \"--as\".",
164       "I was expecting \"buildfile\", \"label\", or \"output\" but you\n"
165       "said \"" +
166           value + "\".")
167       .PrintToStdout();
168   return false;
169 }
170 
171 // Returns the target type filter based on the command line flags for the
172 // current process. Returns true on success. On error, prints a message to the
173 // console and returns false.
174 //
175 // Target::UNKNOWN will be set if there is no filter. Target::ACTION_FOREACH
176 // will never be returned. Code applying the filters should apply Target::ACTION
177 // to both ACTION and ACTION_FOREACH.
GetTargetTypeFilter(Target::OutputType * type)178 bool GetTargetTypeFilter(Target::OutputType* type) {
179   std::string switch_key = "type";
180   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
181 
182   if (!cmdline->HasSwitch(switch_key)) {
183     // Default to unknown -> no filtering.
184     *type = Target::UNKNOWN;
185     return true;
186   }
187 
188   std::string value = cmdline->GetSwitchValueASCII(switch_key);
189   if (value == "group") {
190     *type = Target::GROUP;
191     return true;
192   }
193   if (value == "executable") {
194     *type = Target::EXECUTABLE;
195     return true;
196   }
197   if (value == "shared_library") {
198     *type = Target::SHARED_LIBRARY;
199     return true;
200   }
201   if (value == "loadable_module") {
202     *type = Target::LOADABLE_MODULE;
203     return true;
204   }
205   if (value == "static_library") {
206     *type = Target::STATIC_LIBRARY;
207     return true;
208   }
209   if (value == "source_set") {
210     *type = Target::SOURCE_SET;
211     return true;
212   }
213   if (value == "copy") {
214     *type = Target::COPY_FILES;
215     return true;
216   }
217   if (value == "action") {
218     *type = Target::ACTION;
219     return true;
220   }
221 
222   Err(Location(), "Invalid value for \"--type\".").PrintToStdout();
223   return false;
224 }
225 
226 // Applies any testonly filtering specified on the command line to the given
227 // target set. On failure, prints an error and returns false.
ApplyTestonlyFilter(std::vector<const Target * > * targets)228 bool ApplyTestonlyFilter(std::vector<const Target*>* targets) {
229   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
230   std::string testonly_key = "testonly";
231 
232   if (targets->empty() || !cmdline->HasSwitch(testonly_key))
233     return true;
234 
235   std::string testonly_value = cmdline->GetSwitchValueASCII(testonly_key);
236   bool testonly = false;
237   if (testonly_value == "true") {
238     testonly = true;
239   } else if (testonly_value != "false") {
240     Err(Location(), "Bad value for --testonly.",
241         "I was expecting --testonly=true or --testonly=false.")
242         .PrintToStdout();
243     return false;
244   }
245 
246   // Filter into a copy of the vector, then replace the output.
247   std::vector<const Target*> result;
248   result.reserve(targets->size());
249 
250   for (const Target* target : *targets) {
251     if (target->testonly() == testonly)
252       result.push_back(target);
253   }
254 
255   *targets = std::move(result);
256   return true;
257 }
258 
259 // Applies any target type filtering specified on the command line to the given
260 // target set. On failure, prints an error and returns false.
ApplyTypeFilter(std::vector<const Target * > * targets)261 bool ApplyTypeFilter(std::vector<const Target*>* targets) {
262   Target::OutputType type = Target::UNKNOWN;
263   if (!GetTargetTypeFilter(&type))
264     return false;
265   if (targets->empty() || type == Target::UNKNOWN)
266     return true;  // Nothing to filter out.
267 
268   // Filter into a copy of the vector, then replace the output.
269   std::vector<const Target*> result;
270   result.reserve(targets->size());
271 
272   for (const Target* target : *targets) {
273     // Make "action" also apply to ACTION_FOREACH.
274     if (target->output_type() == type ||
275         (type == Target::ACTION &&
276          target->output_type() == Target::ACTION_FOREACH))
277       result.push_back(target);
278   }
279 
280   *targets = std::move(result);
281   return true;
282 }
283 
284 // Returns the file path generating this item.
BuildFileForItem(const Item * item)285 base::FilePath BuildFileForItem(const Item* item) {
286   return item->defined_from()->GetRange().begin().file()->physical_name();
287 }
288 
PrintTargetsAsBuildfiles(const std::vector<const Target * > & targets,base::ListValue * out)289 void PrintTargetsAsBuildfiles(const std::vector<const Target*>& targets,
290                               base::ListValue* out) {
291   // Output the set of unique source files.
292   std::set<std::string> unique_files;
293   for (const Target* target : targets)
294     unique_files.insert(FilePathToUTF8(BuildFileForItem(target)));
295 
296   for (const std::string& file : unique_files) {
297     out->AppendString(file);
298   }
299 }
300 
PrintTargetsAsLabels(const std::vector<const Target * > & targets,base::ListValue * out)301 void PrintTargetsAsLabels(const std::vector<const Target*>& targets,
302                           base::ListValue* out) {
303   // Putting the labels into a set automatically sorts them for us.
304   std::set<Label> unique_labels;
305   for (auto* target : targets)
306     unique_labels.insert(target->label());
307 
308   // Grab the label of the default toolchain from the first target.
309   Label default_tc_label = targets[0]->settings()->default_toolchain_label();
310 
311   for (const Label& label : unique_labels) {
312     // Print toolchain only for ones not in the default toolchain.
313     out->AppendString(label.GetUserVisibleName(label.GetToolchainLabel() !=
314                                                default_tc_label));
315   }
316 }
317 
PrintTargetsAsOutputs(const std::vector<const Target * > & targets,base::ListValue * out)318 void PrintTargetsAsOutputs(const std::vector<const Target*>& targets,
319                            base::ListValue* out) {
320   if (targets.empty())
321     return;
322 
323   // Grab the build settings from a random target.
324   const BuildSettings* build_settings =
325       targets[0]->settings()->build_settings();
326 
327   for (const Target* target : targets) {
328     // Use the link output file if there is one, otherwise fall back to the
329     // dependency output file (for actions, for example).
330     OutputFile output_file = target->link_output_file();
331     if (output_file.value().empty())
332       output_file = target->dependency_output_file();
333 
334     SourceFile output_as_source = output_file.AsSourceFile(build_settings);
335     std::string result =
336         RebasePath(output_as_source.value(), build_settings->build_dir(),
337                    build_settings->root_path_utf8());
338     out->AppendString(result);
339   }
340 }
341 
342 #if defined(OS_WIN)
343 // Git bash will remove the first "/" in "//" paths
344 // This also happens for labels assigned to command line parameters, e.g.
345 // --filters
346 // Fix "//" paths, but not absolute and relative paths
FixGitBashLabelEdit(const std::string & label)347 inline std::string FixGitBashLabelEdit(const std::string& label) {
348   static std::unique_ptr<base::Environment> git_bash_env;
349   if (!git_bash_env)
350     git_bash_env = base::Environment::Create();
351 
352   std::string temp_label(label);
353 
354   if (git_bash_env->HasVar(
355           "MSYSTEM") &&        // Only for MinGW based shells like Git Bash
356       temp_label[0] == '/' &&  // Only fix for //foo paths, not /f:oo paths
357       (temp_label.length() < 2 ||
358        (temp_label[1] != '/' &&
359         (temp_label.length() < 3 || temp_label[1] != ':'))))
360     temp_label.insert(0, "/");
361   return temp_label;
362 }
363 #else
364 // Only repair on Windows
FixGitBashLabelEdit(const std::string & label)365 inline std::string FixGitBashLabelEdit(const std::string& label) {
366   return label;
367 }
368 #endif
369 
TargetContainsFile(const Target * target,const SourceFile & file)370 std::optional<HowTargetContainsFile> TargetContainsFile(
371     const Target* target,
372     const SourceFile& file) {
373   for (const auto& cur_file : target->sources()) {
374     if (cur_file == file)
375       return HowTargetContainsFile::kSources;
376   }
377   for (const auto& cur_file : target->public_headers()) {
378     if (cur_file == file)
379       return HowTargetContainsFile::kPublic;
380   }
381   for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
382     for (const auto& cur_file : iter.cur().inputs()) {
383       if (cur_file == file)
384         return HowTargetContainsFile::kInputs;
385     }
386   }
387   for (const auto& cur_file : target->data()) {
388     if (cur_file == file.value())
389       return HowTargetContainsFile::kData;
390     if (cur_file.back() == '/' &&
391         base::StartsWith(file.value(), cur_file, base::CompareCase::SENSITIVE))
392       return HowTargetContainsFile::kData;
393   }
394 
395   if (target->action_values().script().value() == file.value())
396     return HowTargetContainsFile::kScript;
397 
398   std::vector<SourceFile> output_sources;
399   target->action_values().GetOutputsAsSourceFiles(target, &output_sources);
400   for (const auto& cur_file : output_sources) {
401     if (cur_file == file)
402       return HowTargetContainsFile::kOutput;
403   }
404 
405   for (const auto& cur_file : target->computed_outputs()) {
406     if (cur_file.AsSourceFile(target->settings()->build_settings()) == file)
407       return HowTargetContainsFile::kOutput;
408   }
409   return std::nullopt;
410 }
411 
412 }  // namespace
413 
CommandInfo()414 CommandInfo::CommandInfo()
415     : help_short(nullptr), help(nullptr), runner(nullptr) {}
416 
CommandInfo(const char * in_help_short,const char * in_help,CommandRunner in_runner)417 CommandInfo::CommandInfo(const char* in_help_short,
418                          const char* in_help,
419                          CommandRunner in_runner)
420     : help_short(in_help_short), help(in_help), runner(in_runner) {}
421 
GetCommands()422 const CommandInfoMap& GetCommands() {
423   static CommandInfoMap info_map;
424   if (info_map.empty()) {
425 #define INSERT_COMMAND(cmd) \
426   info_map[k##cmd] = CommandInfo(k##cmd##_HelpShort, k##cmd##_Help, &Run##cmd);
427 
428     INSERT_COMMAND(Analyze)
429     INSERT_COMMAND(Args)
430     INSERT_COMMAND(Check)
431     INSERT_COMMAND(Clean)
432     INSERT_COMMAND(Desc)
433     INSERT_COMMAND(Gen)
434     INSERT_COMMAND(Format)
435     INSERT_COMMAND(Help)
436     INSERT_COMMAND(Meta)
437     INSERT_COMMAND(Ls)
438     INSERT_COMMAND(Outputs)
439     INSERT_COMMAND(Path)
440     INSERT_COMMAND(Refs)
441 
442 #undef INSERT_COMMAND
443   }
444   return info_map;
445 }
446 
ResolveTargetFromCommandLineString(Setup * setup,const std::string & label_string)447 const Target* ResolveTargetFromCommandLineString(
448     Setup* setup,
449     const std::string& label_string) {
450   // Need to resolve the label after we know the default toolchain.
451   Label default_toolchain = setup->loader()->default_toolchain_label();
452   Value arg_value(nullptr, FixGitBashLabelEdit(label_string));
453   Err err;
454   Label label = Label::Resolve(
455       SourceDirForCurrentDirectory(setup->build_settings().root_path()),
456       setup->build_settings().root_path_utf8(), default_toolchain, arg_value,
457       &err);
458   if (err.has_error()) {
459     err.PrintToStdout();
460     return nullptr;
461   }
462 
463   const Item* item = setup->builder().GetItem(label);
464   if (!item) {
465     Err(Location(), "Label not found.",
466         label.GetUserVisibleName(false) + " not found.")
467         .PrintToStdout();
468     return nullptr;
469   }
470 
471   const Target* target = item->AsTarget();
472   if (!target) {
473     Err(Location(), "Not a target.",
474         "The \"" + label.GetUserVisibleName(false) +
475             "\" thing\n"
476             "is not a target. Somebody should probably implement this command "
477             "for "
478             "other\nitem types.")
479         .PrintToStdout();
480     return nullptr;
481   }
482 
483   return target;
484 }
485 
ResolveFromCommandLineInput(Setup * setup,const std::vector<std::string> & input,bool default_toolchain_only,UniqueVector<const Target * > * target_matches,UniqueVector<const Config * > * config_matches,UniqueVector<const Toolchain * > * toolchain_matches,UniqueVector<SourceFile> * file_matches)486 bool ResolveFromCommandLineInput(
487     Setup* setup,
488     const std::vector<std::string>& input,
489     bool default_toolchain_only,
490     UniqueVector<const Target*>* target_matches,
491     UniqueVector<const Config*>* config_matches,
492     UniqueVector<const Toolchain*>* toolchain_matches,
493     UniqueVector<SourceFile>* file_matches) {
494   if (input.empty()) {
495     Err(Location(), "You need to specify a label, file, or pattern.")
496         .PrintToStdout();
497     return false;
498   }
499 
500   SourceDir cur_dir =
501       SourceDirForCurrentDirectory(setup->build_settings().root_path());
502   for (const auto& cur : input) {
503     if (!ResolveStringFromCommandLineInput(
504             setup, cur_dir, cur, default_toolchain_only, target_matches,
505             config_matches, toolchain_matches, file_matches))
506       return false;
507   }
508   return true;
509 }
510 
FilterTargetsByPatterns(const std::vector<const Target * > & input,const std::vector<LabelPattern> & filter,std::vector<const Target * > * output)511 void FilterTargetsByPatterns(const std::vector<const Target*>& input,
512                              const std::vector<LabelPattern>& filter,
513                              std::vector<const Target*>* output) {
514   for (auto* target : input) {
515     for (const auto& pattern : filter) {
516       if (pattern.Matches(target->label())) {
517         output->push_back(target);
518         break;
519       }
520     }
521   }
522 }
523 
FilterTargetsByPatterns(const std::vector<const Target * > & input,const std::vector<LabelPattern> & filter,UniqueVector<const Target * > * output)524 void FilterTargetsByPatterns(const std::vector<const Target*>& input,
525                              const std::vector<LabelPattern>& filter,
526                              UniqueVector<const Target*>* output) {
527   for (auto* target : input) {
528     for (const auto& pattern : filter) {
529       if (pattern.Matches(target->label())) {
530         output->push_back(target);
531         break;
532       }
533     }
534   }
535 }
536 
FilterOutTargetsByPatterns(const std::vector<const Target * > & input,const std::vector<LabelPattern> & filter,std::vector<const Target * > * output)537 void FilterOutTargetsByPatterns(const std::vector<const Target*>& input,
538                                 const std::vector<LabelPattern>& filter,
539                                 std::vector<const Target*>* output) {
540   for (auto* target : input) {
541     bool match = false;
542     for (const auto& pattern : filter) {
543       if (pattern.Matches(target->label())) {
544         match = true;
545         break;
546       }
547     }
548     if (!match) {
549       output->push_back(target);
550     }
551   }
552 }
553 
FilterPatternsFromString(const BuildSettings * build_settings,const std::string & label_list_string,std::vector<LabelPattern> * filters,Err * err)554 bool FilterPatternsFromString(const BuildSettings* build_settings,
555                               const std::string& label_list_string,
556                               std::vector<LabelPattern>* filters,
557                               Err* err) {
558   std::vector<std::string> tokens = base::SplitString(
559       label_list_string, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
560   SourceDir root_dir("//");
561 
562   filters->reserve(tokens.size());
563   for (const std::string& token : tokens) {
564     LabelPattern pattern = LabelPattern::GetPattern(
565         root_dir, build_settings->root_path_utf8(),
566         Value(nullptr, FixGitBashLabelEdit(token)), err);
567     if (err->has_error())
568       return false;
569     filters->push_back(pattern);
570   }
571 
572   return true;
573 }
574 
FilterAndPrintTargets(std::vector<const Target * > * targets,base::ListValue * out)575 void FilterAndPrintTargets(std::vector<const Target*>* targets,
576                            base::ListValue* out) {
577   if (targets->empty())
578     return;
579 
580   if (!ApplyTestonlyFilter(targets))
581     return;
582   if (!ApplyTypeFilter(targets))
583     return;
584 
585   TargetPrintingMode printing_mode = TARGET_PRINT_LABEL;
586   if (targets->empty() || !GetTargetPrintingMode(&printing_mode))
587     return;
588   switch (printing_mode) {
589     case TARGET_PRINT_BUILDFILE:
590       PrintTargetsAsBuildfiles(*targets, out);
591       break;
592     case TARGET_PRINT_LABEL:
593       PrintTargetsAsLabels(*targets, out);
594       break;
595     case TARGET_PRINT_OUTPUT:
596       PrintTargetsAsOutputs(*targets, out);
597       break;
598   }
599 }
600 
FilterAndPrintTargets(bool indent,std::vector<const Target * > * targets)601 void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets) {
602   base::ListValue tmp;
603   FilterAndPrintTargets(targets, &tmp);
604   for (const auto& value : tmp) {
605     std::string string;
606     value.GetAsString(&string);
607     if (indent)
608       OutputString("  ");
609     OutputString(string);
610     OutputString("\n");
611   }
612 }
613 
FilterAndPrintTargetSet(bool indent,const std::set<const Target * > & targets)614 void FilterAndPrintTargetSet(bool indent,
615                              const std::set<const Target*>& targets) {
616   std::vector<const Target*> target_vector(targets.begin(), targets.end());
617   FilterAndPrintTargets(indent, &target_vector);
618 }
619 
FilterAndPrintTargetSet(const std::set<const Target * > & targets,base::ListValue * out)620 void FilterAndPrintTargetSet(const std::set<const Target*>& targets,
621                              base::ListValue* out) {
622   std::vector<const Target*> target_vector(targets.begin(), targets.end());
623   FilterAndPrintTargets(&target_vector, out);
624 }
625 
GetTargetsContainingFile(Setup * setup,const std::vector<const Target * > & all_targets,const SourceFile & file,bool default_toolchain_only,std::vector<TargetContainingFile> * matches)626 void GetTargetsContainingFile(Setup* setup,
627                               const std::vector<const Target*>& all_targets,
628                               const SourceFile& file,
629                               bool default_toolchain_only,
630                               std::vector<TargetContainingFile>* matches) {
631   Label default_toolchain = setup->loader()->default_toolchain_label();
632   for (auto* target : all_targets) {
633     if (default_toolchain_only) {
634       // Only check targets in the default toolchain.
635       if (target->label().GetToolchainLabel() != default_toolchain)
636         continue;
637     }
638     if (auto how = TargetContainsFile(target, file))
639       matches->emplace_back(target, *how);
640   }
641 }
642 
643 }  // namespace commands
644