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"
16 // We print user visible label names with no trailing slash after the
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);
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
,
34 // No rule, use the current location.
36 *result
= current_dir
;
40 *result
= current_dir
.ResolveRelativeDir(input_value
, input
, err
);
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
48 bool ComputeTargetNameFromDep(const Value
& input_value
,
49 const SourceDir
& computed_location
,
50 const base::StringPiece
& input
,
54 // Easy case: input is specified, just use it.
55 result
->assign(input
.data(), input
.size());
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");
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);
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
,
92 std::string
* out_name
,
93 SourceDir
* out_toolchain_dir
,
94 std::string
* out_toolchain_name
,
96 // To workaround the problem that StringPiece operator[] doesn't return a ref.
97 const char* input_str
= input
.data();
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() + "\".");
107 if (input
.size() > 3 && input
[2] == ':' && IsSlash(input
[3]) &&
108 IsAsciiAlpha(input
[1])) {
109 // Skip over the drive letter colon.
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.
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.");
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
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.");
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.");
170 if (!ComputeBuildLocationFromDep(original_value
, current_dir
, location_piece
,
174 if (!ComputeTargetNameFromDep(original_value
, *out_dir
, name_piece
,
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
183 if (toolchain_piece
.empty()) {
184 *out_toolchain_dir
= current_toolchain
.dir();
185 *out_toolchain_name
= current_toolchain
.name();
188 return Resolve(current_dir
, current_toolchain
, original_value
,
189 toolchain_piece
, out_toolchain_dir
, out_toolchain_name
,
190 nullptr, nullptr, err
);
201 Label::Label(const SourceDir
& dir
,
202 const base::StringPiece
& name
,
203 const SourceDir
& toolchain_dir
,
204 const base::StringPiece
& toolchain_name
)
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
)
213 name_
.assign(name
.data(), name
.size());
220 Label
Label::Resolve(const SourceDir
& current_dir
,
221 const Label
& current_toolchain
,
225 if (input
.type() != Value::STRING
) {
226 *err
= Err(input
, "Dependency is not a string.");
229 const std::string
& input_string
= input
.string_value();
230 if (input_string
.empty()) {
231 *err
= Err(input
, "Dependency string is empty.");
235 if (!::Resolve(current_dir
, current_toolchain
, input
, input_string
,
236 &ret
.dir_
, &ret
.name_
,
237 &ret
.toolchain_dir_
, &ret
.toolchain_name_
,
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 {
253 ret
.reserve(dir_
.value().size() + name_
.size() + 1);
258 ret
= DirWithNoTrailingSlash(dir_
);
262 if (include_toolchain
) {
264 if (!toolchain_dir_
.is_null() && !toolchain_name_
.empty()) {
265 ret
.append(DirWithNoTrailingSlash(toolchain_dir_
));
267 ret
.append(toolchain_name_
);
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
);