- add sources.
[platform/framework/web/crosswalk.git] / src / tools / gn / input_conversion.cc
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 size_t 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   Settings settings(&build_settings, std::string());
121   Scope scope(&settings);
122
123   Err nested_err;
124   Value result = expression->Execute(&scope, &nested_err);
125   if (nested_err.has_error()) {
126     *err = MakeParseErr(input, origin, nested_err);
127     return Value();
128   }
129
130   // The returned value will have references to the temporary parse nodes we
131   // made on the stack. If the values are used in an error message in the
132   // future, this will crash. Reset the origin of all values to be our
133   // containing origin.
134   RecursivelySetOrigin(&result, origin);
135   return result;
136 }
137
138 Value ParseList(const std::string& input,
139                 const ParseNode* origin,
140                 Err* err) {
141   Value ret(origin, Value::LIST);
142   std::vector<std::string> as_lines;
143   base::SplitString(input, '\n', &as_lines);
144
145   // Trim empty lines from the end.
146   // Do we want to make this configurable?
147   while (!as_lines.empty() && as_lines[as_lines.size() - 1].empty())
148     as_lines.resize(as_lines.size() - 1);
149
150   ret.list_value().reserve(as_lines.size());
151   for (size_t i = 0; i < as_lines.size(); i++)
152     ret.list_value().push_back(Value(origin, as_lines[i]));
153   return ret;
154 }
155
156 }  // namespace
157
158 extern const char kInputConversion_Help[] =
159     "input_conversion: Specifies how to transform input to a variable.\n"
160     "\n"
161     "  input_conversion is an argument to read_file and exec_script that\n"
162     "  specifies how the result of the read operation should be converted\n"
163     "  into a variable.\n"
164     "\n"
165     "  \"list lines\"\n"
166     "      Return the file contents as a list, with a string for each line.\n"
167     "      The newlines will not be present in the result. Empty newlines\n"
168     "      will be trimmed from the trailing end of the returned list.\n"
169     "\n"
170     "  \"value\"\n"
171     "      Parse the input as if it was a literal rvalue in a buildfile.\n"
172     "      Examples of typical program output using this mode:\n"
173     "        [ \"foo\", \"bar\" ]     (result will be a list)\n"
174     "      or\n"
175     "        \"foo bar\"            (result will be a string)\n"
176     "      or\n"
177     "        5                    (result will be an integer)\n"
178     "\n"
179     "      Note that if the input is empty, the result will be a null value\n"
180     "      which will produce an error if assigned to a variable.\n"
181     "\n"
182     "  \"string\"\n"
183     "      Return the file contents into a single string.\n";
184
185 Value ConvertInputToValue(const std::string& input,
186                           const ParseNode* origin,
187                           const Value& input_conversion_value,
188                           Err* err) {
189   if (!input_conversion_value.VerifyTypeIs(Value::STRING, err))
190     return Value();
191   const std::string& input_conversion = input_conversion_value.string_value();
192
193   if (input_conversion == "value")
194     return ParseString(input, origin, err);
195   if (input_conversion == "string")
196     return Value(origin, input);
197   if (input_conversion == "list lines")
198     return ParseList(input, origin, err);
199
200   *err = Err(input_conversion_value, "Not a valid read file mode.",
201              "Have you considered a career in retail?");
202   return Value();
203 }