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

Unified 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, 5 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/gn/input_conversion.h ('k') | tools/gn/input_conversion_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/gn/input_conversion.cc
diff --git a/tools/gn/input_conversion.cc b/tools/gn/input_conversion.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c0a0685ca36877fca9116358f698c74ed39313bf
--- /dev/null
+++ b/tools/gn/input_conversion.cc
@@ -0,0 +1,205 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "input_conversion.h"
+
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/err.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/label.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/parser.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/tokenizer.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+// Returns the "first bit" of some script output for writing to error messages.
+std::string GetExampleOfBadInput(const std::string& input) {
+ std::string result(input);
+
+ // Maybe the result starts with a blank line or something, which we don't
+ // want.
+ TrimWhitespaceASCII(result, TRIM_ALL, &result);
+
+ // Now take the first line, or the first set of chars, whichever is shorter.
+ bool trimmed = false;
+ size_t newline_offset = result.find('\n');
+ if (newline_offset != std::string::npos) {
+ trimmed = true;
+ result.resize(newline_offset);
+ }
+ TrimWhitespaceASCII(result, TRIM_ALL, &result);
+
+ const int kMaxSize = 50;
+ if (result.size() > kMaxSize) {
+ trimmed = true;
+ result.resize(kMaxSize);
+ }
+
+ if (trimmed)
+ result.append("...");
+ return result;
+}
+
+// When parsing the result as a value, we may get various types of errors.
+// This creates an error message for this case with an optional nested error
+// message to reference. If there is no nested err, pass Err().
+//
+// This code also takes care to rewrite the original error which will reference
+// the temporary InputFile which won't exist when the error is propogated
+// out to a higher level.
+Err MakeParseErr(const std::string& input,
+ const ParseNode* origin,
+ const Err& nested) {
+ std::string help_text =
+ "When parsing a result as a \"value\" it should look like a list:\n"
+ " [ \"a\", \"b\", 5 ]\n"
+ "or a single literal:\n"
+ " \"my result\"\n"
+ "but instead I got this, which I find very confusing:\n";
+ help_text.append(input);
+ if (nested.has_error())
+ help_text.append("\nThe exact error was:");
+
+ Err result(origin, "Script result wasn't a valid value.", help_text);
+ if (nested.has_error()) {
+ result.AppendSubErr(Err(LocationRange(), nested.message(),
+ nested.help_text()));
+ }
+ return result;
+}
+
+// Sets the origin of the value and any nested values with the given node.
+void RecursivelySetOrigin(Value* value, const ParseNode* origin) {
+ value->set_origin(origin);
+ if (value->type() == Value::LIST) {
+ std::vector<Value>& list_value = value->list_value();
+ for (size_t i = 0; i < list_value.size(); i++)
+ RecursivelySetOrigin(&list_value[i], origin);
+ }
+}
+
+Value ParseString(const std::string& input,
+ const ParseNode* origin,
+ Err* err) {
+ SourceFile empty_source_for_most_vexing_parse;
+ InputFile input_file(empty_source_for_most_vexing_parse);
+ input_file.SetContents(input);
+
+ std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, err);
+ if (err->has_error()) {
+ *err = MakeParseErr(input, origin, *err);
+ return Value();
+ }
+
+ scoped_ptr<ParseNode> expression = Parser::ParseExpression(tokens, err);
+ if (err->has_error()) {
+ *err = MakeParseErr(input, origin, *err);
+ return Value();
+ }
+
+ // It's valid for the result to be a null pointer, this just means that the
+ // script returned nothing.
+ if (!expression)
+ return Value();
+
+ // The result should either be a list or a literal, anything else is
+ // invalid.
+ if (!expression->AsList() && !expression->AsLiteral()) {
+ *err = MakeParseErr(input, origin, Err());
+ return Value();
+ }
+
+ BuildSettings build_settings;
+ Label empty_label;
+ Toolchain toolchain(empty_label);
+ Settings settings(&build_settings, &toolchain, std::string());
+ Scope scope(&settings);
+
+ Err nested_err;
+ Value result = expression->Execute(&scope, &nested_err);
+ if (nested_err.has_error()) {
+ *err = MakeParseErr(input, origin, nested_err);
+ return Value();
+ }
+
+ // The returned value will have references to the temporary parse nodes we
+ // made on the stack. If the values are used in an error message in the
+ // future, this will crash. Reset the origin of all values to be our
+ // containing origin.
+ RecursivelySetOrigin(&result, origin);
+ return result;
+}
+
+Value ParseList(const std::string& input,
+ const ParseNode* origin,
+ Err* err) {
+ Value ret(origin, Value::LIST);
+ std::vector<std::string> as_lines;
+ base::SplitString(input, '\n', &as_lines);
+
+ // Trim empty lines from the end.
+ // Do we want to make this configurable?
+ while (!as_lines.empty() && as_lines[as_lines.size() - 1].empty())
+ as_lines.resize(as_lines.size() - 1);
+
+ ret.list_value().reserve(as_lines.size());
+ for (size_t i = 0; i < as_lines.size(); i++)
+ ret.list_value().push_back(Value(origin, as_lines[i]));
+ return ret;
+}
+
+} // namespace
+
+/*
+input_conversion: Specifies how to transform input to a variable.
+
+ input_conversion is an argument to read_file and exec_script that specifies
+ how the result of the read operation should be converted into a variable.
+
+ "list lines":
+ Return the file contents as a list, with a string for each line. The
+ newlines will not be present in the result. Empty newlines will be
+ trimmed from the trailing end of the returned list.
+
+ "value":
+ Parse the input as if it was a literal rvalue in a buildfile.
+ Examples of typical program output using this mode:
+ [ "foo", "bar" ] (result will be a list)
+ or
+ "foo bar" (result will be a string)
+ or
+ 5 (result will be an integer)
+
+ Note that if the input is empty, the result will be a null value which
+ will produce an error if assigned to a variable.
+
+ "string":
+ Return the file contents into a single string.
+*/
+
+Value ConvertInputToValue(const std::string& input,
+ const ParseNode* origin,
+ const Value& input_conversion_value,
+ Err* err) {
+ if (!input_conversion_value.VerifyTypeIs(Value::STRING, err))
+ return Value();
+ const std::string& input_conversion = input_conversion_value.string_value();
+
+ if (input_conversion == "value")
+ return ParseString(input, origin, err);
+ if (input_conversion == "string")
+ return Value(origin, input);
+ if (input_conversion == "list lines")
+ return ParseList(input, origin, err);
+
+ *err = Err(input_conversion_value, "Not a valid read file mode.",
+ "Have you considered a career in retail?");
+ return Value();
+}
« 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