Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/command_line.h" | |
| 6 #include "tools/gn/builder.h" | |
| 5 #include "tools/gn/commands.h" | 7 #include "tools/gn/commands.h" |
| 6 #include "tools/gn/filesystem_utils.h" | 8 #include "tools/gn/filesystem_utils.h" |
| 7 #include "tools/gn/item.h" | 9 #include "tools/gn/item.h" |
| 8 #include "tools/gn/label.h" | 10 #include "tools/gn/label.h" |
| 9 #include "tools/gn/label_pattern.h" | 11 #include "tools/gn/label_pattern.h" |
| 10 #include "tools/gn/setup.h" | 12 #include "tools/gn/setup.h" |
| 11 #include "tools/gn/standard_out.h" | 13 #include "tools/gn/standard_out.h" |
| 12 #include "tools/gn/target.h" | 14 #include "tools/gn/target.h" |
| 13 | 15 |
| 14 namespace commands { | 16 namespace commands { |
| 15 | 17 |
| 18 namespace { | |
| 19 | |
| 20 // Like above but the input string can be a pattern that matches multiple | |
| 21 // targets. If the input does not parse as a pattern, prints and error and | |
| 22 // returns false. If the pattern is valid, fills the vector (which might be | |
| 23 // empty if there are no matches) and returns true. | |
| 24 // | |
| 25 // If all_tolchains is false, a pattern with an unspecified toolchain will | |
|
scottmg
2015/02/19 21:52:01
all_toolchains
| |
| 26 // match the default toolchain only. If true, all toolchains will be matched. | |
| 27 bool ResolveTargetsFromCommandLinePattern( | |
| 28 Setup* setup, | |
| 29 const std::string& label_pattern, | |
| 30 bool all_toolchains, | |
| 31 std::vector<const Target*>* matches) { | |
| 32 Value pattern_value(nullptr, label_pattern); | |
| 33 | |
| 34 Err err; | |
| 35 LabelPattern pattern = LabelPattern::GetPattern( | |
| 36 SourceDirForCurrentDirectory(setup->build_settings().root_path()), | |
| 37 pattern_value, | |
| 38 &err); | |
| 39 if (err.has_error()) { | |
| 40 err.PrintToStdout(); | |
| 41 return false; | |
| 42 } | |
| 43 | |
| 44 if (!all_toolchains) { | |
| 45 // By default a pattern with an empty toolchain will match all toolchains. | |
| 46 // If the caller wants to default to the main toolchain only, set it | |
| 47 // explicitly. | |
| 48 if (pattern.toolchain().is_null()) { | |
| 49 // No explicit toolchain set. | |
| 50 pattern.set_toolchain(setup->loader()->default_toolchain_label()); | |
| 51 } | |
| 52 } | |
| 53 | |
| 54 std::vector<LabelPattern> pattern_vector; | |
| 55 pattern_vector.push_back(pattern); | |
| 56 FilterTargetsByPatterns(setup->builder()->GetAllResolvedTargets(), | |
| 57 pattern_vector, matches); | |
| 58 return true; | |
| 59 } | |
| 60 | |
| 61 | |
| 62 // If there's an error, it will be printed and false will be returned. | |
| 63 bool ResolveStringFromCommandLineInput( | |
| 64 Setup* setup, | |
| 65 const SourceDir& current_dir, | |
| 66 const std::string& input, | |
| 67 bool all_toolchains, | |
| 68 UniqueVector<const Target*>* target_matches, | |
| 69 UniqueVector<const Config*>* config_matches, | |
| 70 UniqueVector<const Toolchain*>* toolchain_matches, | |
| 71 UniqueVector<SourceFile>* file_matches) { | |
| 72 if (LabelPattern::HasWildcard(input)) { | |
| 73 // For now, only match patterns against targets. It might be nice in the | |
| 74 // future to allow the user to specify which types of things they want to | |
| 75 // match, but it should probably only match targets by default. | |
| 76 std::vector<const Target*> target_match_vector; | |
| 77 if (!ResolveTargetsFromCommandLinePattern(setup, input, all_toolchains, | |
| 78 &target_match_vector)) | |
| 79 return false; | |
| 80 for (const Target* target : target_match_vector) | |
| 81 target_matches->push_back(target); | |
| 82 return true; | |
| 83 } | |
| 84 | |
| 85 // Try to figure out what this thing is. | |
| 86 Err err; | |
| 87 Label label = Label::Resolve(current_dir, | |
| 88 setup->loader()->default_toolchain_label(), | |
| 89 Value(nullptr, input), &err); | |
| 90 if (err.has_error()) { | |
| 91 err.PrintToStdout(); | |
| 92 return false; | |
| 93 } | |
| 94 | |
| 95 const Item* item = setup->builder()->GetItem(label); | |
| 96 if (item) { | |
| 97 if (const Config* as_config = item->AsConfig()) | |
| 98 config_matches->push_back(as_config); | |
| 99 else if (const Target* as_target = item->AsTarget()) | |
| 100 target_matches->push_back(as_target); | |
| 101 else if (const Toolchain* as_toolchain = item->AsToolchain()) | |
| 102 toolchain_matches->push_back(as_toolchain); | |
| 103 } else { | |
| 104 // Not an item, assume this must be a file. | |
| 105 file_matches->push_back(current_dir.ResolveRelativeFile( | |
| 106 input, setup->build_settings().root_path_utf8())); | |
| 107 } | |
| 108 | |
| 109 return true; | |
| 110 } | |
| 111 | |
| 112 enum TargetPrintingMode { | |
| 113 TARGET_PRINT_BUILDFILE, | |
| 114 TARGET_PRINT_LABEL, | |
| 115 TARGET_PRINT_OUTPUT, | |
| 116 }; | |
| 117 | |
| 118 // Retrieves the target printing mode based on the command line flags for the | |
| 119 // current process. Returns true on success. On error, prints a message to the | |
| 120 // console and returns false. | |
| 121 bool GetTargetPrintingMode(TargetPrintingMode* mode) { | |
| 122 std::string switch_key = "as"; | |
| 123 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); | |
| 124 | |
| 125 if (!cmdline->HasSwitch(switch_key)) { | |
| 126 // Default to labels. | |
| 127 *mode = TARGET_PRINT_LABEL; | |
| 128 return true; | |
| 129 } | |
| 130 | |
| 131 std::string value = cmdline->GetSwitchValueASCII(switch_key); | |
| 132 if (value == "buildfile") { | |
| 133 *mode = TARGET_PRINT_BUILDFILE; | |
| 134 return true; | |
| 135 } | |
| 136 if (value == "label") { | |
| 137 *mode = TARGET_PRINT_LABEL; | |
| 138 return true; | |
| 139 } | |
| 140 if (value == "output") { | |
| 141 *mode = TARGET_PRINT_OUTPUT; | |
| 142 return true; | |
| 143 } | |
| 144 | |
| 145 Err(Location(), "Invalid value for \"--as\".", | |
| 146 "I was expecting \"buildfile\", \"label\", or \"output\" but you\n" | |
| 147 "said \"" + value + "\".").PrintToStdout(); | |
| 148 return false; | |
| 149 } | |
| 150 | |
| 151 // Returns the target type filter based on the command line flags for the | |
| 152 // current process. Returns true on success. On error, prints a message to the | |
| 153 // console and returns false. | |
| 154 // | |
| 155 // Target::UNKNOWN will be set if there is no filter. Target::ACTION_FOREACH | |
| 156 // will never be returned. Code applying the filters should apply Target::ACTION | |
| 157 // to both ACTION and ACTION_FOREACH. | |
| 158 bool GetTargetTypeFilter(Target::OutputType* type) { | |
| 159 std::string switch_key = "type"; | |
| 160 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); | |
| 161 | |
| 162 if (!cmdline->HasSwitch(switch_key)) { | |
| 163 // Default to unknown -> no filtering. | |
| 164 *type = Target::UNKNOWN; | |
| 165 return true; | |
| 166 } | |
| 167 | |
| 168 std::string value = cmdline->GetSwitchValueASCII(switch_key); | |
| 169 if (value == "group") { | |
| 170 *type = Target::GROUP; | |
| 171 return true; | |
| 172 } | |
| 173 if (value == "executable") { | |
| 174 *type = Target::EXECUTABLE; | |
| 175 return true; | |
| 176 } | |
| 177 if (value == "shared_library") { | |
| 178 *type = Target::SHARED_LIBRARY; | |
| 179 return true; | |
| 180 } | |
| 181 if (value == "static_library") { | |
| 182 *type = Target::STATIC_LIBRARY; | |
| 183 return true; | |
| 184 } | |
| 185 if (value == "source_set") { | |
| 186 *type = Target::SOURCE_SET; | |
| 187 return true; | |
| 188 } | |
| 189 if (value == "copy") { | |
| 190 *type = Target::COPY_FILES; | |
| 191 return true; | |
| 192 } | |
| 193 if (value == "action") { | |
| 194 *type = Target::ACTION; | |
| 195 return true; | |
| 196 } | |
| 197 | |
| 198 Err(Location(), "Invalid value for \"--type\".").PrintToStdout(); | |
| 199 return false; | |
| 200 } | |
| 201 | |
| 202 | |
| 203 // Applies any testonly filtering specified on the command line to the given | |
| 204 // target set. On failure, prints an error and returns false. | |
| 205 bool ApplyTestonlyFilter(std::vector<const Target*>* targets) { | |
| 206 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); | |
| 207 std::string testonly_key = "testonly"; | |
| 208 | |
| 209 if (targets->empty() || !cmdline->HasSwitch(testonly_key)) | |
| 210 return true; | |
| 211 | |
| 212 std::string testonly_value = cmdline->GetSwitchValueASCII(testonly_key); | |
| 213 bool testonly = false; | |
| 214 if (testonly_value == "true") { | |
| 215 testonly = true; | |
| 216 } else if (testonly_value != "false") { | |
| 217 Err(Location(), "Bad value for --testonly.", | |
| 218 "I was expecting --testonly=true or --testonly=false.") | |
| 219 .PrintToStdout(); | |
| 220 return false; | |
| 221 } | |
| 222 | |
| 223 // Filter into a copy of the vector, then swap to output. | |
| 224 std::vector<const Target*> result; | |
| 225 result.reserve(targets->size()); | |
| 226 | |
| 227 for (const Target* target : *targets) { | |
| 228 if (target->testonly() == testonly) | |
| 229 result.push_back(target); | |
| 230 } | |
| 231 | |
| 232 targets->swap(result); | |
| 233 return true; | |
| 234 } | |
| 235 | |
| 236 // Applies any target type filtering specified on the command line to the given | |
| 237 // target set. On failure, prints an error and returns false. | |
| 238 bool ApplyTypeFilter(std::vector<const Target*>* targets) { | |
| 239 Target::OutputType type = Target::UNKNOWN; | |
| 240 if (!GetTargetTypeFilter(&type)) | |
| 241 return false; | |
| 242 if (targets->empty() || type == Target::UNKNOWN) | |
| 243 return true; // Nothing to filter out. | |
| 244 | |
| 245 // Filter into a copy of the vector, then swap to output. | |
| 246 std::vector<const Target*> result; | |
| 247 result.reserve(targets->size()); | |
| 248 | |
| 249 for (const Target* target : *targets) { | |
| 250 // Make "action" also apply to ACTION_FOREACH. | |
| 251 if (target->output_type() == type || | |
| 252 (type == Target::ACTION && | |
| 253 target->output_type() == Target::ACTION_FOREACH)) | |
| 254 result.push_back(target); | |
| 255 } | |
| 256 | |
| 257 targets->swap(result); | |
| 258 return true; | |
| 259 } | |
| 260 | |
| 261 // Returns the file path generating this item. | |
| 262 base::FilePath BuildFileForItem(const Item* item) { | |
| 263 return item->defined_from()->GetRange().begin().file()->physical_name(); | |
| 264 } | |
| 265 | |
| 266 void PrintTargetsAsBuildfiles(bool indent, | |
| 267 const std::vector<const Target*>& targets) { | |
| 268 // Output the set of unique source files. | |
| 269 std::set<std::string> unique_files; | |
| 270 for (const Target* target : targets) | |
| 271 unique_files.insert(FilePathToUTF8(BuildFileForItem(target))); | |
| 272 | |
| 273 for (const std::string& file : unique_files) { | |
| 274 if (indent) | |
| 275 OutputString(" "); | |
| 276 OutputString(file + "\n"); | |
| 277 } | |
| 278 } | |
| 279 | |
| 280 void PrintTargetsAsLabels(bool indent, | |
| 281 const std::vector<const Target*>& targets) { | |
| 282 // Putting the labels into a set automatically sorts them for us. | |
| 283 std::set<Label> unique_labels; | |
| 284 for (const auto& target : targets) | |
| 285 unique_labels.insert(target->label()); | |
| 286 | |
| 287 // Grab the label of the default toolchain from the first target. | |
| 288 Label default_tc_label = | |
| 289 targets[0]->settings()->default_toolchain_label(); | |
| 290 | |
| 291 for (const Label& label : unique_labels) { | |
| 292 // Print toolchain only for ones not in the default toolchain. | |
| 293 if (indent) | |
| 294 OutputString(" "); | |
| 295 OutputString(label.GetUserVisibleName( | |
| 296 label.GetToolchainLabel() != default_tc_label)); | |
| 297 OutputString("\n"); | |
| 298 } | |
| 299 } | |
| 300 | |
| 301 void PrintTargetsAsOutputs(bool indent, | |
| 302 const std::vector<const Target*>& targets) { | |
| 303 if (targets.empty()) | |
| 304 return; | |
| 305 | |
| 306 // Grab the build settings from a random target. | |
| 307 const BuildSettings* build_settings = | |
| 308 targets[0]->settings()->build_settings(); | |
| 309 | |
| 310 SourceDir current_dir = SourceDirForCurrentDirectory( | |
| 311 build_settings->root_path()); | |
| 312 for (const Target* target : targets) { | |
| 313 // Use the link output file if there is one, otherwise fall back to the | |
| 314 // dependency output file (for actions, for example). | |
| 315 OutputFile output_file = target->link_output_file(); | |
| 316 if (output_file.value().empty()) | |
| 317 output_file = target->dependency_output_file(); | |
| 318 | |
| 319 SourceFile output_as_source = | |
| 320 output_file.AsSourceFile(build_settings); | |
| 321 std::string result = RebasePath(output_as_source.value(), current_dir, | |
| 322 build_settings->root_path_utf8()); | |
| 323 if (indent) | |
| 324 OutputString(" "); | |
| 325 OutputString(result); | |
| 326 OutputString("\n"); | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 } // namespace | |
| 331 | |
| 16 CommandInfo::CommandInfo() | 332 CommandInfo::CommandInfo() |
| 17 : help_short(nullptr), | 333 : help_short(nullptr), |
| 18 help(nullptr), | 334 help(nullptr), |
| 19 runner(nullptr) { | 335 runner(nullptr) { |
| 20 } | 336 } |
| 21 | 337 |
| 22 CommandInfo::CommandInfo(const char* in_help_short, | 338 CommandInfo::CommandInfo(const char* in_help_short, |
| 23 const char* in_help, | 339 const char* in_help, |
| 24 CommandRunner in_runner) | 340 CommandRunner in_runner) |
| 25 : help_short(in_help_short), | 341 : help_short(in_help_short), |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 77 Err(Location(), "Not a target.", | 393 Err(Location(), "Not a target.", |
| 78 "The \"" + label.GetUserVisibleName(false) + "\" thing\n" | 394 "The \"" + label.GetUserVisibleName(false) + "\" thing\n" |
| 79 "is not a target. Somebody should probably implement this command for " | 395 "is not a target. Somebody should probably implement this command for " |
| 80 "other\nitem types."); | 396 "other\nitem types."); |
| 81 return nullptr; | 397 return nullptr; |
| 82 } | 398 } |
| 83 | 399 |
| 84 return target; | 400 return target; |
| 85 } | 401 } |
| 86 | 402 |
| 87 bool ResolveTargetsFromCommandLinePattern( | 403 bool ResolveFromCommandLineInput( |
| 88 Setup* setup, | 404 Setup* setup, |
| 89 const std::string& label_pattern, | 405 const std::vector<std::string>& input, |
| 90 bool all_toolchains, | 406 bool all_toolchains, |
| 91 std::vector<const Target*>* matches) { | 407 UniqueVector<const Target*>* target_matches, |
| 92 Value pattern_value(nullptr, label_pattern); | 408 UniqueVector<const Config*>* config_matches, |
| 93 | 409 UniqueVector<const Toolchain*>* toolchain_matches, |
| 94 Err err; | 410 UniqueVector<SourceFile>* file_matches) { |
| 95 LabelPattern pattern = LabelPattern::GetPattern( | 411 if (input.empty()) { |
| 96 SourceDirForCurrentDirectory(setup->build_settings().root_path()), | 412 Err(Location(), "You need to specify a label, file, or pattern.") |
| 97 pattern_value, | 413 .PrintToStdout(); |
| 98 &err); | |
| 99 if (err.has_error()) { | |
| 100 err.PrintToStdout(); | |
| 101 return false; | 414 return false; |
| 102 } | 415 } |
| 103 | 416 |
| 104 if (!all_toolchains) { | 417 SourceDir cur_dir = |
| 105 // By default a pattern with an empty toolchain will match all toolchains. | 418 SourceDirForCurrentDirectory(setup->build_settings().root_path()); |
| 106 // IF the caller wants to default to the main toolchain only, set it | 419 for (const auto& cur : input) { |
| 107 // explicitly. | 420 if (!ResolveStringFromCommandLineInput(setup, cur_dir, cur, |
| 108 if (pattern.toolchain().is_null()) { | 421 all_toolchains, target_matches, |
| 109 // No explicit toolchain set. | 422 config_matches, toolchain_matches, |
| 110 pattern.set_toolchain(setup->loader()->default_toolchain_label()); | 423 file_matches)) |
| 111 } | 424 return false; |
| 112 } | 425 } |
| 113 | |
| 114 std::vector<LabelPattern> pattern_vector; | |
| 115 pattern_vector.push_back(pattern); | |
| 116 FilterTargetsByPatterns(setup->builder()->GetAllResolvedTargets(), | |
| 117 pattern_vector, matches); | |
| 118 return true; | 426 return true; |
| 119 } | 427 } |
| 120 | 428 |
| 429 void FilterTargetsByPatterns(const std::vector<const Target*>& input, | |
|
scottmg
2015/02/19 21:52:01
this could be template <class O> for output
brettw
2015/02/19 22:12:02
Yeah, but I didn't feel like I should implement th
| |
| 430 const std::vector<LabelPattern>& filter, | |
| 431 std::vector<const Target*>* output) { | |
| 432 for (const auto& target : input) { | |
| 433 for (const auto& pattern : filter) { | |
| 434 if (pattern.Matches(target->label())) { | |
| 435 output->push_back(target); | |
| 436 break; | |
| 437 } | |
| 438 } | |
| 439 } | |
| 440 } | |
| 441 | |
| 442 void FilterTargetsByPatterns(const std::vector<const Target*>& input, | |
| 443 const std::vector<LabelPattern>& filter, | |
| 444 UniqueVector<const Target*>* output) { | |
| 445 for (const auto& target : input) { | |
| 446 for (const auto& pattern : filter) { | |
| 447 if (pattern.Matches(target->label())) { | |
| 448 output->push_back(target); | |
| 449 break; | |
| 450 } | |
| 451 } | |
| 452 } | |
| 453 } | |
| 454 | |
| 455 void FilterAndPrintTargets(bool indent, std::vector<const Target*>* targets) { | |
| 456 if (targets->empty()) | |
| 457 return; | |
| 458 | |
| 459 if (!ApplyTestonlyFilter(targets)) | |
| 460 return; | |
| 461 if (!ApplyTypeFilter(targets)) | |
| 462 return; | |
| 463 | |
| 464 TargetPrintingMode printing_mode = TARGET_PRINT_LABEL; | |
| 465 if (targets->empty() || !GetTargetPrintingMode(&printing_mode)) | |
| 466 return; | |
| 467 switch (printing_mode) { | |
| 468 case TARGET_PRINT_BUILDFILE: | |
| 469 PrintTargetsAsBuildfiles(indent, *targets); | |
| 470 break; | |
| 471 case TARGET_PRINT_LABEL: | |
| 472 PrintTargetsAsLabels(indent, *targets); | |
| 473 break; | |
| 474 case TARGET_PRINT_OUTPUT: | |
| 475 PrintTargetsAsOutputs(indent, *targets); | |
| 476 break; | |
| 477 } | |
| 478 } | |
| 479 | |
| 480 void FilterAndPrintTargetSet(bool indent, | |
| 481 const std::set<const Target*>& targets) { | |
| 482 std::vector<const Target*> target_vector(targets.begin(), targets.end()); | |
| 483 FilterAndPrintTargets(indent, &target_vector); | |
| 484 } | |
| 485 | |
| 121 } // namespace commands | 486 } // namespace commands |
| OLD | NEW |