Add "filter_{exclude,include}" intrinsic functions to GN
Some templates in chromium abuses set_sources_assignment_filter to
filter elements out of a list. As set_sources_assignment_filter is
considered a mis-feature (and a consensus to remove it), there is
a need for an alternative way to filter lists of strings.
Add two intrinsics functions "function_exclude" & "filter_include"
that take a list of string and a list of pattern and return all
the elements from the first list that match respectively none or
at least one of the patterns.
Bug: 35
Change-Id: I94546e15fa35122e66c3a0bbadb5410b18e8be65
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/6400
Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/build/gen.py b/build/gen.py
index 758c51a..bcd9f15 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -485,6 +485,7 @@
'src/gn/filesystem_utils.cc',
'src/gn/frameworks_utils.cc',
'src/gn/function_exec_script.cc',
+ 'src/gn/function_filter.cc',
'src/gn/function_foreach.cc',
'src/gn/function_forward_variables_from.cc',
'src/gn/function_get_label_info.cc',
@@ -607,6 +608,7 @@
'src/gn/exec_process_unittest.cc',
'src/gn/filesystem_utils_unittest.cc',
'src/gn/frameworks_utils_unittest.cc',
+ 'src/gn/function_filter_unittest.cc',
'src/gn/function_foreach_unittest.cc',
'src/gn/function_forward_variables_from_unittest.cc',
'src/gn/function_get_label_info_unittest.cc',
diff --git a/docs/reference.md b/docs/reference.md
index cbf9db1..c158fa4 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -39,6 +39,8 @@
* [declare_args: Declare build arguments.](#func_declare_args)
* [defined: Returns whether an identifier is defined.](#func_defined)
* [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)
* [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)
@@ -158,6 +160,7 @@
* [execution: Build graph and execution overview.](#execution)
* [grammar: Language and grammar for GN build files.](#grammar)
* [input_conversion: Processing input from exec_script and read_file.](#io_conversion)
+ * [file_pattern: Matching more than one file.](#file_pattern)
* [label_pattern: Matching more than one label.](#label_pattern)
* [labels: About labels.](#labels)
* [metadata_collection: About metadata and its collection.](#metadata_collection)
@@ -749,6 +752,9 @@
Override defaut workspace file name ("all"). The workspace file is
written to the root build directory.
+ --ninja-executable=<string>
+ Can be used to specify the ninja executable to use when building.
+
--ninja-extra-args=<string>
This string is passed without any quoting to the ninja invocation
command-line. Can be used to configure ninja flags, like "-j".
@@ -2249,6 +2255,42 @@
# result.
exec_script("//foo/bar/myscript.py")
```
+### <a name="func_filter_exclude"></a>**filter_exclude**: Remove values that match a set of patterns.
+
+```
+ filter_exclude(values, exclude_patterns)
+
+ The argument values must be a list of strings.
+
+ The argument exclude_patterns must be a list of file patterns (see
+ "gn help file_pattern"). Any elements in values matching at least one
+ of those patterns will be excluded.
+```
+
+#### **Examples**
+```
+ values = [ "foo.cc", "foo.h", "foo.proto" ]
+ result = filter_exclude(values, [ "*.proto" ])
+ # result will be [ "foo.cc", "foo.h" ]
+```
+### <a name="func_filter_include"></a>**filter_include**: Remove values that do not match a set of patterns.
+
+```
+ filter_include(values, include_patterns)
+
+ The argument values must be a list of strings.
+
+ The argument include_patterns must be a list of file patterns (see
+ "gn help file_pattern"). Only elements from values matching at least
+ one of the pattern will be included.
+```
+
+#### **Examples**
+```
+ values = [ "foo.cc", "foo.h", "foo.proto" ]
+ result = filter_include(values, [ "*.proto" ])
+ # result will be [ "foo.proto" ]
+```
### <a name="func_foreach"></a>**foreach**: Iterate over a list.
```
@@ -2956,42 +2998,9 @@
If you want to bypass the filter and add a file even if it might be filtered
out, call set_sources_assignment_filter([]) to clear the list of filters.
- This will apply until the current scope exits
-```
+ This will apply until the current scope exits.
-#### **How to use patterns**
-
-```
- File patterns are VERY limited regular expressions. They must match the
- entire input string to be counted as a match. In regular expression parlance,
- there is an implicit "^...$" surrounding your input. If you want to match a
- substring, you need to use wildcards at the beginning and end.
-
- There are only two special tokens understood by the pattern matcher.
- Everything else is a literal.
-
- - "*" Matches zero or more of any character. It does not depend on the
- preceding character (in regular expression parlance it is equivalent to
- ".*").
-
- - "\b" Matches a path boundary. This will match the beginning or end of a
- string, or a slash.
-```
-
-#### **Pattern examples**
-
-```
- "*asdf*"
- Matches a string containing "asdf" anywhere.
-
- "asdf"
- Matches only the exact string "asdf".
-
- "*.cc"
- Matches strings ending in the literal ".cc".
-
- "\bwin/*"
- Matches "win/foo" and "foo/win/bar.cc" but not "iwin/foo".
+ See "gn help file_pattern" for more information on file pattern.
```
#### **Sources assignment example**
@@ -6958,6 +6967,40 @@
Note that "trim value" is useless because the value parser skips
whitespace anyway.
```
+### <a name="file_pattern"></a>**File patterns**
+
+```
+ File patterns are VERY limited regular expressions. They must match the
+ entire input string to be counted as a match. In regular expression parlance,
+ there is an implicit "^...$" surrounding your input. If you want to match a
+ substring, you need to use wildcards at the beginning and end.
+
+ There are only two special tokens understood by the pattern matcher.
+ Everything else is a literal.
+
+ - "*" Matches zero or more of any character. It does not depend on the
+ preceding character (in regular expression parlance it is equivalent to
+ ".*").
+
+ - "\b" Matches a path boundary. This will match the beginning or end of a
+ string, or a slash.
+```
+
+#### **Pattern examples**
+
+```
+ "*asdf*"
+ Matches a string containing "asdf" anywhere.
+
+ "asdf"
+ Matches only the exact string "asdf".
+
+ "*.cc"
+ Matches strings ending in the literal ".cc".
+
+ "\bwin/*"
+ Matches "win/foo" and "foo/win/bar.cc" but not "iwin/foo".
+```
### <a name="label_pattern"></a>**Label patterns**
```
diff --git a/src/gn/command_help.cc b/src/gn/command_help.cc
index fdbd679..f03a4e7 100644
--- a/src/gn/command_help.cc
+++ b/src/gn/command_help.cc
@@ -14,6 +14,7 @@
#include "gn/ninja_build_writer.h"
#include "gn/output_conversion.h"
#include "gn/parser.h"
+#include "gn/pattern.h"
#include "gn/runtime_deps.h"
#include "gn/setup.h"
#include "gn/standard_out.h"
@@ -78,6 +79,7 @@
PrintShortHelp(
"input_conversion: Processing input from exec_script and read_file.",
"io_conversion");
+ PrintShortHelp("file_pattern: Matching more than one file.", "file_pattern");
PrintShortHelp("label_pattern: Matching more than one label.",
"label_pattern");
PrintShortHelp("labels: About labels.", "labels");
@@ -195,6 +197,7 @@
PrintLongHelp(kExecution_Help, "execution");
PrintLongHelp(kGrammar_Help, "grammar");
PrintLongHelp(kInputOutputConversion_Help, "io_conversion");
+ PrintLongHelp(kFilePattern_Help, "file_pattern");
PrintLongHelp(kLabelPattern_Help, "label_pattern");
PrintLongHelp(kLabels_Help, "labels");
PrintLongHelp(kMetadata_Help, "metadata_collection");
@@ -336,6 +339,7 @@
random_topics["io_conversion"] = []() {
PrintLongHelp(kInputOutputConversion_Help);
};
+ random_topics["file_pattern"] = []() { PrintLongHelp(kFilePattern_Help); };
random_topics["label_pattern"] = []() { PrintLongHelp(kLabelPattern_Help); };
random_topics["labels"] = []() { PrintLongHelp(kLabels_Help); };
random_topics["metadata_collection"] = []() {
diff --git a/src/gn/function_filter.cc b/src/gn/function_filter.cc
new file mode 100644
index 0000000..49eacfa
--- /dev/null
+++ b/src/gn/function_filter.cc
@@ -0,0 +1,124 @@
+// 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/err.h"
+#include "gn/filesystem_utils.h"
+#include "gn/functions.h"
+#include "gn/parse_tree.h"
+#include "gn/scope.h"
+#include "gn/value.h"
+
+namespace functions {
+
+const char kFilterExclude[] = "filter_exclude";
+const char kFilterExclude_HelpShort[] =
+ "filter_exclude: Remove values that match a set of patterns.";
+const char kFilterExclude_Help[] =
+ R"(filter_exclude: Remove values that match a set of patterns.
+
+ filter_exclude(values, exclude_patterns)
+
+ The argument values must be a list of strings.
+
+ The argument exclude_patterns must be a list of file patterns (see
+ "gn help file_pattern"). Any elements in values matching at least one
+ of those patterns will be excluded.
+
+Examples
+ values = [ "foo.cc", "foo.h", "foo.proto" ]
+ result = filter_exclude(values, [ "*.proto" ])
+ # result will be [ "foo.cc", "foo.h" ]
+)";
+
+const char kFilterInclude[] = "filter_include";
+const char kFilterInclude_HelpShort[] =
+ "filter_include: Remove values that do not match a set of patterns.";
+const char kFilterInclude_Help[] =
+ R"(filter_include: Remove values that do not match a set of patterns.
+
+ filter_include(values, include_patterns)
+
+ The argument values must be a list of strings.
+
+ The argument include_patterns must be a list of file patterns (see
+ "gn help file_pattern"). Only elements from values matching at least
+ one of the pattern will be included.
+
+Examples
+ values = [ "foo.cc", "foo.h", "foo.proto" ]
+ result = filter_include(values, [ "*.proto" ])
+ # result will be [ "foo.proto" ]
+)";
+
+namespace {
+
+enum FilterSelection {
+ kExcludeFilter,
+ kIncludeFilter,
+};
+
+Value RunFilter(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();
+ }
+
+ // Extract "values".
+ if (args[0].type() != Value::LIST) {
+ *err = Err(args[0], "First argument must be a list of strings.");
+ return Value();
+ }
+
+ // Extract "patterns".
+ PatternList patterns;
+ patterns.SetFromValue(args[1], err);
+ if (err->has_error())
+ return Value();
+
+ Value result(function, Value::LIST);
+ for (const auto& value : args[0].list_value()) {
+ if (value.type() != Value::STRING) {
+ *err = Err(args[0], "First argument must be a list of strings.");
+ return Value();
+ }
+
+ const bool matches_pattern = patterns.MatchesValue(value);
+ 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;
+}
+
+} // anonymous namespace
+
+Value RunFilterExclude(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ return RunFilter(scope, function, args, kExcludeFilter, err);
+}
+
+Value RunFilterInclude(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ return RunFilter(scope, function, args, kIncludeFilter, err);
+}
+
+} // namespace functions
diff --git a/src/gn/function_filter_unittest.cc b/src/gn/function_filter_unittest.cc
new file mode 100644
index 0000000..4269f18
--- /dev/null
+++ b/src/gn/function_filter_unittest.cc
@@ -0,0 +1,244 @@
+// 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(FilterExcludeTest, Filter) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*.proto"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterExclude(setup.scope(), &function, args, &err);
+ ASSERT_FALSE(err.has_error());
+ ASSERT_EQ(result.type(), Value::LIST);
+ ASSERT_EQ(result.list_value().size(), 2);
+ EXPECT_EQ(result.list_value()[0].type(), Value::STRING);
+ EXPECT_EQ(result.list_value()[0].string_value(), "foo.cc");
+ EXPECT_EQ(result.list_value()[1].type(), Value::STRING);
+ EXPECT_EQ(result.list_value()[1].string_value(), "foo.h");
+}
+
+TEST(FilterExcludeTest, NotEnoughArguments) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values};
+
+ Err err;
+ Value result =
+ functions::RunFilterExclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterExcludeTest, TooManyArguments) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*"));
+
+ Value extra_argument(nullptr, Value::LIST);
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns, extra_argument};
+
+ Err err;
+ Value result =
+ functions::RunFilterExclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterExcludeTest, IncorrectValuesType) {
+ TestWithScope setup;
+
+ Value values(nullptr, "foo.cc");
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterExclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterExcludeTest, IncorrectValuesElementType) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, Value::LIST));
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterExclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterExcludeTest, IncorrectPatternsType) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ Value patterns(nullptr, "foo.cc");
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterExclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterIncludeTest, Filter) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*.proto"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterInclude(setup.scope(), &function, args, &err);
+ ASSERT_FALSE(err.has_error());
+ ASSERT_EQ(result.type(), Value::LIST);
+ ASSERT_EQ(result.list_value().size(), 1);
+ EXPECT_EQ(result.list_value()[0].type(), Value::STRING);
+ EXPECT_EQ(result.list_value()[0].string_value(), "foo.proto");
+}
+
+TEST(FilterIncludeTest, NotEnoughArguments) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values};
+
+ Err err;
+ Value result =
+ functions::RunFilterInclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterIncludeTest, TooManyArguments) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*"));
+
+ Value extra_argument(nullptr, Value::LIST);
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns, extra_argument};
+
+ Err err;
+ Value result =
+ functions::RunFilterInclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterIncludeTest, IncorrectValuesType) {
+ TestWithScope setup;
+
+ Value values(nullptr, "foo.cc");
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterInclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterIncludeTest, IncorrectValuesElementType) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, Value::LIST));
+
+ Value patterns(nullptr, Value::LIST);
+ patterns.list_value().push_back(Value(nullptr, "*"));
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterInclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
+
+TEST(FilterIncludeTest, IncorrectPatternsType) {
+ TestWithScope setup;
+
+ Value values(nullptr, Value::LIST);
+ values.list_value().push_back(Value(nullptr, "foo.cc"));
+ values.list_value().push_back(Value(nullptr, "foo.h"));
+ values.list_value().push_back(Value(nullptr, "foo.proto"));
+
+ Value patterns(nullptr, "foo.cc");
+
+ FunctionCallNode function;
+ std::vector<Value> args = {values, patterns};
+
+ Err err;
+ Value result =
+ functions::RunFilterInclude(setup.scope(), &function, args, &err);
+ EXPECT_TRUE(err.has_error());
+}
diff --git a/src/gn/functions.cc b/src/gn/functions.cc
index abbf188..314173b 100644
--- a/src/gn/functions.cc
+++ b/src/gn/functions.cc
@@ -828,38 +828,9 @@
If you want to bypass the filter and add a file even if it might be filtered
out, call set_sources_assignment_filter([]) to clear the list of filters.
- This will apply until the current scope exits
+ This will apply until the current scope exits.
-How to use patterns
-
- File patterns are VERY limited regular expressions. They must match the
- entire input string to be counted as a match. In regular expression parlance,
- there is an implicit "^...$" surrounding your input. If you want to match a
- substring, you need to use wildcards at the beginning and end.
-
- There are only two special tokens understood by the pattern matcher.
- Everything else is a literal.
-
- - "*" Matches zero or more of any character. It does not depend on the
- preceding character (in regular expression parlance it is equivalent to
- ".*").
-
- - "\b" Matches a path boundary. This will match the beginning or end of a
- string, or a slash.
-
-Pattern examples
-
- "*asdf*"
- Matches a string containing "asdf" anywhere.
-
- "asdf"
- Matches only the exact string "asdf".
-
- "*.cc"
- Matches strings ending in the literal ".cc".
-
- "\bwin/*"
- Matches "win/foo" and "foo/win/bar.cc" but not "iwin/foo".
+ See "gn help file_pattern" for more information on file pattern.
Sources assignment example
@@ -1459,6 +1430,8 @@
INSERT_FUNCTION(DeclareArgs, false)
INSERT_FUNCTION(Defined, false)
INSERT_FUNCTION(ExecScript, false)
+ INSERT_FUNCTION(FilterExclude, false)
+ INSERT_FUNCTION(FilterInclude, false)
INSERT_FUNCTION(ForEach, false)
INSERT_FUNCTION(ForwardVariablesFrom, false)
INSERT_FUNCTION(GetEnv, false)
diff --git a/src/gn/functions.h b/src/gn/functions.h
index 5027559..dbcce39 100644
--- a/src/gn/functions.h
+++ b/src/gn/functions.h
@@ -146,6 +146,22 @@
BlockNode* block,
Err* err);
+extern const char kFilterExclude[];
+extern const char kFilterExclude_HelpShort[];
+extern const char kFilterExclude_Help[];
+Value RunFilterExclude(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err);
+
+extern const char kFilterInclude[];
+extern const char kFilterInclude_HelpShort[];
+extern const char kFilterInclude_Help[];
+Value RunFilterInclude(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[];
diff --git a/src/gn/pattern.cc b/src/gn/pattern.cc
index da3f134..cf03bab 100644
--- a/src/gn/pattern.cc
+++ b/src/gn/pattern.cc
@@ -6,6 +6,40 @@
#include "gn/value.h"
+const char kFilePattern_Help[] =
+ R"*(File patterns
+
+ File patterns are VERY limited regular expressions. They must match the
+ entire input string to be counted as a match. In regular expression parlance,
+ there is an implicit "^...$" surrounding your input. If you want to match a
+ substring, you need to use wildcards at the beginning and end.
+
+ There are only two special tokens understood by the pattern matcher.
+ Everything else is a literal.
+
+ - "*" Matches zero or more of any character. It does not depend on the
+ preceding character (in regular expression parlance it is equivalent to
+ ".*").
+
+ - "\b" Matches a path boundary. This will match the beginning or end of a
+ string, or a slash.
+
+Pattern examples
+
+ "*asdf*"
+ Matches a string containing "asdf" anywhere.
+
+ "asdf"
+ Matches only the exact string "asdf".
+
+ "*.cc"
+ Matches strings ending in the literal ".cc".
+
+ "\bwin/*"
+ Matches "win/foo" and "foo/win/bar.cc" but not "iwin/foo".
+
+)*";
+
namespace {
void ParsePattern(const std::string& s, std::vector<Pattern::Subrange>* out) {
diff --git a/src/gn/pattern.h b/src/gn/pattern.h
index a176206..f833ca4 100644
--- a/src/gn/pattern.h
+++ b/src/gn/pattern.h
@@ -12,6 +12,8 @@
#include "gn/value.h"
+extern const char kFilePattern_Help[];
+
class Pattern {
public:
struct Subrange {