| 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_toolchains is false, a pattern with an unspecified toolchain will |
| 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, |
| 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 |