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.
5 #include "tools/gn/input_conversion.h"
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/scheduler.h"
16 #include "tools/gn/scope.h"
17 #include "tools/gn/settings.h"
18 #include "tools/gn/tokenizer.h"
19 #include "tools/gn/value.h"
24 PARSE_VALUE
, // Treat the input as an expression.
25 PARSE_SCOPE
, // Treat the input as code and return the resulting scope.
28 // Sets the origin of the value and any nested values with the given node.
29 Value
ParseValueOrScope(const Settings
* settings
,
30 const std::string
& input
,
32 const ParseNode
* origin
,
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(
40 SourceFile(), &input_file
, &tokens
, &parse_root_ptr
);
42 input_file
->SetContents(input
);
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) +
53 input_file
->set_friendly_name("dynamic input");
56 *tokens
= Tokenizer::Tokenize(input_file
, err
);
60 // Parse the file according to what we're looking for.
61 if (what
== PARSE_VALUE
)
62 *parse_root_ptr
= Parser::ParseExpression(*tokens
, err
);
64 *parse_root_ptr
= Parser::Parse(*tokens
, err
); // Will return a Block.
67 ParseNode
* parse_root
= parse_root_ptr
->get(); // For nicer syntax below.
69 // It's valid for the result to be a null pointer, this just means that the
70 // script returned nothing.
74 // When parsing as a value, the result should either be a list or a literal,
75 // anything else is invalid.
76 if (what
== PARSE_VALUE
) {
77 if (!parse_root
->AsList() && !parse_root
->AsLiteral())
81 scoped_ptr
<Scope
> scope(new Scope(settings
));
83 Value result
= parse_root
->Execute(scope
.get(), err
);
87 // When we want the result as a scope, the result is actually the scope
88 // we made, rather than the result of running the block (which will be empty).
89 if (what
== PARSE_SCOPE
) {
90 DCHECK(result
.type() == Value::NONE
);
91 result
= Value(origin
, scope
.Pass());
96 Value
ParseList(const std::string
& input
, const ParseNode
* origin
, Err
* err
) {
97 Value
ret(origin
, Value::LIST
);
98 std::vector
<std::string
> as_lines
;
99 base::SplitString(input
, '\n', &as_lines
);
101 // Trim one empty line from the end since the last line might end in a
102 // newline. If the user wants more trimming, they'll specify "trim" in the
103 // input conversion options.
104 if (!as_lines
.empty() && as_lines
[as_lines
.size() - 1].empty())
105 as_lines
.resize(as_lines
.size() - 1);
107 ret
.list_value().reserve(as_lines
.size());
108 for (const auto& line
: as_lines
)
109 ret
.list_value().push_back(Value(origin
, line
));
113 // Backend for ConvertInputToValue, this takes the extracted string for the
114 // input conversion so we can recursively call ourselves to handle the optional
115 // "trim" prefix. This original value is also kept for the purposes of throwing
117 Value
DoConvertInputToValue(const Settings
* settings
,
118 const std::string
& input
,
119 const ParseNode
* origin
,
120 const Value
& original_input_conversion
,
121 const std::string
& input_conversion
,
123 if (input_conversion
.empty())
124 return Value(); // Empty string means discard the result.
126 const char kTrimPrefix
[] = "trim ";
127 if (StartsWithASCII(input_conversion
, kTrimPrefix
, true)) {
129 base::TrimWhitespaceASCII(input
, base::TRIM_ALL
, &trimmed
);
131 // Remove "trim" prefix from the input conversion and re-run.
132 return DoConvertInputToValue(
133 settings
, trimmed
, origin
, original_input_conversion
,
134 input_conversion
.substr(arraysize(kTrimPrefix
) - 1), err
);
137 if (input_conversion
== "value")
138 return ParseValueOrScope(settings
, input
, PARSE_VALUE
, origin
, err
);
139 if (input_conversion
== "string")
140 return Value(origin
, input
);
141 if (input_conversion
== "list lines")
142 return ParseList(input
, origin
, err
);
143 if (input_conversion
== "scope")
144 return ParseValueOrScope(settings
, input
, PARSE_SCOPE
, origin
, err
);
146 *err
= Err(original_input_conversion
, "Not a valid input_conversion.",
147 "Have you considered a career in retail?");
153 extern const char kInputConversion_Help
[] =
154 "input_conversion: Specifies how to transform input to a variable.\n"
156 " input_conversion is an argument to read_file and exec_script that\n"
157 " specifies how the result of the read operation should be converted\n"
158 " into a variable.\n"
160 " \"\" (the default)\n"
161 " Discard the result and return None.\n"
164 " Return the file contents as a list, with a string for each line.\n"
165 " The newlines will not be present in the result. The last line may\n"
166 " or may not end in a newline.\n"
168 " After splitting, each individual line will be trimmed of\n"
169 " whitespace on both ends.\n"
172 " Execute the block as GN code and return a scope with the\n"
173 " resulting values in it. If the input was:\n"
174 " a = [ \"hello.cc\", \"world.cc\" ]\n"
176 " and you read the result into a variable named \"val\", then you\n"
177 " could access contents the \".\" operator on \"val\":\n"
179 " some_count = val.b\n"
182 " Return the file contents into a single string.\n"
185 " Parse the input as if it was a literal rvalue in a buildfile.\n"
186 " Examples of typical program output using this mode:\n"
187 " [ \"foo\", \"bar\" ] (result will be a list)\n"
189 " \"foo bar\" (result will be a string)\n"
191 " 5 (result will be an integer)\n"
193 " Note that if the input is empty, the result will be a null value\n"
194 " which will produce an error if assigned to a variable.\n"
197 " Prefixing any of the other transformations with the word \"trim\"\n"
198 " will result in whitespace being trimmed from the beginning and end\n"
199 " of the result before processing.\n"
201 " Examples: \"trim string\" or \"trim list lines\"\n"
203 " Note that \"trim value\" is useless because the value parser skips\n"
204 " whitespace anyway.\n";
206 Value
ConvertInputToValue(const Settings
* settings
,
207 const std::string
& input
,
208 const ParseNode
* origin
,
209 const Value
& input_conversion_value
,
211 if (input_conversion_value
.type() == Value::NONE
)
212 return Value(); // Allow null inputs to mean discard the result.
213 if (!input_conversion_value
.VerifyTypeIs(Value::STRING
, err
))
215 return DoConvertInputToValue(settings
, input
, origin
, input_conversion_value
,
216 input_conversion_value
.string_value(), err
);