blob: 3d9d355c8a346c3e38be5bff40cceabb45aa4d06 [file] [log] [blame]
[email protected]96ea63d2013-07-30 10:17:071// 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
[email protected]99bcf022013-11-05 14:17:275#include "tools/gn/input_conversion.h"
[email protected]96ea63d2013-07-30 10:17:076
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"
[email protected]530bf7e82014-04-06 04:35:1015#include "tools/gn/scheduler.h"
[email protected]96ea63d2013-07-30 10:17:0716#include "tools/gn/scope.h"
17#include "tools/gn/settings.h"
18#include "tools/gn/tokenizer.h"
19#include "tools/gn/value.h"
20
21namespace {
22
[email protected]530bf7e82014-04-06 04:35:1023enum ValueOrScope {
24 PARSE_VALUE, // Treat the input as an expression.
25 PARSE_SCOPE, // Treat the input as code and return the resulting scope.
26};
[email protected]96ea63d2013-07-30 10:17:0727
28// Sets the origin of the value and any nested values with the given node.
[email protected]530bf7e82014-04-06 04:35:1029Value ParseValueOrScope(const Settings* settings,
30 const std::string& input,
31 ValueOrScope what,
32 const ParseNode* origin,
33 Err* err) {
34 // The memory for these will be kept around by the input file manager
35 // so the origin parse nodes for the values will be preserved.
36 InputFile* input_file;
37 std::vector<Token>* tokens;
38 scoped_ptr<ParseNode>* parse_root_ptr;
39 g_scheduler->input_file_manager()->AddDynamicInput(
[email protected]fce5c3fe2014-04-10 21:13:0540 SourceFile(), &input_file, &tokens, &parse_root_ptr);
[email protected]96ea63d2013-07-30 10:17:0741
[email protected]530bf7e82014-04-06 04:35:1042 input_file->SetContents(input);
43 if (origin) {
44 // This description will be the blame for any error messages caused by
45 // script parsing or if a value is blamed. It will say
46 // "Error at <...>:line:char" so here we try to make a string for <...>
47 // that reads well in this context.
48 input_file->set_friendly_name(
49 "dynamically parsed input that " +
50 origin->GetRange().begin().Describe(true) +
51 " loaded ");
52 } else {
53 input_file->set_friendly_name("dynamic input");
[email protected]96ea63d2013-07-30 10:17:0754 }
55
[email protected]530bf7e82014-04-06 04:35:1056 *tokens = Tokenizer::Tokenize(input_file, err);
57 if (err->has_error())
[email protected]96ea63d2013-07-30 10:17:0758 return Value();
[email protected]530bf7e82014-04-06 04:35:1059
60 // Parse the file according to what we're looking for.
61 if (what == PARSE_VALUE)
mdempsky5557a412015-04-01 00:01:0162 *parse_root_ptr = Parser::ParseValue(*tokens, err);
[email protected]530bf7e82014-04-06 04:35:1063 else
64 *parse_root_ptr = Parser::Parse(*tokens, err); // Will return a Block.
65 if (err->has_error())
66 return Value();
67 ParseNode* parse_root = parse_root_ptr->get(); // For nicer syntax below.
[email protected]96ea63d2013-07-30 10:17:0768
69 // It's valid for the result to be a null pointer, this just means that the
70 // script returned nothing.
[email protected]530bf7e82014-04-06 04:35:1071 if (!parse_root)
[email protected]96ea63d2013-07-30 10:17:0772 return Value();
73
[email protected]530bf7e82014-04-06 04:35:1074 scoped_ptr<Scope> scope(new Scope(settings));
[email protected]530bf7e82014-04-06 04:35:1075 Value result = parse_root->Execute(scope.get(), err);
76 if (err->has_error())
[email protected]96ea63d2013-07-30 10:17:0777 return Value();
[email protected]96ea63d2013-07-30 10:17:0778
[email protected]530bf7e82014-04-06 04:35:1079 // When we want the result as a scope, the result is actually the scope
80 // we made, rather than the result of running the block (which will be empty).
81 if (what == PARSE_SCOPE) {
82 DCHECK(result.type() == Value::NONE);
83 result = Value(origin, scope.Pass());
84 }
[email protected]96ea63d2013-07-30 10:17:0785 return result;
86}
87
[email protected]530bf7e82014-04-06 04:35:1088Value ParseList(const std::string& input, const ParseNode* origin, Err* err) {
[email protected]96ea63d2013-07-30 10:17:0789 Value ret(origin, Value::LIST);
90 std::vector<std::string> as_lines;
91 base::SplitString(input, '\n', &as_lines);
92
[email protected]97537972014-01-13 21:33:0893 // Trim one empty line from the end since the last line might end in a
94 // newline. If the user wants more trimming, they'll specify "trim" in the
95 // input conversion options.
96 if (!as_lines.empty() && as_lines[as_lines.size() - 1].empty())
[email protected]96ea63d2013-07-30 10:17:0797 as_lines.resize(as_lines.size() - 1);
98
99 ret.list_value().reserve(as_lines.size());
brettwd1033b62014-09-30 21:44:05100 for (const auto& line : as_lines)
101 ret.list_value().push_back(Value(origin, line));
[email protected]96ea63d2013-07-30 10:17:07102 return ret;
103}
104
[email protected]97537972014-01-13 21:33:08105// Backend for ConvertInputToValue, this takes the extracted string for the
106// input conversion so we can recursively call ourselves to handle the optional
107// "trim" prefix. This original value is also kept for the purposes of throwing
108// errors.
[email protected]530bf7e82014-04-06 04:35:10109Value DoConvertInputToValue(const Settings* settings,
110 const std::string& input,
[email protected]97537972014-01-13 21:33:08111 const ParseNode* origin,
112 const Value& original_input_conversion,
113 const std::string& input_conversion,
114 Err* err) {
115 if (input_conversion.empty())
116 return Value(); // Empty string means discard the result.
117
118 const char kTrimPrefix[] = "trim ";
119 if (StartsWithASCII(input_conversion, kTrimPrefix, true)) {
120 std::string trimmed;
[email protected]8af69c6c2014-03-03 19:05:31121 base::TrimWhitespaceASCII(input, base::TRIM_ALL, &trimmed);
[email protected]97537972014-01-13 21:33:08122
123 // Remove "trim" prefix from the input conversion and re-run.
124 return DoConvertInputToValue(
[email protected]530bf7e82014-04-06 04:35:10125 settings, trimmed, origin, original_input_conversion,
[email protected]97537972014-01-13 21:33:08126 input_conversion.substr(arraysize(kTrimPrefix) - 1), err);
127 }
128
129 if (input_conversion == "value")
[email protected]530bf7e82014-04-06 04:35:10130 return ParseValueOrScope(settings, input, PARSE_VALUE, origin, err);
[email protected]97537972014-01-13 21:33:08131 if (input_conversion == "string")
132 return Value(origin, input);
133 if (input_conversion == "list lines")
134 return ParseList(input, origin, err);
[email protected]530bf7e82014-04-06 04:35:10135 if (input_conversion == "scope")
136 return ParseValueOrScope(settings, input, PARSE_SCOPE, origin, err);
[email protected]97537972014-01-13 21:33:08137
138 *err = Err(original_input_conversion, "Not a valid input_conversion.",
139 "Have you considered a career in retail?");
140 return Value();
141}
142
[email protected]96ea63d2013-07-30 10:17:07143} // namespace
144
[email protected]ceb18412013-07-31 19:17:58145extern const char kInputConversion_Help[] =
146 "input_conversion: Specifies how to transform input to a variable.\n"
147 "\n"
148 " input_conversion is an argument to read_file and exec_script that\n"
149 " specifies how the result of the read operation should be converted\n"
150 " into a variable.\n"
151 "\n"
[email protected]97537972014-01-13 21:33:08152 " \"\" (the default)\n"
153 " Discard the result and return None.\n"
154 "\n"
[email protected]ceb18412013-07-31 19:17:58155 " \"list lines\"\n"
156 " Return the file contents as a list, with a string for each line.\n"
[email protected]97537972014-01-13 21:33:08157 " The newlines will not be present in the result. The last line may\n"
158 " or may not end in a newline.\n"
159 "\n"
160 " After splitting, each individual line will be trimmed of\n"
161 " whitespace on both ends.\n"
[email protected]ceb18412013-07-31 19:17:58162 "\n"
[email protected]530bf7e82014-04-06 04:35:10163 " \"scope\"\n"
164 " Execute the block as GN code and return a scope with the\n"
165 " resulting values in it. If the input was:\n"
166 " a = [ \"hello.cc\", \"world.cc\" ]\n"
167 " b = 26\n"
168 " and you read the result into a variable named \"val\", then you\n"
169 " could access contents the \".\" operator on \"val\":\n"
170 " sources = val.a\n"
171 " some_count = val.b\n"
172 "\n"
173 " \"string\"\n"
174 " Return the file contents into a single string.\n"
175 "\n"
[email protected]ceb18412013-07-31 19:17:58176 " \"value\"\n"
177 " Parse the input as if it was a literal rvalue in a buildfile.\n"
178 " Examples of typical program output using this mode:\n"
179 " [ \"foo\", \"bar\" ] (result will be a list)\n"
180 " or\n"
181 " \"foo bar\" (result will be a string)\n"
182 " or\n"
183 " 5 (result will be an integer)\n"
184 "\n"
185 " Note that if the input is empty, the result will be a null value\n"
186 " which will produce an error if assigned to a variable.\n"
187 "\n"
[email protected]97537972014-01-13 21:33:08188 " \"trim ...\"\n"
189 " Prefixing any of the other transformations with the word \"trim\"\n"
190 " will result in whitespace being trimmed from the beginning and end\n"
191 " of the result before processing.\n"
192 "\n"
193 " Examples: \"trim string\" or \"trim list lines\"\n"
194 "\n"
195 " Note that \"trim value\" is useless because the value parser skips\n"
196 " whitespace anyway.\n";
[email protected]96ea63d2013-07-30 10:17:07197
[email protected]530bf7e82014-04-06 04:35:10198Value ConvertInputToValue(const Settings* settings,
199 const std::string& input,
[email protected]96ea63d2013-07-30 10:17:07200 const ParseNode* origin,
201 const Value& input_conversion_value,
202 Err* err) {
[email protected]97537972014-01-13 21:33:08203 if (input_conversion_value.type() == Value::NONE)
204 return Value(); // Allow null inputs to mean discard the result.
[email protected]96ea63d2013-07-30 10:17:07205 if (!input_conversion_value.VerifyTypeIs(Value::STRING, err))
206 return Value();
[email protected]530bf7e82014-04-06 04:35:10207 return DoConvertInputToValue(settings, input, origin, input_conversion_value,
[email protected]97537972014-01-13 21:33:08208 input_conversion_value.string_value(), err);
[email protected]96ea63d2013-07-30 10:17:07209}