OLD | NEW |
(Empty) | |
| 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 |
| 47 LabelPattern::LabelPattern() : type_(MATCH) { |
| 48 } |
| 49 |
| 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 |
| 60 LabelPattern::~LabelPattern() { |
| 61 } |
| 62 |
| 63 // static |
| 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 |
| 202 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 |
| 226 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 } |
OLD | NEW |