• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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