1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stddef.h>
6
7 #include "gn/build_settings.h"
8 #include "gn/err.h"
9 #include "gn/functions.h"
10 #include "gn/label_pattern.h"
11 #include "gn/parse_tree.h"
12 #include "gn/scope.h"
13 #include "gn/settings.h"
14 #include "gn/value.h"
15
16 namespace functions {
17
18 enum FilterSelection {
19 kExcludeFilter,
20 kIncludeFilter,
21 };
22
RunFilterLabels(Scope * scope,const FunctionCallNode * function,const std::vector<Value> & args,FilterSelection selection,Err * err)23 Value RunFilterLabels(Scope* scope,
24 const FunctionCallNode* function,
25 const std::vector<Value>& args,
26 FilterSelection selection,
27 Err* err) {
28 if (args.size() != 2) {
29 *err = Err(function, "Expecting exactly two arguments.");
30 return Value();
31 }
32
33 // Validate "labels" and "patterns" are both lists
34 if (args[0].type() != Value::LIST) {
35 *err = Err(args[0], "First argument must be a list of target labels.");
36 return Value();
37 }
38 if (args[1].type() != Value::LIST) {
39 *err = Err(args[1], "Second argument must be a list of label patterns.");
40 return Value();
41 }
42
43 // Extract "patterns"
44 std::vector<LabelPattern> patterns;
45 patterns.reserve(args[1].list_value().size());
46
47 for (const auto& value : args[1].list_value()) {
48 if (value.type() != Value::STRING) {
49 *err = Err(args[1], "Second argument must be a list of label patterns.");
50 return Value();
51 }
52 LabelPattern pattern = LabelPattern::GetPattern(
53 scope->GetSourceDir(),
54 scope->settings()->build_settings()->root_path_utf8(), value, err);
55 if (err->has_error()) {
56 return Value();
57 }
58 patterns.push_back(std::move(pattern));
59 }
60
61 // Iterate over "labels", resolving and matching against the list of patterns.
62 Value result(function, Value::LIST);
63 for (const auto& value : args[0].list_value()) {
64 Label label =
65 Label::Resolve(scope->GetSourceDir(),
66 scope->settings()->build_settings()->root_path_utf8(),
67 ToolchainLabelForScope(scope), value, err);
68 if (err->has_error()) {
69 // Change the error message to be more applicable than what Resolve will
70 // produce.
71 *err = Err(value, "First argument must be a list of target labels.");
72 return Value();
73 }
74
75 const bool matches_pattern = LabelPattern::VectorMatches(patterns, label);
76 switch (selection) {
77 case kIncludeFilter:
78 if (matches_pattern)
79 result.list_value().push_back(value);
80 break;
81
82 case kExcludeFilter:
83 if (!matches_pattern)
84 result.list_value().push_back(value);
85 break;
86 }
87 }
88 return result;
89 }
90
91 const char kFilterLabelsInclude[] = "filter_labels_include";
92 const char kFilterLabelsInclude_HelpShort[] =
93 "filter_labels_include: Remove labels that do not match a set of patterns.";
94 const char kFilterLabelsInclude_Help[] =
95 R"(filter_labels_include: Remove labels that do not match a set of patterns.
96
97 filter_labels_include(labels, include_patterns)
98
99 The argument labels must be a list of strings.
100
101 The argument include_patterns must be a list of label patterns (see
102 "gn help label_pattern"). Only elements from labels matching at least
103 one of the patterns will be included.
104
105 Examples
106 labels = [ "//foo:baz", "//foo/bar:baz", "//bar:baz" ]
107 result = filter_labels_include(labels, [ "//foo:*" ])
108 # result will be [ "//foo:baz" ]
109 )";
110
RunFilterLabelsInclude(Scope * scope,const FunctionCallNode * function,const std::vector<Value> & args,Err * err)111 Value RunFilterLabelsInclude(Scope* scope,
112 const FunctionCallNode* function,
113 const std::vector<Value>& args,
114 Err* err) {
115 return RunFilterLabels(scope, function, args, kIncludeFilter, err);
116 }
117
118 const char kFilterLabelsExclude[] = "filter_labels_exclude";
119 const char kFilterLabelsExclude_HelpShort[] =
120 "filter_labels_exclude: Remove labels that match a set of patterns.";
121 const char kFilterLabelsExclude_Help[] =
122 R"(filter_labels_exclude: Remove labels that match a set of patterns.
123
124 filter_labels_exclude(labels, exclude_patterns)
125
126 The argument labels must be a list of strings.
127
128 The argument exclude_patterns must be a list of label patterns (see
129 "gn help label_pattern"). Only elements from labels matching at least
130 one of the patterns will be excluded.
131
132 Examples
133 labels = [ "//foo:baz", "//foo/bar:baz", "//bar:baz" ]
134 result = filter_labels_exclude(labels, [ "//foo:*" ])
135 # result will be [ "//foo/bar:baz", "//bar:baz" ]
136 )";
137
138 Value RunFilterLabelsExclude(Scope* scope,
139 const FunctionCallNode* function,
140 const std::vector<Value>& args,
141 Err* err) {
142 return RunFilterLabels(scope, function, args, kExcludeFilter, err);
143 }
144
145 } // namespace functions
146