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