Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / tools / gn / file_template.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 "tools/gn/file_template.h"
6
7 #include <algorithm>
8 #include <iostream>
9
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"
14
15 const char FileTemplate::kSource[] = "{{source}}";
16 const char FileTemplate::kSourceNamePart[] = "{{source_name_part}}";
17 const char FileTemplate::kSourceFilePart[] = "{{source_file_part}}";
18
19 const char kSourceExpansion_Help[] =
20     "How Source Expansion Works\n"
21     "\n"
22     "  Source expansion is used for the action_foreach and copy target types\n"
23     "  to map source file names to output file names or arguments.\n"
24     "\n"
25     "  To perform source expansion in the outputs, GN maps every entry in the\n"
26     "  sources to every entry in the outputs list, producing the cross\n"
27     "  product of all combinations, expanding placeholders (see below).\n"
28     "\n"
29     "  Source expansion in the args works similarly, but performing the\n"
30     "  placeholder substitution produces a different set of arguments for\n"
31     "  each invocation of the script.\n"
32     "\n"
33     "  If no placeholders are found, the outputs or args list will be treated\n"
34     "  as a static list of literal file names that do not depend on the\n"
35     "  sources.\n"
36     "\n"
37     "  See \"gn help copy\" and \"gn help action_foreach\" for more on how\n"
38     "  this is applied.\n"
39     "\n"
40     "Placeholders\n"
41     "\n"
42     "  {{source}}\n"
43     "      The name of the source file relative to the root build output\n"
44     "      directory (which is the current directory when running compilers\n"
45     "      and scripts). This will generally be used for specifying inputs\n"
46     "      to a script in the \"args\" variable.\n"
47     "\n"
48     "  {{source_file_part}}\n"
49     "      The file part of the source including the extension. For the\n"
50     "      source \"foo/bar.txt\" the source file part will be \"bar.txt\".\n"
51     "\n"
52     "  {{source_name_part}}\n"
53     "      The filename part of the source file with no directory or\n"
54     "      extension. This will generally be used for specifying a\n"
55     "      transformation from a soruce file to a destination file with the\n"
56     "      same name but different extension. For the source \"foo/bar.txt\"\n"
57     "      the source name part will be \"bar\".\n"
58     "\n"
59     "Examples\n"
60     "\n"
61     "  Non-varying outputs:\n"
62     "    action(\"hardcoded_outputs\") {\n"
63     "      sources = [ \"input1.idl\", \"input2.idl\" ]\n"
64     "      outputs = [ \"$target_out_dir/output1.dat\",\n"
65     "                  \"$target_out_dir/output2.dat\" ]\n"
66     "    }\n"
67     "  The outputs in this case will be the two literal files given.\n"
68     "\n"
69     "  Varying outputs:\n"
70     "    action_foreach(\"varying_outputs\") {\n"
71     "      sources = [ \"input1.idl\", \"input2.idl\" ]\n"
72     "      outputs = [ \"$target_out_dir/{{source_name_part}}.h\",\n"
73     "                  \"$target_out_dir/{{source_name_part}}.cc\" ]\n"
74     "    }\n"
75     "  Performing source expansion will result in the following output names:\n"
76     "    //out/Debug/obj/mydirectory/input1.h\n"
77     "    //out/Debug/obj/mydirectory/input1.cc\n"
78     "    //out/Debug/obj/mydirectory/input2.h\n"
79     "    //out/Debug/obj/mydirectory/input2.cc\n";
80
81 FileTemplate::FileTemplate(const Value& t, Err* err)
82     : has_substitutions_(false) {
83   std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false);
84   ParseInput(t, err);
85 }
86
87 FileTemplate::FileTemplate(const std::vector<std::string>& t)
88     : has_substitutions_(false) {
89   std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false);
90   for (size_t i = 0; i < t.size(); i++)
91     ParseOneTemplateString(t[i]);
92 }
93
94 FileTemplate::FileTemplate(const std::vector<SourceFile>& t)
95     : has_substitutions_(false) {
96   std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false);
97   for (size_t i = 0; i < t.size(); i++)
98     ParseOneTemplateString(t[i].value());
99 }
100
101 FileTemplate::~FileTemplate() {
102 }
103
104 // static
105 FileTemplate FileTemplate::GetForTargetOutputs(const Target* target) {
106   const Target::FileList& outputs = target->action_values().outputs();
107   std::vector<std::string> output_template_args;
108   for (size_t i = 0; i < outputs.size(); i++)
109     output_template_args.push_back(outputs[i].value());
110   return FileTemplate(output_template_args);
111 }
112
113 bool FileTemplate::IsTypeUsed(Subrange::Type type) const {
114   DCHECK(type > Subrange::LITERAL && type < Subrange::NUM_TYPES);
115   return types_required_[type];
116 }
117
118 void FileTemplate::Apply(const Value& sources,
119                          const ParseNode* origin,
120                          std::vector<Value>* dest,
121                          Err* err) const {
122   if (!sources.VerifyTypeIs(Value::LIST, err))
123     return;
124   dest->reserve(sources.list_value().size() * templates_.container().size());
125
126   // Temporary holding place, allocate outside to re-use- buffer.
127   std::vector<std::string> string_output;
128
129   const std::vector<Value>& sources_list = sources.list_value();
130   for (size_t i = 0; i < sources_list.size(); i++) {
131     string_output.clear();
132     if (!sources_list[i].VerifyTypeIs(Value::STRING, err))
133       return;
134
135     ApplyString(sources_list[i].string_value(), &string_output);
136     for (size_t out_i = 0; out_i < string_output.size(); out_i++)
137       dest->push_back(Value(origin, string_output[out_i]));
138   }
139 }
140
141 void FileTemplate::ApplyString(const std::string& str,
142                                std::vector<std::string>* output) const {
143   // Compute all substitutions needed so we can just do substitutions below.
144   // We skip the LITERAL one since that varies each time.
145   std::string subst[Subrange::NUM_TYPES];
146   for (int i = 1; i < Subrange::NUM_TYPES; i++) {
147     if (types_required_[i])
148       subst[i] = GetSubstitution(str, static_cast<Subrange::Type>(i));
149   }
150
151   size_t first_output_index = output->size();
152   output->resize(output->size() + templates_.container().size());
153   for (size_t template_i = 0;
154        template_i < templates_.container().size(); template_i++) {
155     const Template& t = templates_[template_i];
156     std::string& cur_output = (*output)[first_output_index + template_i];
157     for (size_t subrange_i = 0; subrange_i < t.container().size();
158          subrange_i++) {
159       if (t[subrange_i].type == Subrange::LITERAL)
160         cur_output.append(t[subrange_i].literal);
161       else
162         cur_output.append(subst[t[subrange_i].type]);
163     }
164   }
165 }
166
167 void FileTemplate::WriteWithNinjaExpansions(std::ostream& out) const {
168   EscapeOptions escape_options;
169   escape_options.mode = ESCAPE_NINJA_SHELL;
170   escape_options.inhibit_quoting = true;
171
172   for (size_t template_i = 0;
173        template_i < templates_.container().size(); template_i++) {
174     out << " ";  // Separate args with spaces.
175
176     const Template& t = templates_[template_i];
177
178     // Escape each subrange into a string. Since we're writing out Ninja
179     // variables, we can't quote the whole thing, so we write in pieces, only
180     // escaping the literals, and then quoting the whole thing at the end if
181     // necessary.
182     bool needs_quoting = false;
183     std::string item_str;
184     for (size_t subrange_i = 0; subrange_i < t.container().size();
185          subrange_i++) {
186       if (t[subrange_i].type == Subrange::LITERAL) {
187         item_str.append(EscapeString(t[subrange_i].literal, escape_options,
188                                      &needs_quoting));
189       } else {
190         // Don't escape this since we need to preserve the $.
191         item_str.append("${");
192         item_str.append(GetNinjaVariableNameForType(t[subrange_i].type));
193         item_str.append("}");
194       }
195     }
196
197     if (needs_quoting) {
198       // Need to shell quote the whole string.
199       out << '"' << item_str << '"';
200     } else {
201       out << item_str;
202     }
203   }
204 }
205
206 void FileTemplate::WriteNinjaVariablesForSubstitution(
207     std::ostream& out,
208     const std::string& source,
209     const EscapeOptions& escape_options) const {
210   for (int i = 1; i < Subrange::NUM_TYPES; i++) {
211     if (types_required_[i]) {
212       Subrange::Type type = static_cast<Subrange::Type>(i);
213       out << "  " << GetNinjaVariableNameForType(type) << " = ";
214       EscapeStringToStream(out, GetSubstitution(source, type), escape_options);
215       out << std::endl;
216     }
217   }
218 }
219
220 // static
221 const char* FileTemplate::GetNinjaVariableNameForType(Subrange::Type type) {
222   switch (type) {
223     case Subrange::SOURCE:
224       return "source";
225     case Subrange::NAME_PART:
226       return "source_name_part";
227     case Subrange::FILE_PART:
228       return "source_file_part";
229     default:
230       NOTREACHED();
231   }
232   return "";
233 }
234
235 // static
236 std::string FileTemplate::GetSubstitution(const std::string& source,
237                                           Subrange::Type type) {
238   switch (type) {
239     case Subrange::SOURCE:
240       return source;
241     case Subrange::NAME_PART:
242       return FindFilenameNoExtension(&source).as_string();
243     case Subrange::FILE_PART:
244       return FindFilename(&source).as_string();
245     default:
246       NOTREACHED();
247   }
248   return std::string();
249 }
250
251 void FileTemplate::ParseInput(const Value& value, Err* err) {
252   switch (value.type()) {
253     case Value::STRING:
254       ParseOneTemplateString(value.string_value());
255       break;
256     case Value::LIST:
257       for (size_t i = 0; i < value.list_value().size(); i++) {
258         if (!value.list_value()[i].VerifyTypeIs(Value::STRING, err))
259           return;
260         ParseOneTemplateString(value.list_value()[i].string_value());
261       }
262       break;
263     default:
264       *err = Err(value, "File template must be a string or list.",
265                  "A sarcastic comment about your skills goes here.");
266   }
267 }
268
269 void FileTemplate::ParseOneTemplateString(const std::string& str) {
270   templates_.container().resize(templates_.container().size() + 1);
271   Template& t = templates_[templates_.container().size() - 1];
272
273   size_t cur = 0;
274   while (true) {
275     size_t next = str.find("{{", cur);
276
277     // Pick up everything from the previous spot to here as a literal.
278     if (next == std::string::npos) {
279       if (cur != str.size())
280         t.container().push_back(Subrange(Subrange::LITERAL, str.substr(cur)));
281       break;
282     } else if (next > cur) {
283       t.container().push_back(
284           Subrange(Subrange::LITERAL, str.substr(cur, next - cur)));
285     }
286
287     // Decode the template param.
288     if (str.compare(next, arraysize(kSource) - 1, kSource) == 0) {
289       t.container().push_back(Subrange(Subrange::SOURCE));
290       types_required_[Subrange::SOURCE] = true;
291       has_substitutions_ = true;
292       cur = next + arraysize(kSource) - 1;
293     } else if (str.compare(next, arraysize(kSourceNamePart) - 1,
294                            kSourceNamePart) == 0) {
295       t.container().push_back(Subrange(Subrange::NAME_PART));
296       types_required_[Subrange::NAME_PART] = true;
297       has_substitutions_ = true;
298       cur = next + arraysize(kSourceNamePart) - 1;
299     } else if (str.compare(next, arraysize(kSourceFilePart) - 1,
300                            kSourceFilePart) == 0) {
301       t.container().push_back(Subrange(Subrange::FILE_PART));
302       types_required_[Subrange::FILE_PART] = true;
303       has_substitutions_ = true;
304       cur = next + arraysize(kSourceFilePart) - 1;
305     } else {
306       // If it's not a match, treat it like a one-char literal (this will be
307       // rare, so it's not worth the bother to add to the previous literal) so
308       // we can keep going.
309       t.container().push_back(Subrange(Subrange::LITERAL, "{"));
310       cur = next + 1;
311     }
312   }
313 }