|  | // 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 <stddef.h> | 
|  |  | 
|  | #include "base/strings/string_util.h" | 
|  | #include "tools/gn/err.h" | 
|  | #include "tools/gn/filesystem_utils.h" | 
|  | #include "tools/gn/value.h" | 
|  | #include "util/build_config.h" | 
|  |  | 
|  | const char kLabelPattern_Help[] = | 
|  | R"*(Label patterns | 
|  |  | 
|  | A label pattern is a way of expressing one or more labels in a portion of the | 
|  | source tree. They are not general regular expressions. | 
|  |  | 
|  | They can take the following forms only: | 
|  |  | 
|  | - Explicit (no wildcard): | 
|  | "//foo/bar:baz" | 
|  | ":baz" | 
|  |  | 
|  | - Wildcard target names: | 
|  | "//foo/bar:*" (all targets in the //foo/bar/BUILD.gn file) | 
|  | ":*"  (all targets in the current build file) | 
|  |  | 
|  | - Wildcard directory names ("*" is only supported at the end) | 
|  | "*"  (all targets) | 
|  | "//foo/bar/*"  (all targets in any subdir of //foo/bar) | 
|  | "./*"  (all targets in the current build file or sub dirs) | 
|  |  | 
|  | Any of the above forms can additionally take an explicit toolchain | 
|  | in parenthesis at the end of the label pattern. In this case, the | 
|  | toolchain must be fully qualified (no wildcards are supported in the | 
|  | toolchain name). | 
|  |  | 
|  | "//foo:bar(//build/toolchain:mac)" | 
|  | An explicit target in an explicit toolchain. | 
|  |  | 
|  | ":*(//build/toolchain/linux:32bit)" | 
|  | All targets in the current build file using the 32-bit Linux toolchain. | 
|  |  | 
|  | "//foo/*(//build/toolchain:win)" | 
|  | All targets in //foo and any subdirectory using the Windows | 
|  | toolchain. | 
|  | )*"; | 
|  |  | 
|  | 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(const LabelPattern& other) = default; | 
|  |  | 
|  | LabelPattern::~LabelPattern() = default; | 
|  |  | 
|  | // 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)) { | 
|  | size_t drive_letter_pos = str[0] == '/' ? 1 : 0; | 
|  | if (str.size() > drive_letter_pos + 2 && str[drive_letter_pos + 1] == ':' && | 
|  | IsSlash(str[drive_letter_pos + 2]) && | 
|  | base::IsAsciiAlpha(str[drive_letter_pos])) { | 
|  | // Skip over the drive letter colon. | 
|  | offset = drive_letter_pos + 2; | 
|  | } | 
|  | } | 
|  | #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. <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 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | // static | 
|  | bool LabelPattern::VectorMatches(const std::vector<LabelPattern>& patterns, | 
|  | const Label& label) { | 
|  | for (const auto& pattern : patterns) { | 
|  | if (pattern.Matches(label)) | 
|  | return true; | 
|  | } | 
|  | 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; | 
|  | } |