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