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/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"
26 NinjaTargetWriter::NinjaTargetWriter(const Target
* target
,
28 : settings_(target
->settings()),
31 path_output_(settings_
->build_settings()->build_dir(),
32 settings_
->build_settings()->root_path_utf8(),
36 NinjaTargetWriter::~NinjaTargetWriter() {
40 void NinjaTargetWriter::RunAndWriteFile(const Target
* target
) {
41 const Settings
* settings
= target
->settings();
43 ScopedTrace
trace(TraceItem::TRACE_FILE_WRITE
,
44 target
->label().GetUserVisibleName(false));
45 trace
.SetToolchain(settings
->toolchain_label());
47 base::FilePath
ninja_file(settings
->build_settings()->GetFullPath(
48 GetNinjaFileForTarget(target
)));
50 if (g_scheduler
->verbose_logging())
51 g_scheduler
->Log("Writing", FilePathToUTF8(ninja_file
));
53 base::CreateDirectory(ninja_file
.DirName());
55 // It's rediculously faster to write to a string and then write that to
56 // disk in one operation than to use an fstream here.
57 std::stringstream file
;
59 // Call out to the correct sub-type of writer.
60 if (target
->output_type() == Target::COPY_FILES
) {
61 NinjaCopyTargetWriter
writer(target
, file
);
63 } else if (target
->output_type() == Target::ACTION
||
64 target
->output_type() == Target::ACTION_FOREACH
) {
65 NinjaActionTargetWriter
writer(target
, file
);
67 } else if (target
->output_type() == Target::GROUP
) {
68 NinjaGroupTargetWriter
writer(target
, file
);
70 } else if (target
->output_type() == Target::EXECUTABLE
||
71 target
->output_type() == Target::STATIC_LIBRARY
||
72 target
->output_type() == Target::SHARED_LIBRARY
||
73 target
->output_type() == Target::SOURCE_SET
) {
74 NinjaBinaryTargetWriter
writer(target
, file
);
80 std::string contents
= file
.str();
81 base::WriteFile(ninja_file
, contents
.c_str(),
82 static_cast<int>(contents
.size()));
85 void NinjaTargetWriter::WriteSharedVars(const SubstitutionBits
& bits
) {
86 bool written_anything
= false;
89 if (bits
.used
[SUBSTITUTION_LABEL
]) {
90 out_
<< kSubstitutionNinjaNames
[SUBSTITUTION_LABEL
] << " = "
91 << SubstitutionWriter::GetTargetSubstitution(
92 target_
, SUBSTITUTION_LABEL
)
94 written_anything
= true;
98 if (bits
.used
[SUBSTITUTION_ROOT_GEN_DIR
]) {
99 out_
<< kSubstitutionNinjaNames
[SUBSTITUTION_ROOT_GEN_DIR
] << " = "
100 << SubstitutionWriter::GetTargetSubstitution(
101 target_
, SUBSTITUTION_ROOT_GEN_DIR
)
103 written_anything
= true;
107 if (bits
.used
[SUBSTITUTION_ROOT_OUT_DIR
]) {
108 out_
<< kSubstitutionNinjaNames
[SUBSTITUTION_ROOT_OUT_DIR
] << " = "
109 << SubstitutionWriter::GetTargetSubstitution(
110 target_
, SUBSTITUTION_ROOT_OUT_DIR
)
112 written_anything
= true;
116 if (bits
.used
[SUBSTITUTION_TARGET_GEN_DIR
]) {
117 out_
<< kSubstitutionNinjaNames
[SUBSTITUTION_TARGET_GEN_DIR
] << " = "
118 << SubstitutionWriter::GetTargetSubstitution(
119 target_
, SUBSTITUTION_TARGET_GEN_DIR
)
121 written_anything
= true;
125 if (bits
.used
[SUBSTITUTION_TARGET_OUT_DIR
]) {
126 out_
<< kSubstitutionNinjaNames
[SUBSTITUTION_TARGET_OUT_DIR
] << " = "
127 << SubstitutionWriter::GetTargetSubstitution(
128 target_
, SUBSTITUTION_TARGET_OUT_DIR
)
130 written_anything
= true;
133 // Target output name.
134 if (bits
.used
[SUBSTITUTION_TARGET_OUTPUT_NAME
]) {
135 out_
<< kSubstitutionNinjaNames
[SUBSTITUTION_TARGET_OUTPUT_NAME
] << " = "
136 << SubstitutionWriter::GetTargetSubstitution(
137 target_
, SUBSTITUTION_TARGET_OUTPUT_NAME
)
139 written_anything
= true;
142 // If we wrote any vars, separate them from the rest of the file that follows
143 // with a blank line.
144 if (written_anything
)
148 OutputFile
NinjaTargetWriter::WriteInputDepsStampAndGetDep(
149 const std::vector
<const Target
*>& extra_hard_deps
) const {
150 CHECK(target_
->toolchain())
151 << "Toolchain not set on target "
152 << target_
->label().GetUserVisibleName(true);
154 // For an action (where we run a script only once) the sources are the same
155 // as the source prereqs.
156 bool list_sources_as_input_deps
= (target_
->output_type() == Target::ACTION
);
158 // Actions get implicit dependencies on the script itself.
159 bool add_script_source_as_dep
=
160 (target_
->output_type() == Target::ACTION
) ||
161 (target_
->output_type() == Target::ACTION_FOREACH
);
163 if (!add_script_source_as_dep
&&
164 extra_hard_deps
.empty() &&
165 target_
->inputs().empty() &&
166 target_
->recursive_hard_deps().empty() &&
167 (!list_sources_as_input_deps
|| target_
->sources().empty()) &&
168 target_
->toolchain()->deps().empty())
169 return OutputFile(); // No input/hard deps.
171 // One potential optimization is if there are few input dependencies (or
172 // potentially few sources that depend on these) it's better to just write
173 // all hard deps on each sources line than have this intermediate stamp. We
174 // do the stamp file because duplicating all the order-only deps for each
175 // source file can really explode the ninja file but this won't be the most
176 // optimal thing in all cases.
178 OutputFile
input_stamp_file(
179 RebasePath(GetTargetOutputDir(target_
).value(),
180 settings_
->build_settings()->build_dir(),
181 settings_
->build_settings()->root_path_utf8()));
182 input_stamp_file
.value().append(target_
->label().name());
183 input_stamp_file
.value().append(".inputdeps.stamp");
186 path_output_
.WriteFile(out_
, input_stamp_file
);
188 << GetNinjaRulePrefixForToolchain(settings_
)
189 << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP
);
191 // Script file (if applicable).
192 if (add_script_source_as_dep
) {
194 path_output_
.WriteFile(out_
, target_
->action_values().script());
197 // Input files are order-only deps.
198 const Target::FileList
& prereqs
= target_
->inputs();
199 for (size_t i
= 0; i
< prereqs
.size(); i
++) {
201 path_output_
.WriteFile(out_
, prereqs
[i
]);
203 if (list_sources_as_input_deps
) {
204 const Target::FileList
& sources
= target_
->sources();
205 for (size_t i
= 0; i
< sources
.size(); i
++) {
207 path_output_
.WriteFile(out_
, sources
[i
]);
211 // The different souces of input deps may duplicate some targets, so uniquify
212 // them (ordering doesn't matter for this case).
213 std::set
<const Target
*> unique_deps
;
215 // Hard dependencies that are direct or indirect dependencies.
216 const std::set
<const Target
*>& hard_deps
= target_
->recursive_hard_deps();
217 for (const auto& dep
: hard_deps
)
218 unique_deps
.insert(dep
);
220 // Extra hard dependencies passed in.
221 unique_deps
.insert(extra_hard_deps
.begin(), extra_hard_deps
.end());
223 // Toolchain dependencies. These must be resolved before doing anything.
224 // This just writs all toolchain deps for simplicity. If we find that
225 // toolchains often have more than one dependency, we could consider writing
226 // a toolchain-specific stamp file and only include the stamp here.
227 const LabelTargetVector
& toolchain_deps
= target_
->toolchain()->deps();
228 for (size_t i
= 0; i
< toolchain_deps
.size(); i
++)
229 unique_deps
.insert(toolchain_deps
[i
].ptr
);
231 for (const auto& dep
: unique_deps
) {
232 DCHECK(!dep
->dependency_output_file().value().empty());
234 path_output_
.WriteFile(out_
, dep
->dependency_output_file());
238 return input_stamp_file
;
241 void 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();
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() << "\"";
253 path_output_
.WriteFile(out_
, stamp_file
);
256 << GetNinjaRulePrefixForToolchain(settings_
)
257 << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP
);
258 path_output_
.WriteFiles(out_
, files
);
260 if (!order_only_deps
.empty()) {
262 path_output_
.WriteFiles(out_
, order_only_deps
);