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