• 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/substitution_writer.h"
17 #include "gn/target.h"
18 
19 namespace {
20 
21 // Returns the proper escape options for writing compiler and linker flags.
GetFlagOptions()22 EscapeOptions GetFlagOptions() {
23   EscapeOptions opts;
24   opts.mode = ESCAPE_NINJA_COMMAND;
25   return opts;
26 }
27 
WriteVar(const char * name,const std::string & value,EscapeOptions opts,std::ostream & out)28 void WriteVar(const char* name,
29               const std::string& value,
30               EscapeOptions opts,
31               std::ostream& out) {
32   out << name << " = ";
33   EscapeStringToStream(out, value, opts);
34   out << std::endl;
35 }
36 
WriteCrateVars(const Target * target,const Tool * tool,EscapeOptions opts,std::ostream & out)37 void WriteCrateVars(const Target* target,
38                     const Tool* tool,
39                     EscapeOptions opts,
40                     std::ostream& out) {
41   WriteVar(kRustSubstitutionCrateName.ninja_name,
42            target->rust_values().crate_name(), opts, out);
43 
44   std::string crate_type;
45   switch (target->rust_values().crate_type()) {
46     // Auto-select the crate type for executables, static libraries, and rlibs.
47     case RustValues::CRATE_AUTO: {
48       switch (target->output_type()) {
49         case Target::EXECUTABLE:
50           crate_type = "bin";
51           break;
52         case Target::STATIC_LIBRARY:
53           crate_type = "staticlib";
54           break;
55         case Target::RUST_LIBRARY:
56           crate_type = "rlib";
57           break;
58         case Target::RUST_PROC_MACRO:
59           crate_type = "proc-macro";
60           break;
61         default:
62           NOTREACHED();
63       }
64       break;
65     }
66     case RustValues::CRATE_BIN:
67       crate_type = "bin";
68       break;
69     case RustValues::CRATE_CDYLIB:
70       crate_type = "cdylib";
71       break;
72     case RustValues::CRATE_DYLIB:
73       crate_type = "dylib";
74       break;
75     case RustValues::CRATE_PROC_MACRO:
76       crate_type = "proc-macro";
77       break;
78     case RustValues::CRATE_RLIB:
79       crate_type = "rlib";
80       break;
81     case RustValues::CRATE_STATICLIB:
82       crate_type = "staticlib";
83       break;
84     default:
85       NOTREACHED();
86   }
87   WriteVar(kRustSubstitutionCrateType.ninja_name, crate_type, opts, out);
88 
89   WriteVar(SubstitutionOutputExtension.ninja_name,
90            SubstitutionWriter::GetLinkerSubstitution(
91                target, tool, &SubstitutionOutputExtension),
92            opts, out);
93   WriteVar(SubstitutionOutputDir.ninja_name,
94            SubstitutionWriter::GetLinkerSubstitution(target, tool,
95                                                      &SubstitutionOutputDir),
96            opts, out);
97 }
98 
99 }  // namespace
100 
NinjaRustBinaryTargetWriter(const Target * target,std::ostream & out)101 NinjaRustBinaryTargetWriter::NinjaRustBinaryTargetWriter(const Target* target,
102                                                          std::ostream& out)
103     : NinjaBinaryTargetWriter(target, out),
104       tool_(target->toolchain()->GetToolForTargetFinalOutputAsRust(target)) {}
105 
106 NinjaRustBinaryTargetWriter::~NinjaRustBinaryTargetWriter() = default;
107 
108 // TODO(juliehockett): add inherited library support? and IsLinkable support?
109 // for c-cross-compat
Run()110 void NinjaRustBinaryTargetWriter::Run() {
111   OutputFile input_dep = WriteInputsStampAndGetDep();
112 
113   // The input dependencies will be an order-only dependency. This will cause
114   // Ninja to make sure the inputs are up to date before compiling this source,
115   // but changes in the inputs deps won't cause the file to be recompiled. See
116   // the comment on NinjaCBinaryTargetWriter::Run for more detailed explanation.
117   size_t num_stamp_uses = target_->sources().size();
118   std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep(
119       std::vector<const Target*>(), num_stamp_uses);
120 
121   // Public rust_library deps go in a --extern rlibs, public non-rust deps go in
122   // -Ldependency rustdeps, and non-public source_sets get passed in as normal
123   // source files
124   UniqueVector<OutputFile> deps;
125   AddSourceSetFiles(target_, &deps);
126   if (target_->output_type() == Target::SOURCE_SET) {
127     WriteSharedVars(target_->toolchain()->substitution_bits());
128     WriteSourceSetStamp(deps.vector());
129   } else {
130     WriteCompilerVars();
131     UniqueVector<const Target*> linkable_deps;
132     UniqueVector<const Target*> non_linkable_deps;
133     UniqueVector<const Target*> framework_deps;
134     GetDeps(&deps, &linkable_deps, &non_linkable_deps, &framework_deps);
135 
136     if (!input_dep.value().empty())
137       order_only_deps.push_back(input_dep);
138 
139     std::vector<OutputFile> rustdeps;
140     std::vector<OutputFile> nonrustdeps;
141     for (const auto* framework_dep : framework_deps) {
142       order_only_deps.push_back(framework_dep->dependency_output_file());
143     }
144     for (const auto* non_linkable_dep : non_linkable_deps) {
145       if (non_linkable_dep->source_types_used().RustSourceUsed() &&
146           non_linkable_dep->output_type() != Target::SOURCE_SET) {
147         rustdeps.push_back(non_linkable_dep->dependency_output_file());
148       }
149       order_only_deps.push_back(non_linkable_dep->dependency_output_file());
150     }
151     for (const auto* linkable_dep : linkable_deps) {
152       if (linkable_dep->source_types_used().RustSourceUsed()) {
153         rustdeps.push_back(linkable_dep->link_output_file());
154       } else {
155         nonrustdeps.push_back(linkable_dep->link_output_file());
156       }
157       deps.push_back(linkable_dep->dependency_output_file());
158     }
159 
160     // Rust libraries specified by paths.
161     for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
162       const ConfigValues& cur = iter.cur();
163       for (const auto& e : cur.externs()) {
164         if (e.second.is_source_file()) {
165           deps.push_back(OutputFile(settings_->build_settings(),
166                                     e.second.source_file()));
167         }
168       }
169     }
170 
171     std::vector<OutputFile> tool_outputs;
172     SubstitutionWriter::ApplyListToLinkerAsOutputFile(
173         target_, tool_, tool_->outputs(), &tool_outputs);
174     WriteCompilerBuildLine(target_->rust_values().crate_root(), deps.vector(),
175                            order_only_deps, tool_->name(), tool_outputs);
176 
177     std::vector<const Target*> extern_deps(linkable_deps.vector());
178     std::copy(non_linkable_deps.begin(), non_linkable_deps.end(),
179               std::back_inserter(extern_deps));
180     WriteExterns(extern_deps);
181     WriteRustdeps(rustdeps, nonrustdeps);
182   }
183 }
184 
WriteCompilerVars()185 void NinjaRustBinaryTargetWriter::WriteCompilerVars() {
186   const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
187 
188   EscapeOptions opts = GetFlagOptions();
189   WriteCrateVars(target_, tool_, opts, out_);
190 
191   WriteOneFlag(target_, &kRustSubstitutionRustFlags, false, Tool::kToolNone,
192                &ConfigValues::rustflags, opts, path_output_, out_);
193 
194   WriteOneFlag(target_, &kRustSubstitutionRustEnv, false, Tool::kToolNone,
195                &ConfigValues::rustenv, opts, path_output_, out_);
196 
197   WriteSharedVars(subst);
198 }
199 
WriteExterns(const std::vector<const Target * > & deps)200 void NinjaRustBinaryTargetWriter::WriteExterns(
201     const std::vector<const Target*>& deps) {
202   out_ << "  externs =";
203 
204   for (const Target* target : deps) {
205     if (target->output_type() == Target::RUST_LIBRARY ||
206         target->output_type() == Target::RUST_PROC_MACRO) {
207       out_ << " --extern ";
208       const auto& renamed_dep =
209           target_->rust_values().aliased_deps().find(target->label());
210       if (renamed_dep != target_->rust_values().aliased_deps().end()) {
211         out_ << renamed_dep->second << "=";
212       } else {
213         out_ << std::string(target->rust_values().crate_name()) << "=";
214       }
215       path_output_.WriteFile(out_, target->dependency_output_file());
216     }
217   }
218 
219   EscapeOptions extern_escape_opts;
220   extern_escape_opts.mode = ESCAPE_NINJA_COMMAND;
221 
222   for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
223     const ConfigValues& cur = iter.cur();
224     for (const auto& e : cur.externs()) {
225       out_ << " --extern " << std::string(e.first) << "=";
226       if (e.second.is_source_file()) {
227         path_output_.WriteFile(out_, e.second.source_file());
228       } else {
229         EscapeStringToStream(out_, e.second.value(), extern_escape_opts);
230       }
231     }
232   }
233 
234   out_ << std::endl;
235 }
236 
WriteRustdeps(const std::vector<OutputFile> & rustdeps,const std::vector<OutputFile> & nonrustdeps)237 void NinjaRustBinaryTargetWriter::WriteRustdeps(
238     const std::vector<OutputFile>& rustdeps,
239     const std::vector<OutputFile>& nonrustdeps) {
240   out_ << "  rustdeps =";
241 
242   // Rust dependencies.
243   for (const auto& rustdep : rustdeps) {
244     out_ << " -Ldependency=";
245     path_output_.WriteDir(
246         out_, rustdep.AsSourceFile(settings_->build_settings()).GetDir(),
247         PathOutput::DIR_NO_LAST_SLASH);
248   }
249 
250   EscapeOptions lib_escape_opts;
251   lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
252   const std::string_view lib_prefix("lib");
253 
254   // Non-Rust native dependencies.
255   for (const auto& nonrustdep : nonrustdeps) {
256     out_ << " -Lnative=";
257     path_output_.WriteDir(
258         out_, nonrustdep.AsSourceFile(settings_->build_settings()).GetDir(),
259         PathOutput::DIR_NO_LAST_SLASH);
260     std::string_view file = FindFilenameNoExtension(&nonrustdep.value());
261     if (!file.compare(0, lib_prefix.size(), lib_prefix)) {
262       out_ << " -l";
263       EscapeStringToStream(out_, file.substr(lib_prefix.size()),
264                            lib_escape_opts);
265     } else {
266       out_ << " -Clink-arg=";
267       path_output_.WriteFile(out_, nonrustdep);
268     }
269   }
270 
271   WriteLinkerFlags(out_, tool_, nullptr);
272   WriteLibs(out_, tool_);
273   out_ << std::endl;
274 }
275