blob: 967d1464b8f49b5cf3a6d27470c66477b96a4f38 [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
brettw189c461d2016-11-17 05:38:32196const char kLabels_Help[] =
197 R"*(About labels
198
199 Everything that can participate in the dependency graph (targets, configs,
200 and toolchains) are identified by labels. A common label looks like:
201
202 //base/test:test_support
203
204 This consists of a source-root-absolute path, a colon, and a name. This means
205 to look for the thing named "test_support" in "base/test/BUILD.gn".
206
207 You can also specify system absolute paths if necessary. Typically such
208 paths would be specified via a build arg so the developer can specify where
209 the component is on their system.
210
211 /usr/local/foo:bar (Posix)
212 /C:/Program Files/MyLibs:bar (Windows)
213
214Toolchains
215
216 A canonical label includes the label of the toolchain being used. Normally,
217 the toolchain label is implicitly inherited from the current execution
218 context, but you can override this to specify cross-toolchain dependencies:
219
220 //base/test:test_support(//build/toolchain/win:msvc)
221
222 Here GN will look for the toolchain definition called "msvc" in the file
223 "//build/toolchain/win" to know how to compile this target.
224
225Relative labels
226
227 If you want to refer to something in the same buildfile, you can omit
228 the path name and just start with a colon. This format is recommended for
229 all same-file references.
230
231 :base
232
233 Labels can be specified as being relative to the current directory.
234 Stylistically, we prefer to use absolute paths for all non-file-local
235 references unless a build file needs to be run in different contexts (like a
236 project needs to be both standalone and pulled into other projects in
237 difference places in the directory hierarchy).
238
239 source/plugin:myplugin
240 ../net:url_request
241
242Implicit names
243
244 If a name is unspecified, it will inherit the directory name. Stylistically,
245 we prefer to omit the colon and name when possible:
246
247 //net -> //net:net
248 //tools/gn -> //tools/gn:gn
249)*";
250
[email protected]96ea63d2013-07-30 10:17:07251Label::Label() {
252}
253
254Label::Label(const SourceDir& dir,
255 const base::StringPiece& name,
256 const SourceDir& toolchain_dir,
257 const base::StringPiece& toolchain_name)
258 : dir_(dir),
259 toolchain_dir_(toolchain_dir) {
260 name_.assign(name.data(), name.size());
261 toolchain_name_.assign(toolchain_name.data(), toolchain_name.size());
262}
263
[email protected]3c1274d2013-09-10 22:21:21264Label::Label(const SourceDir& dir, const base::StringPiece& name)
265 : dir_(dir) {
266 name_.assign(name.data(), name.size());
267}
268
vmpstr7d50ff542016-02-24 20:45:08269Label::Label(const Label& other) = default;
270
[email protected]96ea63d2013-07-30 10:17:07271Label::~Label() {
272}
273
274// static
275Label Label::Resolve(const SourceDir& current_dir,
276 const Label& current_toolchain,
277 const Value& input,
278 Err* err) {
279 Label ret;
280 if (input.type() != Value::STRING) {
281 *err = Err(input, "Dependency is not a string.");
282 return ret;
283 }
284 const std::string& input_string = input.string_value();
285 if (input_string.empty()) {
286 *err = Err(input, "Dependency string is empty.");
287 return ret;
288 }
289
290 if (!::Resolve(current_dir, current_toolchain, input, input_string,
291 &ret.dir_, &ret.name_,
292 &ret.toolchain_dir_, &ret.toolchain_name_,
293 err))
294 return Label();
295 return ret;
296}
297
298Label Label::GetToolchainLabel() const {
[email protected]3c1274d2013-09-10 22:21:21299 return Label(toolchain_dir_, toolchain_name_);
[email protected]96ea63d2013-07-30 10:17:07300}
301
[email protected]a17a7832013-12-13 17:25:10302Label Label::GetWithNoToolchain() const {
303 return Label(dir_, name_);
304}
305
[email protected]96ea63d2013-07-30 10:17:07306std::string Label::GetUserVisibleName(bool include_toolchain) const {
307 std::string ret;
308 ret.reserve(dir_.value().size() + name_.size() + 1);
309
310 if (dir_.is_null())
311 return ret;
312
313 ret = DirWithNoTrailingSlash(dir_);
314 ret.push_back(':');
315 ret.append(name_);
316
317 if (include_toolchain) {
318 ret.push_back('(');
319 if (!toolchain_dir_.is_null() && !toolchain_name_.empty()) {
320 ret.append(DirWithNoTrailingSlash(toolchain_dir_));
321 ret.push_back(':');
322 ret.append(toolchain_name_);
323 }
324 ret.push_back(')');
325 }
326 return ret;
327}
328
329std::string Label::GetUserVisibleName(const Label& default_toolchain) const {
330 bool include_toolchain =
331 default_toolchain.dir() != toolchain_dir_ ||
332 default_toolchain.name() != toolchain_name_;
333 return GetUserVisibleName(include_toolchain);
334}