Update parsing of dumpsys batterystats
[chromium-blink-merge.git] / tools / gn / file_template.cc
blob6ad37e5be9c7f296dac56306d23df40d1177ea2d
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/file_template.h"
7 #include <algorithm>
8 #include <iostream>
10 #include "tools/gn/escape.h"
11 #include "tools/gn/filesystem_utils.h"
12 #include "tools/gn/string_utils.h"
13 #include "tools/gn/target.h"
15 const char FileTemplate::kSource[] = "{{source}}";
16 const char FileTemplate::kSourceNamePart[] = "{{source_name_part}}";
17 const char FileTemplate::kSourceFilePart[] = "{{source_file_part}}";
18 const char FileTemplate::kSourceDir[] = "{{source_dir}}";
19 const char FileTemplate::kRootRelDir[] = "{{source_root_relative_dir}}";
20 const char FileTemplate::kSourceGenDir[] = "{{source_gen_dir}}";
21 const char FileTemplate::kSourceOutDir[] = "{{source_out_dir}}";
23 const char kSourceExpansion_Help[] =
24 "How Source Expansion Works\n"
25 "\n"
26 " Source expansion is used for the action_foreach and copy target types\n"
27 " to map source file names to output file names or arguments.\n"
28 "\n"
29 " To perform source expansion in the outputs, GN maps every entry in the\n"
30 " sources to every entry in the outputs list, producing the cross\n"
31 " product of all combinations, expanding placeholders (see below).\n"
32 "\n"
33 " Source expansion in the args works similarly, but performing the\n"
34 " placeholder substitution produces a different set of arguments for\n"
35 " each invocation of the script.\n"
36 "\n"
37 " If no placeholders are found, the outputs or args list will be treated\n"
38 " as a static list of literal file names that do not depend on the\n"
39 " sources.\n"
40 "\n"
41 " See \"gn help copy\" and \"gn help action_foreach\" for more on how\n"
42 " this is applied.\n"
43 "\n"
44 "Placeholders\n"
45 "\n"
46 " {{source}}\n"
47 " The name of the source file including directory (*). This will\n"
48 " generally be used for specifying inputs to a script in the\n"
49 " \"args\" variable.\n"
50 " \"//foo/bar/baz.txt\" => \"../../foo/bar/baz.txt\"\n"
51 "\n"
52 " {{source_file_part}}\n"
53 " The file part of the source including the extension.\n"
54 " \"//foo/bar/baz.txt\" => \"baz.txt\"\n"
55 "\n"
56 " {{source_name_part}}\n"
57 " The filename part of the source file with no directory or\n"
58 " extension. This will generally be used for specifying a\n"
59 " transformation from a soruce file to a destination file with the\n"
60 " same name but different extension.\n"
61 " \"//foo/bar/baz.txt\" => \"baz\"\n"
62 "\n"
63 " {{source_dir}}\n"
64 " The directory (*) containing the source file with no\n"
65 " trailing slash.\n"
66 " \"//foo/bar/baz.txt\" => \"../../foo/bar\"\n"
67 "\n"
68 " {{source_root_relative_dir}}\n"
69 " The path to the source file's directory relative to the source\n"
70 " root, with no leading \"//\" or trailing slashes. If the path is\n"
71 " system-absolute, (beginning in a single slash) this will just\n"
72 " return the path with no trailing slash. This value will always\n"
73 " be the same, regardless of whether it appears in the \"outputs\"\n"
74 " or \"args\" section.\n"
75 " \"//foo/bar/baz.txt\" => \"foo/bar\"\n"
76 "\n"
77 " {{source_gen_dir}}\n"
78 " The generated file directory (*) corresponding to the source\n"
79 " file's path. This will be different than the target's generated\n"
80 " file directory if the source file is in a different directory\n"
81 " than the BUILD.gn file.\n"
82 " \"//foo/bar/baz.txt\" => \"gen/foo/bar\"\n"
83 "\n"
84 " {{source_out_dir}}\n"
85 " The object file directory (*) corresponding to the source file's\n"
86 " path, relative to the build directory. this us be different than\n"
87 " the target's out directory if the source file is in a different\n"
88 " directory than the build.gn file.\n"
89 " \"//foo/bar/baz.txt\" => \"obj/foo/bar\"\n"
90 "\n"
91 "(*) Note on directories\n"
92 "\n"
93 " Paths containing directories (except the source_root_relative_dir)\n"
94 " will be different depending on what context the expansion is evaluated\n"
95 " in. Generally it should \"just work\" but it means you can't\n"
96 " concatenate strings containing these values with reasonable results.\n"
97 "\n"
98 " Details: source expansions can be used in the \"outputs\" variable,\n"
99 " the \"args\" variable, and in calls to \"process_file_template\". The\n"
100 " \"args\" are passed to a script which is run from the build directory,\n"
101 " so these directories will relative to the build directory for the\n"
102 " script to find. In the other cases, the directories will be source-\n"
103 " absolute (begin with a \"//\") because the results of those expansions\n"
104 " will be handled by GN internally.\n"
105 "\n"
106 "Examples\n"
107 "\n"
108 " Non-varying outputs:\n"
109 " action(\"hardcoded_outputs\") {\n"
110 " sources = [ \"input1.idl\", \"input2.idl\" ]\n"
111 " outputs = [ \"$target_out_dir/output1.dat\",\n"
112 " \"$target_out_dir/output2.dat\" ]\n"
113 " }\n"
114 " The outputs in this case will be the two literal files given.\n"
115 "\n"
116 " Varying outputs:\n"
117 " action_foreach(\"varying_outputs\") {\n"
118 " sources = [ \"input1.idl\", \"input2.idl\" ]\n"
119 " outputs = [ \"{{source_gen_dir}}/{{source_name_part}}.h\",\n"
120 " \"{{source_gen_dir}}/{{source_name_part}}.cc\" ]\n"
121 " }\n"
122 " Performing source expansion will result in the following output names:\n"
123 " //out/Debug/obj/mydirectory/input1.h\n"
124 " //out/Debug/obj/mydirectory/input1.cc\n"
125 " //out/Debug/obj/mydirectory/input2.h\n"
126 " //out/Debug/obj/mydirectory/input2.cc\n";
128 FileTemplate::FileTemplate(const Settings* settings,
129 const Value& t,
130 OutputStyle output_style,
131 const SourceDir& relative_to,
132 Err* err)
133 : settings_(settings),
134 output_style_(output_style),
135 relative_to_(relative_to),
136 has_substitutions_(false) {
137 std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false);
138 ParseInput(t, err);
141 FileTemplate::FileTemplate(const Settings* settings,
142 const std::vector<std::string>& t,
143 OutputStyle output_style,
144 const SourceDir& relative_to)
145 : settings_(settings),
146 output_style_(output_style),
147 relative_to_(relative_to),
148 has_substitutions_(false) {
149 std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false);
150 for (size_t i = 0; i < t.size(); i++)
151 ParseOneTemplateString(t[i]);
154 FileTemplate::FileTemplate(const Settings* settings,
155 const std::vector<SourceFile>& t,
156 OutputStyle output_style,
157 const SourceDir& relative_to)
158 : settings_(settings),
159 output_style_(output_style),
160 relative_to_(relative_to),
161 has_substitutions_(false) {
162 std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false);
163 for (size_t i = 0; i < t.size(); i++)
164 ParseOneTemplateString(t[i].value());
167 FileTemplate::~FileTemplate() {
170 // static
171 FileTemplate FileTemplate::GetForTargetOutputs(const Target* target) {
172 const std::vector<std::string>& outputs = target->action_values().outputs();
173 std::vector<std::string> output_template_args;
174 for (size_t i = 0; i < outputs.size(); i++)
175 output_template_args.push_back(outputs[i]);
176 return FileTemplate(target->settings(), output_template_args,
177 OUTPUT_ABSOLUTE, SourceDir());
180 bool FileTemplate::IsTypeUsed(Subrange::Type type) const {
181 DCHECK(type > Subrange::LITERAL && type < Subrange::NUM_TYPES);
182 return types_required_[type];
185 void FileTemplate::Apply(const SourceFile& source,
186 std::vector<std::string>* output) const {
187 // Compute all substitutions needed so we can just do substitutions below.
188 // We skip the LITERAL one since that varies each time.
189 std::string subst[Subrange::NUM_TYPES];
190 for (int i = 1; i < Subrange::NUM_TYPES; i++) {
191 if (types_required_[i]) {
192 subst[i] = GetSubstitution(settings_, source,
193 static_cast<Subrange::Type>(i),
194 output_style_, relative_to_);
198 size_t first_output_index = output->size();
199 output->resize(output->size() + templates_.container().size());
200 for (size_t template_i = 0;
201 template_i < templates_.container().size(); template_i++) {
202 const Template& t = templates_[template_i];
203 std::string& cur_output = (*output)[first_output_index + template_i];
204 for (size_t subrange_i = 0; subrange_i < t.container().size();
205 subrange_i++) {
206 if (t[subrange_i].type == Subrange::LITERAL)
207 cur_output.append(t[subrange_i].literal);
208 else
209 cur_output.append(subst[t[subrange_i].type]);
214 void FileTemplate::WriteWithNinjaExpansions(std::ostream& out) const {
215 EscapeOptions escape_options;
216 escape_options.mode = ESCAPE_NINJA_COMMAND;
217 escape_options.inhibit_quoting = true;
219 for (size_t template_i = 0;
220 template_i < templates_.container().size(); template_i++) {
221 out << " "; // Separate args with spaces.
223 const Template& t = templates_[template_i];
225 // Escape each subrange into a string. Since we're writing out Ninja
226 // variables, we can't quote the whole thing, so we write in pieces, only
227 // escaping the literals, and then quoting the whole thing at the end if
228 // necessary.
229 bool needs_quoting = false;
230 std::string item_str;
231 for (size_t subrange_i = 0; subrange_i < t.container().size();
232 subrange_i++) {
233 if (t[subrange_i].type == Subrange::LITERAL) {
234 bool cur_needs_quoting = false;
235 item_str.append(EscapeString(t[subrange_i].literal, escape_options,
236 &cur_needs_quoting));
237 needs_quoting |= cur_needs_quoting;
238 } else {
239 // Don't escape this since we need to preserve the $.
240 item_str.append("${");
241 item_str.append(GetNinjaVariableNameForType(t[subrange_i].type));
242 item_str.append("}");
246 if (needs_quoting || item_str.empty()) {
247 // Need to shell quote the whole string. We also need to quote empty
248 // strings or it would be impossible to pass "" as a command-line
249 // argument.
250 out << '"' << item_str << '"';
251 } else {
252 out << item_str;
257 void FileTemplate::WriteNinjaVariablesForSubstitution(
258 std::ostream& out,
259 const SourceFile& source,
260 const EscapeOptions& escape_options) const {
261 for (int i = 1; i < Subrange::NUM_TYPES; i++) {
262 if (types_required_[i]) {
263 Subrange::Type type = static_cast<Subrange::Type>(i);
264 out << " " << GetNinjaVariableNameForType(type) << " = ";
265 EscapeStringToStream(
266 out,
267 GetSubstitution(settings_, source, type, output_style_, relative_to_),
268 escape_options);
269 out << std::endl;
274 // static
275 const char* FileTemplate::GetNinjaVariableNameForType(Subrange::Type type) {
276 switch (type) {
277 case Subrange::SOURCE:
278 return "source";
279 case Subrange::NAME_PART:
280 return "source_name_part";
281 case Subrange::FILE_PART:
282 return "source_file_part";
283 case Subrange::SOURCE_DIR:
284 return "source_dir";
285 case Subrange::ROOT_RELATIVE_DIR:
286 return "source_root_rel_dir";
287 case Subrange::SOURCE_GEN_DIR:
288 return "source_gen_dir";
289 case Subrange::SOURCE_OUT_DIR:
290 return "source_out_dir";
292 default:
293 NOTREACHED();
295 return "";
298 // static
299 std::string FileTemplate::GetSubstitution(const Settings* settings,
300 const SourceFile& source,
301 Subrange::Type type,
302 OutputStyle output_style,
303 const SourceDir& relative_to) {
304 std::string to_rebase;
305 switch (type) {
306 case Subrange::SOURCE:
307 if (source.is_system_absolute())
308 return source.value();
309 to_rebase = source.value();
310 break;
312 case Subrange::NAME_PART:
313 return FindFilenameNoExtension(&source.value()).as_string();
315 case Subrange::FILE_PART:
316 return source.GetName();
318 case Subrange::SOURCE_DIR:
319 if (source.is_system_absolute())
320 return DirectoryWithNoLastSlash(source.GetDir());
321 to_rebase = DirectoryWithNoLastSlash(source.GetDir());
322 break;
324 case Subrange::ROOT_RELATIVE_DIR:
325 if (source.is_system_absolute())
326 return DirectoryWithNoLastSlash(source.GetDir());
327 return RebaseSourceAbsolutePath(
328 DirectoryWithNoLastSlash(source.GetDir()), SourceDir("//"));
330 case Subrange::SOURCE_GEN_DIR:
331 to_rebase = DirectoryWithNoLastSlash(
332 GetGenDirForSourceDir(settings, source.GetDir()));
333 break;
335 case Subrange::SOURCE_OUT_DIR:
336 to_rebase = DirectoryWithNoLastSlash(
337 GetOutputDirForSourceDir(settings, source.GetDir()));
338 break;
340 default:
341 NOTREACHED();
342 return std::string();
345 // If we get here, the result is a path that should be made relative or
346 // absolute according to the output_style. Other cases (just file name or
347 // extension extraction) will have been handled via early return above.
348 if (output_style == OUTPUT_ABSOLUTE)
349 return to_rebase;
350 return RebaseSourceAbsolutePath(to_rebase, relative_to);
353 void FileTemplate::ParseInput(const Value& value, Err* err) {
354 switch (value.type()) {
355 case Value::STRING:
356 ParseOneTemplateString(value.string_value());
357 break;
358 case Value::LIST:
359 for (size_t i = 0; i < value.list_value().size(); i++) {
360 if (!value.list_value()[i].VerifyTypeIs(Value::STRING, err))
361 return;
362 ParseOneTemplateString(value.list_value()[i].string_value());
364 break;
365 default:
366 *err = Err(value, "File template must be a string or list.",
367 "A sarcastic comment about your skills goes here.");
371 void FileTemplate::ParseOneTemplateString(const std::string& str) {
372 templates_.container().resize(templates_.container().size() + 1);
373 Template& t = templates_[templates_.container().size() - 1];
375 size_t cur = 0;
376 while (true) {
377 size_t next = str.find("{{", cur);
379 // Pick up everything from the previous spot to here as a literal.
380 if (next == std::string::npos) {
381 if (cur != str.size())
382 t.container().push_back(Subrange(Subrange::LITERAL, str.substr(cur)));
383 break;
384 } else if (next > cur) {
385 t.container().push_back(
386 Subrange(Subrange::LITERAL, str.substr(cur, next - cur)));
389 // Given the name of the string constant and enum for a template parameter,
390 // checks for it and stores it. Writing this as a function requires passing
391 // the entire state of this function as arguments, so this actually ends
392 // up being more clear.
393 #define IF_MATCH_THEN_STORE(const_name, enum_name) \
394 if (str.compare(next, arraysize(const_name) - 1, const_name) == 0) { \
395 t.container().push_back(Subrange(Subrange::enum_name)); \
396 types_required_[Subrange::enum_name] = true; \
397 has_substitutions_ = true; \
398 cur = next + arraysize(const_name) - 1; \
401 // Decode the template param.
402 IF_MATCH_THEN_STORE(kSource, SOURCE)
403 else IF_MATCH_THEN_STORE(kSourceNamePart, NAME_PART)
404 else IF_MATCH_THEN_STORE(kSourceFilePart, FILE_PART)
405 else IF_MATCH_THEN_STORE(kSourceDir, SOURCE_DIR)
406 else IF_MATCH_THEN_STORE(kRootRelDir, ROOT_RELATIVE_DIR)
407 else IF_MATCH_THEN_STORE(kSourceGenDir, SOURCE_GEN_DIR)
408 else IF_MATCH_THEN_STORE(kSourceOutDir, SOURCE_OUT_DIR)
409 else {
410 // If it's not a match, treat it like a one-char literal (this will be
411 // rare, so it's not worth the bother to add to the previous literal) so
412 // we can keep going.
413 t.container().push_back(Subrange(Subrange::LITERAL, "{"));
414 cur = next + 1;
417 #undef IF_MATCH_THEN_STORE