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 11const 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 47LabelPattern::LabelPattern() : type_(MATCH) { 48} 49 50LabelPattern::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 60LabelPattern::~LabelPattern() { 61} 62 63// static 64LabelPattern 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 202bool 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 226std::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