Add --exclude-type command-line argument It essentially does the opposite of `--type`. It's needed for efficient target filtering in `autotest.py` in Chromium: https://chromium-review.googlesource.com/c/chromium/src/+/8139431 Bug: 534608615 Change-Id: If5b2e4ab8cad5529fbc3477adaac83f86a6a6964 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/24520 Reviewed-by: Takuto Ikuta <tikuta@google.com> Commit-Queue: Zgroza (Luke) Klimek <zgroza@chromium.org> Reviewed-by: Andrew Grieve <agrieve@google.com>
diff --git a/docs/reference.md b/docs/reference.md index 62eb514..b726a19 100644 --- a/docs/reference.md +++ b/docs/reference.md
@@ -670,7 +670,7 @@ tree. Tree output can not be used with the filtering or output flags: --as, - --type, --testonly. + --type, --exclude-type, --testonly. --type=(action|bundle_data|copy|create_bundle|executable| generated_file|group|loadable_module|rust_library| @@ -678,6 +678,16 @@ Restrict outputs to targets matching the given type. If unspecified, no filtering will be performed. You can specify a comma-separated list of types to match multiple types. + Can not be used with --exclude-type. + + --exclude-type=(action|bundle_data|copy|create_bundle|executable| + generated_file|group|loadable_module|rust_library| + rust_proc_macro|shared_library|source_set| + static_library) + Exclude targets matching the given type from the outputs. If + unspecified, no filtering will be performed. You can specify + a comma-separated list of types to match multiple types. + Can not be used with --type. ``` #### **Note** @@ -1047,7 +1057,7 @@ ``` ### <a name="cmd_ls"></a>**gn ls <out_dir> [<label_pattern>] [\--default-toolchain] [\--as=...]** [Back to Top](#gn-reference) ``` - [--type=...] [--testonly=...] + [--type=...|--exclude-type=...] [--testonly=...] Lists all targets matching the given pattern for the given build directory. By default, only targets in the default toolchain will be matched unless a @@ -1092,6 +1102,16 @@ Restrict outputs to targets matching the given type. If unspecified, no filtering will be performed. You can specify a comma-separated list of types to match multiple types. + Can not be used with --exclude-type. + + --exclude-type=(action|bundle_data|copy|create_bundle|executable| + generated_file|group|loadable_module|rust_library| + rust_proc_macro|shared_library|source_set| + static_library) + Exclude targets matching the given type from the outputs. If + unspecified, no filtering will be performed. You can specify + a comma-separated list of types to match multiple types. + Can not be used with --type. ``` #### **Examples** @@ -1276,7 +1296,8 @@ ``` gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)* [--all] - [--default-toolchain] [--as=...] [--testonly=...] [--type=...] + [--default-toolchain] [--as=...] [--testonly=...] + [--type=...|--exclude-type=...] Finds reverse dependencies (which targets reference something). The input is a list containing: @@ -1348,7 +1369,7 @@ be elided. Combine with --all to see a full dependency tree. Tree output can not be used with the filtering or output flags: --as, - --type, --testonly. + --type, --exclude-type, --testonly. --type=(action|bundle_data|copy|create_bundle|executable| generated_file|group|loadable_module|rust_library| @@ -1356,6 +1377,16 @@ Restrict outputs to targets matching the given type. If unspecified, no filtering will be performed. You can specify a comma-separated list of types to match multiple types. + Can not be used with --exclude-type. + + --exclude-type=(action|bundle_data|copy|create_bundle|executable| + generated_file|group|loadable_module|rust_library| + rust_proc_macro|shared_library|source_set| + static_library) + Exclude targets matching the given type from the outputs. If + unspecified, no filtering will be performed. You can specify + a comma-separated list of types to match multiple types. + Can not be used with --type. --relation=(source|public|input|data|script|output) Restricts output to targets which refer to input files by a specific
diff --git a/src/gn/command_desc.cc b/src/gn/command_desc.cc index e7b0c99..5b9dd2b 100644 --- a/src/gn/command_desc.cc +++ b/src/gn/command_desc.cc
@@ -604,7 +604,7 @@ tree. Tree output can not be used with the filtering or output flags: --as, - --type, --testonly. + --type, --exclude-type, --testonly. )"
diff --git a/src/gn/command_ls.cc b/src/gn/command_ls.cc index 0dbf911..b2879ac 100644 --- a/src/gn/command_ls.cc +++ b/src/gn/command_ls.cc
@@ -19,7 +19,7 @@ const char kLs_HelpShort[] = "ls: List matching targets."; const char kLs_Help[] = R"(gn ls <out_dir> [<label_pattern>] [--default-toolchain] [--as=...] - [--type=...] [--testonly=...] + [--type=...|--exclude-type=...] [--testonly=...] Lists all targets matching the given pattern for the given build directory. By default, only targets in the default toolchain will be matched unless a
diff --git a/src/gn/command_refs.cc b/src/gn/command_refs.cc index 0e7770b..4f08886 100644 --- a/src/gn/command_refs.cc +++ b/src/gn/command_refs.cc
@@ -237,7 +237,8 @@ R"(gn refs gn refs <out_dir> (<label_pattern>|<label>|<file>|@<response_file>)* [--all] - [--default-toolchain] [--as=...] [--testonly=...] [--type=...] + [--default-toolchain] [--as=...] [--testonly=...] + [--type=...|--exclude-type=...] Finds reverse dependencies (which targets reference something). The input is a list containing: @@ -292,7 +293,7 @@ be elided. Combine with --all to see a full dependency tree. Tree output can not be used with the filtering or output flags: --as, - --type, --testonly. + --type, --exclude-type, --testonly. )"
diff --git a/src/gn/commands.cc b/src/gn/commands.cc index a2cca9a..582ba01 100644 --- a/src/gn/commands.cc +++ b/src/gn/commands.cc
@@ -4,6 +4,7 @@ #include "gn/commands.h" +#include <algorithm> #include <fstream> #include <optional> @@ -153,6 +154,18 @@ return true; } +// Returns the target exclude type filter based on the command line flags for +// the current process. Returns true on success. On error, prints a message to +// the console and returns false. +// +// The vector will be empty if there is no filter. Target::ACTION_FOREACH +// will never be returned. Code applying the filters should apply Target::ACTION +// to both ACTION and ACTION_FOREACH. +bool GetTargetExcludeTypeFilter(std::vector<Target::OutputType>* types) { + *types = CommandSwitches::Get().target_exclude_types(); + return true; +} + // Applies any testonly filtering specified on the command line to the given // target set. On failure, prints an error and returns false. bool ApplyTestonlyFilter(std::vector<const Target*>* targets) { @@ -177,13 +190,16 @@ return true; } -// Applies any target type filtering specified on the command line to the given -// target set. On failure, prints an error and returns false. +// Applies any target type and exclude type filtering specified on the command +// line to the given target set. On failure, prints an error and returns false. bool ApplyTypeFilter(std::vector<const Target*>* targets) { std::vector<Target::OutputType> types; if (!GetTargetTypeFilter(&types)) return false; - if (targets->empty() || types.empty()) + std::vector<Target::OutputType> exclude_types; + if (!GetTargetExcludeTypeFilter(&exclude_types)) + return false; + if (targets->empty() || (types.empty() && exclude_types.empty())) return true; // Nothing to filter out. // Filter into a copy of the vector, then replace the output. @@ -191,14 +207,18 @@ result.reserve(targets->size()); for (const Target* target : *targets) { - if (std::ranges::any_of(types, [target](Target::OutputType type) { - // Make "action" also apply to ACTION_FOREACH. - return target->output_type() == type || - (type == Target::ACTION && - target->output_type() == Target::ACTION_FOREACH); - })) { - result.push_back(target); + Target::OutputType target_type = + target->output_type() == Target::ACTION_FOREACH ? Target::ACTION + : target->output_type(); + if (!types.empty() && + std::ranges::find(types, target_type) == types.end()) { + continue; } + if (!exclude_types.empty() && + std::ranges::find(exclude_types, target_type) != exclude_types.end()) { + continue; + } + result.push_back(target); } *targets = std::move(result); @@ -464,8 +484,19 @@ } std::string_view target_type_switch = "type"; - if (cmdline.HasSwitch(target_type_switch)) { - std::string value = cmdline.GetSwitchValueString(target_type_switch); + std::string_view target_exclude_type_switch = "exclude-type"; + if (cmdline.HasSwitch(target_type_switch) && + cmdline.HasSwitch(target_exclude_type_switch)) { + Err(Location(), "Can't specify both \"--type\" and \"--exclude-type\".") + .PrintToStdout(); + return false; + } + if (cmdline.HasSwitch(target_type_switch) || + cmdline.HasSwitch(target_exclude_type_switch)) { + std::string_view switch_name = cmdline.HasSwitch(target_type_switch) + ? target_type_switch + : target_exclude_type_switch; + std::string value = cmdline.GetSwitchValueString(switch_name); std::vector<std::string> tokens = base::SplitString( value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); static const struct { @@ -486,17 +517,21 @@ {"bundle_data", Target::BUNDLE_DATA}, {"create_bundle", Target::CREATE_BUNDLE}, }; + std::vector<Target::OutputType>& target_types_vector = + cmdline.HasSwitch(target_type_switch) ? result.target_types_ + : result.target_exclude_types_; for (const std::string& token : tokens) { bool found = false; for (const auto& type : kTypes) { if (token == type.name) { - result.target_types_.push_back(type.type); + target_types_vector.push_back(type.type); found = true; break; } } if (!found) { - Err(Location(), "Invalid value \"" + token + "\" for \"--type\".") + Err(Location(), "Invalid value \"" + token + "\" for \"--" + + std::string(switch_name) + "\".") .PrintToStdout(); return false; }
diff --git a/src/gn/commands.h b/src/gn/commands.h index 27fd81e..812d342 100644 --- a/src/gn/commands.h +++ b/src/gn/commands.h
@@ -202,6 +202,11 @@ return target_types_; } + // For --exclude-type=TARGET_TYPE + const std::vector<Target::OutputType>& target_exclude_types() const { + return target_exclude_types_; + } + enum TestonlyMode { TESTONLY_NONE, // no --testonly used. TESTONLY_FALSE, // --testonly=false @@ -248,6 +253,7 @@ TargetPrintMode target_print_mode_ = TARGET_PRINT_LABEL; std::vector<Target::OutputType> target_types_; + std::vector<Target::OutputType> target_exclude_types_; TestonlyMode testonly_mode_ = TESTONLY_NONE; std::string meta_rebase_dir_; @@ -362,21 +368,32 @@ " output\n" \ " Prints the first output file for the target relative to the\n" \ " root build directory.\n" -#define TARGET_TYPE_FILTER_COMMAND_LINE_HELP \ - " --type=(action|bundle_data|copy|create_bundle|executable|\n" \ - " generated_file|group|loadable_module|rust_library|\n" \ - " rust_proc_macro|shared_library|source_set|static_library)\n" \ - " Restrict outputs to targets matching the given type. If\n" \ - " unspecified, no filtering will be performed. You can specify\n" \ - " a comma-separated list of types to match multiple types.\n" +#define TARGET_TYPE_FILTER_COMMAND_LINE_HELP \ + " --type=(action|bundle_data|copy|create_bundle|executable|\n" \ + " generated_file|group|loadable_module|rust_library|\n" \ + " rust_proc_macro|shared_library|source_set|static_library)\n" \ + " Restrict outputs to targets matching the given type. If\n" \ + " unspecified, no filtering will be performed. You can specify\n" \ + " a comma-separated list of types to match multiple types.\n" \ + " Can not be used with --exclude-type.\n" \ + "\n" \ + " --exclude-type=(action|bundle_data|copy|create_bundle|executable|\n" \ + " generated_file|group|loadable_module|rust_library|\n" \ + " rust_proc_macro|shared_library|source_set|\n" \ + " static_library)\n" \ + " Exclude targets matching the given type from the outputs. If\n" \ + " unspecified, no filtering will be performed. You can specify\n" \ + " a comma-separated list of types to match multiple types.\n" \ + " Can not be used with --type.\n" #define TARGET_TESTONLY_FILTER_COMMAND_LINE_HELP \ " --testonly=(true|false)\n" \ " Restrict outputs to targets with the testonly flag set\n" \ " accordingly. When unspecified, the target's testonly flags are\n" \ " ignored.\n" -// Applies any testonly and type filters specified on the command line, -// and prints the targets as specified by the --as command line flag. +// Applies any testonly, type, and exclude-type filters specified on the +// command line, and prints the targets as specified by the --as command line +// flag. // // If indent is true, the results will be indented two spaces. //
diff --git a/src/gn/commands_unittest.cc b/src/gn/commands_unittest.cc index eb95f68..8c7ce5f 100644 --- a/src/gn/commands_unittest.cc +++ b/src/gn/commands_unittest.cc
@@ -109,4 +109,125 @@ commands::CommandSwitches empty_switches; commands::CommandSwitches::Set(empty_switches); } + + for (const auto& test_case : cases) { + std::vector<const Target*> targets_to_filter = all_targets; + + base::CommandLine cmdline(base::CommandLine::NO_PROGRAM); + cmdline.AppendSwitch("exclude-type", test_case.first); + + commands::CommandSwitches::Init(cmdline); + + base::ListValue out; + commands::FilterAndPrintTargets(&targets_to_filter, &out); + + ASSERT_EQ(all_targets.size() - 1u, targets_to_filter.size()) + << "Failed for type: " << test_case.first; + EXPECT_FALSE(std::ranges::any_of(targets_to_filter, + [&test_case](const Target* target) { + return target->output_type() == + test_case.second; + })) + << "Failed for type: " << test_case.first; + + commands::CommandSwitches empty_switches; + commands::CommandSwitches::Set(empty_switches); + } + + // Test multiple exclude types separated by a comma. + { + std::vector<const Target*> targets_to_filter = all_targets; + + base::CommandLine cmdline(base::CommandLine::NO_PROGRAM); + cmdline.AppendSwitch("exclude-type", "executable,rust_library"); + + commands::CommandSwitches::Init(cmdline); + + base::ListValue out; + commands::FilterAndPrintTargets(&targets_to_filter, &out); + + ASSERT_EQ(all_targets.size() - 2u, targets_to_filter.size()); + EXPECT_FALSE( + std::ranges::any_of(targets_to_filter, [](const Target* target) { + return target->output_type() == Target::EXECUTABLE; + })); + EXPECT_FALSE( + std::ranges::any_of(targets_to_filter, [](const Target* target) { + return target->output_type() == Target::RUST_LIBRARY; + })); + + commands::CommandSwitches empty_switches; + commands::CommandSwitches::Set(empty_switches); + } + + // Test that supplying both --type and --exclude-type fails. + { + base::CommandLine cmdline(base::CommandLine::NO_PROGRAM); + cmdline.AppendSwitch("type", "executable"); + cmdline.AppendSwitch("exclude-type", "shared_library"); + + EXPECT_FALSE(commands::CommandSwitches::Init(cmdline)); + } + + // Test invalid switch values for --type and --exclude-type. + { + base::CommandLine cmdline(base::CommandLine::NO_PROGRAM); + cmdline.AppendSwitch("type", "invalid_type"); + EXPECT_FALSE(commands::CommandSwitches::Init(cmdline)); + } + { + base::CommandLine cmdline(base::CommandLine::NO_PROGRAM); + cmdline.AppendSwitch("exclude-type", "invalid_type"); + EXPECT_FALSE(commands::CommandSwitches::Init(cmdline)); + } + + // Test Target::ACTION_FOREACH aliasing with --exclude-type=action. + { + auto action_foreach_target = std::make_unique<Target>( + setup.settings(), Label(SourceDir("//a/"), "action_foreach_target")); + action_foreach_target->set_output_type(Target::ACTION_FOREACH); + std::vector<const Target*> targets_to_filter = all_targets; + targets_to_filter.push_back(action_foreach_target.get()); + + base::CommandLine cmdline(base::CommandLine::NO_PROGRAM); + cmdline.AppendSwitch("exclude-type", "action"); + + commands::CommandSwitches::Init(cmdline); + + base::ListValue out; + commands::FilterAndPrintTargets(&targets_to_filter, &out); + + EXPECT_FALSE( + std::ranges::any_of(targets_to_filter, [](const Target* target) { + return target->output_type() == Target::ACTION || + target->output_type() == Target::ACTION_FOREACH; + })); + + commands::CommandSwitches empty_switches; + commands::CommandSwitches::Set(empty_switches); + } + + // Test combined --exclude-type and --testonly filtering. + { + std::vector<const Target*> targets_to_filter = all_targets; + // Mark one target as testonly=true. + created_targets[0]->set_testonly(true); + + base::CommandLine cmdline(base::CommandLine::NO_PROGRAM); + cmdline.AppendSwitch("exclude-type", "shared_library"); + cmdline.AppendSwitch("testonly", "true"); + + commands::CommandSwitches::Init(cmdline); + + base::ListValue out; + commands::FilterAndPrintTargets(&targets_to_filter, &out); + + for (const Target* target : targets_to_filter) { + EXPECT_TRUE(target->testonly()); + EXPECT_NE(target->output_type(), Target::SHARED_LIBRARY); + } + + commands::CommandSwitches empty_switches; + commands::CommandSwitches::Set(empty_switches); + } }