1use crate::util::data_structures::HashMap;
4use std::collections::BTreeSet;
5use std::ffi::{OsStr, OsString};
6use std::path::Path;
7use std::path::PathBuf;
8
9use cargo_platform::CfgExpr;
10use cargo_util::{ProcessBuilder, paths};
11
12use crate::core::Package;
13use crate::core::compiler::BuildContext;
14use crate::core::compiler::CompileTarget;
15use crate::core::compiler::RustdocFingerprint;
16use crate::core::compiler::apply_env_config;
17use crate::core::compiler::build_context::host_artifact_uses_only_host_config;
18use crate::core::compiler::{CompileKind, Unit, UnitHash};
19use crate::util::{CargoResult, GlobalContext};
20
21#[derive(Debug)]
23enum ToolKind {
24 Rustc,
26 Rustdoc,
28 HostProcess,
30 TargetProcess,
32}
33
34impl ToolKind {
35 fn is_rustc_tool(&self) -> bool {
36 matches!(self, ToolKind::Rustc | ToolKind::Rustdoc)
37 }
38}
39
40pub struct Doctest {
42 pub unit: Unit,
44 pub args: Vec<OsString>,
46 pub unstable_opts: bool,
48 pub linker: Option<PathBuf>,
50 pub script_metas: Option<Vec<UnitHash>>,
54
55 pub env: HashMap<String, OsString>,
57}
58
59pub struct UnitOutput {
61 pub unit: Unit,
63 pub path: PathBuf,
65 pub script_metas: Option<Vec<UnitHash>>,
69
70 pub env: HashMap<String, OsString>,
72}
73
74pub struct Compilation<'gctx> {
76 pub tests: Vec<UnitOutput>,
78
79 pub binaries: Vec<UnitOutput>,
81
82 pub cdylibs: Vec<UnitOutput>,
84
85 pub root_crate_names: Vec<String>,
87
88 pub native_dirs: BTreeSet<PathBuf>,
95
96 pub root_output: HashMap<CompileKind, PathBuf>,
98
99 pub deps_output: HashMap<CompileKind, BTreeSet<PathBuf>>,
102
103 sysroot_target_libdir: HashMap<CompileKind, PathBuf>,
105
106 pub extra_env: HashMap<UnitHash, Vec<(String, String)>>,
112
113 pub to_doc_test: Vec<Doctest>,
115
116 pub rustdoc_fingerprints: Option<HashMap<CompileKind, RustdocFingerprint>>,
120
121 pub host: String,
123
124 gctx: &'gctx GlobalContext,
125
126 rustc_process: ProcessBuilder,
128 rustc_workspace_wrapper_process: ProcessBuilder,
130 primary_rustc_process: Option<ProcessBuilder>,
133
134 runners: HashMap<CompileKind, Option<(PathBuf, Vec<String>)>>,
136 linkers: HashMap<CompileKind, Option<PathBuf>>,
138
139 pub lint_warning_count: usize,
141}
142
143impl<'gctx> Compilation<'gctx> {
144 pub fn new<'a>(bcx: &BuildContext<'a, 'gctx>) -> CargoResult<Compilation<'gctx>> {
145 let rustc_process = bcx.rustc().process();
146 let primary_rustc_process = bcx.build_config.primary_unit_rustc.clone();
147 let rustc_workspace_wrapper_process = bcx.rustc().workspace_process();
148 let host = bcx.host_triple().to_string();
149
150 let insert_explicit_host_runner = !bcx.gctx.target_applies_to_host()?
155 && bcx
156 .build_config
157 .requested_kinds
158 .iter()
159 .any(CompileKind::is_host);
160 let mut runners = bcx
161 .build_config
162 .requested_kinds
163 .iter()
164 .chain(Some(&CompileKind::Host))
165 .map(|kind| Ok((*kind, target_runner(bcx, *kind)?)))
166 .collect::<CargoResult<HashMap<_, _>>>()?;
167 if insert_explicit_host_runner {
168 let kind = explicit_host_kind(&host);
169 runners.insert(kind, target_runner(bcx, kind)?);
170 }
171
172 let mut linkers = bcx
173 .build_config
174 .requested_kinds
175 .iter()
176 .chain(Some(&CompileKind::Host))
177 .map(|kind| Ok((*kind, target_linker(bcx, *kind)?)))
178 .collect::<CargoResult<HashMap<_, _>>>()?;
179 if insert_explicit_host_runner {
180 let kind = explicit_host_kind(&host);
181 linkers.insert(kind, target_linker(bcx, kind)?);
182 }
183 Ok(Compilation {
184 native_dirs: BTreeSet::new(),
185 root_output: HashMap::default(),
186 deps_output: HashMap::default(),
187 sysroot_target_libdir: get_sysroot_target_libdir(bcx)?,
188 tests: Vec::new(),
189 binaries: Vec::new(),
190 cdylibs: Vec::new(),
191 root_crate_names: Vec::new(),
192 extra_env: HashMap::default(),
193 to_doc_test: Vec::new(),
194 rustdoc_fingerprints: None,
195 gctx: bcx.gctx,
196 host,
197 rustc_process,
198 rustc_workspace_wrapper_process,
199 primary_rustc_process,
200 runners,
201 linkers,
202 lint_warning_count: 0,
203 })
204 }
205
206 pub fn rustc_process(
214 &self,
215 unit: &Unit,
216 is_primary: bool,
217 is_workspace: bool,
218 ) -> CargoResult<ProcessBuilder> {
219 let mut rustc = if is_primary && self.primary_rustc_process.is_some() {
220 self.primary_rustc_process.clone().unwrap()
221 } else if is_workspace {
222 self.rustc_workspace_wrapper_process.clone()
223 } else {
224 self.rustc_process.clone()
225 };
226 if self.gctx.extra_verbose() {
227 rustc.display_env_vars();
228 }
229 let cmd = fill_rustc_tool_env(rustc, unit);
230 self.fill_env(cmd, &unit.pkg, None, unit.kind, ToolKind::Rustc)
231 }
232
233 pub fn rustdoc_process(
235 &self,
236 unit: &Unit,
237 script_metas: Option<&Vec<UnitHash>>,
238 ) -> CargoResult<ProcessBuilder> {
239 let mut rustdoc = ProcessBuilder::new(&*self.gctx.rustdoc()?);
240 if self.gctx.extra_verbose() {
241 rustdoc.display_env_vars();
242 }
243 let cmd = fill_rustc_tool_env(rustdoc, unit);
244 let mut cmd = self.fill_env(cmd, &unit.pkg, script_metas, unit.kind, ToolKind::Rustdoc)?;
245 cmd.retry_with_argfile(true);
246 unit.target.edition().cmd_edition_arg(&mut cmd);
247
248 for crate_type in unit.target.rustc_crate_types() {
249 cmd.arg("--crate-type").arg(crate_type.as_str());
250 }
251
252 Ok(cmd)
253 }
254
255 pub fn host_process<T: AsRef<OsStr>>(
262 &self,
263 cmd: T,
264 pkg: &Package,
265 ) -> CargoResult<ProcessBuilder> {
266 let builder = if !self.gctx.target_applies_to_host()?
269 && let Some((runner, args)) = self
270 .runners
271 .get(&CompileKind::Host)
272 .and_then(|x| x.as_ref())
273 {
274 let mut builder = ProcessBuilder::new(runner);
275 builder.args(args);
276 builder.arg(cmd);
277 builder
278 } else {
279 ProcessBuilder::new(cmd)
280 };
281 self.fill_env(builder, pkg, None, CompileKind::Host, ToolKind::HostProcess)
282 }
283
284 pub fn target_runner(&self, kind: CompileKind) -> Option<&(PathBuf, Vec<String>)> {
285 let target_applies_to_host = self.gctx.target_applies_to_host().unwrap_or(true);
286 let kind = if !target_applies_to_host && kind.is_host() {
287 explicit_host_kind(&self.host)
290 } else {
291 kind
292 };
293 self.runners.get(&kind).and_then(|x| x.as_ref())
294 }
295
296 pub fn host_linker(&self) -> Option<&Path> {
298 self.linkers
299 .get(&CompileKind::Host)
300 .and_then(|x| x.as_ref())
301 .map(|x| x.as_path())
302 }
303
304 pub fn target_linker(&self, kind: CompileKind) -> Option<&Path> {
306 let target_applies_to_host = self.gctx.target_applies_to_host().unwrap_or(true);
307 let kind = if !target_applies_to_host && kind.is_host() {
308 explicit_host_kind(&self.host)
311 } else {
312 kind
313 };
314 self.linkers
315 .get(&kind)
316 .and_then(|x| x.as_ref())
317 .map(|x| x.as_path())
318 }
319
320 pub fn target_process<T: AsRef<OsStr>>(
328 &self,
329 cmd: T,
330 kind: CompileKind,
331 pkg: &Package,
332 script_metas: Option<&Vec<UnitHash>>,
333 ) -> CargoResult<ProcessBuilder> {
334 let builder = if let Some((runner, args)) = self.target_runner(kind) {
335 let mut builder = ProcessBuilder::new(runner);
336 builder.args(args);
337 builder.arg(cmd);
338 builder
339 } else {
340 ProcessBuilder::new(cmd)
341 };
342 let tool_kind = ToolKind::TargetProcess;
343 let mut builder = self.fill_env(builder, pkg, script_metas, kind, tool_kind)?;
344
345 if let Some(client) = self.gctx.jobserver_from_env() {
346 builder.inherit_jobserver(client);
347 }
348
349 Ok(builder)
350 }
351
352 fn fill_env(
358 &self,
359 mut cmd: ProcessBuilder,
360 pkg: &Package,
361 script_metas: Option<&Vec<UnitHash>>,
362 kind: CompileKind,
363 tool_kind: ToolKind,
364 ) -> CargoResult<ProcessBuilder> {
365 let mut search_path = Vec::new();
366 if tool_kind.is_rustc_tool() {
367 if matches!(tool_kind, ToolKind::Rustdoc) {
368 search_path.extend(super::filter_dynamic_search_path(
375 self.native_dirs.iter(),
376 &self.root_output[&CompileKind::Host],
377 ));
378 }
379 search_path.extend(self.deps_output[&CompileKind::Host].clone());
380 } else {
381 if let Some(path) = self.root_output.get(&kind) {
382 search_path.extend(super::filter_dynamic_search_path(
383 self.native_dirs.iter(),
384 path,
385 ));
386 search_path.push(path.clone());
387 }
388 search_path.extend(self.deps_output[&kind].clone());
389 if self.gctx.cli_unstable().build_std.is_none() ||
394 pkg.proc_macro()
396 {
397 search_path.push(self.sysroot_target_libdir[&kind].clone());
398 }
399 }
400
401 let dylib_path = paths::dylib_path();
402 let dylib_path_is_empty = dylib_path.is_empty();
403 if dylib_path.starts_with(&search_path) {
404 search_path = dylib_path;
405 } else {
406 search_path.extend(dylib_path.into_iter());
407 }
408 if cfg!(target_os = "macos") && dylib_path_is_empty {
409 if let Some(home) = self.gctx.get_env_os("HOME") {
413 search_path.push(PathBuf::from(home).join("lib"));
414 }
415 search_path.push(PathBuf::from("/usr/local/lib"));
416 search_path.push(PathBuf::from("/usr/lib"));
417 }
418 let search_path = paths::join_paths(&search_path, paths::dylib_path_envvar())?;
419
420 cmd.env(paths::dylib_path_envvar(), &search_path);
421 if let Some(meta_vec) = script_metas {
422 for meta in meta_vec {
423 if let Some(env) = self.extra_env.get(meta) {
424 for (k, v) in env {
425 cmd.env(k, v);
426 }
427 }
428 }
429 }
430
431 let cargo_exe = self.gctx.cargo_exe()?;
432 cmd.env(crate::CARGO_ENV, cargo_exe);
433
434 cmd.env("CARGO_MANIFEST_DIR", pkg.root())
439 .env("CARGO_MANIFEST_PATH", pkg.manifest_path())
440 .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string())
441 .env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string())
442 .env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string())
443 .env("CARGO_PKG_VERSION_PRE", pkg.version().pre.as_str())
444 .env("CARGO_PKG_VERSION", &pkg.version().to_string())
445 .env("CARGO_PKG_NAME", &*pkg.name());
446
447 for (key, value) in pkg.manifest().metadata().env_vars() {
448 cmd.env(key, value.as_ref());
449 }
450
451 cmd.cwd(pkg.root());
452
453 apply_env_config(self.gctx, &mut cmd)?;
454
455 Ok(cmd)
456 }
457}
458
459fn fill_rustc_tool_env(mut cmd: ProcessBuilder, unit: &Unit) -> ProcessBuilder {
462 if unit.target.is_executable() {
463 let name = unit
464 .target
465 .binary_filename()
466 .unwrap_or(unit.target.name().to_string());
467
468 cmd.env("CARGO_BIN_NAME", name);
469 }
470 cmd.env("CARGO_CRATE_NAME", unit.target.crate_name());
471 cmd
472}
473
474fn get_sysroot_target_libdir(
475 bcx: &BuildContext<'_, '_>,
476) -> CargoResult<HashMap<CompileKind, PathBuf>> {
477 bcx.all_kinds
478 .iter()
479 .map(|&kind| {
480 let Some(info) = bcx.target_data.get_info(kind) else {
481 let target = match kind {
482 CompileKind::Host => "host".to_owned(),
483 CompileKind::Target(s) => s.short_name().to_owned(),
484 };
485
486 let dependency = bcx
487 .unit_graph
488 .iter()
489 .find_map(|(u, _)| (u.kind == kind).then_some(u.pkg.summary().package_id()))
490 .unwrap();
491
492 anyhow::bail!(
493 "could not find specification for target `{target}`.\n \
494 Dependency `{dependency}` requires to build for target `{target}`."
495 )
496 };
497
498 Ok((kind, info.sysroot_target_libdir.clone()))
499 })
500 .collect()
501}
502
503fn target_runner(
504 bcx: &BuildContext<'_, '_>,
505 kind: CompileKind,
506) -> CargoResult<Option<(PathBuf, Vec<String>)>> {
507 if let Some(runner) = bcx.target_data.target_config(kind).runner.as_ref() {
508 let path = runner.val.path.clone().resolve_program(bcx.gctx);
509 return Ok(Some((path, runner.val.args.clone())));
510 }
511
512 if host_artifact_uses_only_host_config(bcx.gctx, &bcx.build_config.requested_kinds, kind)? {
514 return Ok(None);
515 }
516
517 let target_cfg = bcx.target_data.info(kind).cfg();
519 let mut cfgs = bcx
520 .gctx
521 .target_cfgs()?
522 .iter()
523 .filter_map(|(key, cfg)| cfg.runner.as_ref().map(|runner| (key, runner)))
524 .filter(|(key, _runner)| CfgExpr::matches_key(key, target_cfg));
525 let matching_runner = cfgs.next();
526 if let Some((key, runner)) = cfgs.next() {
527 anyhow::bail!(
528 "several matching instances of `target.'cfg(..)'.runner` in configurations\n\
529 first match `{}` located in {}\n\
530 second match `{}` located in {}",
531 matching_runner.unwrap().0,
532 matching_runner.unwrap().1.definition,
533 key,
534 runner.definition
535 );
536 }
537 Ok(matching_runner.map(|(_k, runner)| {
538 (
539 runner.val.path.clone().resolve_program(bcx.gctx),
540 runner.val.args.clone(),
541 )
542 }))
543}
544
545fn target_linker(bcx: &BuildContext<'_, '_>, kind: CompileKind) -> CargoResult<Option<PathBuf>> {
547 if let Some(path) = bcx
549 .target_data
550 .target_config(kind)
551 .linker
552 .as_ref()
553 .map(|l| l.val.clone().resolve_program(bcx.gctx))
554 {
555 return Ok(Some(path));
556 }
557
558 if host_artifact_uses_only_host_config(bcx.gctx, &bcx.build_config.requested_kinds, kind)? {
560 return Ok(None);
561 }
562
563 let target_cfg = bcx.target_data.info(kind).cfg();
565 let mut cfgs = bcx
566 .gctx
567 .target_cfgs()?
568 .iter()
569 .filter_map(|(key, cfg)| cfg.linker.as_ref().map(|linker| (key, linker)))
570 .filter(|(key, _linker)| CfgExpr::matches_key(key, target_cfg));
571 let matching_linker = cfgs.next();
572 if let Some((key, linker)) = cfgs.next() {
573 anyhow::bail!(
574 "several matching instances of `target.'cfg(..)'.linker` in configurations\n\
575 first match `{}` located in {}\n\
576 second match `{}` located in {}",
577 matching_linker.unwrap().0,
578 matching_linker.unwrap().1.definition,
579 key,
580 linker.definition
581 );
582 }
583 Ok(matching_linker.map(|(_k, linker)| linker.val.clone().resolve_program(bcx.gctx)))
584}
585
586fn explicit_host_kind(host: &str) -> CompileKind {
587 let target = CompileTarget::new(host, false).expect("must be a host tuple");
588 CompileKind::Target(target)
589}