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

Side by Side Diff: tools/gn/input_conversion.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/input_conversion.h ('k') | tools/gn/input_conversion_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 "input_conversion.h"
6
7 #include "base/strings/string_split.h"
8 #include "base/strings/string_util.h"
9 #include "tools/gn/build_settings.h"
10 #include "tools/gn/err.h"
11 #include "tools/gn/input_file.h"
12 #include "tools/gn/label.h"
13 #include "tools/gn/parse_tree.h"
14 #include "tools/gn/parser.h"
15 #include "tools/gn/scope.h"
16 #include "tools/gn/settings.h"
17 #include "tools/gn/tokenizer.h"
18 #include "tools/gn/value.h"
19
20 namespace {
21
22 // Returns the "first bit" of some script output for writing to error messages.
23 std::string GetExampleOfBadInput(const std::string& input) {
24 std::string result(input);
25
26 // Maybe the result starts with a blank line or something, which we don't
27 // want.
28 TrimWhitespaceASCII(result, TRIM_ALL, &result);
29
30 // Now take the first line, or the first set of chars, whichever is shorter.
31 bool trimmed = false;
32 size_t newline_offset = result.find('\n');
33 if (newline_offset != std::string::npos) {
34 trimmed = true;
35 result.resize(newline_offset);
36 }
37 TrimWhitespaceASCII(result, TRIM_ALL, &result);
38
39 const int kMaxSize = 50;
40 if (result.size() > kMaxSize) {
41 trimmed = true;
42 result.resize(kMaxSize);
43 }
44
45 if (trimmed)
46 result.append("...");
47 return result;
48 }
49
50 // When parsing the result as a value, we may get various types of errors.
51 // This creates an error message for this case with an optional nested error
52 // message to reference. If there is no nested err, pass Err().
53 //
54 // This code also takes care to rewrite the original error which will reference
55 // the temporary InputFile which won't exist when the error is propogated
56 // out to a higher level.
57 Err MakeParseErr(const std::string& input,
58 const ParseNode* origin,
59 const Err& nested) {
60 std::string help_text =
61 "When parsing a result as a \"value\" it should look like a list:\n"
62 " [ \"a\", \"b\", 5 ]\n"
63 "or a single literal:\n"
64 " \"my result\"\n"
65 "but instead I got this, which I find very confusing:\n";
66 help_text.append(input);
67 if (nested.has_error())
68 help_text.append("\nThe exact error was:");
69
70 Err result(origin, "Script result wasn't a valid value.", help_text);
71 if (nested.has_error()) {
72 result.AppendSubErr(Err(LocationRange(), nested.message(),
73 nested.help_text()));
74 }
75 return result;
76 }
77
78 // Sets the origin of the value and any nested values with the given node.
79 void RecursivelySetOrigin(Value* value, const ParseNode* origin) {
80 value->set_origin(origin);
81 if (value->type() == Value::LIST) {
82 std::vector<Value>& list_value = value->list_value();
83 for (size_t i = 0; i < list_value.size(); i++)
84 RecursivelySetOrigin(&list_value[i], origin);
85 }
86 }
87
88 Value ParseString(const std::string& input,
89 const ParseNode* origin,
90 Err* err) {
91 SourceFile empty_source_for_most_vexing_parse;
92 InputFile input_file(empty_source_for_most_vexing_parse);
93 input_file.SetContents(input);
94
95 std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, err);
96 if (err->has_error()) {
97 *err = MakeParseErr(input, origin, *err);
98 return Value();
99 }
100
101 scoped_ptr<ParseNode> expression = Parser::ParseExpression(tokens, err);
102 if (err->has_error()) {
103 *err = MakeParseErr(input, origin, *err);
104 return Value();
105 }
106
107 // It's valid for the result to be a null pointer, this just means that the
108 // script returned nothing.
109 if (!expression)
110 return Value();
111
112 // The result should either be a list or a literal, anything else is
113 // invalid.
114 if (!expression->AsList() && !expression->AsLiteral()) {
115 *err = MakeParseErr(input, origin, Err());
116 return Value();
117 }
118
119 BuildSettings build_settings;
120 Label empty_label;
121 Toolchain toolchain(empty_label);
122 Settings settings(&build_settings, &toolchain, std::string());
123 Scope scope(&settings);
124
125 Err nested_err;
126 Value result = expression->Execute(&scope, &nested_err);
127 if (nested_err.has_error()) {
128 *err = MakeParseErr(input, origin, nested_err);
129 return Value();
130 }
131
132 // The returned value will have references to the temporary parse nodes we
133 // made on the stack. If the values are used in an error message in the
134 // future, this will crash. Reset the origin of all values to be our
135 // containing origin.
136 RecursivelySetOrigin(&result, origin);
137 return result;
138 }
139
140 Value ParseList(const std::string& input,
141 const ParseNode* origin,
142 Err* err) {
143 Value ret(origin, Value::LIST);
144 std::vector<std::string> as_lines;
145 base::SplitString(input, '\n', &as_lines);
146
147 // Trim empty lines from the end.
148 // Do we want to make this configurable?
149 while (!as_lines.empty() && as_lines[as_lines.size() - 1].empty())
150 as_lines.resize(as_lines.size() - 1);
151
152 ret.list_value().reserve(as_lines.size());
153 for (size_t i = 0; i < as_lines.size(); i++)
154 ret.list_value().push_back(Value(origin, as_lines[i]));
155 return ret;
156 }
157
158 } // namespace
159
160 /*
161 input_conversion: Specifies how to transform input to a variable.
162
163 input_conversion is an argument to read_file and exec_script that specifies
164 how the result of the read operation should be converted into a variable.
165
166 "list lines":
167 Return the file contents as a list, with a string for each line. The
168 newlines will not be present in the result. Empty newlines will be
169 trimmed from the trailing end of the returned list.
170
171 "value":
172 Parse the input as if it was a literal rvalue in a buildfile.
173 Examples of typical program output using this mode:
174 [ "foo", "bar" ] (result will be a list)
175 or
176 "foo bar" (result will be a string)
177 or
178 5 (result will be an integer)
179
180 Note that if the input is empty, the result will be a null value which
181 will produce an error if assigned to a variable.
182
183 "string":
184 Return the file contents into a single string.
185 */
186
187 Value ConvertInputToValue(const std::string& input,
188 const ParseNode* origin,
189 const Value& input_conversion_value,
190 Err* err) {
191 if (!input_conversion_value.VerifyTypeIs(Value::STRING, err))
192 return Value();
193 const std::string& input_conversion = input_conversion_value.string_value();
194
195 if (input_conversion == "value")
196 return ParseString(input, origin, err);
197 if (input_conversion == "string")
198 return Value(origin, input);
199 if (input_conversion == "list lines")
200 return ParseList(input, origin, err);
201
202 *err = Err(input_conversion_value, "Not a valid read file mode.",
203 "Have you considered a career in retail?");
204 return Value();
205 }
OLDNEW
« no previous file with comments | « tools/gn/input_conversion.h ('k') | tools/gn/input_conversion_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698