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