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 "gn/ninja_target_writer.h"
6
7 #include <sstream>
8
9 #include "base/files/file_util.h"
10 #include "base/strings/string_util.h"
11 #include "gn/c_substitution_type.h"
12 #include "gn/config_values_extractors.h"
13 #include "gn/err.h"
14 #include "gn/escape.h"
15 #include "gn/filesystem_utils.h"
16 #include "gn/general_tool.h"
17 #include "gn/ninja_action_target_writer.h"
18 #include "gn/ninja_binary_target_writer.h"
19 #include "gn/ninja_bundle_data_target_writer.h"
20 #include "gn/ninja_copy_target_writer.h"
21 #include "gn/ninja_create_bundle_target_writer.h"
22 #include "gn/ninja_generated_file_target_writer.h"
23 #include "gn/ninja_group_target_writer.h"
24 #include "gn/ninja_target_command_util.h"
25 #include "gn/ninja_utils.h"
26 #include "gn/output_file.h"
27 #include "gn/rust_substitution_type.h"
28 #include "gn/scheduler.h"
29 #include "gn/string_output_buffer.h"
30 #include "gn/string_utils.h"
31 #include "gn/substitution_writer.h"
32 #include "gn/target.h"
33 #include "gn/trace.h"
34
NinjaTargetWriter(const Target * target,std::ostream & out)35 NinjaTargetWriter::NinjaTargetWriter(const Target* target, std::ostream& out)
36 : settings_(target->settings()),
37 target_(target),
38 out_(out),
39 path_output_(settings_->build_settings()->build_dir(),
40 settings_->build_settings()->root_path_utf8(),
41 ESCAPE_NINJA) {}
42
43 NinjaTargetWriter::~NinjaTargetWriter() = default;
44
45 // static
RunAndWriteFile(const Target * target)46 std::string NinjaTargetWriter::RunAndWriteFile(const Target* target) {
47 const Settings* settings = target->settings();
48
49 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE,
50 target->label().GetUserVisibleName(false));
51 trace.SetToolchain(settings->toolchain_label());
52
53 if (g_scheduler->verbose_logging())
54 g_scheduler->Log("Computing", target->label().GetUserVisibleName(true));
55
56 // It's ridiculously faster to write to a string and then write that to
57 // disk in one operation than to use an fstream here.
58 StringOutputBuffer storage;
59 std::ostream rules(&storage);
60
61 // Call out to the correct sub-type of writer. Binary targets need to be
62 // written to separate files for compiler flag scoping, but other target
63 // types can have their rules coalesced.
64 //
65 // In ninja, if a rule uses a variable (like $include_dirs) it will use
66 // the value set by indenting it under the build line or it takes the value
67 // from the end of the invoking scope (otherwise the current file). It does
68 // not copy the value from what it was when the build line was encountered.
69 // To avoid writing lots of duplicate rules for defines and cflags, etc. on
70 // each source file build line, we use separate .ninja files with the shared
71 // variables set at the top.
72 //
73 // Groups and actions don't use this type of flag, they make unique rules
74 // or write variables scoped under each build line. As a result, they don't
75 // need the separate files.
76 bool needs_file_write = false;
77 if (target->output_type() == Target::BUNDLE_DATA) {
78 NinjaBundleDataTargetWriter writer(target, rules);
79 writer.Run();
80 } else if (target->output_type() == Target::CREATE_BUNDLE) {
81 NinjaCreateBundleTargetWriter writer(target, rules);
82 writer.Run();
83 } else if (target->output_type() == Target::COPY_FILES) {
84 NinjaCopyTargetWriter writer(target, rules);
85 writer.Run();
86 } else if (target->output_type() == Target::ACTION ||
87 target->output_type() == Target::ACTION_FOREACH) {
88 NinjaActionTargetWriter writer(target, rules);
89 writer.Run();
90 } else if (target->output_type() == Target::GROUP) {
91 NinjaGroupTargetWriter writer(target, rules);
92 writer.Run();
93 } else if (target->output_type() == Target::GENERATED_FILE) {
94 NinjaGeneratedFileTargetWriter writer(target, rules);
95 writer.Run();
96 } else if (target->IsBinary()) {
97 needs_file_write = true;
98 NinjaBinaryTargetWriter writer(target, rules);
99 writer.Run();
100 } else {
101 CHECK(0) << "Output type of target not handled.";
102 }
103
104 if (needs_file_write) {
105 // Write the ninja file.
106 SourceFile ninja_file = GetNinjaFileForTarget(target);
107 base::FilePath full_ninja_file =
108 settings->build_settings()->GetFullPath(ninja_file);
109 storage.WriteToFileIfChanged(full_ninja_file, nullptr);
110
111 EscapeOptions options;
112 options.mode = ESCAPE_NINJA;
113
114 // Return the subninja command to load the rules file.
115 std::string result = "subninja ";
116 result.append(EscapeString(
117 OutputFile(target->settings()->build_settings(), ninja_file).value(),
118 options, nullptr));
119 result.push_back('\n');
120 return result;
121 }
122
123 // No separate file required, just return the rules.
124 return storage.str();
125 }
126
WriteEscapedSubstitution(const Substitution * type)127 void NinjaTargetWriter::WriteEscapedSubstitution(const Substitution* type) {
128 EscapeOptions opts;
129 opts.mode = ESCAPE_NINJA;
130
131 out_ << type->ninja_name << " = ";
132 EscapeStringToStream(
133 out_, SubstitutionWriter::GetTargetSubstitution(target_, type), opts);
134 out_ << std::endl;
135 }
136
WriteSharedVars(const SubstitutionBits & bits)137 void NinjaTargetWriter::WriteSharedVars(const SubstitutionBits& bits) {
138 bool written_anything = false;
139
140 // Target label.
141 if (bits.used.count(&SubstitutionLabel)) {
142 WriteEscapedSubstitution(&SubstitutionLabel);
143 written_anything = true;
144 }
145
146 // Target label name.
147 if (bits.used.count(&SubstitutionLabelName)) {
148 WriteEscapedSubstitution(&SubstitutionLabelName);
149 written_anything = true;
150 }
151
152 // Target label name without toolchain.
153 if (bits.used.count(&SubstitutionLabelNoToolchain)) {
154 WriteEscapedSubstitution(&SubstitutionLabelNoToolchain);
155 written_anything = true;
156 }
157
158 // Root gen dir.
159 if (bits.used.count(&SubstitutionRootGenDir)) {
160 WriteEscapedSubstitution(&SubstitutionRootGenDir);
161 written_anything = true;
162 }
163
164 // Root out dir.
165 if (bits.used.count(&SubstitutionRootOutDir)) {
166 WriteEscapedSubstitution(&SubstitutionRootOutDir);
167 written_anything = true;
168 }
169
170 // Target gen dir.
171 if (bits.used.count(&SubstitutionTargetGenDir)) {
172 WriteEscapedSubstitution(&SubstitutionTargetGenDir);
173 written_anything = true;
174 }
175
176 // Target out dir.
177 if (bits.used.count(&SubstitutionTargetOutDir)) {
178 WriteEscapedSubstitution(&SubstitutionTargetOutDir);
179 written_anything = true;
180 }
181
182 // Target output name.
183 if (bits.used.count(&SubstitutionTargetOutputName)) {
184 WriteEscapedSubstitution(&SubstitutionTargetOutputName);
185 written_anything = true;
186 }
187
188 // If we wrote any vars, separate them from the rest of the file that follows
189 // with a blank line.
190 if (written_anything)
191 out_ << std::endl;
192 }
193
WriteCCompilerVars(const SubstitutionBits & bits,bool indent,bool respect_source_used)194 void NinjaTargetWriter::WriteCCompilerVars(const SubstitutionBits& bits,
195 bool indent,
196 bool respect_source_used) {
197 // Defines.
198 if (bits.used.count(&CSubstitutionDefines)) {
199 if (indent)
200 out_ << " ";
201 out_ << CSubstitutionDefines.ninja_name << " =";
202 RecursiveTargetConfigToStream<std::string>(kRecursiveWriterSkipDuplicates,
203 target_, &ConfigValues::defines,
204 DefineWriter(), out_);
205 out_ << std::endl;
206 }
207
208 // Framework search path.
209 if (bits.used.count(&CSubstitutionFrameworkDirs)) {
210 const Tool* tool = target_->toolchain()->GetTool(CTool::kCToolLink);
211
212 if (indent)
213 out_ << " ";
214 out_ << CSubstitutionFrameworkDirs.ninja_name << " =";
215 PathOutput framework_dirs_output(
216 path_output_.current_dir(),
217 settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
218 RecursiveTargetConfigToStream<SourceDir>(
219 kRecursiveWriterSkipDuplicates, target_, &ConfigValues::framework_dirs,
220 FrameworkDirsWriter(framework_dirs_output,
221 tool->framework_dir_switch()),
222 out_);
223 out_ << std::endl;
224 }
225
226 // Include directories.
227 if (bits.used.count(&CSubstitutionIncludeDirs)) {
228 if (indent)
229 out_ << " ";
230 out_ << CSubstitutionIncludeDirs.ninja_name << " =";
231 PathOutput include_path_output(
232 path_output_.current_dir(),
233 settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
234 RecursiveTargetConfigToStream<SourceDir>(
235 kRecursiveWriterSkipDuplicates, target_, &ConfigValues::include_dirs,
236 IncludeWriter(include_path_output), out_);
237 out_ << std::endl;
238 }
239
240 bool has_precompiled_headers =
241 target_->config_values().has_precompiled_headers();
242
243 EscapeOptions opts;
244 opts.mode = ESCAPE_NINJA_COMMAND;
245 if (respect_source_used
246 ? target_->source_types_used().Get(SourceFile::SOURCE_S)
247 : bits.used.count(&CSubstitutionAsmFlags)) {
248 WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
249 &CSubstitutionAsmFlags, false, Tool::kToolNone,
250 &ConfigValues::asmflags, opts, path_output_, out_, true,
251 indent);
252 }
253 if (respect_source_used
254 ? (target_->source_types_used().Get(SourceFile::SOURCE_C) ||
255 target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
256 target_->source_types_used().Get(SourceFile::SOURCE_M) ||
257 target_->source_types_used().Get(SourceFile::SOURCE_MM) ||
258 target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP))
259 : bits.used.count(&CSubstitutionCFlags)) {
260 WriteOneFlag(kRecursiveWriterKeepDuplicates, target_, &CSubstitutionCFlags,
261 false, Tool::kToolNone, &ConfigValues::cflags, opts,
262 path_output_, out_, true, indent);
263 }
264 if (respect_source_used
265 ? target_->source_types_used().Get(SourceFile::SOURCE_C)
266 : bits.used.count(&CSubstitutionCFlagsC)) {
267 WriteOneFlag(kRecursiveWriterKeepDuplicates, target_, &CSubstitutionCFlagsC,
268 has_precompiled_headers, CTool::kCToolCc,
269 &ConfigValues::cflags_c, opts, path_output_, out_, true,
270 indent);
271 }
272 if (respect_source_used
273 ? (target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
274 target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP))
275 : bits.used.count(&CSubstitutionCFlagsCc)) {
276 WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
277 &CSubstitutionCFlagsCc, has_precompiled_headers,
278 CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output_,
279 out_, true, indent);
280 }
281 if (respect_source_used
282 ? target_->source_types_used().Get(SourceFile::SOURCE_M)
283 : bits.used.count(&CSubstitutionCFlagsObjC)) {
284 WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
285 &CSubstitutionCFlagsObjC, has_precompiled_headers,
286 CTool::kCToolObjC, &ConfigValues::cflags_objc, opts,
287 path_output_, out_, true, indent);
288 }
289 if (respect_source_used
290 ? target_->source_types_used().Get(SourceFile::SOURCE_MM)
291 : bits.used.count(&CSubstitutionCFlagsObjCc)) {
292 WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
293 &CSubstitutionCFlagsObjCc, has_precompiled_headers,
294 CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts,
295 path_output_, out_, true, indent);
296 }
297 if (target_->source_types_used().SwiftSourceUsed() || !respect_source_used) {
298 if (bits.used.count(&CSubstitutionSwiftModuleName)) {
299 if (indent)
300 out_ << " ";
301 out_ << CSubstitutionSwiftModuleName.ninja_name << " = ";
302 EscapeStringToStream(out_, target_->swift_values().module_name(), opts);
303 out_ << std::endl;
304 }
305
306 if (bits.used.count(&CSubstitutionSwiftBridgeHeader)) {
307 if (indent)
308 out_ << " ";
309 out_ << CSubstitutionSwiftBridgeHeader.ninja_name << " = ";
310 if (!target_->swift_values().bridge_header().is_null()) {
311 path_output_.WriteFile(out_, target_->swift_values().bridge_header());
312 } else {
313 out_ << R"("")";
314 }
315 out_ << std::endl;
316 }
317
318 if (bits.used.count(&CSubstitutionSwiftModuleDirs)) {
319 // Uniquify the list of swiftmodule dirs (in case multiple swiftmodules
320 // are generated in the same directory).
321 UniqueVector<SourceDir> swiftmodule_dirs;
322 for (const Target* dep : target_->swift_values().modules())
323 swiftmodule_dirs.push_back(dep->swift_values().module_output_dir());
324
325 if (indent)
326 out_ << " ";
327 out_ << CSubstitutionSwiftModuleDirs.ninja_name << " =";
328 PathOutput swiftmodule_path_output(
329 path_output_.current_dir(),
330 settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
331 IncludeWriter swiftmodule_path_writer(swiftmodule_path_output);
332 for (const SourceDir& swiftmodule_dir : swiftmodule_dirs) {
333 swiftmodule_path_writer(swiftmodule_dir, out_);
334 }
335 out_ << std::endl;
336 }
337
338 WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
339 &CSubstitutionSwiftFlags, false, CTool::kCToolSwift,
340 &ConfigValues::swiftflags, opts, path_output_, out_, true,
341 indent);
342 }
343 }
344
WriteRustCompilerVars(const SubstitutionBits & bits,bool indent,bool always_write)345 void NinjaTargetWriter::WriteRustCompilerVars(const SubstitutionBits& bits,
346 bool indent,
347 bool always_write) {
348 EscapeOptions opts;
349 opts.mode = ESCAPE_NINJA_COMMAND;
350
351 if (bits.used.count(&kRustSubstitutionRustFlags) || always_write) {
352 WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
353 &kRustSubstitutionRustFlags, false, Tool::kToolNone,
354 &ConfigValues::rustflags, opts, path_output_, out_, true,
355 indent);
356 }
357
358 if (bits.used.count(&kRustSubstitutionRustEnv) || always_write) {
359 WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
360 &kRustSubstitutionRustEnv, false, Tool::kToolNone,
361 &ConfigValues::rustenv, opts, path_output_, out_, true,
362 indent);
363 }
364 }
365
WriteInputDepsStampAndGetDep(const std::vector<const Target * > & additional_hard_deps,size_t num_stamp_uses) const366 std::vector<OutputFile> NinjaTargetWriter::WriteInputDepsStampAndGetDep(
367 const std::vector<const Target*>& additional_hard_deps,
368 size_t num_stamp_uses) const {
369 CHECK(target_->toolchain()) << "Toolchain not set on target "
370 << target_->label().GetUserVisibleName(true);
371
372 // ----------
373 // Collect all input files that are input deps of this target. Knowing the
374 // number before writing allows us to either skip writing the input deps
375 // stamp or optimize it. Use pointers to avoid copies here.
376 std::vector<const SourceFile*> input_deps_sources;
377 input_deps_sources.reserve(32);
378
379 // Actions get implicit dependencies on the script itself.
380 if (target_->output_type() == Target::ACTION ||
381 target_->output_type() == Target::ACTION_FOREACH)
382 input_deps_sources.push_back(&target_->action_values().script());
383
384 // Input files are only considered for non-binary targets which use an
385 // implicit dependency instead. The implicit dependency in this case is
386 // handled separately by the binary target writer.
387 if (!target_->IsBinary()) {
388 for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
389 for (const auto& input : iter.cur().inputs())
390 input_deps_sources.push_back(&input);
391 }
392 }
393
394 // For an action (where we run a script only once) the sources are the same
395 // as the inputs. For action_foreach, the sources will be operated on
396 // separately so don't handle them here.
397 if (target_->output_type() == Target::ACTION) {
398 for (const auto& source : target_->sources())
399 input_deps_sources.push_back(&source);
400 }
401
402 // ----------
403 // Collect all target input dependencies of this target as was done for the
404 // files above.
405 std::vector<const Target*> input_deps_targets;
406 input_deps_targets.reserve(32);
407
408 // Hard dependencies that are direct or indirect dependencies.
409 // These are large (up to 100s), hence why we check other
410 const TargetSet& hard_deps(target_->recursive_hard_deps());
411 for (const Target* target : hard_deps) {
412 // BUNDLE_DATA should normally be treated as a data-only dependency
413 // (see Target::IsDataOnly()). Only the CREATE_BUNDLE target, that actually
414 // consumes this data, needs to have the BUNDLE_DATA as an input dependency.
415 if (target->output_type() != Target::BUNDLE_DATA ||
416 target_->output_type() == Target::CREATE_BUNDLE)
417 input_deps_targets.push_back(target);
418 }
419
420 // Additional hard dependencies passed in. These are usually empty or small,
421 // and we don't want to duplicate the explicit hard deps of the target.
422 for (const Target* target : additional_hard_deps) {
423 if (!hard_deps.contains(target))
424 input_deps_targets.push_back(target);
425 }
426
427 // Toolchain dependencies. These must be resolved before doing anything.
428 // This just writes all toolchain deps for simplicity. If we find that
429 // toolchains often have more than one dependency, we could consider writing
430 // a toolchain-specific stamp file and only include the stamp here.
431 // Note that these are usually empty/small.
432 const LabelTargetVector& toolchain_deps = target_->toolchain()->deps();
433 for (const auto& toolchain_dep : toolchain_deps) {
434 // This could theoretically duplicate dependencies already in the list,
435 // but it shouldn't happen in practice, is inconvenient to check for,
436 // and only results in harmless redundant dependencies listed.
437 input_deps_targets.push_back(toolchain_dep.ptr);
438 }
439
440 // ---------
441 // Write the outputs.
442
443 if (input_deps_sources.size() + input_deps_targets.size() == 0)
444 return std::vector<OutputFile>(); // No input dependencies.
445
446 // If we're only generating one input dependency, return it directly instead
447 // of writing a stamp file for it.
448 if (input_deps_sources.size() == 1 && input_deps_targets.size() == 0)
449 return std::vector<OutputFile>{
450 OutputFile(settings_->build_settings(), *input_deps_sources[0])};
451 if (input_deps_sources.size() == 0 && input_deps_targets.size() == 1) {
452 const OutputFile& dep = input_deps_targets[0]->dependency_output_file();
453 DCHECK(!dep.value().empty());
454 return std::vector<OutputFile>{dep};
455 }
456
457 std::vector<OutputFile> outs;
458 // File input deps.
459 for (const SourceFile* source : input_deps_sources)
460 outs.push_back(OutputFile(settings_->build_settings(), *source));
461 // Target input deps. Sort by label so the output is deterministic (otherwise
462 // some of the targets will have gone through std::sets which will have
463 // sorted them by pointer).
464 std::sort(
465 input_deps_targets.begin(), input_deps_targets.end(),
466 [](const Target* a, const Target* b) { return a->label() < b->label(); });
467 for (auto* dep : input_deps_targets) {
468 DCHECK(!dep->dependency_output_file().value().empty());
469 outs.push_back(dep->dependency_output_file());
470 }
471
472 // If there are multiple inputs, but the stamp file would be referenced only
473 // once, don't write it but depend on the inputs directly.
474 if (num_stamp_uses == 1u)
475 return outs;
476
477 // Make a stamp file.
478 OutputFile input_stamp_file =
479 GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
480 input_stamp_file.value().append(target_->label().name());
481 input_stamp_file.value().append(".inputdeps.stamp");
482
483 out_ << "build ";
484 path_output_.WriteFile(out_, input_stamp_file);
485 out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
486 << GeneralTool::kGeneralToolStamp;
487 path_output_.WriteFiles(out_, outs);
488
489 out_ << "\n";
490 return std::vector<OutputFile>{input_stamp_file};
491 }
492
WriteStampForTarget(const std::vector<OutputFile> & files,const std::vector<OutputFile> & order_only_deps)493 void NinjaTargetWriter::WriteStampForTarget(
494 const std::vector<OutputFile>& files,
495 const std::vector<OutputFile>& order_only_deps) {
496 const OutputFile& stamp_file = target_->dependency_output_file();
497
498 // First validate that the target's dependency is a stamp file. Otherwise,
499 // we shouldn't have gotten here!
500 CHECK(base::EndsWith(stamp_file.value(), ".stamp",
501 base::CompareCase::INSENSITIVE_ASCII))
502 << "Output should end in \".stamp\" for stamp file output. Instead got: "
503 << "\"" << stamp_file.value() << "\"";
504
505 out_ << "build ";
506 path_output_.WriteFile(out_, stamp_file);
507
508 out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
509 << GeneralTool::kGeneralToolStamp;
510 path_output_.WriteFiles(out_, files);
511
512 if (!order_only_deps.empty()) {
513 out_ << " ||";
514 path_output_.WriteFiles(out_, order_only_deps);
515 }
516 out_ << std::endl;
517 }
518