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 "tools/gn/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/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" 20 21 namespace { 22 23 enum ValueOrScope { 24 PARSE_VALUE, // Treat the input as an expression. 25 PARSE_SCOPE, // Treat the input as code and return the resulting scope. 26 }; 27 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, 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( 40 SourceFile(), &input_file, &tokens, &parse_root_ptr); 41 42 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"); 54 } 55 56 *tokens = Tokenizer::Tokenize(input_file, err); 57 if (err->has_error()) 58 return Value(); 59 60 // Parse the file according to what we're looking for. 61 if (what == PARSE_VALUE) 62 *parse_root_ptr = Parser::ParseExpression(*tokens, err); 63 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. 68 69 // It's valid for the result to be a null pointer, this just means that the 70 // script returned nothing. 71 if (!parse_root) 72 return Value(); 73 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()) 78 return Value(); 79 } 80 81 scoped_ptr<Scope> scope(new Scope(settings)); 82 83 Value result = parse_root->Execute(scope.get(), err); 84 if (err->has_error()) 85 return Value(); 86 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()); 92 } 93 return result; 94 } 95 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); 100 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); 106 107 ret.list_value().reserve(as_lines.size()); 108 for (size_t i = 0; i < as_lines.size(); i++) 109 ret.list_value().push_back(Value(origin, as_lines[i])); 110 return ret; 111 } 112 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 116 // errors. 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, 122 Err* err) { 123 if (input_conversion.empty()) 124 return Value(); // Empty string means discard the result. 125 126 const char kTrimPrefix[] = "trim "; 127 if (StartsWithASCII(input_conversion, kTrimPrefix, true)) { 128 std::string trimmed; 129 base::TrimWhitespaceASCII(input, base::TRIM_ALL, &trimmed); 130 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); 135 } 136 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); 145 146 *err = Err(original_input_conversion, "Not a valid input_conversion.", 147 "Have you considered a career in retail?"); 148 return Value(); 149 } 150 151 } // namespace 152 153 extern const char kInputConversion_Help[] = 154 "input_conversion: Specifies how to transform input to a variable.\n" 155 "\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" 159 "\n" 160 " \"\" (the default)\n" 161 " Discard the result and return None.\n" 162 "\n" 163 " \"list lines\"\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" 167 "\n" 168 " After splitting, each individual line will be trimmed of\n" 169 " whitespace on both ends.\n" 170 "\n" 171 " \"scope\"\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" 175 " b = 26\n" 176 " and you read the result into a variable named \"val\", then you\n" 177 " could access contents the \".\" operator on \"val\":\n" 178 " sources = val.a\n" 179 " some_count = val.b\n" 180 "\n" 181 " \"string\"\n" 182 " Return the file contents into a single string.\n" 183 "\n" 184 " \"value\"\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" 188 " or\n" 189 " \"foo bar\" (result will be a string)\n" 190 " or\n" 191 " 5 (result will be an integer)\n" 192 "\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" 195 "\n" 196 " \"trim ...\"\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" 200 "\n" 201 " Examples: \"trim string\" or \"trim list lines\"\n" 202 "\n" 203 " Note that \"trim value\" is useless because the value parser skips\n" 204 " whitespace anyway.\n"; 205 206 Value ConvertInputToValue(const Settings* settings, 207 const std::string& input, 208 const ParseNode* origin, 209 const Value& input_conversion_value, 210 Err* err) { 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)) 214 return Value(); 215 return DoConvertInputToValue(settings, input, origin, input_conversion_value, 216 input_conversion_value.string_value(), err); 217 } 218