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_target_writer.h"
6
7#include <fstream>
8#include <sstream>
9
10#include "base/files/file_util.h"
11#include "base/strings/string_util.h"
12#include "tools/gn/err.h"
13#include "tools/gn/filesystem_utils.h"
14#include "tools/gn/ninja_action_target_writer.h"
15#include "tools/gn/ninja_binary_target_writer.h"
16#include "tools/gn/ninja_copy_target_writer.h"
17#include "tools/gn/ninja_group_target_writer.h"
18#include "tools/gn/ninja_utils.h"
19#include "tools/gn/output_file.h"
20#include "tools/gn/scheduler.h"
21#include "tools/gn/string_utils.h"
22#include "tools/gn/substitution_writer.h"
23#include "tools/gn/target.h"
24#include "tools/gn/trace.h"
25
26NinjaTargetWriter::NinjaTargetWriter(const Target* target,
27                                     std::ostream& out)
28    : settings_(target->settings()),
29      target_(target),
30      out_(out),
31      path_output_(settings_->build_settings()->build_dir(), ESCAPE_NINJA) {
32}
33
34NinjaTargetWriter::~NinjaTargetWriter() {
35}
36
37// static
38void NinjaTargetWriter::RunAndWriteFile(const Target* target) {
39  const Settings* settings = target->settings();
40
41  ScopedTrace trace(TraceItem::TRACE_FILE_WRITE,
42                    target->label().GetUserVisibleName(false));
43  trace.SetToolchain(settings->toolchain_label());
44
45  base::FilePath ninja_file(settings->build_settings()->GetFullPath(
46      GetNinjaFileForTarget(target)));
47
48  if (g_scheduler->verbose_logging())
49    g_scheduler->Log("Writing", FilePathToUTF8(ninja_file));
50
51  base::CreateDirectory(ninja_file.DirName());
52
53  // It's rediculously faster to write to a string and then write that to
54  // disk in one operation than to use an fstream here.
55  std::stringstream file;
56
57  // Call out to the correct sub-type of writer.
58  if (target->output_type() == Target::COPY_FILES) {
59    NinjaCopyTargetWriter writer(target, file);
60    writer.Run();
61  } else if (target->output_type() == Target::ACTION ||
62             target->output_type() == Target::ACTION_FOREACH) {
63    NinjaActionTargetWriter writer(target, file);
64    writer.Run();
65  } else if (target->output_type() == Target::GROUP) {
66    NinjaGroupTargetWriter writer(target, file);
67    writer.Run();
68  } else if (target->output_type() == Target::EXECUTABLE ||
69             target->output_type() == Target::STATIC_LIBRARY ||
70             target->output_type() == Target::SHARED_LIBRARY ||
71             target->output_type() == Target::SOURCE_SET) {
72    NinjaBinaryTargetWriter writer(target, file);
73    writer.Run();
74  } else {
75    CHECK(0);
76  }
77
78  std::string contents = file.str();
79  base::WriteFile(ninja_file, contents.c_str(),
80                  static_cast<int>(contents.size()));
81}
82
83void NinjaTargetWriter::WriteSharedVars(const SubstitutionBits& bits) {
84  bool written_anything = false;
85
86  // Target label.
87  if (bits.used[SUBSTITUTION_LABEL]) {
88    out_ << kSubstitutionNinjaNames[SUBSTITUTION_LABEL] << " = "
89         << SubstitutionWriter::GetTargetSubstitution(
90                target_, SUBSTITUTION_LABEL)
91         << std::endl;
92    written_anything = true;
93  }
94
95  // Root gen dir.
96  if (bits.used[SUBSTITUTION_ROOT_GEN_DIR]) {
97    out_ << kSubstitutionNinjaNames[SUBSTITUTION_ROOT_GEN_DIR] << " = "
98         << SubstitutionWriter::GetTargetSubstitution(
99                target_, SUBSTITUTION_ROOT_GEN_DIR)
100         << std::endl;
101    written_anything = true;
102  }
103
104  // Root out dir.
105  if (bits.used[SUBSTITUTION_ROOT_OUT_DIR]) {
106    out_ << kSubstitutionNinjaNames[SUBSTITUTION_ROOT_OUT_DIR] << " = "
107         << SubstitutionWriter::GetTargetSubstitution(
108                target_, SUBSTITUTION_ROOT_OUT_DIR)
109         << std::endl;
110    written_anything = true;
111  }
112
113  // Target gen dir.
114  if (bits.used[SUBSTITUTION_TARGET_GEN_DIR]) {
115    out_ << kSubstitutionNinjaNames[SUBSTITUTION_TARGET_GEN_DIR] << " = "
116         << SubstitutionWriter::GetTargetSubstitution(
117                target_, SUBSTITUTION_TARGET_GEN_DIR)
118         << std::endl;
119    written_anything = true;
120  }
121
122  // Target out dir.
123  if (bits.used[SUBSTITUTION_TARGET_OUT_DIR]) {
124    out_ << kSubstitutionNinjaNames[SUBSTITUTION_TARGET_OUT_DIR] << " = "
125         << SubstitutionWriter::GetTargetSubstitution(
126                target_, SUBSTITUTION_TARGET_OUT_DIR)
127         << std::endl;
128    written_anything = true;
129  }
130
131  // Target output name.
132  if (bits.used[SUBSTITUTION_TARGET_OUTPUT_NAME]) {
133    out_ << kSubstitutionNinjaNames[SUBSTITUTION_TARGET_OUTPUT_NAME] << " = "
134         << SubstitutionWriter::GetTargetSubstitution(
135                target_, SUBSTITUTION_TARGET_OUTPUT_NAME)
136         << std::endl;
137    written_anything = true;
138  }
139
140  // If we wrote any vars, separate them from the rest of the file that follows
141  // with a blank line.
142  if (written_anything)
143    out_ << std::endl;
144}
145
146OutputFile NinjaTargetWriter::WriteInputDepsStampAndGetDep(
147    const std::vector<const Target*>& extra_hard_deps) const {
148  CHECK(target_->toolchain())
149      << "Toolchain not set on target "
150      << target_->label().GetUserVisibleName(true);
151
152  // For an action (where we run a script only once) the sources are the same
153  // as the source prereqs.
154  bool list_sources_as_input_deps = (target_->output_type() == Target::ACTION);
155
156  // Actions get implicit dependencies on the script itself.
157  bool add_script_source_as_dep =
158      (target_->output_type() == Target::ACTION) ||
159      (target_->output_type() == Target::ACTION_FOREACH);
160
161  if (!add_script_source_as_dep &&
162      extra_hard_deps.empty() &&
163      target_->inputs().empty() &&
164      target_->recursive_hard_deps().empty() &&
165      (!list_sources_as_input_deps || target_->sources().empty()) &&
166      target_->toolchain()->deps().empty())
167    return OutputFile();  // No input/hard deps.
168
169  // One potential optimization is if there are few input dependencies (or
170  // potentially few sources that depend on these) it's better to just write
171  // all hard deps on each sources line than have this intermediate stamp. We
172  // do the stamp file because duplicating all the order-only deps for each
173  // source file can really explode the ninja file but this won't be the most
174  // optimal thing in all cases.
175
176  OutputFile input_stamp_file(
177      RebaseSourceAbsolutePath(GetTargetOutputDir(target_).value(),
178                               settings_->build_settings()->build_dir()));
179  input_stamp_file.value().append(target_->label().name());
180  input_stamp_file.value().append(".inputdeps.stamp");
181
182  out_ << "build ";
183  path_output_.WriteFile(out_, input_stamp_file);
184  out_ << ": "
185       << GetNinjaRulePrefixForToolchain(settings_)
186       << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
187
188  // Script file (if applicable).
189  if (add_script_source_as_dep) {
190    out_ << " ";
191    path_output_.WriteFile(out_, target_->action_values().script());
192  }
193
194  // Input files are order-only deps.
195  const Target::FileList& prereqs = target_->inputs();
196  for (size_t i = 0; i < prereqs.size(); i++) {
197    out_ << " ";
198    path_output_.WriteFile(out_, prereqs[i]);
199  }
200  if (list_sources_as_input_deps) {
201    const Target::FileList& sources = target_->sources();
202    for (size_t i = 0; i < sources.size(); i++) {
203      out_ << " ";
204      path_output_.WriteFile(out_, sources[i]);
205    }
206  }
207
208  // The different souces of input deps may duplicate some targets, so uniquify
209  // them (ordering doesn't matter for this case).
210  std::set<const Target*> unique_deps;
211
212  // Hard dependencies that are direct or indirect dependencies.
213  const std::set<const Target*>& hard_deps = target_->recursive_hard_deps();
214  for (std::set<const Target*>::const_iterator i = hard_deps.begin();
215       i != hard_deps.end(); ++i) {
216    unique_deps.insert(*i);
217  }
218
219  // Extra hard dependencies passed in.
220  unique_deps.insert(extra_hard_deps.begin(), extra_hard_deps.end());
221
222  // Toolchain dependencies. These must be resolved before doing anything.
223  // This just writs all toolchain deps for simplicity. If we find that
224  // toolchains often have more than one dependency, we could consider writing
225  // a toolchain-specific stamp file and only include the stamp here.
226  const LabelTargetVector& toolchain_deps = target_->toolchain()->deps();
227  for (size_t i = 0; i < toolchain_deps.size(); i++)
228    unique_deps.insert(toolchain_deps[i].ptr);
229
230  for (std::set<const Target*>::const_iterator i = unique_deps.begin();
231       i != unique_deps.end(); ++i) {
232    DCHECK(!(*i)->dependency_output_file().value().empty());
233    out_ << " ";
234    path_output_.WriteFile(out_, (*i)->dependency_output_file());
235  }
236
237  out_ << "\n";
238  return input_stamp_file;
239}
240
241void NinjaTargetWriter::WriteStampForTarget(
242    const std::vector<OutputFile>& files,
243    const std::vector<OutputFile>& order_only_deps) {
244  const OutputFile& stamp_file = target_->dependency_output_file();
245
246  // First validate that the target's dependency is a stamp file. Otherwise,
247  // we shouldn't have gotten here!
248  CHECK(EndsWith(stamp_file.value(), ".stamp", false))
249      << "Output should end in \".stamp\" for stamp file output. Instead got: "
250      << "\"" << stamp_file.value() << "\"";
251
252  out_ << "build ";
253  path_output_.WriteFile(out_, stamp_file);
254
255  out_ << ": "
256       << GetNinjaRulePrefixForToolchain(settings_)
257       << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
258  path_output_.WriteFiles(out_, files);
259
260  if (!order_only_deps.empty()) {
261    out_ << " ||";
262    path_output_.WriteFiles(out_, order_only_deps);
263  }
264  out_ << std::endl;
265}
266