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