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