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_copy_target_writer.h"
6
7 #include "base/strings/string_util.h"
8 #include "gn/general_tool.h"
9 #include "gn/ninja_utils.h"
10 #include "gn/output_file.h"
11 #include "gn/scheduler.h"
12 #include "gn/string_utils.h"
13 #include "gn/substitution_list.h"
14 #include "gn/substitution_writer.h"
15 #include "gn/target.h"
16 #include "gn/toolchain.h"
17
NinjaCopyTargetWriter(const Target * target,std::ostream & out)18 NinjaCopyTargetWriter::NinjaCopyTargetWriter(const Target* target,
19 std::ostream& out)
20 : NinjaTargetWriter(target, out) {}
21
22 NinjaCopyTargetWriter::~NinjaCopyTargetWriter() = default;
23
Run()24 void NinjaCopyTargetWriter::Run() {
25 const Tool* copy_tool =
26 target_->toolchain()->GetTool(GeneralTool::kGeneralToolCopy);
27 if (!copy_tool) {
28 g_scheduler->FailWithError(Err(
29 nullptr, "Copy tool not defined",
30 "The toolchain " +
31 target_->toolchain()->label().GetUserVisibleName(false) +
32 "\n used by target " + target_->label().GetUserVisibleName(false) +
33 "\n doesn't define a \"copy\" tool."));
34 return;
35 }
36
37 const Tool* stamp_tool =
38 target_->toolchain()->GetTool(GeneralTool::kGeneralToolStamp);
39 if (!stamp_tool) {
40 g_scheduler->FailWithError(Err(
41 nullptr, "Copy tool not defined",
42 "The toolchain " +
43 target_->toolchain()->label().GetUserVisibleName(false) +
44 "\n used by target " + target_->label().GetUserVisibleName(false) +
45 "\n doesn't define a \"stamp\" tool."));
46 return;
47 }
48
49 // Figure out the substitutions used by the copy and stamp tools.
50 SubstitutionBits required_bits = copy_tool->substitution_bits();
51 required_bits.MergeFrom(stamp_tool->substitution_bits());
52
53 // General target-related substitutions needed by both tools.
54 WriteSharedVars(required_bits);
55
56 std::vector<OutputFile> output_files;
57 WriteCopyRules(&output_files);
58 out_ << std::endl;
59 WriteStampForTarget(output_files, std::vector<OutputFile>());
60 }
61
WriteCopyRules(std::vector<OutputFile> * output_files)62 void NinjaCopyTargetWriter::WriteCopyRules(
63 std::vector<OutputFile>* output_files) {
64 CHECK(target_->action_values().outputs().list().size() == 1);
65 const SubstitutionList& output_subst_list =
66 target_->action_values().outputs();
67 CHECK_EQ(1u, output_subst_list.list().size())
68 << "Should have one entry exactly.";
69 const SubstitutionPattern& output_subst = output_subst_list.list()[0];
70
71 std::string tool_name =
72 GetNinjaRulePrefixForToolchain(settings_) + GeneralTool::kGeneralToolCopy;
73
74 size_t num_stamp_uses = target_->sources().size();
75 std::vector<OutputFile> input_deps = WriteInputDepsStampAndGetDep(
76 std::vector<const Target*>(), num_stamp_uses);
77
78 std::vector<OutputFile> data_outs;
79 for (const Target* data_dep : resolved().GetDataDeps(target_))
80 data_outs.push_back(data_dep->dependency_output_file());
81
82 // Note that we don't write implicit deps for copy steps. "copy" only
83 // depends on the output files themselves, rather than having includes
84 // (the possibility of generated #includes is the main reason for implicit
85 // dependencies).
86 //
87 // It would seem that specifying implicit dependencies on the deps of the
88 // copy command would still be harmless. But Chrome implements copy tools
89 // as hard links (much faster) which don't change the timestamp. If the
90 // ninja rule looks like this:
91 // output: copy input | foo.stamp
92 // The copy will not make a new timestamp on the output file, but the
93 // foo.stamp file generated from a previous step will have a new timestamp.
94 // The copy rule will therefore look out-of-date to Ninja and the rule will
95 // get rebuilt.
96 //
97 // If this copy is copying a generated file, not listing the implicit
98 // dependency will be fine as long as the input to the copy is properly
99 // listed as the output from the step that generated it.
100 //
101 // Moreover, doing this assumes that the copy step is always a simple
102 // locally run command, so there is no need for a toolchain dependency.
103 //
104 // Note that there is the need in some cases for order-only dependencies
105 // where a command might need to make sure something else runs before it runs
106 // to avoid conflicts. This is also needed for data_deps on a copy target.
107 // Such cases should be avoided where possible, but sometimes that's not
108 // possible.
109 for (const auto& input_file : target_->sources()) {
110 OutputFile output_file =
111 SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
112 target_, target_->settings(), output_subst, input_file);
113 output_files->push_back(output_file);
114
115 out_ << "build ";
116 WriteOutput(std::move(output_file));
117
118 out_ << ": " << tool_name << " ";
119 path_output_.WriteFile(out_, input_file);
120 if (!input_deps.empty() || !data_outs.empty()) {
121 out_ << " ||";
122 path_output_.WriteFiles(out_, input_deps);
123 path_output_.WriteFiles(out_, data_outs);
124 }
125 out_ << std::endl;
126 }
127 }
128