| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013 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.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "tools/gn/err.h" | |
| 9 #include "tools/gn/parse_tree.h" | |
| 10 #include "tools/gn/value.h" | |
| 11 | |
| 12 namespace { | |
| 13 | |
| 14 // We print user visible label names with no trailing slash after the | |
| 15 // directory name. | |
| 16 std::string DirWithNoTrailingSlash(const SourceDir& dir) { | |
| 17 // Be careful not to trim if the input is just "/" or "//". | |
| 18 if (dir.value().size() > 2) | |
| 19 return dir.value().substr(0, dir.value().size() - 1); | |
| 20 return dir.value(); | |
| 21 } | |
| 22 | |
| 23 // Given the separate-out input (everything before the colon) in the dep rule, | |
| 24 // computes the final build rule. Sets err on failure. On success, | |
| 25 // |*used_implicit| will be set to whether the implicit current directory was | |
| 26 // used. The value is used only for generating error messages. | |
| 27 bool ComputeBuildLocationFromDep(const Value& input_value, | |
| 28 const SourceDir& current_dir, | |
| 29 const base::StringPiece& input, | |
| 30 SourceDir* result, | |
| 31 Err* err) { | |
| 32 // No rule, use the current locaton. | |
| 33 if (input.empty()) { | |
| 34 *result = current_dir; | |
| 35 return true; | |
| 36 } | |
| 37 | |
| 38 // Don't allow directories to start with a single slash. All labels must be | |
| 39 // in the source root. | |
| 40 if (input[0] == '/' && (input.size() == 1 || input[1] != '/')) { | |
| 41 *err = Err(input_value, "Label can't start with a single slash", | |
| 42 "Labels must be either relative (no slash at the beginning) or be " | |
| 43 "absolute\ninside the source root (two slashes at the beginning)."); | |
| 44 return false; | |
| 45 } | |
| 46 | |
| 47 *result = current_dir.ResolveRelativeDir(input); | |
| 48 return true; | |
| 49 } | |
| 50 | |
| 51 // Given the separated-out target name (after the colon) computes the final | |
| 52 // name, using the implicit name from the previously-generated | |
| 53 // computed_location if necessary. The input_value is used only for generating | |
| 54 // error messages. | |
| 55 bool ComputeTargetNameFromDep(const Value& input_value, | |
| 56 const SourceDir& computed_location, | |
| 57 const base::StringPiece& input, | |
| 58 std::string* result, | |
| 59 Err* err) { | |
| 60 if (!input.empty()) { | |
| 61 // Easy case: input is specified, just use it. | |
| 62 result->assign(input.data(), input.size()); | |
| 63 return true; | |
| 64 } | |
| 65 | |
| 66 const std::string& loc = computed_location.value(); | |
| 67 | |
| 68 // Use implicit name. The path will be "//", "//base/", "//base/i18n/", etc. | |
| 69 if (loc.size() <= 1) { | |
| 70 *err = Err(input_value, "This dependency name is empty"); | |
| 71 return false; | |
| 72 } | |
| 73 | |
| 74 size_t next_to_last_slash = loc.rfind('/', loc.size() - 2); | |
| 75 DCHECK(next_to_last_slash != std::string::npos); | |
| 76 result->assign(&loc[next_to_last_slash + 1], | |
| 77 loc.size() - next_to_last_slash - 2); | |
| 78 return true; | |
| 79 } | |
| 80 | |
| 81 // The original value is used only for error reporting, use the |input| as the | |
| 82 // input to this function (which may be a substring of the original value when | |
| 83 // we're parsing toolchains. | |
| 84 // | |
| 85 // If the output toolchain vars are NULL, then we'll report an error if we | |
| 86 // find a toolchain specified (this is used when recursively parsing toolchain | |
| 87 // labels which themselves can't have toolchain specs). | |
| 88 // | |
| 89 // We assume that the output variables are initialized to empty so we don't | |
| 90 // write them unless we need them to contain something. | |
| 91 // | |
| 92 // Returns true on success. On failure, the out* variables might be written to | |
| 93 // but shouldn't be used. | |
| 94 bool Resolve(const SourceDir& current_dir, | |
| 95 const Label& current_toolchain, | |
| 96 const Value& original_value, | |
| 97 const base::StringPiece& input, | |
| 98 SourceDir* out_dir, | |
| 99 std::string* out_name, | |
| 100 SourceDir* out_toolchain_dir, | |
| 101 std::string* out_toolchain_name, | |
| 102 Err* err) { | |
| 103 // To workaround the problem that StringPiece operator[] doesn't return a ref. | |
| 104 const char* input_str = input.data(); | |
| 105 | |
| 106 size_t path_separator = input.find_first_of(":("); | |
| 107 base::StringPiece location_piece; | |
| 108 base::StringPiece name_piece; | |
| 109 base::StringPiece toolchain_piece; | |
| 110 if (path_separator == std::string::npos) { | |
| 111 location_piece = input; | |
| 112 // Leave name & toolchain piece null. | |
| 113 } else { | |
| 114 location_piece = base::StringPiece(&input_str[0], path_separator); | |
| 115 | |
| 116 size_t toolchain_separator = input.find('(', path_separator); | |
| 117 if (toolchain_separator == std::string::npos) { | |
| 118 name_piece = base::StringPiece(&input_str[path_separator + 1], | |
| 119 input.size() - path_separator - 1); | |
| 120 // Leave location piece null. | |
| 121 } else if (!out_toolchain_dir) { | |
| 122 // Toolchain specified but not allows in this context. | |
| 123 *err = Err(original_value, "Toolchain has a toolchain.", | |
| 124 "Your toolchain definition (inside the parens) seems to itself " | |
| 125 "have a\ntoolchain. Don't do this."); | |
| 126 return false; | |
| 127 } else { | |
| 128 // Name piece is everything between the two separators. Note that the | |
| 129 // separators may be the same (e.g. "//foo(bar)" which means empty name. | |
| 130 if (toolchain_separator > path_separator) { | |
| 131 name_piece = base::StringPiece( | |
| 132 &input_str[path_separator + 1], | |
| 133 toolchain_separator - path_separator - 1); | |
| 134 } | |
| 135 | |
| 136 // Toolchain name should end in a ) and this should be the end of the | |
| 137 // string. | |
| 138 if (input[input.size() - 1] != ')') { | |
| 139 *err = Err(original_value, "Bad toolchain name.", | |
| 140 "Toolchain name must end in a \")\" at the end of the label."); | |
| 141 return false; | |
| 142 } | |
| 143 | |
| 144 // Subtract off the two parens to just get the toolchain name. | |
| 145 toolchain_piece = base::StringPiece( | |
| 146 &input_str[toolchain_separator + 1], | |
| 147 input.size() - toolchain_separator - 2); | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 // Everything before the separator is the filename. | |
| 152 // We allow three cases: | |
| 153 // Absolute: "//foo:bar" -> /foo:bar | |
| 154 // Target in current file: ":foo" -> <currentdir>:foo | |
| 155 // Path with implicit name: "/foo" -> /foo:foo | |
| 156 if (location_piece.empty() && name_piece.empty()) { | |
| 157 // Can't use both implicit filename and name (":"). | |
| 158 *err = Err(original_value, "This doesn't specify a dependency."); | |
| 159 return false; | |
| 160 } | |
| 161 | |
| 162 if (!ComputeBuildLocationFromDep(original_value, current_dir, location_piece, | |
| 163 out_dir, err)) | |
| 164 return false; | |
| 165 | |
| 166 if (!ComputeTargetNameFromDep(original_value, *out_dir, name_piece, | |
| 167 out_name, err)) | |
| 168 return false; | |
| 169 | |
| 170 // Last, do the toolchains. | |
| 171 if (out_toolchain_dir) { | |
| 172 // Handle empty toolchain strings. We don't allow normal labels to be | |
| 173 // empty so we can't allow the recursive call of this function to do this | |
| 174 // check. | |
| 175 if (toolchain_piece.empty()) { | |
| 176 *out_toolchain_dir = current_toolchain.dir(); | |
| 177 *out_toolchain_name = current_toolchain.name(); | |
| 178 return true; | |
| 179 } else { | |
| 180 return Resolve(current_dir, current_toolchain, | |
| 181 original_value, toolchain_piece, | |
| 182 out_toolchain_dir, out_toolchain_name, NULL, NULL, err); | |
| 183 } | |
| 184 } | |
| 185 return true; | |
| 186 } | |
| 187 | |
| 188 } // namespace | |
| 189 | |
| 190 Label::Label() { | |
| 191 } | |
| 192 | |
| 193 Label::Label(const SourceDir& dir, | |
| 194 const base::StringPiece& name, | |
| 195 const SourceDir& toolchain_dir, | |
| 196 const base::StringPiece& toolchain_name) | |
| 197 : dir_(dir), | |
| 198 toolchain_dir_(toolchain_dir) { | |
| 199 name_.assign(name.data(), name.size()); | |
| 200 toolchain_name_.assign(toolchain_name.data(), toolchain_name.size()); | |
| 201 } | |
| 202 | |
| 203 Label::~Label() { | |
| 204 } | |
| 205 | |
| 206 // static | |
| 207 Label Label::Resolve(const SourceDir& current_dir, | |
| 208 const Label& current_toolchain, | |
| 209 const Value& input, | |
| 210 Err* err) { | |
| 211 Label ret; | |
| 212 if (input.type() != Value::STRING) { | |
| 213 *err = Err(input, "Dependency is not a string."); | |
| 214 return ret; | |
| 215 } | |
| 216 const std::string& input_string = input.string_value(); | |
| 217 if (input_string.empty()) { | |
| 218 *err = Err(input, "Dependency string is empty."); | |
| 219 return ret; | |
| 220 } | |
| 221 | |
| 222 if (!::Resolve(current_dir, current_toolchain, input, input_string, | |
| 223 &ret.dir_, &ret.name_, | |
| 224 &ret.toolchain_dir_, &ret.toolchain_name_, | |
| 225 err)) | |
| 226 return Label(); | |
| 227 return ret; | |
| 228 } | |
| 229 | |
| 230 Label Label::GetToolchainLabel() const { | |
| 231 return Label(toolchain_dir_, toolchain_name_, | |
| 232 SourceDir(), base::StringPiece()); | |
| 233 } | |
| 234 | |
| 235 std::string Label::GetUserVisibleName(bool include_toolchain) const { | |
| 236 std::string ret; | |
| 237 ret.reserve(dir_.value().size() + name_.size() + 1); | |
| 238 | |
| 239 if (dir_.is_null()) | |
| 240 return ret; | |
| 241 | |
| 242 ret = DirWithNoTrailingSlash(dir_); | |
| 243 ret.push_back(':'); | |
| 244 ret.append(name_); | |
| 245 | |
| 246 if (include_toolchain) { | |
| 247 ret.push_back('('); | |
| 248 if (!toolchain_dir_.is_null() && !toolchain_name_.empty()) { | |
| 249 ret.append(DirWithNoTrailingSlash(toolchain_dir_)); | |
| 250 ret.push_back(':'); | |
| 251 ret.append(toolchain_name_); | |
| 252 } | |
| 253 ret.push_back(')'); | |
| 254 } | |
| 255 return ret; | |
| 256 } | |
| 257 | |
| 258 std::string Label::GetUserVisibleName(const Label& default_toolchain) const { | |
| 259 bool include_toolchain = | |
| 260 default_toolchain.dir() != toolchain_dir_ || | |
| 261 default_toolchain.name() != toolchain_name_; | |
| 262 return GetUserVisibleName(include_toolchain); | |
| 263 } | |
| OLD | NEW |