1 // Copyright 2019 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_rust_binary_target_writer.h"
6
7 #include <sstream>
8
9 #include "base/strings/string_util.h"
10 #include "gn/deps_iterator.h"
11 #include "gn/filesystem_utils.h"
12 #include "gn/general_tool.h"
13 #include "gn/ninja_target_command_util.h"
14 #include "gn/ninja_utils.h"
15 #include "gn/rust_substitution_type.h"
16 #include "gn/rust_values.h"
17 #include "gn/substitution_writer.h"
18 #include "gn/target.h"
19
20 namespace {
21
22 // Returns the proper escape options for writing compiler and linker flags.
GetFlagOptions()23 EscapeOptions GetFlagOptions() {
24 EscapeOptions opts;
25 opts.mode = ESCAPE_NINJA_COMMAND;
26 return opts;
27 }
28
WriteVar(const char * name,const std::string & value,EscapeOptions opts,std::ostream & out)29 void WriteVar(const char* name,
30 const std::string& value,
31 EscapeOptions opts,
32 std::ostream& out) {
33 out << name << " = ";
34 EscapeStringToStream(out, value, opts);
35 out << std::endl;
36 }
37
WriteCrateVars(const Target * target,const Tool * tool,EscapeOptions opts,std::ostream & out)38 void WriteCrateVars(const Target* target,
39 const Tool* tool,
40 EscapeOptions opts,
41 std::ostream& out) {
42 WriteVar(kRustSubstitutionCrateName.ninja_name,
43 target->rust_values().crate_name(), opts, out);
44
45 std::string crate_type;
46 switch (target->rust_values().crate_type()) {
47 // Auto-select the crate type for executables, static libraries, and rlibs.
48 case RustValues::CRATE_AUTO: {
49 switch (target->output_type()) {
50 case Target::EXECUTABLE:
51 crate_type = "bin";
52 break;
53 case Target::STATIC_LIBRARY:
54 crate_type = "staticlib";
55 break;
56 case Target::RUST_LIBRARY:
57 crate_type = "rlib";
58 break;
59 case Target::RUST_PROC_MACRO:
60 crate_type = "proc-macro";
61 break;
62 default:
63 NOTREACHED();
64 }
65 break;
66 }
67 case RustValues::CRATE_BIN:
68 crate_type = "bin";
69 break;
70 case RustValues::CRATE_CDYLIB:
71 crate_type = "cdylib";
72 break;
73 case RustValues::CRATE_DYLIB:
74 crate_type = "dylib";
75 break;
76 case RustValues::CRATE_PROC_MACRO:
77 crate_type = "proc-macro";
78 break;
79 case RustValues::CRATE_RLIB:
80 crate_type = "rlib";
81 break;
82 case RustValues::CRATE_STATICLIB:
83 crate_type = "staticlib";
84 break;
85 default:
86 NOTREACHED();
87 }
88 WriteVar(kRustSubstitutionCrateType.ninja_name, crate_type, opts, out);
89
90 WriteVar(SubstitutionOutputExtension.ninja_name,
91 SubstitutionWriter::GetLinkerSubstitution(
92 target, tool, &SubstitutionOutputExtension),
93 opts, out);
94 WriteVar(SubstitutionOutputDir.ninja_name,
95 SubstitutionWriter::GetLinkerSubstitution(target, tool,
96 &SubstitutionOutputDir),
97 opts, out);
98 }
99
100 } // namespace
101
NinjaRustBinaryTargetWriter(const Target * target,std::ostream & out)102 NinjaRustBinaryTargetWriter::NinjaRustBinaryTargetWriter(const Target* target,
103 std::ostream& out)
104 : NinjaBinaryTargetWriter(target, out),
105 tool_(target->toolchain()->GetToolForTargetFinalOutputAsRust(target)) {}
106
107 NinjaRustBinaryTargetWriter::~NinjaRustBinaryTargetWriter() = default;
108
109 // TODO(juliehockett): add inherited library support? and IsLinkable support?
110 // for c-cross-compat
Run()111 void NinjaRustBinaryTargetWriter::Run() {
112 DCHECK(target_->output_type() != Target::SOURCE_SET);
113
114 size_t num_stamp_uses = target_->sources().size();
115
116 std::vector<OutputFile> input_deps =
117 WriteInputsStampAndGetDep(num_stamp_uses);
118
119 WriteCompilerVars();
120
121 // Classify our dependencies.
122 ClassifiedDeps classified_deps = GetClassifiedDeps();
123
124 // The input dependencies will be an order-only dependency. This will cause
125 // Ninja to make sure the inputs are up to date before compiling this source,
126 // but changes in the inputs deps won't cause the file to be recompiled. See
127 // the comment on NinjaCBinaryTargetWriter::Run for more detailed explanation.
128 std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep(
129 std::vector<const Target*>(), num_stamp_uses);
130 std::copy(input_deps.begin(), input_deps.end(),
131 std::back_inserter(order_only_deps));
132
133 // Build lists which will go into different bits of the rustc command line.
134 // Public rust_library deps go in a --extern rlibs, public non-rust deps go in
135 // -Ldependency. Also assemble a list of extra (i.e. implicit) deps
136 // for ninja dependency tracking.
137 UniqueVector<OutputFile> implicit_deps;
138 AppendSourcesAndInputsToImplicitDeps(&implicit_deps);
139 implicit_deps.Append(classified_deps.extra_object_files.begin(),
140 classified_deps.extra_object_files.end());
141
142 std::vector<OutputFile> rustdeps;
143 std::vector<OutputFile> nonrustdeps;
144 nonrustdeps.insert(nonrustdeps.end(),
145 classified_deps.extra_object_files.begin(),
146 classified_deps.extra_object_files.end());
147 for (const auto* framework_dep : classified_deps.framework_deps) {
148 order_only_deps.push_back(framework_dep->dependency_output_file());
149 }
150 for (const auto* non_linkable_dep : classified_deps.non_linkable_deps) {
151 if (non_linkable_dep->source_types_used().RustSourceUsed() &&
152 non_linkable_dep->output_type() != Target::SOURCE_SET) {
153 rustdeps.push_back(non_linkable_dep->dependency_output_file());
154 }
155 order_only_deps.push_back(non_linkable_dep->dependency_output_file());
156 }
157 for (const auto* linkable_dep : classified_deps.linkable_deps) {
158 // Rust cdylibs are treated as non-Rust dependencies for linking purposes.
159 if (linkable_dep->source_types_used().RustSourceUsed() &&
160 linkable_dep->rust_values().crate_type() != RustValues::CRATE_CDYLIB) {
161 rustdeps.push_back(linkable_dep->link_output_file());
162 } else {
163 nonrustdeps.push_back(linkable_dep->link_output_file());
164 }
165 implicit_deps.push_back(linkable_dep->dependency_output_file());
166 }
167
168 // Rust libraries specified by paths.
169 for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
170 const ConfigValues& cur = iter.cur();
171 for (const auto& e : cur.externs()) {
172 if (e.second.is_source_file()) {
173 implicit_deps.push_back(
174 OutputFile(settings_->build_settings(), e.second.source_file()));
175 }
176 }
177 }
178
179 // Collect the full transitive set of rust libraries that this target depends
180 // on, and the public flag represents if the target has direct access to the
181 // dependency through a chain of public_deps.
182 std::vector<ExternCrate> transitive_crates;
183 for (const auto& inherited : resolved().GetRustInheritedLibraries(target_)) {
184 const Target* dep = inherited.target();
185 bool has_direct_access = inherited.is_public();
186 // We will tell rustc to look for crate metadata for any rust crate
187 // dependencies except cdylibs, as they have no metadata present.
188 if (dep->source_types_used().RustSourceUsed() &&
189 RustValues::IsRustLibrary(dep)) {
190 transitive_crates.push_back({dep, has_direct_access});
191 // If the current crate can directly acccess the `dep` crate, then the
192 // current crate needs an implicit dependency on `dep` so it will be
193 // rebuilt if `dep` changes.
194 if (has_direct_access) {
195 implicit_deps.push_back(dep->dependency_output_file());
196 }
197 }
198 }
199
200 std::vector<OutputFile> tool_outputs;
201 SubstitutionWriter::ApplyListToLinkerAsOutputFile(
202 target_, tool_, tool_->outputs(), &tool_outputs);
203 WriteCompilerBuildLine({target_->rust_values().crate_root()},
204 implicit_deps.vector(), order_only_deps, tool_->name(),
205 tool_outputs);
206
207 std::vector<const Target*> extern_deps(
208 classified_deps.linkable_deps.vector());
209 std::copy(classified_deps.non_linkable_deps.begin(),
210 classified_deps.non_linkable_deps.end(),
211 std::back_inserter(extern_deps));
212
213 WriteExternsAndDeps(extern_deps, transitive_crates, rustdeps, nonrustdeps);
214 WriteSourcesAndInputs();
215 WritePool(out_);
216 }
217
WriteCompilerVars()218 void NinjaRustBinaryTargetWriter::WriteCompilerVars() {
219 const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
220
221 EscapeOptions opts = GetFlagOptions();
222 WriteCrateVars(target_, tool_, opts, out_);
223
224 WriteRustCompilerVars(subst, /*indent=*/false, /*always_write=*/true);
225
226 WriteSharedVars(subst);
227 }
228
AppendSourcesAndInputsToImplicitDeps(UniqueVector<OutputFile> * deps) const229 void NinjaRustBinaryTargetWriter::AppendSourcesAndInputsToImplicitDeps(
230 UniqueVector<OutputFile>* deps) const {
231 // Only the crate_root file needs to be given to rustc as input.
232 // Any other 'sources' are just implicit deps.
233 // Most Rust targets won't bother specifying the "sources =" line
234 // because it is handled sufficiently by crate_root and the generation
235 // of depfiles by rustc. But for those which do...
236 for (const auto& source : target_->sources()) {
237 deps->push_back(OutputFile(settings_->build_settings(), source));
238 }
239 for (const auto& data : target_->config_values().inputs()) {
240 deps->push_back(OutputFile(settings_->build_settings(), data));
241 }
242 }
243
WriteSourcesAndInputs()244 void NinjaRustBinaryTargetWriter::WriteSourcesAndInputs() {
245 out_ << " sources =";
246 for (const auto& source : target_->sources()) {
247 out_ << " ";
248 path_output_.WriteFile(out_,
249 OutputFile(settings_->build_settings(), source));
250 }
251 for (const auto& data : target_->config_values().inputs()) {
252 out_ << " ";
253 path_output_.WriteFile(out_, OutputFile(settings_->build_settings(), data));
254 }
255 out_ << std::endl;
256 }
257
WriteExternsAndDeps(const std::vector<const Target * > & deps,const std::vector<ExternCrate> & transitive_rust_deps,const std::vector<OutputFile> & rustdeps,const std::vector<OutputFile> & nonrustdeps)258 void NinjaRustBinaryTargetWriter::WriteExternsAndDeps(
259 const std::vector<const Target*>& deps,
260 const std::vector<ExternCrate>& transitive_rust_deps,
261 const std::vector<OutputFile>& rustdeps,
262 const std::vector<OutputFile>& nonrustdeps) {
263 // Writes a external LibFile which comes from user-specified externs, and may
264 // be either a string or a SourceFile.
265 auto write_extern_lib_file = [this](std::string_view crate_name,
266 LibFile lib_file) {
267 out_ << " --extern ";
268 out_ << crate_name;
269 out_ << "=";
270 if (lib_file.is_source_file()) {
271 path_output_.WriteFile(out_, lib_file.source_file());
272 } else {
273 EscapeOptions escape_opts_command;
274 escape_opts_command.mode = ESCAPE_NINJA_COMMAND;
275 EscapeStringToStream(out_, lib_file.value(), escape_opts_command);
276 }
277 };
278 // Writes an external OutputFile which comes from a dependency of the current
279 // target.
280 auto write_extern_target = [this](const Target& dep) {
281 std::string_view crate_name;
282 const auto& aliased_deps = target_->rust_values().aliased_deps();
283 if (auto it = aliased_deps.find(dep.label()); it != aliased_deps.end()) {
284 crate_name = it->second;
285 } else {
286 crate_name = dep.rust_values().crate_name();
287 }
288
289 out_ << " --extern ";
290 out_ << crate_name;
291 out_ << "=";
292 path_output_.WriteFile(out_, dep.dependency_output_file());
293 };
294
295 // Write accessible crates with `--extern` to add them to the extern prelude.
296 out_ << " externs =";
297
298 // Tracking to avoid emitted the same lib twice. We track it instead of
299 // pre-emptively constructing a UniqueVector since we would have to also store
300 // the crate name, and in the future the public-ness.
301 std::unordered_set<OutputFile> emitted_rust_libs;
302 // TODO: We defer private dependencies to -Ldependency until --extern priv is
303 // stabilized.
304 UniqueVector<SourceDir> private_extern_dirs;
305
306 // Walk the transitive closure of all rust dependencies.
307 //
308 // For dependencies that are meant to be accessible we pass them to --extern
309 // in order to add them to the crate's extern prelude.
310 //
311 // For all transitive dependencies, we add them to `private_extern_dirs` in
312 // order to generate a -Ldependency switch that points to them. This ensures
313 // that rustc can find them if they are used by other dependencies. For
314 // example:
315 //
316 // A -> C --public--> D
317 // -> B --private-> D
318 //
319 // Here A has direct access to D, but B and C also make use of D, and they
320 // will only search the paths specified to -Ldependency, thus D needs to
321 // appear as both a --extern (for A) and -Ldependency (for B and C).
322 for (const auto& crate : transitive_rust_deps) {
323 const OutputFile& rust_lib = crate.target->dependency_output_file();
324 if (emitted_rust_libs.count(rust_lib) == 0) {
325 if (crate.has_direct_access) {
326 write_extern_target(*crate.target);
327 }
328 emitted_rust_libs.insert(rust_lib);
329 }
330 private_extern_dirs.push_back(
331 rust_lib.AsSourceFile(settings_->build_settings()).GetDir());
332 }
333
334 // Add explicitly specified externs from the GN target.
335 for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
336 const ConfigValues& cur = iter.cur();
337 for (const auto& [crate_name, lib_file] : cur.externs()) {
338 write_extern_lib_file(crate_name, lib_file);
339 }
340 }
341
342 out_ << std::endl;
343 out_ << " rustdeps =";
344
345 for (const SourceDir& dir : private_extern_dirs) {
346 // TODO: switch to using `--extern priv:name` after stabilization.
347 out_ << " -Ldependency=";
348 path_output_.WriteDir(out_, dir, PathOutput::DIR_NO_LAST_SLASH);
349 }
350
351 UniqueVector<SourceDir> nonrustdep_dirs;
352
353 // Non-Rust native dependencies. A dependency from Rust implies the ability
354 // to specify it in #[link], and GN will ensure that rustc can find it by
355 // adding it to the native library search paths.
356 for (const auto& nonrustdep : nonrustdeps) {
357 nonrustdep_dirs.push_back(
358 nonrustdep.AsSourceFile(settings_->build_settings()).GetDir());
359 }
360 for (const auto& nonrustdep_dir : nonrustdep_dirs) {
361 out_ << " -Lnative=";
362 path_output_.WriteDir(out_, nonrustdep_dir, PathOutput::DIR_NO_LAST_SLASH);
363 }
364
365 // If rustc will invoke a linker, then pass linker arguments to include those
366 // non-Rust native dependencies in the linking step.
367
368 // Before outputting any libraries to link, ensure the linker is in a mode
369 // that allows dynamic linking, as rustc may have previously put it into
370 // static-only mode.
371 if (nonrustdeps.size() > 0) {
372 out_ << " " << tool_->dynamic_link_switch();
373 }
374 for (const auto& nonrustdep : nonrustdeps) {
375 out_ << " -Clink-arg=";
376 path_output_.WriteFile(out_, nonrustdep);
377 }
378
379 // Library search paths are required to find system libraries named in #[link]
380 // directives, which will not be specified in non-Rust native dependencies.
381 WriteLibrarySearchPath(out_, tool_);
382 // If rustc will invoke a linker, all libraries need the passed through to the
383 // linker.
384 WriteLibs(out_, tool_);
385
386 out_ << std::endl;
387 out_ << " ldflags =";
388 // If rustc will invoke a linker, linker flags need to be forwarded through to
389 // the linker.
390 WriteCustomLinkerFlags(out_, tool_);
391
392 out_ << std::endl;
393 }
394