blob: d45275925dbce6987550a744b84c1b62b9f05b59 [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
dcheng424dfa52015-12-27 23:15:507#include <utility>
8
avicab0f832015-12-26 07:00:399#include "base/macros.h"
[email protected]96ea63d2013-07-30 10:17:0710#include "base/strings/string_split.h"
11#include "base/strings/string_util.h"
12#include "tools/gn/build_settings.h"
13#include "tools/gn/err.h"
14#include "tools/gn/input_file.h"
15#include "tools/gn/label.h"
16#include "tools/gn/parse_tree.h"
17#include "tools/gn/parser.h"
[email protected]530bf7e82014-04-06 04:35:1018#include "tools/gn/scheduler.h"
[email protected]96ea63d2013-07-30 10:17:0719#include "tools/gn/scope.h"
20#include "tools/gn/settings.h"
21#include "tools/gn/tokenizer.h"
22#include "tools/gn/value.h"
23
24namespace {
25
[email protected]530bf7e82014-04-06 04:35:1026enum ValueOrScope {
27 PARSE_VALUE, // Treat the input as an expression.
28 PARSE_SCOPE, // Treat the input as code and return the resulting scope.
29};
[email protected]96ea63d2013-07-30 10:17:0730
31// Sets the origin of the value and any nested values with the given node.
[email protected]530bf7e82014-04-06 04:35:1032Value ParseValueOrScope(const Settings* settings,
33 const std::string& input,
34 ValueOrScope what,
35 const ParseNode* origin,
36 Err* err) {
37 // The memory for these will be kept around by the input file manager
38 // so the origin parse nodes for the values will be preserved.
39 InputFile* input_file;
40 std::vector<Token>* tokens;
dchenga500b692016-04-08 19:55:4241 std::unique_ptr<ParseNode>* parse_root_ptr;
[email protected]530bf7e82014-04-06 04:35:1042 g_scheduler->input_file_manager()->AddDynamicInput(
[email protected]fce5c3fe2014-04-10 21:13:0543 SourceFile(), &input_file, &tokens, &parse_root_ptr);
[email protected]96ea63d2013-07-30 10:17:0744
[email protected]530bf7e82014-04-06 04:35:1045 input_file->SetContents(input);
46 if (origin) {
47 // This description will be the blame for any error messages caused by
48 // script parsing or if a value is blamed. It will say
49 // "Error at <...>:line:char" so here we try to make a string for <...>
50 // that reads well in this context.
51 input_file->set_friendly_name(
52 "dynamically parsed input that " +
53 origin->GetRange().begin().Describe(true) +
54 " loaded ");
55 } else {
56 input_file->set_friendly_name("dynamic input");
[email protected]96ea63d2013-07-30 10:17:0757 }
58
[email protected]530bf7e82014-04-06 04:35:1059 *tokens = Tokenizer::Tokenize(input_file, err);
60 if (err->has_error())
[email protected]96ea63d2013-07-30 10:17:0761 return Value();
[email protected]530bf7e82014-04-06 04:35:1062
63 // Parse the file according to what we're looking for.
64 if (what == PARSE_VALUE)
mdempsky5557a412015-04-01 00:01:0165 *parse_root_ptr = Parser::ParseValue(*tokens, err);
[email protected]530bf7e82014-04-06 04:35:1066 else
67 *parse_root_ptr = Parser::Parse(*tokens, err); // Will return a Block.
68 if (err->has_error())
69 return Value();
70 ParseNode* parse_root = parse_root_ptr->get(); // For nicer syntax below.
[email protected]96ea63d2013-07-30 10:17:0771
72 // It's valid for the result to be a null pointer, this just means that the
73 // script returned nothing.
[email protected]530bf7e82014-04-06 04:35:1074 if (!parse_root)
[email protected]96ea63d2013-07-30 10:17:0775 return Value();
76
Dirk Prankec9126ec982017-08-17 15:05:0777 std::unique_ptr<Scope> scope(new Scope(settings));
[email protected]530bf7e82014-04-06 04:35:1078 Value result = parse_root->Execute(scope.get(), err);
79 if (err->has_error())
[email protected]96ea63d2013-07-30 10:17:0780 return Value();
[email protected]96ea63d2013-07-30 10:17:0781
[email protected]530bf7e82014-04-06 04:35:1082 // When we want the result as a scope, the result is actually the scope
83 // we made, rather than the result of running the block (which will be empty).
84 if (what == PARSE_SCOPE) {
85 DCHECK(result.type() == Value::NONE);
dcheng424dfa52015-12-27 23:15:5086 result = Value(origin, std::move(scope));
[email protected]530bf7e82014-04-06 04:35:1087 }
[email protected]96ea63d2013-07-30 10:17:0788 return result;
89}
90
[email protected]530bf7e82014-04-06 04:35:1091Value ParseList(const std::string& input, const ParseNode* origin, Err* err) {
[email protected]96ea63d2013-07-30 10:17:0792 Value ret(origin, Value::LIST);
brettw83dc1612015-08-12 07:31:1893 std::vector<std::string> as_lines = base::SplitString(
94 input, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
[email protected]96ea63d2013-07-30 10:17:0795
[email protected]97537972014-01-13 21:33:0896 // Trim one empty line from the end since the last line might end in a
97 // newline. If the user wants more trimming, they'll specify "trim" in the
98 // input conversion options.
99 if (!as_lines.empty() && as_lines[as_lines.size() - 1].empty())
[email protected]96ea63d2013-07-30 10:17:07100 as_lines.resize(as_lines.size() - 1);
101
102 ret.list_value().reserve(as_lines.size());
brettwd1033b62014-09-30 21:44:05103 for (const auto& line : as_lines)
104 ret.list_value().push_back(Value(origin, line));
[email protected]96ea63d2013-07-30 10:17:07105 return ret;
106}
107
[email protected]97537972014-01-13 21:33:08108// Backend for ConvertInputToValue, this takes the extracted string for the
109// input conversion so we can recursively call ourselves to handle the optional
110// "trim" prefix. This original value is also kept for the purposes of throwing
111// errors.
[email protected]530bf7e82014-04-06 04:35:10112Value DoConvertInputToValue(const Settings* settings,
113 const std::string& input,
[email protected]97537972014-01-13 21:33:08114 const ParseNode* origin,
115 const Value& original_input_conversion,
116 const std::string& input_conversion,
117 Err* err) {
118 if (input_conversion.empty())
119 return Value(); // Empty string means discard the result.
120
121 const char kTrimPrefix[] = "trim ";
brettw95509312015-07-16 23:57:33122 if (base::StartsWith(input_conversion, kTrimPrefix,
123 base::CompareCase::SENSITIVE)) {
[email protected]97537972014-01-13 21:33:08124 std::string trimmed;
[email protected]8af69c6c2014-03-03 19:05:31125 base::TrimWhitespaceASCII(input, base::TRIM_ALL, &trimmed);
[email protected]97537972014-01-13 21:33:08126
127 // Remove "trim" prefix from the input conversion and re-run.
128 return DoConvertInputToValue(
[email protected]530bf7e82014-04-06 04:35:10129 settings, trimmed, origin, original_input_conversion,
[email protected]97537972014-01-13 21:33:08130 input_conversion.substr(arraysize(kTrimPrefix) - 1), err);
131 }
132
133 if (input_conversion == "value")
[email protected]530bf7e82014-04-06 04:35:10134 return ParseValueOrScope(settings, input, PARSE_VALUE, origin, err);
[email protected]97537972014-01-13 21:33:08135 if (input_conversion == "string")
136 return Value(origin, input);
137 if (input_conversion == "list lines")
138 return ParseList(input, origin, err);
[email protected]530bf7e82014-04-06 04:35:10139 if (input_conversion == "scope")
140 return ParseValueOrScope(settings, input, PARSE_SCOPE, origin, err);
[email protected]97537972014-01-13 21:33:08141
142 *err = Err(original_input_conversion, "Not a valid input_conversion.",
mtklein1c41cca2016-08-01 18:46:31143 "Run gn help input_conversion to see your options.");
[email protected]97537972014-01-13 21:33:08144 return Value();
145}
146
[email protected]96ea63d2013-07-30 10:17:07147} // namespace
148
[email protected]ceb18412013-07-31 19:17:58149extern const char kInputConversion_Help[] =
brettw085a9b22016-11-08 19:10:03150 R"(input_conversion: Specifies how to transform input to a variable.
151
152 input_conversion is an argument to read_file and exec_script that specifies
153 how the result of the read operation should be converted into a variable.
154
155 "" (the default)
156 Discard the result and return None.
157
158 "list lines"
159 Return the file contents as a list, with a string for each line. The
160 newlines will not be present in the result. The last line may or may not
161 end in a newline.
162
163 After splitting, each individual line will be trimmed of whitespace on
164 both ends.
165
166 "scope"
167 Execute the block as GN code and return a scope with the resulting values
168 in it. If the input was:
169 a = [ "hello.cc", "world.cc" ]
170 b = 26
171 and you read the result into a variable named "val", then you could
172 access contents the "." operator on "val":
173 sources = val.a
174 some_count = val.b
175
176 "string"
177 Return the file contents into a single string.
178
179 "value"
180 Parse the input as if it was a literal rvalue in a buildfile. Examples of
181 typical program output using this mode:
182 [ "foo", "bar" ] (result will be a list)
183 or
184 "foo bar" (result will be a string)
185 or
186 5 (result will be an integer)
187
188 Note that if the input is empty, the result will be a null value which
189 will produce an error if assigned to a variable.
190
191 "trim ..."
192 Prefixing any of the other transformations with the word "trim" will
193 result in whitespace being trimmed from the beginning and end of the
194 result before processing.
195
196 Examples: "trim string" or "trim list lines"
197
198 Note that "trim value" is useless because the value parser skips
199 whitespace anyway.
200)";
[email protected]96ea63d2013-07-30 10:17:07201
[email protected]530bf7e82014-04-06 04:35:10202Value ConvertInputToValue(const Settings* settings,
203 const std::string& input,
[email protected]96ea63d2013-07-30 10:17:07204 const ParseNode* origin,
205 const Value& input_conversion_value,
206 Err* err) {
[email protected]97537972014-01-13 21:33:08207 if (input_conversion_value.type() == Value::NONE)
208 return Value(); // Allow null inputs to mean discard the result.
[email protected]96ea63d2013-07-30 10:17:07209 if (!input_conversion_value.VerifyTypeIs(Value::STRING, err))
210 return Value();
[email protected]530bf7e82014-04-06 04:35:10211 return DoConvertInputToValue(settings, input, origin, input_conversion_value,
[email protected]97537972014-01-13 21:33:08212 input_conversion_value.string_value(), err);
[email protected]96ea63d2013-07-30 10:17:07213}