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