// 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 #include "base/strings/string_util.h" #include "build/build_config.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 offset = 0; #if defined(OS_WIN) if (IsPathAbsolute(str)) { if (str[0] != '/') { *err = Err(value, "Bad absolute path.", "Absolute paths must be of the form /C:\\ but this is \"" + str.as_string() + "\"."); return LabelPattern(); } if (str.size() > 3 && str[2] == ':' && IsSlash(str[3]) && base::IsAsciiAlpha(str[1])) { // Skip over the drive letter colon. offset = 3; } } #endif size_t colon = str.find(':', offset); 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. (use current dir) // 2. (send through directory resolution) // 3. * (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 the end."); return LabelPattern(); } // Resolve the non-wildcard stuff. dir = current_dir.ResolveRelativeDir(value, path, err); if (err->has_error()) 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::HasWildcard(const std::string& str) { // Just look for a star. In the future, we may want to handle escaping or // other types of patterns. return str.find('*') != std::string::npos; } 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; }