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.
5 #include "tools/gn/ninja_target_writer.h"
10 #include "base/file_util.h"
11 #include "tools/gn/err.h"
12 #include "tools/gn/ninja_action_target_writer.h"
13 #include "tools/gn/ninja_binary_target_writer.h"
14 #include "tools/gn/ninja_copy_target_writer.h"
15 #include "tools/gn/ninja_group_target_writer.h"
16 #include "tools/gn/scheduler.h"
17 #include "tools/gn/string_utils.h"
18 #include "tools/gn/target.h"
19 #include "tools/gn/trace.h"
21 NinjaTargetWriter::NinjaTargetWriter(const Target* target,
22 const Toolchain* toolchain,
24 : settings_(target->settings()),
26 toolchain_(toolchain),
28 path_output_(settings_->build_settings()->build_dir(), ESCAPE_NINJA),
29 helper_(settings_->build_settings()) {
32 NinjaTargetWriter::~NinjaTargetWriter() {
36 void NinjaTargetWriter::RunAndWriteFile(const Target* target,
37 const Toolchain* toolchain) {
38 const Settings* settings = target->settings();
39 NinjaHelper helper(settings->build_settings());
41 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE,
42 target->label().GetUserVisibleName(false));
43 trace.SetToolchain(settings->toolchain_label());
45 base::FilePath ninja_file(settings->build_settings()->GetFullPath(
46 helper.GetNinjaFileForTarget(target).GetSourceFile(
47 settings->build_settings())));
49 if (g_scheduler->verbose_logging())
50 g_scheduler->Log("Writing", FilePathToUTF8(ninja_file));
52 base::CreateDirectory(ninja_file.DirName());
54 // It's rediculously faster to write to a string and then write that to
55 // disk in one operation than to use an fstream here.
56 std::stringstream file;
58 // Call out to the correct sub-type of writer.
59 if (target->output_type() == Target::COPY_FILES) {
60 NinjaCopyTargetWriter writer(target, toolchain, file);
62 } else if (target->output_type() == Target::ACTION ||
63 target->output_type() == Target::ACTION_FOREACH) {
64 NinjaActionTargetWriter writer(target, toolchain, file);
66 } else if (target->output_type() == Target::GROUP) {
67 NinjaGroupTargetWriter writer(target, toolchain, file);
69 } else if (target->output_type() == Target::EXECUTABLE ||
70 target->output_type() == Target::STATIC_LIBRARY ||
71 target->output_type() == Target::SHARED_LIBRARY ||
72 target->output_type() == Target::SOURCE_SET) {
73 NinjaBinaryTargetWriter writer(target, toolchain, file);
79 std::string contents = file.str();
80 base::WriteFile(ninja_file, contents.c_str(),
81 static_cast<int>(contents.size()));
84 std::string NinjaTargetWriter::WriteInputDepsStampAndGetDep(
85 const std::vector<const Target*>& extra_hard_deps) const {
86 // For an action (where we run a script only once) the sources are the same
87 // as the source prereqs.
88 bool list_sources_as_input_deps = (target_->output_type() == Target::ACTION);
90 // Actions get implicit dependencies on the script itself.
91 bool add_script_source_as_dep =
92 (target_->output_type() == Target::ACTION) ||
93 (target_->output_type() == Target::ACTION_FOREACH);
95 if (!add_script_source_as_dep &&
96 extra_hard_deps.empty() &&
97 target_->inputs().empty() &&
98 target_->recursive_hard_deps().empty() &&
99 (!list_sources_as_input_deps || target_->sources().empty()) &&
100 toolchain_->deps().empty())
101 return std::string(); // No input/hard deps.
103 // One potential optimization is if there are few input dependencies (or
104 // potentially few sources that depend on these) it's better to just write
105 // all hard deps on each sources line than have this intermediate stamp. We
106 // do the stamp file because duplicating all the order-only deps for each
107 // source file can really explode the ninja file but this won't be the most
108 // optimal thing in all cases.
110 OutputFile input_stamp_file = helper_.GetTargetOutputDir(target_);
111 input_stamp_file.value().append(target_->label().name());
112 input_stamp_file.value().append(".inputdeps.stamp");
114 std::ostringstream stamp_file_stream;
115 path_output_.WriteFile(stamp_file_stream, input_stamp_file);
116 std::string stamp_file_string = stamp_file_stream.str();
118 out_ << "build " << stamp_file_string << ": " +
119 helper_.GetRulePrefix(settings_) + "stamp";
121 // Script file (if applicable).
122 if (add_script_source_as_dep) {
124 path_output_.WriteFile(out_, target_->action_values().script());
127 // Input files are order-only deps.
128 const Target::FileList& prereqs = target_->inputs();
129 for (size_t i = 0; i < prereqs.size(); i++) {
131 path_output_.WriteFile(out_, prereqs[i]);
133 if (list_sources_as_input_deps) {
134 const Target::FileList& sources = target_->sources();
135 for (size_t i = 0; i < sources.size(); i++) {
137 path_output_.WriteFile(out_, sources[i]);
141 // Add on any hard deps that are direct or indirect dependencies.
142 const std::set<const Target*>& hard_deps = target_->recursive_hard_deps();
143 for (std::set<const Target*>::const_iterator i = hard_deps.begin();
144 i != hard_deps.end(); ++i) {
146 path_output_.WriteFile(out_, helper_.GetTargetOutputFile(*i));
149 // Toolchain dependencies. These must be resolved before doing anything.
150 // This just writs all toolchain deps for simplicity. If we find that
151 // toolchains often have more than one dependency, we could consider writing
152 // a toolchain-specific stamp file and only include the stamp here.
153 const LabelTargetVector& toolchain_deps = toolchain_->deps();
154 for (size_t i = 0; i < toolchain_deps.size(); i++) {
156 path_output_.WriteFile(out_,
157 helper_.GetTargetOutputFile(toolchain_deps[i].ptr));
160 // Extra hard deps passed in.
161 for (size_t i = 0; i < extra_hard_deps.size(); i++) {
163 path_output_.WriteFile(out_,
164 helper_.GetTargetOutputFile(extra_hard_deps[i]));
168 return " | " + stamp_file_string;