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