1use crate::util::data_structures::HashMap;
4use std::collections::BTreeSet;
5use std::ffi::{OsStr, OsString};
6use std::path::Path;
7use std::path::PathBuf;
8use std::rc::Rc;
9
10use cargo_platform::CfgExpr;
11use cargo_util::{ProcessBuilder, paths};
12
13use crate::compiler::BuildContext;
14use crate::compiler::CompileTarget;
15use crate::compiler::RustdocFingerprint;
16use crate::compiler::apply_env_config;
17use crate::compiler::{CompileKind, Unit, UnitHash};
18use crate::util::{CargoResult, GlobalContext};
19use crate::workspace::Package;
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 rustdocflags: HashMap<CompileKind, Rc<[String]>>,
123
124 pub host: String,
126
127 gctx: &'gctx GlobalContext,
128
129 rustc_process: ProcessBuilder,
131 rustc_workspace_wrapper_process: ProcessBuilder,
133 primary_rustc_process: Option<ProcessBuilder>,
136
137 runners: HashMap<CompileKind, Option<(PathBuf, Vec<String>)>>,
139 linkers: HashMap<CompileKind, Option<PathBuf>>,
141
142 pub lint_warning_count: usize,
144}
145
146impl<'gctx> Compilation<'gctx> {
147 pub fn new<'a>(bcx: &BuildContext<'a, 'gctx>) -> CargoResult<Compilation<'gctx>> {
148 let rustc_process = bcx.rustc().process();
149 let primary_rustc_process = bcx.build_config.primary_unit_rustc.clone();
150 let rustc_workspace_wrapper_process = bcx.rustc().workspace_process();
151 let host = bcx.host_triple().to_string();
152 let sysroot_target_libdir = get_sysroot_target_libdir(bcx)?;
153 let rustdocflags = bcx
154 .all_kinds
155 .iter()
156 .map(|&kind| (kind, bcx.target_data.info(kind).rustdocflags.clone()))
157 .collect();
158
159 let insert_explicit_host_runner = !bcx.gctx.target_applies_to_host()?
164 && bcx
165 .build_config
166 .requested_kinds
167 .iter()
168 .any(CompileKind::is_host);
169 let mut runners = bcx
170 .build_config
171 .requested_kinds
172 .iter()
173 .chain(Some(&CompileKind::Host))
174 .map(|kind| Ok((*kind, target_runner(bcx, *kind)?)))
175 .collect::<CargoResult<HashMap<_, _>>>()?;
176 if insert_explicit_host_runner {
177 let kind = explicit_host_kind(&host);
178 runners.insert(kind, target_runner(bcx, kind)?);
179 }
180
181 let mut linkers = bcx
182 .build_config
183 .requested_kinds
184 .iter()
185 .chain(Some(&CompileKind::Host))
186 .map(|kind| Ok((*kind, target_linker(bcx, *kind)?)))
187 .collect::<CargoResult<HashMap<_, _>>>()?;
188 if insert_explicit_host_runner {
189 let kind = explicit_host_kind(&host);
190 linkers.insert(kind, target_linker(bcx, kind)?);
191 }
192 Ok(Compilation {
193 native_dirs: BTreeSet::new(),
194 root_output: HashMap::default(),
195 deps_output: HashMap::default(),
196 sysroot_target_libdir,
197 tests: Vec::new(),
198 binaries: Vec::new(),
199 cdylibs: Vec::new(),
200 root_crate_names: Vec::new(),
201 extra_env: HashMap::default(),
202 to_doc_test: Vec::new(),
203 rustdoc_fingerprints: None,
204 rustdocflags,
205 gctx: bcx.gctx,
206 host,
207 rustc_process,
208 rustc_workspace_wrapper_process,
209 primary_rustc_process,
210 runners,
211 linkers,
212 lint_warning_count: 0,
213 })
214 }
215
216 pub fn rustc_process(
224 &self,
225 unit: &Unit,
226 is_primary: bool,
227 is_workspace: bool,
228 ) -> CargoResult<ProcessBuilder> {
229 let mut rustc = if is_primary && self.primary_rustc_process.is_some() {
230 self.primary_rustc_process.clone().unwrap()
231 } else if is_workspace {
232 self.rustc_workspace_wrapper_process.clone()
233 } else {
234 self.rustc_process.clone()
235 };
236 if self.gctx.extra_verbose() {
237 rustc.display_env_vars();
238 }
239 let cmd = fill_rustc_tool_env(rustc, unit);
240 self.fill_env(cmd, &unit.pkg, None, unit.kind, ToolKind::Rustc)
241 }
242
243 pub fn rustdoc_process(
245 &self,
246 unit: &Unit,
247 script_metas: Option<&Vec<UnitHash>>,
248 ) -> CargoResult<ProcessBuilder> {
249 let mut rustdoc = ProcessBuilder::new(&*self.gctx.rustdoc()?);
250 if self.gctx.extra_verbose() {
251 rustdoc.display_env_vars();
252 }
253 let cmd = fill_rustc_tool_env(rustdoc, unit);
254 let mut cmd = self.fill_env(cmd, &unit.pkg, script_metas, unit.kind, ToolKind::Rustdoc)?;
255 cmd.retry_with_argfile(true);
256 unit.target.edition().cmd_edition_arg(&mut cmd);
257
258 for crate_type in unit.target.rustc_crate_types() {
259 cmd.arg("--crate-type").arg(crate_type.as_str());
260 }
261
262 Ok(cmd)
263 }
264
265 pub fn host_process<T: AsRef<OsStr>>(
272 &self,
273 cmd: T,
274 pkg: &Package,
275 ) -> CargoResult<ProcessBuilder> {
276 let builder = if !self.gctx.target_applies_to_host()?
279 && let Some((runner, args)) = self
280 .runners
281 .get(&CompileKind::Host)
282 .and_then(|x| x.as_ref())
283 {
284 let mut builder = ProcessBuilder::new(runner);
285 builder.args(args);
286 builder.arg(cmd);
287 builder
288 } else {
289 ProcessBuilder::new(cmd)
290 };
291 self.fill_env(builder, pkg, None, CompileKind::Host, ToolKind::HostProcess)
292 }
293
294 pub fn target_runner(&self, kind: CompileKind) -> Option<&(PathBuf, Vec<String>)> {
295 let target_applies_to_host = self.gctx.target_applies_to_host().unwrap_or(true);
296 let kind = if !target_applies_to_host && kind.is_host() {
297 explicit_host_kind(&self.host)
300 } else {
301 kind
302 };
303 self.runners.get(&kind).and_then(|x| x.as_ref())
304 }
305
306 pub fn host_linker(&self) -> Option<&Path> {
308 self.linkers
309 .get(&CompileKind::Host)
310 .and_then(|x| x.as_ref())
311 .map(|x| x.as_path())
312 }
313
314 pub fn target_linker(&self, kind: CompileKind) -> Option<&Path> {
316 let target_applies_to_host = self.gctx.target_applies_to_host().unwrap_or(true);
317 let kind = if !target_applies_to_host && kind.is_host() {
318 explicit_host_kind(&self.host)
321 } else {
322 kind
323 };
324 self.linkers
325 .get(&kind)
326 .and_then(|x| x.as_ref())
327 .map(|x| x.as_path())
328 }
329
330 pub fn target_process<T: AsRef<OsStr>>(
338 &self,
339 cmd: T,
340 kind: CompileKind,
341 pkg: &Package,
342 script_metas: Option<&Vec<UnitHash>>,
343 ) -> CargoResult<ProcessBuilder> {
344 let builder = if let Some((runner, args)) = self.target_runner(kind) {
345 let mut builder = ProcessBuilder::new(runner);
346 builder.args(args);
347 builder.arg(cmd);
348 builder
349 } else {
350 ProcessBuilder::new(cmd)
351 };
352 let tool_kind = ToolKind::TargetProcess;
353 let mut builder = self.fill_env(builder, pkg, script_metas, kind, tool_kind)?;
354
355 if let Some(client) = self.gctx.jobserver_from_env() {
356 builder.inherit_jobserver(client);
357 }
358
359 Ok(builder)
360 }
361
362 fn fill_env(
368 &self,
369 mut cmd: ProcessBuilder,
370 pkg: &Package,
371 script_metas: Option<&Vec<UnitHash>>,
372 kind: CompileKind,
373 tool_kind: ToolKind,
374 ) -> CargoResult<ProcessBuilder> {
375 let mut search_path = Vec::new();
376 if tool_kind.is_rustc_tool() {
377 if matches!(tool_kind, ToolKind::Rustdoc) {
378 search_path.extend(super::filter_dynamic_search_path(
385 self.native_dirs.iter(),
386 &self.root_output[&CompileKind::Host],
387 ));
388 }
389 if let Some(paths) = self.deps_output.get(&CompileKind::Host) {
390 search_path.extend(paths.clone());
391 }
392 } else {
393 if let Some(path) = self.root_output.get(&kind) {
394 search_path.extend(super::filter_dynamic_search_path(
395 self.native_dirs.iter(),
396 path,
397 ));
398 search_path.push(path.clone());
399 }
400 if let Some(paths) = self.deps_output.get(&kind) {
401 search_path.extend(paths.clone());
402 }
403 if self.gctx.cli_unstable().build_std.is_none() ||
408 pkg.proc_macro()
410 {
411 search_path.push(self.sysroot_target_libdir[&kind].clone());
412 }
413 }
414
415 let dylib_path = paths::dylib_path();
416 let dylib_path_is_empty = dylib_path.is_empty();
417 if dylib_path.starts_with(&search_path) {
418 search_path = dylib_path;
419 } else {
420 search_path.extend(dylib_path.into_iter());
421 }
422 if cfg!(target_os = "macos") && dylib_path_is_empty {
423 if let Some(home) = self.gctx.get_env_os("HOME") {
427 search_path.push(PathBuf::from(home).join("lib"));
428 }
429 search_path.push(PathBuf::from("/usr/local/lib"));
430 search_path.push(PathBuf::from("/usr/lib"));
431 }
432 let search_path = paths::join_paths(&search_path, paths::dylib_path_envvar())?;
433
434 cmd.env(paths::dylib_path_envvar(), &search_path);
435 if let Some(meta_vec) = script_metas {
436 for meta in meta_vec {
437 if let Some(env) = self.extra_env.get(meta) {
438 for (k, v) in env {
439 cmd.env(k, v);
440 }
441 }
442 }
443 }
444
445 let cargo_exe = self.gctx.cargo_exe()?;
446 cmd.env(crate::CARGO_ENV, cargo_exe);
447
448 cmd.env("CARGO_MANIFEST_DIR", pkg.root())
453 .env("CARGO_MANIFEST_PATH", pkg.manifest_path())
454 .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string())
455 .env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string())
456 .env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string())
457 .env("CARGO_PKG_VERSION_PRE", pkg.version().pre.as_str())
458 .env("CARGO_PKG_VERSION", &pkg.version().to_string())
459 .env("CARGO_PKG_NAME", &*pkg.name());
460
461 for (key, value) in pkg.manifest().metadata().env_vars() {
462 cmd.env(key, value.as_ref());
463 }
464
465 cmd.cwd(pkg.root());
466
467 apply_env_config(self.gctx, &mut cmd)?;
468
469 Ok(cmd)
470 }
471}
472
473fn fill_rustc_tool_env(mut cmd: ProcessBuilder, unit: &Unit) -> ProcessBuilder {
476 if unit.target.is_executable() {
477 let name = unit
478 .target
479 .binary_filename()
480 .unwrap_or(unit.target.name().to_string());
481
482 cmd.env("CARGO_BIN_NAME", name);
483 }
484 cmd.env("CARGO_CRATE_NAME", unit.target.crate_name());
485 cmd
486}
487
488fn get_sysroot_target_libdir(
489 bcx: &BuildContext<'_, '_>,
490) -> CargoResult<HashMap<CompileKind, PathBuf>> {
491 bcx.all_kinds
492 .iter()
493 .map(|&kind| {
494 let Some(info) = bcx.target_data.get_info(kind) else {
495 let target = match kind {
496 CompileKind::Host => "host".to_owned(),
497 CompileKind::Target(s) => s.short_name().to_owned(),
498 };
499
500 let dependency = bcx
501 .unit_graph
502 .iter()
503 .find_map(|(u, _)| (u.kind == kind).then_some(u.pkg.summary().package_id()))
504 .unwrap();
505
506 anyhow::bail!(
507 "could not find specification for target `{target}`.\n \
508 Dependency `{dependency}` requires to build for target `{target}`."
509 )
510 };
511
512 Ok((kind, info.sysroot_target_libdir.clone()))
513 })
514 .collect()
515}
516
517fn target_runner(
518 bcx: &BuildContext<'_, '_>,
519 kind: CompileKind,
520) -> CargoResult<Option<(PathBuf, Vec<String>)>> {
521 if let Some(runner) = bcx.target_data.target_config(kind).runner.as_ref() {
522 let path = runner.val.path.clone().resolve_program(bcx.gctx);
523 return Ok(Some((path, runner.val.args.clone())));
524 }
525
526 if kind.is_host() && !bcx.gctx.target_applies_to_host()? {
530 return Ok(None);
531 }
532
533 let target_cfg = bcx.target_data.info(kind).cfg();
535 let mut cfgs = bcx
536 .gctx
537 .target_cfgs()?
538 .iter()
539 .filter_map(|(key, cfg)| cfg.runner.as_ref().map(|runner| (key, runner)))
540 .filter(|(key, _runner)| CfgExpr::matches_key(key, target_cfg));
541 let matching_runner = cfgs.next();
542 if let Some((key, runner)) = cfgs.next() {
543 anyhow::bail!(
544 "several matching instances of `target.'cfg(..)'.runner` in configurations\n\
545 first match `{}` located in {}\n\
546 second match `{}` located in {}",
547 matching_runner.unwrap().0,
548 matching_runner.unwrap().1.definition,
549 key,
550 runner.definition
551 );
552 }
553 Ok(matching_runner.map(|(_k, runner)| {
554 (
555 runner.val.path.clone().resolve_program(bcx.gctx),
556 runner.val.args.clone(),
557 )
558 }))
559}
560
561fn target_linker(bcx: &BuildContext<'_, '_>, kind: CompileKind) -> CargoResult<Option<PathBuf>> {
563 if let Some(path) = bcx
565 .target_data
566 .target_config(kind)
567 .linker
568 .as_ref()
569 .map(|l| l.val.clone().resolve_program(bcx.gctx))
570 {
571 return Ok(Some(path));
572 }
573
574 if kind.is_host() && !bcx.gctx.target_applies_to_host()? {
578 return Ok(None);
579 }
580
581 let target_cfg = bcx.target_data.info(kind).cfg();
583 let mut cfgs = bcx
584 .gctx
585 .target_cfgs()?
586 .iter()
587 .filter_map(|(key, cfg)| cfg.linker.as_ref().map(|linker| (key, linker)))
588 .filter(|(key, _linker)| CfgExpr::matches_key(key, target_cfg));
589 let matching_linker = cfgs.next();
590 if let Some((key, linker)) = cfgs.next() {
591 anyhow::bail!(
592 "several matching instances of `target.'cfg(..)'.linker` in configurations\n\
593 first match `{}` located in {}\n\
594 second match `{}` located in {}",
595 matching_linker.unwrap().0,
596 matching_linker.unwrap().1.definition,
597 key,
598 linker.definition
599 );
600 }
601 Ok(matching_linker.map(|(_k, linker)| linker.val.clone().resolve_program(bcx.gctx)))
602}
603
604fn explicit_host_kind(host: &str) -> CompileKind {
605 let target = CompileTarget::new(host, false).expect("must be a host tuple");
606 CompileKind::Target(target)
607}