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 "tools/gn/label_pattern.h"
6
7 #include "tools/gn/err.h"
8 #include "tools/gn/filesystem_utils.h"
9 #include "tools/gn/value.h"
10
11 const char kLabelPattern_Help[] =
12 "Label patterns\n"
13 "\n"
14 " A label pattern is a way of expressing one or more labels in a portion\n"
15 " of the source tree. They are not general regular expressions.\n"
16 "\n"
17 " They can take the following forms only:\n"
18 "\n"
19 " - Explicit (no wildcard):\n"
20 " \"//foo/bar:baz\"\n"
21 " \":baz\"\n"
22 "\n"
23 " - Wildcard target names:\n"
24 " \"//foo/bar:*\" (all targets in the //foo/bar/BUILD.gn file)\n"
25 " \":*\" (all targets in the current build file)\n"
26 "\n"
27 " - Wildcard directory names (\"*\" is only supported at the end)\n"
28 " \"*\" (all targets)\n"
29 " \"//foo/bar/*\" (all targets in any subdir of //foo/bar)\n"
30 " \"./*\" (all targets in the current build file or sub dirs)\n"
31 "\n"
32 " Any of the above forms can additionally take an explicit toolchain.\n"
33 " In this case, the toolchain must be fully qualified (no wildcards\n"
34 " are supported in the toolchain name).\n"
35 "\n"
36 " \"//foo:bar(//build/toochain:mac)\"\n"
37 " An explicit target in an explicit toolchain.\n"
38 "\n"
39 " \":*(//build/toolchain/linux:32bit)\"\n"
40 " All targets in the current build file using the 32-bit Linux\n"
41 " toolchain.\n"
42 "\n"
43 " \"//foo/*(//build/toolchain:win)\"\n"
44 " All targets in //foo and any subdirectory using the Windows\n"
45 " toolchain.\n";
46
LabelPattern()47 LabelPattern::LabelPattern() : type_(MATCH) {
48 }
49
LabelPattern(Type type,const SourceDir & dir,const base::StringPiece & name,const Label & toolchain_label)50 LabelPattern::LabelPattern(Type type,
51 const SourceDir& dir,
52 const base::StringPiece& name,
53 const Label& toolchain_label)
54 : toolchain_(toolchain_label),
55 type_(type),
56 dir_(dir) {
57 name.CopyToString(&name_);
58 }
59
~LabelPattern()60 LabelPattern::~LabelPattern() {
61 }
62
63 // static
GetPattern(const SourceDir & current_dir,const Value & value,Err * err)64 LabelPattern LabelPattern::GetPattern(const SourceDir& current_dir,
65 const Value& value,
66 Err* err) {
67 if (!value.VerifyTypeIs(Value::STRING, err))
68 return LabelPattern();
69
70 base::StringPiece str(value.string_value());
71 if (str.empty()) {
72 *err = Err(value, "Label pattern must not be empty.");
73 return LabelPattern();
74 }
75
76 // If there's no wildcard, this is specifying an exact label, use the
77 // label resolution code to get all the implicit name stuff.
78 size_t star = str.find('*');
79 if (star == std::string::npos) {
80 Label label = Label::Resolve(current_dir, Label(), value, err);
81 if (err->has_error())
82 return LabelPattern();
83
84 // Toolchain.
85 Label toolchain_label;
86 if (!label.toolchain_dir().is_null() || !label.toolchain_name().empty())
87 toolchain_label = label.GetToolchainLabel();
88
89 return LabelPattern(MATCH, label.dir(), label.name(), toolchain_label);
90 }
91
92 // Wildcard case, need to split apart the label to see what it specifies.
93 Label toolchain_label;
94 size_t open_paren = str.find('(');
95 if (open_paren != std::string::npos) {
96 // Has a toolchain definition, extract inside the parens.
97 size_t close_paren = str.find(')', open_paren);
98 if (close_paren == std::string::npos) {
99 *err = Err(value, "No close paren when looking for toolchain name.");
100 return LabelPattern();
101 }
102
103 std::string toolchain_string =
104 str.substr(open_paren + 1, close_paren - open_paren - 1).as_string();
105 if (toolchain_string.find('*') != std::string::npos) {
106 *err = Err(value, "Can't have a wildcard in the toolchain.");
107 return LabelPattern();
108 }
109
110 // Parse the inside of the parens as a label for a toolchain.
111 Value value_for_toolchain(value.origin(), toolchain_string);
112 toolchain_label =
113 Label::Resolve(current_dir, Label(), value_for_toolchain, err);
114 if (err->has_error())
115 return LabelPattern();
116
117 // Trim off the toolchain for the processing below.
118 str = str.substr(0, open_paren);
119 }
120
121 // Extract path and name.
122 base::StringPiece path;
123 base::StringPiece name;
124 size_t colon = str.find(':');
125 if (colon == std::string::npos) {
126 path = base::StringPiece(str);
127 } else {
128 path = str.substr(0, colon);
129 name = str.substr(colon + 1);
130 }
131
132 // The path can have these forms:
133 // 1. <empty> (use current dir)
134 // 2. <non wildcard stuff> (send through directory resolution)
135 // 3. <non wildcard stuff>* (send stuff through dir resolution, note star)
136 // 4. * (matches anything)
137 SourceDir dir;
138 bool has_path_star = false;
139 if (path.empty()) {
140 // Looks like ":foo".
141 dir = current_dir;
142 } else if (path[path.size() - 1] == '*') {
143 // Case 3 or 4 above.
144 has_path_star = true;
145
146 // Adjust path to contain everything but the star.
147 path = path.substr(0, path.size() - 1);
148
149 if (!path.empty() && path[path.size() - 1] != '/') {
150 // The input was "foo*" which is invalid.
151 *err = Err(value, "'*' must match full directories in a label pattern.",
152 "You did \"foo*\" but this thing doesn't do general pattern\n"
153 "matching. Instead, you have to add a slash: \"foo/*\" to match\n"
154 "all targets in a directory hierarchy.");
155 return LabelPattern();
156 }
157 }
158
159 // Resolve the part of the path that's not the wildcard.
160 if (!path.empty()) {
161 // The non-wildcard stuff better not have a wildcard.
162 if (path.find('*') != base::StringPiece::npos) {
163 *err = Err(value, "Label patterns only support wildcard suffixes.",
164 "The pattern contained a '*' that wasn't at tne end.");
165 return LabelPattern();
166 }
167
168 // Resolve the non-wildcard stuff.
169 dir = current_dir.ResolveRelativeDir(path);
170 if (dir.is_null()) {
171 *err = Err(value, "Label pattern didn't resolve to a dir.",
172 "The directory name \"" + path.as_string() + "\" didn't\n"
173 "resolve to a directory.");
174 return LabelPattern();
175 }
176 }
177
178 // Resolve the name. At this point, we're doing wildcard matches so the
179 // name should either be empty ("foo/*") or a wildcard ("foo:*");
180 if (colon != std::string::npos && name != "*") {
181 *err = Err(value, "Invalid label pattern.",
182 "You seem to be using the wildcard more generally that is supported.\n"
183 "Did you mean \"foo:*\" to match everything in the file, or\n"
184 "\"./*\" to recursively match everything in the currend subtree.");
185 return LabelPattern();
186 }
187
188 Type type;
189 if (has_path_star) {
190 // We know there's a wildcard, so if the name is empty it looks like
191 // "foo/*".
192 type = RECURSIVE_DIRECTORY;
193 } else {
194 // Everything else should be of the form "foo:*".
195 type = DIRECTORY;
196 }
197
198 // When we're doing wildcard matching, the name is always empty.
199 return LabelPattern(type, dir, base::StringPiece(), toolchain_label);
200 }
201
Matches(const Label & label) const202 bool LabelPattern::Matches(const Label& label) const {
203 if (!toolchain_.is_null()) {
204 // Toolchain must match exactly.
205 if (toolchain_.dir() != label.toolchain_dir() ||
206 toolchain_.name() != label.toolchain_name())
207 return false;
208 }
209
210 switch (type_) {
211 case MATCH:
212 return label.name() == name_ && label.dir() == dir_;
213 case DIRECTORY:
214 // The directories must match exactly.
215 return label.dir() == dir_;
216 case RECURSIVE_DIRECTORY:
217 // Our directory must be a prefix of the input label for recursive.
218 return label.dir().value().compare(0, dir_.value().size(), dir_.value())
219 == 0;
220 default:
221 NOTREACHED();
222 return false;
223 }
224 }
225
Describe() const226 std::string LabelPattern::Describe() const {
227 std::string result;
228
229 switch (type()) {
230 case MATCH:
231 result = DirectoryWithNoLastSlash(dir()) + ":" + name();
232 break;
233 case DIRECTORY:
234 result = DirectoryWithNoLastSlash(dir()) + ":*";
235 break;
236 case RECURSIVE_DIRECTORY:
237 result = dir().value() + "*";
238 break;
239 }
240
241 if (!toolchain_.is_null()) {
242 result.push_back('(');
243 result.append(toolchain_.GetUserVisibleName(false));
244 result.push_back(')');
245 }
246 return result;
247 }
248