• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   std::vector<ExternCrate> transitive_crates;
158 
159   for (const auto* linkable_dep : classified_deps.linkable_deps) {
160     if (linkable_dep->copy_rust_file()) {
161       transitive_crates.push_back({linkable_dep, true});
162       continue;
163     }
164     // Rust cdylibs are treated as non-Rust dependencies for linking purposes.
165     if ((linkable_dep->source_types_used().RustSourceUsed() &&
166         linkable_dep->rust_values().crate_type() != RustValues::CRATE_CDYLIB) || linkable_dep->copy_rust_file()) {
167       rustdeps.push_back(linkable_dep->link_output_file());
168     } else {
169       nonrustdeps.push_back(linkable_dep->link_output_file());
170     }
171     implicit_deps.push_back(linkable_dep->dependency_output_file());
172   }
173 
174   // Rust libraries specified by paths.
175   for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
176     const ConfigValues& cur = iter.cur();
177     for (const auto& e : cur.externs()) {
178       if (e.second.is_source_file()) {
179         implicit_deps.push_back(
180             OutputFile(settings_->build_settings(), e.second.source_file()));
181       }
182     }
183   }
184 
185   // Collect the full transitive set of rust libraries that this target depends
186   // on, and the public flag represents if the target has direct access to the
187   // dependency through a chain of public_deps.
188   for (const auto& inherited : resolved().GetRustInheritedLibraries(target_)) {
189     const Target* dep = inherited.target();
190     bool has_direct_access = inherited.is_public();
191     // We will tell rustc to look for crate metadata for any rust crate
192     // dependencies except cdylibs, as they have no metadata present.
193     if ((dep->source_types_used().RustSourceUsed() &&
194         RustValues::IsRustLibrary(dep)) || dep->copy_rust_file()) {
195       transitive_crates.push_back({dep, has_direct_access});
196       // If the current crate can directly acccess the `dep` crate, then the
197       // current crate needs an implicit dependency on `dep` so it will be
198       // rebuilt if `dep` changes.
199       if (has_direct_access) {
200         implicit_deps.push_back(dep->dependency_output_file());
201       }
202     } else if(dep->copy_linkable_file()){
203       nonrustdeps.push_back(dep->link_output_file());
204     }
205   }
206 
207   std::vector<OutputFile> tool_outputs;
208   SubstitutionWriter::ApplyListToLinkerAsOutputFile(
209       target_, tool_, tool_->outputs(), &tool_outputs);
210   WriteCompilerBuildLine({target_->rust_values().crate_root()},
211                          implicit_deps.vector(), order_only_deps, tool_->name(),
212                          tool_outputs);
213 
214   std::vector<const Target*> extern_deps(
215       classified_deps.linkable_deps.vector());
216   std::copy(classified_deps.non_linkable_deps.begin(),
217             classified_deps.non_linkable_deps.end(),
218             std::back_inserter(extern_deps));
219 
220   WriteExternsAndDeps(extern_deps, transitive_crates, rustdeps, nonrustdeps);
221   WriteSourcesAndInputs();
222   WritePool(out_);
223 }
224 
WriteCompilerVars()225 void NinjaRustBinaryTargetWriter::WriteCompilerVars() {
226   const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
227 
228   EscapeOptions opts = GetFlagOptions();
229   WriteCrateVars(target_, tool_, opts, out_);
230 
231   WriteRustCompilerVars(subst, /*indent=*/false, /*always_write=*/true);
232 
233   WriteSharedVars(subst);
234 }
235 
AppendSourcesAndInputsToImplicitDeps(UniqueVector<OutputFile> * deps) const236 void NinjaRustBinaryTargetWriter::AppendSourcesAndInputsToImplicitDeps(
237     UniqueVector<OutputFile>* deps) const {
238   // Only the crate_root file needs to be given to rustc as input.
239   // Any other 'sources' are just implicit deps.
240   // Most Rust targets won't bother specifying the "sources =" line
241   // because it is handled sufficiently by crate_root and the generation
242   // of depfiles by rustc. But for those which do...
243   for (const auto& source : target_->sources()) {
244     deps->push_back(OutputFile(settings_->build_settings(), source));
245   }
246   for (const auto& data : target_->config_values().inputs()) {
247     deps->push_back(OutputFile(settings_->build_settings(), data));
248   }
249 }
250 
WriteSourcesAndInputs()251 void NinjaRustBinaryTargetWriter::WriteSourcesAndInputs() {
252   out_ << "  sources =";
253   for (const auto& source : target_->sources()) {
254     out_ << " ";
255     path_output_.WriteFile(out_,
256                            OutputFile(settings_->build_settings(), source));
257   }
258   for (const auto& data : target_->config_values().inputs()) {
259     out_ << " ";
260     path_output_.WriteFile(out_, OutputFile(settings_->build_settings(), data));
261   }
262   out_ << std::endl;
263 }
264 
WriteExternsAndDeps(const std::vector<const Target * > & deps,const std::vector<ExternCrate> & transitive_rust_deps,const std::vector<OutputFile> & rustdeps,const std::vector<OutputFile> & nonrustdeps)265 void NinjaRustBinaryTargetWriter::WriteExternsAndDeps(
266     const std::vector<const Target*>& deps,
267     const std::vector<ExternCrate>& transitive_rust_deps,
268     const std::vector<OutputFile>& rustdeps,
269     const std::vector<OutputFile>& nonrustdeps) {
270   // Writes a external LibFile which comes from user-specified externs, and may
271   // be either a string or a SourceFile.
272   auto write_extern_lib_file = [this](std::string_view crate_name,
273                                       LibFile lib_file) {
274     out_ << " --extern ";
275     out_ << crate_name;
276     out_ << "=";
277     if (lib_file.is_source_file()) {
278       path_output_.WriteFile(out_, lib_file.source_file());
279     } else {
280       EscapeOptions escape_opts_command;
281       escape_opts_command.mode = ESCAPE_NINJA_COMMAND;
282       EscapeStringToStream(out_, lib_file.value(), escape_opts_command);
283     }
284   };
285   // Writes an external OutputFile which comes from a dependency of the current
286   // target.
287   auto write_extern_target = [this](const Target& dep) {
288     std::string_view crate_name;
289     const auto& aliased_deps = target_->rust_values().aliased_deps();
290     if (auto it = aliased_deps.find(dep.label()); it != aliased_deps.end()) {
291       crate_name = it->second;
292     } else {
293       crate_name = dep.rust_values().crate_name();
294     }
295 
296     out_ << " --extern ";
297     out_ << crate_name;
298     out_ << "=";
299     path_output_.WriteFile(out_, dep.copy_rust_file()
300                                      ? dep.link_output_file()
301                                      : dep.dependency_output_file());
302   };
303 
304   // Write accessible crates with `--extern` to add them to the extern prelude.
305   out_ << "  externs =";
306 
307   // Tracking to avoid emitted the same lib twice. We track it instead of
308   // pre-emptively constructing a UniqueVector since we would have to also store
309   // the crate name, and in the future the public-ness.
310   std::unordered_set<OutputFile> emitted_rust_libs;
311   // TODO: We defer private dependencies to -Ldependency until --extern priv is
312   // stabilized.
313   UniqueVector<SourceDir> private_extern_dirs;
314 
315   // Walk the transitive closure of all rust dependencies.
316   //
317   // For dependencies that are meant to be accessible we pass them to --extern
318   // in order to add them to the crate's extern prelude.
319   //
320   // For all transitive dependencies, we add them to `private_extern_dirs` in
321   // order to generate a -Ldependency switch that points to them. This ensures
322   // that rustc can find them if they are used by other dependencies. For
323   // example:
324   //
325   //   A -> C --public--> D
326   //     -> B --private-> D
327   //
328   // Here A has direct access to D, but B and C also make use of D, and they
329   // will only search the paths specified to -Ldependency, thus D needs to
330   // appear as both a --extern (for A) and -Ldependency (for B and C).
331   for (const auto& crate : transitive_rust_deps) {
332     const OutputFile& rust_lib = crate.target->copy_rust_file()
333                                      ? crate.target->link_output_file()
334                                      : crate.target->dependency_output_file();
335     if (emitted_rust_libs.count(rust_lib) == 0) {
336       if (crate.has_direct_access) {
337         write_extern_target(*crate.target);
338       }
339       emitted_rust_libs.insert(rust_lib);
340     }
341     const SourceDir& dir =
342         rust_lib.AsSourceFile(settings_->build_settings()).GetDir();
343     private_extern_dirs.push_back(dir);
344   }
345 
346   // Add explicitly specified externs from the GN target.
347   for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
348     const ConfigValues& cur = iter.cur();
349     for (const auto& [crate_name, lib_file] : cur.externs()) {
350       write_extern_lib_file(crate_name, lib_file);
351     }
352   }
353 
354   out_ << std::endl;
355   out_ << "  rustdeps =";
356 
357   for (const SourceDir& dir : private_extern_dirs) {
358     // TODO: switch to using `--extern priv:name` after stabilization.
359     out_ << " -Ldependency=";
360     path_output_.WriteDir(out_, dir, PathOutput::DIR_NO_LAST_SLASH);
361   }
362 
363   UniqueVector<SourceDir> nonrustdep_dirs;
364 
365   // Non-Rust native dependencies. A dependency from Rust implies the ability
366   // to specify it in #[link], and GN will ensure that rustc can find it by
367   // adding it to the native library search paths.
368   for (const auto& nonrustdep : nonrustdeps) {
369     nonrustdep_dirs.push_back(
370         nonrustdep.AsSourceFile(settings_->build_settings()).GetDir());
371   }
372   for (const auto& nonrustdep_dir : nonrustdep_dirs) {
373     out_ << " -Lnative=";
374     path_output_.WriteDir(out_, nonrustdep_dir, PathOutput::DIR_NO_LAST_SLASH);
375   }
376 
377   // If rustc will invoke a linker, then pass linker arguments to include those
378   // non-Rust native dependencies in the linking step.
379 
380   // Before outputting any libraries to link, ensure the linker is in a mode
381   // that allows dynamic linking, as rustc may have previously put it into
382   // static-only mode.
383   if (nonrustdeps.size() > 0) {
384     out_ << " " << tool_->dynamic_link_switch();
385   }
386   for (const auto& nonrustdep : nonrustdeps) {
387     out_ << " -Clink-arg=";
388     path_output_.WriteFile(out_, nonrustdep);
389   }
390 
391   // Library search paths are required to find system libraries named in #[link]
392   // directives, which will not be specified in non-Rust native dependencies.
393   WriteLibrarySearchPath(out_, tool_);
394   // If rustc will invoke a linker, all libraries need the passed through to the
395   // linker.
396   WriteLibs(out_, tool_);
397 
398   out_ << std::endl;
399   out_ << "  ldflags =";
400   // If rustc will invoke a linker, linker flags need to be forwarded through to
401   // the linker.
402   WriteCustomLinkerFlags(out_, tool_);
403 
404   out_ << std::endl;
405 }
406