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