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