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