Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(389)

Side by Side Diff: tools/gn/label.cc

Issue 21114002: Add initial prototype for the GN meta-buildsystem. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: add owners and readme Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/gn/label.h ('k') | tools/gn/label_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « tools/gn/label.h ('k') | tools/gn/label_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698