• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <fstream>
8 #include <optional>
9 
10 #include "base/command_line.h"
11 #include "base/environment.h"
12 #include "base/files/file_util.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "gn/builder.h"
18 #include "gn/config_values_extractors.h"
19 #include "gn/filesystem_utils.h"
20 #include "gn/item.h"
21 #include "gn/label.h"
22 #include "gn/label_pattern.h"
23 #include "gn/ninja_build_writer.h"
24 #include "gn/setup.h"
25 #include "gn/standard_out.h"
26 #include "gn/switches.h"
27 #include "gn/target.h"
28 #include "util/atomic_write.h"
29 #include "util/build_config.h"
30 
31 namespace commands {
32 
33 namespace {
34 
35 // Like above but the input string can be a pattern that matches multiple
36 // targets. If the input does not parse as a pattern, prints and error and
37 // returns false. If the pattern is valid, fills the vector (which might be
38 // empty if there are no matches) and returns true.
39 //
40 // If default_toolchain_only is true, a pattern with an unspecified toolchain
41 // will match the default toolchain only. If true, all toolchains will be
42 // matched.
ResolveTargetsFromCommandLinePattern(Setup * setup,const std::string & label_pattern,bool default_toolchain_only,std::vector<const Target * > * matches)43 bool ResolveTargetsFromCommandLinePattern(Setup* setup,
44                                           const std::string& label_pattern,
45                                           bool default_toolchain_only,
46                                           std::vector<const Target*>* matches) {
47   Value pattern_value(nullptr, label_pattern);
48 
49   Err err;
50   LabelPattern pattern = LabelPattern::GetPattern(
51       SourceDirForCurrentDirectory(setup->build_settings().root_path()),
52       setup->build_settings().root_path_utf8(), pattern_value, &err);
53   if (err.has_error()) {
54     err.PrintToStdout();
55     return false;
56   }
57 
58   if (default_toolchain_only) {
59     // By default a pattern with an empty toolchain will match all toolchains.
60     // If the caller wants to default to the main toolchain only, set it
61     // explicitly.
62     if (pattern.toolchain().is_null()) {
63       // No explicit toolchain set.
64       pattern.set_toolchain(setup->loader()->default_toolchain_label());
65     }
66   }
67 
68   std::vector<LabelPattern> pattern_vector;
69   pattern_vector.push_back(pattern);
70   FilterTargetsByPatterns(setup->builder().GetAllResolvedTargets(),
71                           pattern_vector, matches);
72   return true;
73 }
74 
75 // 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)76 bool ResolveStringFromCommandLineInput(
77     Setup* setup,
78     const SourceDir& current_dir,
79     const std::string& input,
80     bool default_toolchain_only,
81     UniqueVector<const Target*>* target_matches,
82     UniqueVector<const Config*>* config_matches,
83     UniqueVector<const Toolchain*>* toolchain_matches,
84     UniqueVector<SourceFile>* file_matches) {
85   if (LabelPattern::HasWildcard(input)) {
86     // For now, only match patterns against targets. It might be nice in the
87     // future to allow the user to specify which types of things they want to
88     // match, but it should probably only match targets by default.
89     std::vector<const Target*> target_match_vector;
90     if (!ResolveTargetsFromCommandLinePattern(
91             setup, input, default_toolchain_only, &target_match_vector))
92       return false;
93     for (const Target* target : target_match_vector)
94       target_matches->push_back(target);
95     return true;
96   }
97 
98   // Try to figure out what this thing is.
99   Err err;
100   Label label = Label::Resolve(
101       current_dir, setup->build_settings().root_path_utf8(),
102       setup->loader()->default_toolchain_label(), Value(nullptr, input), &err);
103   if (err.has_error()) {
104     // Not a valid label, assume this must be a file.
105     err = Err();
106     file_matches->push_back(current_dir.ResolveRelativeFile(
107         Value(nullptr, input), &err, setup->build_settings().root_path_utf8()));
108     if (err.has_error()) {
109       err.PrintToStdout();
110       return false;
111     }
112     return true;
113   }
114 
115   const Item* item = setup->builder().GetItem(label);
116   if (item) {
117     if (const Config* as_config = item->AsConfig())
118       config_matches->push_back(as_config);
119     else if (const Target* as_target = item->AsTarget())
120       target_matches->push_back(as_target);
121     else if (const Toolchain* as_toolchain = item->AsToolchain())
122       toolchain_matches->push_back(as_toolchain);
123   } else {
124     // Not an item, assume this must be a file.
125     file_matches->push_back(current_dir.ResolveRelativeFile(
126         Value(nullptr, input), &err, setup->build_settings().root_path_utf8()));
127     if (err.has_error()) {
128       err.PrintToStdout();
129       return false;
130     }
131   }
132 
133   return true;
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(CommandSwitches::TargetPrintMode * mode)139 bool GetTargetPrintingMode(CommandSwitches::TargetPrintMode* mode) {
140   *mode = CommandSwitches::Get().target_print_mode();
141   return true;
142 }
143 
144 // Returns the target type filter based on the command line flags for the
145 // current process. Returns true on success. On error, prints a message to the
146 // console and returns false.
147 //
148 // Target::UNKNOWN will be set if there is no filter. Target::ACTION_FOREACH
149 // will never be returned. Code applying the filters should apply Target::ACTION
150 // to both ACTION and ACTION_FOREACH.
GetTargetTypeFilter(Target::OutputType * type)151 bool GetTargetTypeFilter(Target::OutputType* type) {
152   *type = CommandSwitches::Get().target_type();
153   return true;
154 }
155 
156 // Applies any testonly filtering specified on the command line to the given
157 // target set. On failure, prints an error and returns false.
ApplyTestonlyFilter(std::vector<const Target * > * targets)158 bool ApplyTestonlyFilter(std::vector<const Target*>* targets) {
159   CommandSwitches::TestonlyMode testonly_mode =
160       CommandSwitches::Get().testonly_mode();
161 
162   if (targets->empty() || testonly_mode == CommandSwitches::TESTONLY_NONE)
163     return true;
164 
165   bool testonly = (testonly_mode == CommandSwitches::TESTONLY_TRUE);
166 
167   // Filter into a copy of the vector, then replace the output.
168   std::vector<const Target*> result;
169   result.reserve(targets->size());
170 
171   for (const Target* target : *targets) {
172     if (target->testonly() == testonly)
173       result.push_back(target);
174   }
175 
176   *targets = std::move(result);
177   return true;
178 }
179 
180 // Applies any target type filtering specified on the command line to the given
181 // target set. On failure, prints an error and returns false.
ApplyTypeFilter(std::vector<const Target * > * targets)182 bool ApplyTypeFilter(std::vector<const Target*>* targets) {
183   Target::OutputType type = Target::UNKNOWN;
184   if (!GetTargetTypeFilter(&type))
185     return false;
186   if (targets->empty() || type == Target::UNKNOWN)
187     return true;  // Nothing to filter out.
188 
189   // Filter into a copy of the vector, then replace the output.
190   std::vector<const Target*> result;
191   result.reserve(targets->size());
192 
193   for (const Target* target : *targets) {
194     // Make "action" also apply to ACTION_FOREACH.
195     if (target->output_type() == type ||
196         (type == Target::ACTION &&
197          target->output_type() == Target::ACTION_FOREACH))
198       result.push_back(target);
199   }
200 
201   *targets = std::move(result);
202   return true;
203 }
204 
205 // Returns the file path of the BUILD.gn file generating this item.
BuildFileForItem(const Item * item)206 base::FilePath BuildFileForItem(const Item* item) {
207   // Find the only BUILD.gn file listed in build_dependency_files() for
208   // this Item. This may not exist if the item is defined in BUILDCONFIG.gn
209   // instead, so account for this too.
210   const SourceFile* buildconfig_gn = nullptr;
211   const SourceFile* build_gn = nullptr;
212   for (const SourceFile& build_file : item->build_dependency_files()) {
213     const std::string& name = build_file.GetName();
214     if (name == "BUILDCONFIG.gn") {
215       buildconfig_gn = &build_file;
216     } else if (name == "BUILD.gn") {
217       build_gn = &build_file;
218       break;
219     }
220   }
221   if (!build_gn)
222     build_gn = buildconfig_gn;
223 
224   CHECK(build_gn) << "No BUILD.gn or BUILDCONFIG.gn file defining "
225                   << item->label().GetUserVisibleName(true);
226   return build_gn->Resolve(item->settings()->build_settings()->root_path());
227 }
228 
PrintTargetsAsBuildfiles(const std::vector<const Target * > & targets,base::ListValue * out)229 void PrintTargetsAsBuildfiles(const std::vector<const Target*>& targets,
230                               base::ListValue* out) {
231   // Output the set of unique source files.
232   std::set<std::string> unique_files;
233   for (const Target* target : targets)
234     unique_files.insert(FilePathToUTF8(BuildFileForItem(target)));
235 
236   for (const std::string& file : unique_files) {
237     out->AppendString(file);
238   }
239 }
240 
PrintTargetsAsLabels(const std::vector<const Target * > & targets,base::ListValue * out)241 void PrintTargetsAsLabels(const std::vector<const Target*>& targets,
242                           base::ListValue* out) {
243   // Putting the labels into a set automatically sorts them for us.
244   std::set<Label> unique_labels;
245   for (auto* target : targets)
246     unique_labels.insert(target->label());
247 
248   // Grab the label of the default toolchain from the first target.
249   Label default_tc_label = targets[0]->settings()->default_toolchain_label();
250 
251   for (const Label& label : unique_labels) {
252     // Print toolchain only for ones not in the default toolchain.
253     out->AppendString(label.GetUserVisibleName(label.GetToolchainLabel() !=
254                                                default_tc_label));
255   }
256 }
257 
PrintTargetsAsOutputs(const std::vector<const Target * > & targets,base::ListValue * out)258 void PrintTargetsAsOutputs(const std::vector<const Target*>& targets,
259                            base::ListValue* out) {
260   if (targets.empty())
261     return;
262 
263   // Grab the build settings from a random target.
264   const BuildSettings* build_settings =
265       targets[0]->settings()->build_settings();
266 
267   for (const Target* target : targets) {
268     // Use the link output file if there is one, otherwise fall back to the
269     // dependency output file (for actions, for example).
270     OutputFile output_file = target->link_output_file();
271     if (output_file.value().empty())
272       output_file = target->dependency_output_file();
273 
274     SourceFile output_as_source = output_file.AsSourceFile(build_settings);
275     std::string result =
276         RebasePath(output_as_source.value(), build_settings->build_dir(),
277                    build_settings->root_path_utf8());
278     out->AppendString(result);
279   }
280 }
281 
282 #if defined(OS_WIN)
283 // Git bash will remove the first "/" in "//" paths
284 // This also happens for labels assigned to command line parameters, e.g.
285 // --filters
286 // Fix "//" paths, but not absolute and relative paths
FixGitBashLabelEdit(const std::string & label)287 inline std::string FixGitBashLabelEdit(const std::string& label) {
288   static std::unique_ptr<base::Environment> git_bash_env;
289   if (!git_bash_env)
290     git_bash_env = base::Environment::Create();
291 
292   std::string temp_label(label);
293 
294   if (git_bash_env->HasVar(
295           "MSYSTEM") &&        // Only for MinGW based shells like Git Bash
296       temp_label[0] == '/' &&  // Only fix for //foo paths, not /f:oo paths
297       (temp_label.length() < 2 ||
298        (temp_label[1] != '/' &&
299         (temp_label.length() < 3 || temp_label[1] != ':'))))
300     temp_label.insert(0, "/");
301   return temp_label;
302 }
303 #else
304 // Only repair on Windows
FixGitBashLabelEdit(const std::string & label)305 inline std::string FixGitBashLabelEdit(const std::string& label) {
306   return label;
307 }
308 #endif
309 
TargetContainsFile(const Target * target,const SourceFile & file)310 std::optional<HowTargetContainsFile> TargetContainsFile(
311     const Target* target,
312     const SourceFile& file) {
313   for (const auto& cur_file : target->sources()) {
314     if (cur_file == file)
315       return HowTargetContainsFile::kSources;
316   }
317   for (const auto& cur_file : target->public_headers()) {
318     if (cur_file == file)
319       return HowTargetContainsFile::kPublic;
320   }
321   for (ConfigValuesIterator iter(target); !iter.done(); iter.Next()) {
322     for (const auto& cur_file : iter.cur().inputs()) {
323       if (cur_file == file)
324         return HowTargetContainsFile::kInputs;
325     }
326   }
327   for (const auto& cur_file : target->data()) {
328     if (cur_file == file.value())
329       return HowTargetContainsFile::kData;
330     if (cur_file.back() == '/' && base::starts_with(file.value(), cur_file))
331       return HowTargetContainsFile::kData;
332   }
333 
334   if (target->action_values().script().value() == file.value())
335     return HowTargetContainsFile::kScript;
336 
337   std::vector<SourceFile> output_sources;
338   target->action_values().GetOutputsAsSourceFiles(target, &output_sources);
339   for (const auto& cur_file : output_sources) {
340     if (cur_file == file)
341       return HowTargetContainsFile::kOutput;
342   }
343 
344   for (const auto& cur_file : target->computed_outputs()) {
345     if (cur_file.AsSourceFile(target->settings()->build_settings()) == file)
346       return HowTargetContainsFile::kOutput;
347   }
348   return std::nullopt;
349 }
350 
ToUTF8(base::FilePath::StringType in)351 std::string ToUTF8(base::FilePath::StringType in) {
352 #if defined(OS_WIN)
353   return base::UTF16ToUTF8(in);
354 #else
355   return in;
356 #endif
357 }
358 
359 }  // namespace
360 
CommandInfo()361 CommandInfo::CommandInfo()
362     : help_short(nullptr), help(nullptr), runner(nullptr) {}
363 
CommandInfo(const char * in_help_short,const char * in_help,CommandRunner in_runner)364 CommandInfo::CommandInfo(const char* in_help_short,
365                          const char* in_help,
366                          CommandRunner in_runner)
367     : help_short(in_help_short), help(in_help), runner(in_runner) {}
368 
GetCommands()369 const CommandInfoMap& GetCommands() {
370   static CommandInfoMap info_map;
371   if (info_map.empty()) {
372 #define INSERT_COMMAND(cmd) \
373   info_map[k##cmd] = CommandInfo(k##cmd##_HelpShort, k##cmd##_Help, &Run##cmd);
374 
375     INSERT_COMMAND(Analyze)
376     INSERT_COMMAND(Args)
377     INSERT_COMMAND(Check)
378     INSERT_COMMAND(Clean)
379     INSERT_COMMAND(Desc)
380     INSERT_COMMAND(Gen)
381     INSERT_COMMAND(Format)
382     INSERT_COMMAND(Help)
383     INSERT_COMMAND(Meta)
384     INSERT_COMMAND(Ls)
385     INSERT_COMMAND(Outputs)
386     INSERT_COMMAND(Path)
387     INSERT_COMMAND(Refs)
388     INSERT_COMMAND(CleanStale)
389 
390 #undef INSERT_COMMAND
391   }
392   return info_map;
393 }
394 
395 // static
396 CommandSwitches CommandSwitches::s_global_switches_ = {};
397 
398 // static
Init(const base::CommandLine & cmdline)399 bool CommandSwitches::Init(const base::CommandLine& cmdline) {
400   CHECK(!s_global_switches_.is_initialized())
401       << "Only call this once from main()";
402   return s_global_switches_.InitFrom(cmdline);
403 }
404 
405 // static
Get()406 const CommandSwitches& CommandSwitches::Get() {
407   CHECK(s_global_switches_.is_initialized())
408       << "Missing previous successful call to CommandSwitches::Init()";
409   return s_global_switches_;
410 }
411 
412 // static
Set(CommandSwitches new_switches)413 CommandSwitches CommandSwitches::Set(CommandSwitches new_switches) {
414   CHECK(s_global_switches_.is_initialized())
415       << "Missing previous successful call to CommandSwitches::Init()";
416   CommandSwitches result = std::move(s_global_switches_);
417   s_global_switches_ = std::move(new_switches);
418   return result;
419 }
420 
InitFrom(const base::CommandLine & cmdline)421 bool CommandSwitches::InitFrom(const base::CommandLine& cmdline) {
422   CommandSwitches result;
423   result.initialized_ = true;
424   result.has_quiet_ = cmdline.HasSwitch("a");
425   result.has_force_ = cmdline.HasSwitch("force");
426   result.has_all_ = cmdline.HasSwitch("all");
427   result.has_blame_ = cmdline.HasSwitch("blame");
428   result.has_tree_ = cmdline.HasSwitch("tree");
429   result.has_format_json_ = cmdline.GetSwitchValueString("format") == "json";
430   result.has_default_toolchain_ =
431       cmdline.HasSwitch(switches::kDefaultToolchain);
432 
433   result.has_check_generated_ = cmdline.HasSwitch("check-generated");
434   result.has_check_system_ = cmdline.HasSwitch("check-system");
435   result.has_public_ = cmdline.HasSwitch("public");
436   result.has_with_data_ = cmdline.HasSwitch("with-data");
437 
438   std::string_view target_print_switch = "as";
439   if (cmdline.HasSwitch(target_print_switch)) {
440     std::string value = cmdline.GetSwitchValueString(target_print_switch);
441     if (value == "buildfile") {
442       result.target_print_mode_ = TARGET_PRINT_BUILDFILE;
443     } else if (value == "label") {
444       result.target_print_mode_ = TARGET_PRINT_LABEL;
445     } else if (value == "output") {
446       result.target_print_mode_ = TARGET_PRINT_OUTPUT;
447     } else {
448       Err(Location(), "Invalid value for \"--as\".",
449           "I was expecting \"buildfile\", \"label\", or \"output\" but you\n"
450           "said \"" +
451               value + "\".")
452           .PrintToStdout();
453       return false;
454     }
455   }
456 
457   std::string_view target_type_switch = "type";
458   if (cmdline.HasSwitch(target_type_switch)) {
459     std::string value = cmdline.GetSwitchValueString(target_type_switch);
460     static const struct {
461       const char* name;
462       Target::OutputType type;
463     } kTypes[] = {
464         {"group", Target::GROUP},
465         {"executable", Target::EXECUTABLE},
466         {"shared_library", Target::SHARED_LIBRARY},
467         {"loadable_module", Target::LOADABLE_MODULE},
468         {"static_library", Target::STATIC_LIBRARY},
469         {"source_set", Target::SOURCE_SET},
470         {"copy", Target::COPY_FILES},
471         {"action", Target::ACTION},
472     };
473     bool found = false;
474     for (const auto& type : kTypes) {
475       if (value == type.name) {
476         result.target_type_ = type.type;
477         found = true;
478         break;
479       }
480     }
481     if (!found) {
482       Err(Location(), "Invalid value for \"--type\".").PrintToStdout();
483       return false;
484     }
485   }
486   std::string_view testonly_switch = "testonly";
487   if (cmdline.HasSwitch(testonly_switch)) {
488     std::string value = cmdline.GetSwitchValueString(testonly_switch);
489     if (value == "true") {
490       result.testonly_mode_ = TESTONLY_TRUE;
491     } else if (value == "false") {
492       result.testonly_mode_ = TESTONLY_FALSE;
493     } else {
494       Err(Location(), "Bad value for --testonly.",
495           "I was expecting --testonly=true or --testonly=false.")
496           .PrintToStdout();
497       return false;
498     }
499   }
500 
501   result.meta_rebase_dir_ = cmdline.GetSwitchValueString("rebase");
502   result.meta_data_keys_ = cmdline.GetSwitchValueString("data");
503   result.meta_walk_keys_ = cmdline.GetSwitchValueString("walk");
504   *this = result;
505   return true;
506 }
507 
PrepareForRegeneration(const BuildSettings * settings)508 bool PrepareForRegeneration(const BuildSettings* settings) {
509   // Write a .d file for the build which references a nonexistent file.
510   // This will make Ninja always mark the build as dirty.
511   base::FilePath build_ninja_d_file(settings->GetFullPath(
512       SourceFile(settings->build_dir().value() + "build.ninja.d")));
513   std::string dummy_depfile("build.ninja.stamp: nonexistent_file.gn\n");
514   if (util::WriteFileAtomically(build_ninja_d_file, dummy_depfile.data(),
515                                 static_cast<int>(dummy_depfile.size())) == -1) {
516     Err(Location(), std::string("Failed to write build.ninja.d."))
517         .PrintToStdout();
518     return false;
519   }
520 
521   // Write a stripped down build.ninja file with just the commands needed
522   // for ninja to call GN and regenerate ninja files.
523   base::FilePath build_ninja_path(settings->GetFullPath(
524       SourceFile(settings->build_dir().value() + "build.ninja")));
525   std::ifstream build_ninja_file(ToUTF8(build_ninja_path.value()));
526   if (!build_ninja_file) {
527     // Couldn't open the build.ninja file.
528     Err(Location(), "Couldn't open build.ninja in this directory.",
529         "Try running \"gn gen\" on it and then re-running \"gn clean\".")
530         .PrintToStdout();
531     return false;
532   }
533   std::string build_commands =
534       NinjaBuildWriter::ExtractRegenerationCommands(build_ninja_file);
535   if (build_commands.empty()) {
536     Err(Location(), "Unexpected build.ninja contents in this directory.",
537         "Try running \"gn gen\" on it and then re-running \"gn clean\".")
538         .PrintToStdout();
539     return false;
540   }
541   // Close build.ninja or else WriteFileAtomically will fail on Windows.
542   build_ninja_file.close();
543   if (util::WriteFileAtomically(build_ninja_path, build_commands.data(),
544                                 static_cast<int>(build_commands.size())) ==
545       -1) {
546     Err(Location(), std::string("Failed to write build.ninja."))
547         .PrintToStdout();
548     return false;
549   }
550 
551   return true;
552 }
553 
ResolveTargetFromCommandLineString(Setup * setup,const std::string & label_string)554 const Target* ResolveTargetFromCommandLineString(
555     Setup* setup,
556     const std::string& label_string) {
557   // Need to resolve the label after we know the default toolchain.
558   Label default_toolchain = setup->loader()->default_toolchain_label();
559   Value arg_value(nullptr, FixGitBashLabelEdit(label_string));
560   Err err;
561   Label label = Label::Resolve(
562       SourceDirForCurrentDirectory(setup->build_settings().root_path()),
563       setup->build_settings().root_path_utf8(), default_toolchain, arg_value,
564       &err);
565   if (err.has_error()) {
566     err.PrintToStdout();
567     return nullptr;
568   }
569 
570   const Item* item = setup->builder().GetItem(label);
571   if (!item) {
572     Err(Location(), "Label not found.",
573         label.GetUserVisibleName(false) + " not found.")
574         .PrintToStdout();
575     return nullptr;
576   }
577 
578   const Target* target = item->AsTarget();
579   if (!target) {
580     Err(Location(), "Not a target.",
581         "The \"" + label.GetUserVisibleName(false) +
582             "\" thing\n"
583             "is not a target. Somebody should probably implement this command "
584             "for "
585             "other\nitem types.")
586         .PrintToStdout();
587     return nullptr;
588   }
589 
590   return target;
591 }
592 
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)593 bool ResolveFromCommandLineInput(
594     Setup* setup,
595     const std::vector<std::string>& input,
596     bool default_toolchain_only,
597     UniqueVector<const Target*>* target_matches,
598     UniqueVector<const Config*>* config_matches,
599     UniqueVector<const Toolchain*>* toolchain_matches,
600     UniqueVector<SourceFile>* file_matches) {
601   if (input.empty()) {
602     Err(Location(), "You need to specify a label, file, or pattern.")
603         .PrintToStdout();
604     return false;
605   }
606 
607   SourceDir cur_dir =
608       SourceDirForCurrentDirectory(setup->build_settings().root_path());
609   for (const auto& cur : input) {
610     if (!ResolveStringFromCommandLineInput(
611             setup, cur_dir, cur, default_toolchain_only, target_matches,
612             config_matches, toolchain_matches, file_matches))
613       return false;
614   }
615   return true;
616 }
617 
FilterTargetsByPatterns(const std::vector<const Target * > & input,const std::vector<LabelPattern> & filter,std::vector<const Target * > * output)618 void FilterTargetsByPatterns(const std::vector<const Target*>& input,
619                              const std::vector<LabelPattern>& filter,
620                              std::vector<const Target*>* output) {
621   for (auto* target : input) {
622     for (const auto& pattern : filter) {
623       if (pattern.Matches(target->label())) {
624         output->push_back(target);
625         break;
626       }
627     }
628   }
629 }
630 
FilterTargetsByPatterns(const std::vector<const Target * > & input,const std::vector<LabelPattern> & filter,UniqueVector<const Target * > * output)631 void FilterTargetsByPatterns(const std::vector<const Target*>& input,
632                              const std::vector<LabelPattern>& filter,
633                              UniqueVector<const Target*>* output) {
634   for (auto* target : input) {
635     for (const auto& pattern : filter) {
636       if (pattern.Matches(target->label())) {
637         output->push_back(target);
638         break;
639       }
640     }
641   }
642 }
643 
FilterOutTargetsByPatterns(const std::vector<const Target * > & input,const std::vector<LabelPattern> & filter,std::vector<const Target * > * output)644 void FilterOutTargetsByPatterns(const std::vector<const Target*>& input,
645                                 const std::vector<LabelPattern>& filter,
646                                 std::vector<const Target*>* output) {
647   for (auto* target : input) {
648     bool match = false;
649     for (const auto& pattern : filter) {
650       if (pattern.Matches(target->label())) {
651         match = true;
652         break;
653       }
654     }
655     if (!match) {
656       output->push_back(target);
657     }
658   }
659 }
660 
FilterPatternsFromString(const BuildSettings * build_settings,const std::string & label_list_string,std::vector<LabelPattern> * filters,Err * err)661 bool FilterPatternsFromString(const BuildSettings* build_settings,
662                               const std::string& label_list_string,
663                               std::vector<LabelPattern>* filters,
664                               Err* err) {
665   std::vector<std::string> tokens = base::SplitString(
666       label_list_string, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
667   SourceDir root_dir("//");
668 
669   filters->reserve(tokens.size());
670   for (const std::string& token : tokens) {
671     LabelPattern pattern = LabelPattern::GetPattern(
672         root_dir, build_settings->root_path_utf8(),
673         Value(nullptr, FixGitBashLabelEdit(token)), err);
674     if (err->has_error())
675       return false;
676     filters->push_back(pattern);
677   }
678 
679   return true;
680 }
681 
FilterAndPrintTargets(std::vector<const Target * > * targets,base::ListValue * out)682 void FilterAndPrintTargets(std::vector<const Target*>* targets,
683                            base::ListValue* out) {
684   if (targets->empty())
685     return;
686 
687   if (!ApplyTestonlyFilter(targets))
688     return;
689   if (!ApplyTypeFilter(targets))
690     return;
691 
692   CommandSwitches::TargetPrintMode printing_mode =
693       CommandSwitches::TARGET_PRINT_LABEL;
694   if (targets->empty() || !GetTargetPrintingMode(&printing_mode))
695     return;
696   switch (printing_mode) {
697     case CommandSwitches::TARGET_PRINT_BUILDFILE:
698       PrintTargetsAsBuildfiles(*targets, out);
699       break;
700     case CommandSwitches::TARGET_PRINT_LABEL:
701       PrintTargetsAsLabels(*targets, out);
702       break;
703     case CommandSwitches::TARGET_PRINT_OUTPUT:
704       PrintTargetsAsOutputs(*targets, out);
705       break;
706   }
707 }
708 
FilterAndPrintTargets(bool indent,std::vector<const Target * > * targets)709 void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets) {
710   base::ListValue tmp;
711   FilterAndPrintTargets(targets, &tmp);
712   for (const auto& value : tmp) {
713     std::string string;
714     value.GetAsString(&string);
715     if (indent)
716       OutputString("  ");
717     OutputString(string);
718     OutputString("\n");
719   }
720 }
721 
FilterAndPrintTargetSet(bool indent,const TargetSet & targets)722 void FilterAndPrintTargetSet(bool indent, const TargetSet& targets) {
723   std::vector<const Target*> target_vector(targets.begin(), targets.end());
724   FilterAndPrintTargets(indent, &target_vector);
725 }
726 
FilterAndPrintTargetSet(const TargetSet & targets,base::ListValue * out)727 void FilterAndPrintTargetSet(const TargetSet& targets, base::ListValue* out) {
728   std::vector<const Target*> target_vector(targets.begin(), targets.end());
729   FilterAndPrintTargets(&target_vector, out);
730 }
731 
GetTargetsContainingFile(Setup * setup,const std::vector<const Target * > & all_targets,const SourceFile & file,bool default_toolchain_only,std::vector<TargetContainingFile> * matches)732 void GetTargetsContainingFile(Setup* setup,
733                               const std::vector<const Target*>& all_targets,
734                               const SourceFile& file,
735                               bool default_toolchain_only,
736                               std::vector<TargetContainingFile>* matches) {
737   Label default_toolchain = setup->loader()->default_toolchain_label();
738   for (auto* target : all_targets) {
739     if (default_toolchain_only) {
740       // Only check targets in the default toolchain.
741       if (target->label().GetToolchainLabel() != default_toolchain)
742         continue;
743     }
744     if (auto how = TargetContainsFile(target, file))
745       matches->emplace_back(target, *how);
746   }
747 }
748 
749 }  // namespace commands
750