[label_matches] Add new functions label_matches(), filter_labels_include() and filter_labels_exclude() Add a new function 'label_matches(target_label, label_patterns)' that can be used to test if a given GN label matches any of the given label patterns. Add a new pair of functions: - 'filter_labels_include(labels, label_patterns)' - 'filter_labels_exclude(labels, label_patterns)' These returns all labels in the first list that match (or don't match, respectively) any of the patterns in the second list (much like the 'filter_include()' and 'filter_exclude()' functions do for files. Change-Id: I7670099dce41c3ff38219adf8fd740b6fb935e78 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/16680 Reviewed-by: Dirk Pranke <dpranke@google.com> Commit-Queue: Aaron Wood <aaronwood@google.com>
diff --git a/build/gen.py b/build/gen.py index e7037bb..3570746 100755 --- a/build/gen.py +++ b/build/gen.py
@@ -664,11 +664,13 @@ 'src/gn/frameworks_utils.cc', 'src/gn/function_exec_script.cc', 'src/gn/function_filter.cc', + 'src/gn/function_filter_labels.cc', 'src/gn/function_foreach.cc', 'src/gn/function_forward_variables_from.cc', 'src/gn/function_get_label_info.cc', 'src/gn/function_get_path_info.cc', 'src/gn/function_get_target_outputs.cc', + 'src/gn/function_label_matches.cc', 'src/gn/function_process_file_template.cc', 'src/gn/function_read_file.cc', 'src/gn/function_rebase_path.cc', @@ -799,11 +801,13 @@ 'src/gn/file_writer_unittest.cc', 'src/gn/frameworks_utils_unittest.cc', 'src/gn/function_filter_unittest.cc', + 'src/gn/function_filter_labels_unittest.cc', 'src/gn/function_foreach_unittest.cc', 'src/gn/function_forward_variables_from_unittest.cc', 'src/gn/function_get_label_info_unittest.cc', 'src/gn/function_get_path_info_unittest.cc', 'src/gn/function_get_target_outputs_unittest.cc', + 'src/gn/function_label_matches_unittest.cc', 'src/gn/function_process_file_template_unittest.cc', 'src/gn/function_rebase_path_unittest.cc', 'src/gn/function_template_unittest.cc',
diff --git a/docs/reference.md b/docs/reference.md index 24036dc..9621765 100644 --- a/docs/reference.md +++ b/docs/reference.md
@@ -43,6 +43,8 @@ * [exec_script: Synchronously run a script and return the output.](#func_exec_script) * [filter_exclude: Remove values that match a set of patterns.](#func_filter_exclude) * [filter_include: Remove values that do not match a set of patterns.](#func_filter_include) + * [filter_labels_exclude: Remove labels that match a set of patterns.](#func_filter_labels_exclude) + * [filter_labels_include: Remove labels that do not match a set of patterns.](#func_filter_labels_include) * [foreach: Iterate over a list.](#func_foreach) * [forward_variables_from: Copies variables from a different scope.](#func_forward_variables_from) * [get_label_info: Get an attribute from a target's label.](#func_get_label_info) @@ -50,6 +52,7 @@ * [get_target_outputs: [file list] Get the list of outputs from a target.](#func_get_target_outputs) * [getenv: Get an environment variable.](#func_getenv) * [import: Import a file into the current scope.](#func_import) + * [label_matches: Returns whether a label matches any of a list of patterns.](#func_label_matches) * [not_needed: Mark variables from scope as not needed.](#func_not_needed) * [pool: Defines a pool object.](#func_pool) * [print: Prints to the console.](#func_print) @@ -2610,6 +2613,42 @@ result = filter_include(values, [ "*.proto" ]) # result will be [ "foo.proto" ] ``` +### <a name="func_filter_labels_exclude"></a>**filter_labels_exclude**: Remove labels that match a set of patterns. + +``` + filter_labels_exclude(labels, exclude_patterns) + + The argument labels must be a list of strings. + + The argument exclude_patterns must be a list of label patterns (see + "gn help label_pattern"). Only elements from labels matching at least + one of the patterns will be excluded. +``` + +#### **Examples** +``` + labels = [ "//foo:baz", "//foo/bar:baz", "//bar:baz" ] + result = filter_labels_exclude(labels, [ "//foo:*" ]) + # result will be [ "//foo/bar:baz", "//bar:baz" ] +``` +### <a name="func_filter_labels_include"></a>**filter_labels_include**: Remove labels that do not match a set of patterns. + +``` + filter_labels_include(labels, include_patterns) + + The argument labels must be a list of strings. + + The argument include_patterns must be a list of label patterns (see + "gn help label_pattern"). Only elements from labels matching at least + one of the patterns will be included. +``` + +#### **Examples** +``` + labels = [ "//foo:baz", "//foo/bar:baz", "//bar:baz" ] + result = filter_labels_include(labels, [ "//foo:*" ]) + # result will be [ "//foo:baz" ] +``` ### <a name="func_foreach"></a>**foreach**: Iterate over a list. ``` @@ -2972,6 +3011,21 @@ # Looks in the current directory. import("my_vars.gni") ``` +### <a name="func_label_matches"></a>**label_matches**: Returns true if the label matches any of a set of patterns. + +``` + label_matches(target_label, patterns) + + The argument patterns must be a list of label patterns (see + "gn help label_pattern"). If the target_label matches any of the patterns, + the function returns the value true. +``` + +#### **Examples** +``` + result = label_matches("//baz:bar", [ "//foo/bar/*", "//baz:*" ]) + # result will be true +``` ### <a name="func_not_needed"></a>**not_needed**: Mark variables from scope as not needed. ```
diff --git a/src/gn/function_filter_labels.cc b/src/gn/function_filter_labels.cc new file mode 100644 index 0000000..eb08def --- /dev/null +++ b/src/gn/function_filter_labels.cc
@@ -0,0 +1,145 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stddef.h> + +#include "gn/build_settings.h" +#include "gn/err.h" +#include "gn/functions.h" +#include "gn/label_pattern.h" +#include "gn/parse_tree.h" +#include "gn/scope.h" +#include "gn/settings.h" +#include "gn/value.h" + +namespace functions { + +enum FilterSelection { + kExcludeFilter, + kIncludeFilter, +}; + +Value RunFilterLabels(Scope* scope, + const FunctionCallNode* function, + const std::vector<Value>& args, + FilterSelection selection, + Err* err) { + if (args.size() != 2) { + *err = Err(function, "Expecting exactly two arguments."); + return Value(); + } + + // Validate "labels" and "patterns" are both lists + if (args[0].type() != Value::LIST) { + *err = Err(args[0], "First argument must be a list of target labels."); + return Value(); + } + if (args[1].type() != Value::LIST) { + *err = Err(args[1], "Second argument must be a list of label patterns."); + return Value(); + } + + // Extract "patterns" + std::vector<LabelPattern> patterns; + patterns.reserve(args[1].list_value().size()); + + for (const auto& value : args[1].list_value()) { + if (value.type() != Value::STRING) { + *err = Err(args[1], "Second argument must be a list of label patterns."); + return Value(); + } + LabelPattern pattern = LabelPattern::GetPattern( + scope->GetSourceDir(), + scope->settings()->build_settings()->root_path_utf8(), value, err); + if (err->has_error()) { + return Value(); + } + patterns.push_back(std::move(pattern)); + } + + // Iterate over "labels", resolving and matching against the list of patterns. + Value result(function, Value::LIST); + for (const auto& value : args[0].list_value()) { + Label label = + Label::Resolve(scope->GetSourceDir(), + scope->settings()->build_settings()->root_path_utf8(), + ToolchainLabelForScope(scope), value, err); + if (err->has_error()) { + // Change the error message to be more applicable than what Resolve will + // produce. + *err = Err(value, "First argument must be a list of target labels."); + return Value(); + } + + const bool matches_pattern = LabelPattern::VectorMatches(patterns, label); + switch (selection) { + case kIncludeFilter: + if (matches_pattern) + result.list_value().push_back(value); + break; + + case kExcludeFilter: + if (!matches_pattern) + result.list_value().push_back(value); + break; + } + } + return result; +} + +const char kFilterLabelsInclude[] = "filter_labels_include"; +const char kFilterLabelsInclude_HelpShort[] = + "filter_labels_include: Remove labels that do not match a set of patterns."; +const char kFilterLabelsInclude_Help[] = + R"(filter_labels_include: Remove labels that do not match a set of patterns. + + filter_labels_include(labels, include_patterns) + + The argument labels must be a list of strings. + + The argument include_patterns must be a list of label patterns (see + "gn help label_pattern"). Only elements from labels matching at least + one of the patterns will be included. + +Examples + labels = [ "//foo:baz", "//foo/bar:baz", "//bar:baz" ] + result = filter_labels_include(labels, [ "//foo:*" ]) + # result will be [ "//foo:baz" ] +)"; + +Value RunFilterLabelsInclude(Scope* scope, + const FunctionCallNode* function, + const std::vector<Value>& args, + Err* err) { + return RunFilterLabels(scope, function, args, kIncludeFilter, err); +} + +const char kFilterLabelsExclude[] = "filter_labels_exclude"; +const char kFilterLabelsExclude_HelpShort[] = + "filter_labels_exclude: Remove labels that match a set of patterns."; +const char kFilterLabelsExclude_Help[] = + R"(filter_labels_exclude: Remove labels that match a set of patterns. + + filter_labels_exclude(labels, exclude_patterns) + + The argument labels must be a list of strings. + + The argument exclude_patterns must be a list of label patterns (see + "gn help label_pattern"). Only elements from labels matching at least + one of the patterns will be excluded. + +Examples + labels = [ "//foo:baz", "//foo/bar:baz", "//bar:baz" ] + result = filter_labels_exclude(labels, [ "//foo:*" ]) + # result will be [ "//foo/bar:baz", "//bar:baz" ] +)"; + +Value RunFilterLabelsExclude(Scope* scope, + const FunctionCallNode* function, + const std::vector<Value>& args, + Err* err) { + return RunFilterLabels(scope, function, args, kExcludeFilter, err); +} + +} // namespace functions
diff --git a/src/gn/function_filter_labels_unittest.cc b/src/gn/function_filter_labels_unittest.cc new file mode 100644 index 0000000..4a0ebf3 --- /dev/null +++ b/src/gn/function_filter_labels_unittest.cc
@@ -0,0 +1,255 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gn/functions.h" +#include "gn/test_with_scope.h" +#include "util/build_config.h" +#include "util/test/test.h" + +TEST(FilterLabelsTest, OneIncluded) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + Value labels(nullptr, Value::LIST); + labels.list_value().push_back(Value(nullptr, "//foo:bar")); + labels.list_value().push_back(Value(nullptr, "//baz:bar")); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//foo/*")); + patterns.list_value().push_back(Value(nullptr, "//bar:*")); + + args.push_back(labels); + args.push_back(patterns); + + Err err; + Value result = + functions::RunFilterLabelsInclude(setup.scope(), &function, args, &err); + ASSERT_FALSE(err.has_error()); + ASSERT_EQ(result.type(), Value::LIST); + ASSERT_EQ(1u, result.list_value().size()); + ASSERT_TRUE(result.list_value()[0].type() == Value::STRING); + ASSERT_EQ("//foo:bar", result.list_value()[0].string_value()); +} + +TEST(FilterLabelsTest, TwoIncluded) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + Value labels(nullptr, Value::LIST); + labels.list_value().push_back(Value(nullptr, "//foo:bar")); + labels.list_value().push_back(Value(nullptr, "//bar")); + labels.list_value().push_back(Value(nullptr, "//baz:bar")); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//foo/*")); + patterns.list_value().push_back(Value(nullptr, "//bar:*")); + + args.push_back(labels); + args.push_back(patterns); + + Err err; + Value result = + functions::RunFilterLabelsInclude(setup.scope(), &function, args, &err); + ASSERT_FALSE(err.has_error()); + ASSERT_EQ(result.type(), Value::LIST); + ASSERT_EQ(2u, result.list_value().size()); + ASSERT_TRUE(result.list_value()[0].type() == Value::STRING); + ASSERT_EQ("//foo:bar", result.list_value()[0].string_value()); + ASSERT_TRUE(result.list_value()[1].type() == Value::STRING); + ASSERT_EQ("//bar", result.list_value()[1].string_value()); +} + +TEST(FilterLabelsTest, NoneIncluded) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + Value labels(nullptr, Value::LIST); + labels.list_value().push_back(Value(nullptr, "//foo:bar")); + labels.list_value().push_back(Value(nullptr, "//baz:bar")); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//fooz/*")); + patterns.list_value().push_back(Value(nullptr, "//bar:*")); + + args.push_back(labels); + args.push_back(patterns); + + Err err; + Value result = + functions::RunFilterLabelsInclude(setup.scope(), &function, args, &err); + ASSERT_FALSE(err.has_error()); + ASSERT_EQ(result.type(), Value::LIST); + ASSERT_EQ(0u, result.list_value().size()); +} + +TEST(FilterLabelsTest, OneExcluded) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + Value labels(nullptr, Value::LIST); + labels.list_value().push_back(Value(nullptr, "//foo:bar")); + labels.list_value().push_back(Value(nullptr, "//baz:bar")); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//foo/*")); + patterns.list_value().push_back(Value(nullptr, "//bar:*")); + + args.push_back(labels); + args.push_back(patterns); + + Err err; + Value result = + functions::RunFilterLabelsExclude(setup.scope(), &function, args, &err); + ASSERT_FALSE(err.has_error()); + ASSERT_EQ(result.type(), Value::LIST); + ASSERT_EQ(1u, result.list_value().size()); + ASSERT_TRUE(result.list_value()[0].type() == Value::STRING); + ASSERT_EQ("//baz:bar", result.list_value()[0].string_value()); +} + +TEST(FilterLabelsTest, TwoExcluded) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + Value labels(nullptr, Value::LIST); + labels.list_value().push_back(Value(nullptr, "//foo:bar")); + labels.list_value().push_back(Value(nullptr, "//bar")); + labels.list_value().push_back(Value(nullptr, "//baz:bar")); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//foo/*")); + patterns.list_value().push_back(Value(nullptr, "//bar:*")); + + args.push_back(labels); + args.push_back(patterns); + + Err err; + Value result = + functions::RunFilterLabelsExclude(setup.scope(), &function, args, &err); + ASSERT_FALSE(err.has_error()); + ASSERT_EQ(result.type(), Value::LIST); + ASSERT_EQ(1u, result.list_value().size()); + ASSERT_TRUE(result.list_value()[0].type() == Value::STRING); + ASSERT_EQ("//baz:bar", result.list_value()[0].string_value()); +} + +TEST(FilterLabelsTest, NoneExcluded) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + Value labels(nullptr, Value::LIST); + labels.list_value().push_back(Value(nullptr, "//foo:bar")); + labels.list_value().push_back(Value(nullptr, "//baz:bar")); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//fooz/*")); + patterns.list_value().push_back(Value(nullptr, "//bar:*")); + + args.push_back(labels); + args.push_back(patterns); + + Err err; + Value result = + functions::RunFilterLabelsExclude(setup.scope(), &function, args, &err); + ASSERT_FALSE(err.has_error()); + ASSERT_EQ(result.type(), Value::LIST); + ASSERT_EQ(2u, result.list_value().size()); + ASSERT_TRUE(result.list_value()[0].type() == Value::STRING); + ASSERT_EQ("//foo:bar", result.list_value()[0].string_value()); + ASSERT_TRUE(result.list_value()[1].type() == Value::STRING); + ASSERT_EQ("//baz:bar", result.list_value()[1].string_value()); +} + +TEST(FilterLabelsTest, LabelsIsList) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + Value labels(nullptr, true); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//foo/*")); + patterns.list_value().push_back(Value(nullptr, "//bar:*")); + + args.push_back(labels); + args.push_back(patterns); + + Err err; + Value result = + functions::RunFilterLabelsInclude(setup.scope(), &function, args, &err); + ASSERT_TRUE(err.has_error()); + ASSERT_EQ(err.message(), "First argument must be a list of target labels."); +} + +TEST(FilterLabelsTest, PatternsIsList) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + Value labels(nullptr, Value::LIST); + labels.list_value().push_back(Value(nullptr, "//foo:bar")); + labels.list_value().push_back(Value(nullptr, "//baz:bar")); + + Value patterns(nullptr, true); + + args.push_back(labels); + args.push_back(patterns); + + Err err; + Value result = + functions::RunFilterLabelsInclude(setup.scope(), &function, args, &err); + ASSERT_TRUE(err.has_error()); + ASSERT_EQ(err.message(), "Second argument must be a list of label patterns."); +} + +TEST(FilterLabelsTest, LabelsAreLabels) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + Value labels(nullptr, Value::LIST); + labels.list_value().push_back(Value(nullptr, "//foo:bar")); + labels.list_value().push_back(Value(nullptr, true)); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//foo/*")); + patterns.list_value().push_back(Value(nullptr, "//bar:*")); + + args.push_back(labels); + args.push_back(patterns); + + Err err; + Value result = + functions::RunFilterLabelsInclude(setup.scope(), &function, args, &err); + ASSERT_TRUE(err.has_error()); + ASSERT_EQ(err.message(), "First argument must be a list of target labels."); +} + +TEST(FilterLabelsTest, PatternsArePatterns) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + Value labels(nullptr, Value::LIST); + labels.list_value().push_back(Value(nullptr, "//foo:bar")); + labels.list_value().push_back(Value(nullptr, "//bar")); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//foo/*:foo")); + + args.push_back(labels); + args.push_back(patterns); + + Err err; + Value result = + functions::RunFilterLabelsInclude(setup.scope(), &function, args, &err); + ASSERT_TRUE(err.has_error()); + ASSERT_EQ(err.message(), "Invalid label pattern."); +} \ No newline at end of file
diff --git a/src/gn/function_label_matches.cc b/src/gn/function_label_matches.cc new file mode 100644 index 0000000..45e11d4 --- /dev/null +++ b/src/gn/function_label_matches.cc
@@ -0,0 +1,84 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stddef.h> + +#include "gn/build_settings.h" +#include "gn/err.h" +#include "gn/functions.h" +#include "gn/label_pattern.h" +#include "gn/parse_tree.h" +#include "gn/scope.h" +#include "gn/settings.h" +#include "gn/value.h" + +namespace functions { + +const char kLabelMatches[] = "label_matches"; +const char kLabelMatches_HelpShort[] = + "label_matches: Returns whether a label matches any of a list of patterns."; +const char kLabelMatches_Help[] = + R"(label_matches: Returns true if the label matches any of a set of patterns. + + label_matches(target_label, patterns) + + The argument patterns must be a list of label patterns (see + "gn help label_pattern"). If the target_label matches any of the patterns, + the function returns the value true. + +Examples + result = label_matches("//baz:bar", [ "//foo/bar/*", "//baz:*" ]) + # result will be true +)"; + +Value RunLabelMatches(Scope* scope, + const FunctionCallNode* function, + const std::vector<Value>& args, + Err* err) { + if (args.size() != 2) { + *err = Err(function, "Expecting exactly two arguments."); + return Value(); + } + + // Extract "label" + if (args[0].type() != Value::STRING) { + *err = Err(args[0], "First argument must be a target label."); + return Value(); + } + Label label = + Label::Resolve(scope->GetSourceDir(), + scope->settings()->build_settings()->root_path_utf8(), + ToolchainLabelForScope(scope), args[0], err); + if (label.is_null()) { + return Value(); + } + + // Extract "patterns". + if (args[1].type() != Value::LIST) { + *err = Err(args[1], "Second argument must be a list of label patterns."); + return Value(); + } + std::vector<LabelPattern> patterns; + patterns.reserve(args[1].list_value().size()); + + for (const auto& pattern_string : args[1].list_value()) { + if (pattern_string.type() != Value::STRING) { + *err = Err(pattern_string, + "Second argument must be a list of label patterns."); + return Value(); + } + LabelPattern pattern = LabelPattern::GetPattern( + scope->GetSourceDir(), + scope->settings()->build_settings()->root_path_utf8(), pattern_string, + err); + if (err->has_error()) { + return Value(); + } + patterns.push_back(std::move(pattern)); + } + + return Value(function, LabelPattern::VectorMatches(patterns, label)); +} + +} // namespace functions
diff --git a/src/gn/function_label_matches_unittest.cc b/src/gn/function_label_matches_unittest.cc new file mode 100644 index 0000000..f7bda69 --- /dev/null +++ b/src/gn/function_label_matches_unittest.cc
@@ -0,0 +1,123 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "gn/functions.h" +#include "gn/test_with_scope.h" +#include "util/build_config.h" +#include "util/test/test.h" + +TEST(LabelMatchesTest, MatchesSubTarget) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + args.push_back(Value(nullptr, "//foo:bar")); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//foo/*")); + patterns.list_value().push_back(Value(nullptr, "//bar:*")); + args.push_back(patterns); + + Err err; + Value result = + functions::RunLabelMatches(setup.scope(), &function, args, &err); + ASSERT_FALSE(err.has_error()); + ASSERT_EQ(result.type(), Value::BOOLEAN); + ASSERT_EQ(result.boolean_value(), true); +} + +TEST(LabelMatchesTest, MatchesTargetInFile) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + args.push_back(Value(nullptr, "//bar:foo")); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//foo/*")); + patterns.list_value().push_back(Value(nullptr, "//bar:*")); + args.push_back(patterns); + + Err err; + Value result = + functions::RunLabelMatches(setup.scope(), &function, args, &err); + ASSERT_FALSE(err.has_error()); + ASSERT_EQ(result.type(), Value::BOOLEAN); + ASSERT_EQ(result.boolean_value(), true); +} + +TEST(LabelMatchesTest, NoMatch) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + args.push_back(Value(nullptr, "//baz/foo:bar")); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//foo/*")); + patterns.list_value().push_back(Value(nullptr, "//bar:*")); + args.push_back(patterns); + + Err err; + Value result = + functions::RunLabelMatches(setup.scope(), &function, args, &err); + ASSERT_FALSE(err.has_error()); + ASSERT_EQ(result.type(), Value::BOOLEAN); + ASSERT_EQ(result.boolean_value(), false); +} + +TEST(LabelMatchesTest, LabelMustBeString) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + args.push_back(Value(nullptr, true)); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//foo/*")); + patterns.list_value().push_back(Value(nullptr, "//bar:*")); + args.push_back(patterns); + + Err err; + Value result = + functions::RunLabelMatches(setup.scope(), &function, args, &err); + ASSERT_TRUE(err.has_error()); + ASSERT_EQ(err.message(), "First argument must be a target label."); +} + +TEST(LabelMatchesTest, PatternsMustBeList) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + args.push_back(Value(nullptr, "//baz/foo:bar")); + + Value patterns(nullptr, true); + args.push_back(patterns); + + Err err; + Value result = + functions::RunLabelMatches(setup.scope(), &function, args, &err); + ASSERT_TRUE(err.has_error()); + ASSERT_EQ(err.message(), "Second argument must be a list of label patterns."); +} + +TEST(LabelMatchesTest, PatternsMustBeListOfStrings) { + TestWithScope setup; + FunctionCallNode function; + + std::vector<Value> args; + args.push_back(Value(nullptr, "//baz/foo:bar")); + + Value patterns(nullptr, Value::LIST); + patterns.list_value().push_back(Value(nullptr, "//foo/*")); + patterns.list_value().push_back(Value(nullptr, true)); + args.push_back(patterns); + + Err err; + Value result = + functions::RunLabelMatches(setup.scope(), &function, args, &err); + ASSERT_TRUE(err.has_error()); + ASSERT_EQ(err.message(), "Second argument must be a list of label patterns."); +}
diff --git a/src/gn/functions.cc b/src/gn/functions.cc index 770ab02..adc1ce3 100644 --- a/src/gn/functions.cc +++ b/src/gn/functions.cc
@@ -1465,6 +1465,8 @@ INSERT_FUNCTION(ExecScript, false) INSERT_FUNCTION(FilterExclude, false) INSERT_FUNCTION(FilterInclude, false) + INSERT_FUNCTION(FilterLabelsInclude, false) + INSERT_FUNCTION(FilterLabelsExclude, false) INSERT_FUNCTION(ForEach, false) INSERT_FUNCTION(ForwardVariablesFrom, false) INSERT_FUNCTION(GetEnv, false) @@ -1472,6 +1474,7 @@ INSERT_FUNCTION(GetPathInfo, false) INSERT_FUNCTION(GetTargetOutputs, false) INSERT_FUNCTION(Import, false) + INSERT_FUNCTION(LabelMatches, false) INSERT_FUNCTION(NotNeeded, false) INSERT_FUNCTION(Pool, false) INSERT_FUNCTION(Print, false)
diff --git a/src/gn/functions.h b/src/gn/functions.h index 1230501..5cfeb82 100644 --- a/src/gn/functions.h +++ b/src/gn/functions.h
@@ -162,6 +162,22 @@ const std::vector<Value>& args, Err* err); +extern const char kFilterLabelsInclude[]; +extern const char kFilterLabelsInclude_HelpShort[]; +extern const char kFilterLabelsInclude_Help[]; +Value RunFilterLabelsInclude(Scope* scope, + const FunctionCallNode* function, + const std::vector<Value>& args, + Err* err); + +extern const char kFilterLabelsExclude[]; +extern const char kFilterLabelsExclude_HelpShort[]; +extern const char kFilterLabelsExclude_Help[]; +Value RunFilterLabelsExclude(Scope* scope, + const FunctionCallNode* function, + const std::vector<Value>& args, + Err* err); + extern const char kForEach[]; extern const char kForEach_HelpShort[]; extern const char kForEach_Help[]; @@ -236,6 +252,14 @@ const std::vector<Value>& args, Err* err); +extern const char kLabelMatches[]; +extern const char kLabelMatches_HelpShort[]; +extern const char kLabelMatches_Help[]; +Value RunLabelMatches(Scope* scope, + const FunctionCallNode* function, + const std::vector<Value>& args, + Err* err); + extern const char kLoadableModule[]; extern const char kLoadableModule_HelpShort[]; extern const char kLoadableModule_Help[];