Index: tools/gn/label_pattern.cc |
diff --git a/tools/gn/label_pattern.cc b/tools/gn/label_pattern.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..752898f1acace2f03bdddc25315da680728b6d00 |
--- /dev/null |
+++ b/tools/gn/label_pattern.cc |
@@ -0,0 +1,247 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "tools/gn/label_pattern.h" |
+ |
+#include "tools/gn/err.h" |
+#include "tools/gn/filesystem_utils.h" |
+#include "tools/gn/value.h" |
+ |
+const char kLabelPattern_Help[] = |
+ "Label patterns\n" |
+ "\n" |
+ " A label pattern is a way of expressing one or more labels in a portion\n" |
+ " of the source tree. They are not general regular expressions.\n" |
+ "\n" |
+ " They can take the following forms only:\n" |
+ "\n" |
+ " - Explicit (no wildcard):\n" |
+ " \"//foo/bar:baz\"\n" |
+ " \":baz\"\n" |
+ "\n" |
+ " - Wildcard target names:\n" |
+ " \"//foo/bar:*\" (all targets in the //foo/bar/BUILD.gn file)\n" |
+ " \":*\" (all targets in the current build file)\n" |
+ "\n" |
+ " - Wildcard directory names (\"*\" is only supported at the end)\n" |
+ " \"*\" (all targets)\n" |
+ " \"//foo/bar/*\" (all targets in any subdir of //foo/bar)\n" |
+ " \"./*\" (all targets in the current build file or sub dirs)\n" |
+ "\n" |
+ " Any of the above forms can additionally take an explicit toolchain.\n" |
+ " In this case, the toolchain must be fully qualified (no wildcards\n" |
+ " are supported in the toolchain name).\n" |
+ "\n" |
+ " \"//foo:bar(//build/toochain:mac)\"\n" |
+ " An explicit target in an explicit toolchain.\n" |
+ "\n" |
+ " \":*(//build/toolchain/linux:32bit)\"\n" |
+ " All targets in the current build file using the 32-bit Linux\n" |
+ " toolchain.\n" |
+ "\n" |
+ " \"//foo/*(//build/toolchain:win)\"\n" |
+ " All targets in //foo and any subdirectory using the Windows\n" |
+ " toolchain.\n"; |
+ |
+LabelPattern::LabelPattern() : type_(MATCH) { |
+} |
+ |
+LabelPattern::LabelPattern(Type type, |
+ const SourceDir& dir, |
+ const base::StringPiece& name, |
+ const Label& toolchain_label) |
+ : toolchain_(toolchain_label), |
+ type_(type), |
+ dir_(dir) { |
+ name.CopyToString(&name_); |
+} |
+ |
+LabelPattern::~LabelPattern() { |
+} |
+ |
+// static |
+LabelPattern LabelPattern::GetPattern(const SourceDir& current_dir, |
+ const Value& value, |
+ Err* err) { |
+ if (!value.VerifyTypeIs(Value::STRING, err)) |
+ return LabelPattern(); |
+ |
+ base::StringPiece str(value.string_value()); |
+ if (str.empty()) { |
+ *err = Err(value, "Label pattern must not be empty."); |
+ return LabelPattern(); |
+ } |
+ |
+ // If there's no wildcard, this is specifying an exact label, use the |
+ // label resolution code to get all the implicit name stuff. |
+ size_t star = str.find('*'); |
+ if (star == std::string::npos) { |
+ Label label = Label::Resolve(current_dir, Label(), value, err); |
+ if (err->has_error()) |
+ return LabelPattern(); |
+ |
+ // Toolchain. |
+ Label toolchain_label; |
+ if (!label.toolchain_dir().is_null() || !label.toolchain_name().empty()) |
+ toolchain_label = label.GetToolchainLabel(); |
+ |
+ return LabelPattern(MATCH, label.dir(), label.name(), toolchain_label); |
+ } |
+ |
+ // Wildcard case, need to split apart the label to see what it specifies. |
+ Label toolchain_label; |
+ size_t open_paren = str.find('('); |
+ if (open_paren != std::string::npos) { |
+ // Has a toolchain definition, extract inside the parens. |
+ size_t close_paren = str.find(')', open_paren); |
+ if (close_paren == std::string::npos) { |
+ *err = Err(value, "No close paren when looking for toolchain name."); |
+ return LabelPattern(); |
+ } |
+ |
+ std::string toolchain_string = |
+ str.substr(open_paren + 1, close_paren - open_paren - 1).as_string(); |
+ if (toolchain_string.find('*') != std::string::npos) { |
+ *err = Err(value, "Can't have a wildcard in the toolchain."); |
+ return LabelPattern(); |
+ } |
+ |
+ // Parse the inside of the parens as a label for a toolchain. |
+ Value value_for_toolchain(value.origin(), toolchain_string); |
+ toolchain_label = |
+ Label::Resolve(current_dir, Label(), value_for_toolchain, err); |
+ if (err->has_error()) |
+ return LabelPattern(); |
+ |
+ // Trim off the toolchain for the processing below. |
+ str = str.substr(0, open_paren); |
+ } |
+ |
+ // Extract path and name. |
+ base::StringPiece path; |
+ base::StringPiece name; |
+ size_t colon = str.find(':'); |
+ if (colon == std::string::npos) { |
+ path = base::StringPiece(str); |
+ } else { |
+ path = str.substr(0, colon); |
+ name = str.substr(colon + 1); |
+ } |
+ |
+ // The path can have these forms: |
+ // 1. <empty> (use current dir) |
+ // 2. <non wildcard stuff> (send through directory resolution) |
+ // 3. <non wildcard stuff>* (send stuff through dir resolution, note star) |
+ // 4. * (matches anything) |
+ SourceDir dir; |
+ bool has_path_star = false; |
+ if (path.empty()) { |
+ // Looks like ":foo". |
+ dir = current_dir; |
+ } else if (path[path.size() - 1] == '*') { |
+ // Case 3 or 4 above. |
+ has_path_star = true; |
+ |
+ // Adjust path to contain everything but the star. |
+ path = path.substr(0, path.size() - 1); |
+ |
+ if (!path.empty() && path[path.size() - 1] != '/') { |
+ // The input was "foo*" which is invalid. |
+ *err = Err(value, "'*' must match full directories in a label pattern.", |
+ "You did \"foo*\" but this thing doesn't do general pattern\n" |
+ "matching. Instead, you have to add a slash: \"foo/*\" to match\n" |
+ "all targets in a directory hierarchy."); |
+ return LabelPattern(); |
+ } |
+ } |
+ |
+ // Resolve the part of the path that's not the wildcard. |
+ if (!path.empty()) { |
+ // The non-wildcard stuff better not have a wildcard. |
+ if (path.find('*') != base::StringPiece::npos) { |
+ *err = Err(value, "Label patterns only support wildcard suffixes.", |
+ "The pattern contained a '*' that wasn't at tne end."); |
+ return LabelPattern(); |
+ } |
+ |
+ // Resolve the non-wildcard stuff. |
+ dir = current_dir.ResolveRelativeDir(path); |
+ if (dir.is_null()) { |
+ *err = Err(value, "Label pattern didn't resolve to a dir.", |
+ "The directory name \"" + path.as_string() + "\" didn't\n" |
+ "resolve to a directory."); |
+ return LabelPattern(); |
+ } |
+ } |
+ |
+ // Resolve the name. At this point, we're doing wildcard matches so the |
+ // name should either be empty ("foo/*") or a wildcard ("foo:*"); |
+ if (colon != std::string::npos && name != "*") { |
+ *err = Err(value, "Invalid label pattern.", |
+ "You seem to be using the wildcard more generally that is supported.\n" |
+ "Did you mean \"foo:*\" to match everything in the file, or\n" |
+ "\"./*\" to recursively match everything in the currend subtree."); |
+ return LabelPattern(); |
+ } |
+ |
+ Type type; |
+ if (has_path_star) { |
+ // We know there's a wildcard, so if the name is empty it looks like |
+ // "foo/*". |
+ type = RECURSIVE_DIRECTORY; |
+ } else { |
+ // Everything else should be of the form "foo:*". |
+ type = DIRECTORY; |
+ } |
+ |
+ // When we're doing wildcard matching, the name is always empty. |
+ return LabelPattern(type, dir, base::StringPiece(), toolchain_label); |
+} |
+ |
+bool LabelPattern::Matches(const Label& label) const { |
+ if (!toolchain_.is_null()) { |
+ // Toolchain must match exactly. |
+ if (toolchain_.dir() != label.toolchain_dir() || |
+ toolchain_.name() != label.toolchain_name()) |
+ return false; |
+ } |
+ |
+ switch (type_) { |
+ case MATCH: |
+ return label.name() == name_ && label.dir() == dir_; |
+ case DIRECTORY: |
+ // The directories must match exactly. |
+ return label.dir() == dir_; |
+ case RECURSIVE_DIRECTORY: |
+ // Our directory must be a prefix of the input label for recursive. |
+ return label.dir().value().compare(0, dir_.value().size(), dir_.value()) |
+ == 0; |
+ default: |
+ NOTREACHED(); |
+ return false; |
+ } |
+} |
+ |
+std::string LabelPattern::Describe() const { |
+ std::string result; |
+ |
+ switch (type()) { |
+ case MATCH: |
+ result = DirectoryWithNoLastSlash(dir()) + ":" + name(); |
+ break; |
+ case DIRECTORY: |
+ result = DirectoryWithNoLastSlash(dir()) + ":*"; |
+ break; |
+ case RECURSIVE_DIRECTORY: |
+ result = dir().value() + "*"; |
+ break; |
+ } |
+ |
+ if (!toolchain_.is_null()) { |
+ result.push_back('('); |
+ result.append(toolchain_.GetUserVisibleName(false)); |
+ result.push_back(')'); |
+ } |
+ return result; |
+} |