blob: 4545525c2ee52a1dc0addbfc438ae159a223a74e [file] [log] [blame]
[email protected]96ea63d2013-07-30 10:17:071// 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"
ohrn8808c5542015-02-10 10:18:388#include "base/strings/string_util.h"
avicab0f832015-12-26 07:00:399#include "build/build_config.h"
[email protected]96ea63d2013-07-30 10:17:0710#include "tools/gn/err.h"
ohrn8808c5542015-02-10 10:18:3811#include "tools/gn/filesystem_utils.h"
[email protected]96ea63d2013-07-30 10:17:0712#include "tools/gn/parse_tree.h"
13#include "tools/gn/value.h"
14
15namespace {
16
17// We print user visible label names with no trailing slash after the
18// directory name.
19std::string DirWithNoTrailingSlash(const SourceDir& dir) {
20 // Be careful not to trim if the input is just "/" or "//".
21 if (dir.value().size() > 2)
22 return dir.value().substr(0, dir.value().size() - 1);
23 return dir.value();
24}
25
26// Given the separate-out input (everything before the colon) in the dep rule,
27// computes the final build rule. Sets err on failure. On success,
28// |*used_implicit| will be set to whether the implicit current directory was
29// used. The value is used only for generating error messages.
30bool ComputeBuildLocationFromDep(const Value& input_value,
31 const SourceDir& current_dir,
32 const base::StringPiece& input,
33 SourceDir* result,
34 Err* err) {
Mostyn Bramley-Moore781d460c2015-01-13 14:56:0435 // No rule, use the current location.
[email protected]96ea63d2013-07-30 10:17:0736 if (input.empty()) {
37 *result = current_dir;
38 return true;
39 }
40
brettwff0986f2015-06-03 21:45:4141 *result = current_dir.ResolveRelativeDir(input_value, input, err);
[email protected]96ea63d2013-07-30 10:17:0742 return true;
43}
44
45// Given the separated-out target name (after the colon) computes the final
46// name, using the implicit name from the previously-generated
47// computed_location if necessary. The input_value is used only for generating
48// error messages.
49bool ComputeTargetNameFromDep(const Value& input_value,
50 const SourceDir& computed_location,
51 const base::StringPiece& input,
52 std::string* result,
53 Err* err) {
54 if (!input.empty()) {
55 // Easy case: input is specified, just use it.
56 result->assign(input.data(), input.size());
57 return true;
58 }
59
60 const std::string& loc = computed_location.value();
61
62 // Use implicit name. The path will be "//", "//base/", "//base/i18n/", etc.
[email protected]37a2f802014-01-06 06:59:5563 if (loc.size() <= 2) {
[email protected]96ea63d2013-07-30 10:17:0764 *err = Err(input_value, "This dependency name is empty");
65 return false;
66 }
67
68 size_t next_to_last_slash = loc.rfind('/', loc.size() - 2);
69 DCHECK(next_to_last_slash != std::string::npos);
70 result->assign(&loc[next_to_last_slash + 1],
71 loc.size() - next_to_last_slash - 2);
72 return true;
73}
74
75// The original value is used only for error reporting, use the |input| as the
76// input to this function (which may be a substring of the original value when
77// we're parsing toolchains.
78//
79// If the output toolchain vars are NULL, then we'll report an error if we
80// find a toolchain specified (this is used when recursively parsing toolchain
81// labels which themselves can't have toolchain specs).
82//
83// We assume that the output variables are initialized to empty so we don't
84// write them unless we need them to contain something.
85//
86// Returns true on success. On failure, the out* variables might be written to
87// but shouldn't be used.
88bool Resolve(const SourceDir& current_dir,
89 const Label& current_toolchain,
90 const Value& original_value,
91 const base::StringPiece& input,
92 SourceDir* out_dir,
93 std::string* out_name,
94 SourceDir* out_toolchain_dir,
95 std::string* out_toolchain_name,
96 Err* err) {
97 // To workaround the problem that StringPiece operator[] doesn't return a ref.
98 const char* input_str = input.data();
ohrn8808c5542015-02-10 10:18:3899 size_t offset = 0;
100#if defined(OS_WIN)
101 if (IsPathAbsolute(input)) {
tmoniuszko006cf0f92016-04-07 07:29:48102 size_t drive_letter_pos = input[0] == '/' ? 1 : 0;
103 if (input.size() > drive_letter_pos + 2 &&
104 input[drive_letter_pos + 1] == ':' &&
105 IsSlash(input[drive_letter_pos + 2]) &&
106 base::IsAsciiAlpha(input[drive_letter_pos])) {
ohrn8808c5542015-02-10 10:18:38107 // Skip over the drive letter colon.
tmoniuszko006cf0f92016-04-07 07:29:48108 offset = drive_letter_pos + 2;
ohrn8808c5542015-02-10 10:18:38109 }
110 }
111#endif
112 size_t path_separator = input.find_first_of(":(", offset);
[email protected]96ea63d2013-07-30 10:17:07113 base::StringPiece location_piece;
114 base::StringPiece name_piece;
115 base::StringPiece toolchain_piece;
116 if (path_separator == std::string::npos) {
117 location_piece = input;
118 // Leave name & toolchain piece null.
119 } else {
120 location_piece = base::StringPiece(&input_str[0], path_separator);
121
122 size_t toolchain_separator = input.find('(', path_separator);
123 if (toolchain_separator == std::string::npos) {
124 name_piece = base::StringPiece(&input_str[path_separator + 1],
125 input.size() - path_separator - 1);
126 // Leave location piece null.
127 } else if (!out_toolchain_dir) {
128 // Toolchain specified but not allows in this context.
129 *err = Err(original_value, "Toolchain has a toolchain.",
130 "Your toolchain definition (inside the parens) seems to itself "
131 "have a\ntoolchain. Don't do this.");
132 return false;
133 } else {
134 // Name piece is everything between the two separators. Note that the
135 // separators may be the same (e.g. "//foo(bar)" which means empty name.
136 if (toolchain_separator > path_separator) {
137 name_piece = base::StringPiece(
138 &input_str[path_separator + 1],
139 toolchain_separator - path_separator - 1);
140 }
141
142 // Toolchain name should end in a ) and this should be the end of the
143 // string.
144 if (input[input.size() - 1] != ')') {
145 *err = Err(original_value, "Bad toolchain name.",
146 "Toolchain name must end in a \")\" at the end of the label.");
147 return false;
148 }
149
150 // Subtract off the two parens to just get the toolchain name.
151 toolchain_piece = base::StringPiece(
152 &input_str[toolchain_separator + 1],
153 input.size() - toolchain_separator - 2);
154 }
155 }
156
157 // Everything before the separator is the filename.
158 // We allow three cases:
159 // Absolute: "//foo:bar" -> /foo:bar
160 // Target in current file: ":foo" -> <currentdir>:foo
161 // Path with implicit name: "/foo" -> /foo:foo
162 if (location_piece.empty() && name_piece.empty()) {
163 // Can't use both implicit filename and name (":").
164 *err = Err(original_value, "This doesn't specify a dependency.");
165 return false;
166 }
167
168 if (!ComputeBuildLocationFromDep(original_value, current_dir, location_piece,
169 out_dir, err))
170 return false;
171
172 if (!ComputeTargetNameFromDep(original_value, *out_dir, name_piece,
173 out_name, err))
174 return false;
175
176 // Last, do the toolchains.
177 if (out_toolchain_dir) {
178 // Handle empty toolchain strings. We don't allow normal labels to be
179 // empty so we can't allow the recursive call of this function to do this
180 // check.
181 if (toolchain_piece.empty()) {
182 *out_toolchain_dir = current_toolchain.dir();
183 *out_toolchain_name = current_toolchain.name();
184 return true;
185 } else {
tfarina9b636af2014-12-23 00:52:07186 return Resolve(current_dir, current_toolchain, original_value,
187 toolchain_piece, out_toolchain_dir, out_toolchain_name,
188 nullptr, nullptr, err);
[email protected]96ea63d2013-07-30 10:17:07189 }
190 }
191 return true;
192}
193
194} // namespace
195
196Label::Label() {
197}
198
199Label::Label(const SourceDir& dir,
200 const base::StringPiece& name,
201 const SourceDir& toolchain_dir,
202 const base::StringPiece& toolchain_name)
203 : dir_(dir),
204 toolchain_dir_(toolchain_dir) {
205 name_.assign(name.data(), name.size());
206 toolchain_name_.assign(toolchain_name.data(), toolchain_name.size());
207}
208
[email protected]3c1274d2013-09-10 22:21:21209Label::Label(const SourceDir& dir, const base::StringPiece& name)
210 : dir_(dir) {
211 name_.assign(name.data(), name.size());
212}
213
vmpstr7d50ff542016-02-24 20:45:08214Label::Label(const Label& other) = default;
215
[email protected]96ea63d2013-07-30 10:17:07216Label::~Label() {
217}
218
219// static
220Label 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;
228 }
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;
233 }
234
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;
241}
242
243Label Label::GetToolchainLabel() const {
[email protected]3c1274d2013-09-10 22:21:21244 return Label(toolchain_dir_, toolchain_name_);
[email protected]96ea63d2013-07-30 10:17:07245}
246
[email protected]a17a7832013-12-13 17:25:10247Label Label::GetWithNoToolchain() const {
248 return Label(dir_, name_);
249}
250
[email protected]96ea63d2013-07-30 10:17:07251std::string Label::GetUserVisibleName(bool include_toolchain) const {
252 std::string ret;
253 ret.reserve(dir_.value().size() + name_.size() + 1);
254
255 if (dir_.is_null())
256 return ret;
257
258 ret = DirWithNoTrailingSlash(dir_);
259 ret.push_back(':');
260 ret.append(name_);
261
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_);
268 }
269 ret.push_back(')');
270 }
271 return ret;
272}
273
274std::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);
279}