1mod build_config;
2mod build_context;
3mod build_plan;
4mod compilation;
5mod compile_kind;
6mod context;
7mod custom_build;
8mod fingerprint;
9mod job;
10mod job_queue;
11mod layout;
12mod links;
13mod output_depinfo;
14pub mod standard_lib;
15mod timings;
16mod unit;
17pub mod unit_dependencies;
18pub mod unit_graph;
19
20use std::env;
21use std::ffi::{OsStr, OsString};
22use std::fs::{self, File};
23use std::io::{BufRead, Write};
24use std::path::PathBuf;
25use std::sync::Arc;
26
27use anyhow::Error;
28use lazycell::LazyCell;
29use log::debug;
30
31pub use self::build_config::{BuildConfig, CompileMode, MessageFormat};
32pub use self::build_context::{BuildContext, FileFlavor, RustcTargetData, TargetInfo};
33use self::build_plan::BuildPlan;
34pub use self::compilation::{Compilation, Doctest};
35pub use self::compile_kind::{CompileKind, CompileTarget};
36pub use self::context::{Context, Metadata};
37pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts};
38pub use self::job::Freshness;
39use self::job::{Job, Work};
40use self::job_queue::{JobQueue, JobState};
41use self::output_depinfo::output_depinfo;
42use self::unit_graph::UnitDep;
43pub use crate::core::compiler::unit::{Unit, UnitInterner};
44use crate::core::manifest::TargetSourcePath;
45use crate::core::profiles::{Lto, PanicStrategy, Profile};
46use crate::core::{Edition, Feature, InternedString, PackageId, Target};
47use crate::util::errors::{self, CargoResult, CargoResultExt, ProcessError, VerboseError};
48use crate::util::machine_message::Message;
49use crate::util::{self, machine_message, ProcessBuilder};
50use crate::util::{internal, join_paths, paths, profile};
51
52const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version";
53
54pub trait Executor: Send + Sync + 'static {
58 fn init<'a, 'cfg>(&self, _cx: &Context<'a, 'cfg>, _unit: &Unit<'a>) {}
62
63 fn exec(
66 &self,
67 cmd: ProcessBuilder,
68 id: PackageId,
69 target: &Target,
70 mode: CompileMode,
71 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
72 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
73 ) -> CargoResult<()>;
74
75 fn force_rebuild(&self, _unit: &Unit<'_>) -> bool {
78 false
79 }
80}
81
82#[derive(Copy, Clone)]
85pub struct DefaultExecutor;
86
87impl Executor for DefaultExecutor {
88 fn exec(
89 &self,
90 cmd: ProcessBuilder,
91 _id: PackageId,
92 _target: &Target,
93 _mode: CompileMode,
94 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
95 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
96 ) -> CargoResult<()> {
97 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
98 .map(drop)
99 }
100}
101
102fn compile<'a, 'cfg: 'a>(
103 cx: &mut Context<'a, 'cfg>,
104 jobs: &mut JobQueue<'a, 'cfg>,
105 plan: &mut BuildPlan,
106 unit: &Unit<'a>,
107 exec: &Arc<dyn Executor>,
108 force_rebuild: bool,
109) -> CargoResult<()> {
110 let bcx = cx.bcx;
111 let build_plan = bcx.build_config.build_plan;
112 if !cx.compiled.insert(*unit) {
113 return Ok(());
114 }
115
116 let skipped_units = bcx.skip_units.borrow();
117 if !skipped_units.contains(unit) {
118 let p = profile::start(format!("preparing: {}/{}", unit.pkg, unit.target.name()));
121 fingerprint::prepare_init(cx, unit)?;
122
123 let job = if unit.mode.is_run_custom_build() {
124 custom_build::prepare(cx, unit)?
125 } else if unit.mode.is_doc_test() {
126 Job::new(Work::noop(), Freshness::Fresh)
128 } else if build_plan {
129 Job::new(rustc(cx, unit, &exec.clone())?, Freshness::Dirty)
130 } else {
131 let force = exec.force_rebuild(unit) || force_rebuild;
132 let mut job = fingerprint::prepare_target(cx, unit, force)?;
133 job.before(if job.freshness() == Freshness::Dirty {
134 let work = if unit.mode.is_doc() {
135 rustdoc(cx, unit)?
136 } else {
137 rustc(cx, unit, exec)?
138 };
139 work.then(link_targets(cx, unit, false)?)
140 } else {
141 let work = if cx.bcx.show_warnings(unit.pkg.package_id()) {
142 replay_output_cache(
143 unit.pkg.package_id(),
144 unit.target,
145 cx.files().message_cache_path(unit),
146 cx.bcx.build_config.message_format,
147 cx.bcx.config.shell().supports_color(),
148 )
149 } else {
150 Work::noop()
151 };
152 work.then(link_targets(cx, unit, true)?)
154 });
155
156 job
157 };
158 jobs.enqueue(cx, unit, job)?;
159 drop(p);
160 }
161
162 let deps = Vec::from(cx.unit_deps(unit)); for dep in deps {
165 compile(cx, jobs, plan, &dep.unit, exec, false)?;
166 }
167 if build_plan {
168 plan.add(cx, unit)?;
169 }
170
171 Ok(())
172}
173
174fn rustc<'a, 'cfg>(
175 cx: &mut Context<'a, 'cfg>,
176 unit: &Unit<'a>,
177 exec: &Arc<dyn Executor>,
178) -> CargoResult<Work> {
179 let mut rustc = prepare_rustc(cx, &unit.target.rustc_crate_types(), unit)?;
180 let build_plan = cx.bcx.build_config.build_plan;
181
182 let name = unit.pkg.name().to_string();
183 let buildkey = unit.buildkey();
184
185 add_cap_lints(cx.bcx, unit, &mut rustc);
186
187 let outputs = cx.outputs(unit)?;
188 let root = cx.files().out_dir(unit);
189
190 let build_script_outputs = Arc::clone(&cx.build_script_outputs);
192 let current_id = unit.pkg.package_id();
193 let build_scripts = cx.build_scripts.get(unit).cloned();
194
195 let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
198 let pass_cdylib_link_args = unit.target.is_cdylib();
199 let do_rename = unit.target.allows_underscores() && !unit.mode.is_any_test();
200 let real_name = unit.target.name().to_string();
201 let crate_name = unit.target.crate_name();
202
203 let rustc_dep_info_loc = if do_rename && cx.files().metadata(unit).is_none() {
205 root.join(&crate_name)
206 } else {
207 root.join(&cx.files().file_stem(unit))
208 }
209 .with_extension("d");
210 let dep_info_loc = fingerprint::dep_info_loc(cx, unit);
211
212 rustc.args(cx.bcx.rustflags_args(unit));
213 if cx.bcx.config.cli_unstable().binary_dep_depinfo {
214 rustc.arg("-Zbinary-dep-depinfo");
215 }
216 let mut output_options = OutputOptions::new(cx, unit);
217 let package_id = unit.pkg.package_id();
218 let target = unit.target.clone();
219 let mode = unit.mode;
220
221 exec.init(cx, unit);
222 let exec = exec.clone();
223
224 let root_output = cx.files().host_root().to_path_buf();
225 let target_dir = cx.bcx.ws.target_dir().into_path_unlocked();
226 let pkg_root = unit.pkg.root().to_path_buf();
227 let cwd = rustc
228 .get_cwd()
229 .unwrap_or_else(|| cx.bcx.config.cwd())
230 .to_path_buf();
231 let fingerprint_dir = cx.files().fingerprint_dir(unit);
232 let script_metadata = cx.find_build_script_metadata(*unit);
233
234 return Ok(Work::new(move |state| {
235 if let Some(build_scripts) = build_scripts {
243 let script_outputs = build_script_outputs.lock().unwrap();
244 if !build_plan {
245 add_native_deps(
246 &mut rustc,
247 &script_outputs,
248 &build_scripts,
249 pass_l_flag,
250 pass_cdylib_link_args,
251 current_id,
252 )?;
253 add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
254 }
255 add_custom_env(&mut rustc, &script_outputs, current_id, script_metadata)?;
256 }
257
258 for output in outputs.iter() {
259 if output.path.extension() == Some(OsStr::new("rmeta")) {
263 let dst = root.join(&output.path).with_extension("rlib");
264 if dst.exists() {
265 paths::remove_file(&dst)?;
266 }
267 }
268 }
269
270 fn verbose_if_simple_exit_code(err: Error) -> Error {
271 match err
274 .downcast_ref::<ProcessError>()
275 .as_ref()
276 .and_then(|perr| perr.exit.and_then(|e| e.code()))
277 {
278 Some(n) if errors::is_simple_exit_code(n) => VerboseError::new(err).into(),
279 _ => err,
280 }
281 }
282
283 state.running(&rustc);
284 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
285 if build_plan {
286 state.build_plan(buildkey, rustc.clone(), outputs.clone());
287 } else {
288 exec.exec(
289 rustc,
290 package_id,
291 &target,
292 mode,
293 &mut |line| on_stdout_line(state, line, package_id, &target),
294 &mut |line| on_stderr_line(state, line, package_id, &target, &mut output_options),
295 )
296 .map_err(verbose_if_simple_exit_code)
297 .chain_err(|| format!("could not compile `{}`.", name))?;
298 }
299
300 if do_rename && real_name != crate_name {
301 let dst = &outputs[0].path;
302 let src = dst.with_file_name(
303 dst.file_name()
304 .unwrap()
305 .to_str()
306 .unwrap()
307 .replace(&real_name, &crate_name),
308 );
309 if src.exists() && src.file_name() != dst.file_name() {
310 fs::rename(&src, &dst).chain_err(|| format!("could not rename crate {:?}", src))?;
311 }
312 }
313
314 if rustc_dep_info_loc.exists() {
315 fingerprint::translate_dep_info(
316 &rustc_dep_info_loc,
317 &dep_info_loc,
318 &cwd,
319 &pkg_root,
320 &target_dir,
321 current_id.source_id().is_path(),
323 )
324 .chain_err(|| {
325 internal(format!(
326 "could not parse/generate dep info at: {}",
327 rustc_dep_info_loc.display()
328 ))
329 })?;
330 debug!("rewinding mtime of {:?} to {}", dep_info_loc, timestamp);
331 filetime::set_file_times(dep_info_loc, timestamp, timestamp)?;
332 }
333
334 Ok(())
335 }));
336
337 fn add_native_deps(
340 rustc: &mut ProcessBuilder,
341 build_script_outputs: &BuildScriptOutputs,
342 build_scripts: &BuildScripts,
343 pass_l_flag: bool,
344 pass_cdylib_link_args: bool,
345 current_id: PackageId,
346 ) -> CargoResult<()> {
347 for key in build_scripts.to_link.iter() {
348 let output = build_script_outputs.get(key.0, key.1).ok_or_else(|| {
349 internal(format!(
350 "couldn't find build script output for {}/{}",
351 key.0, key.1
352 ))
353 })?;
354 for path in output.library_paths.iter() {
355 rustc.arg("-L").arg(path);
356 }
357 if key.0 == current_id {
358 for cfg in &output.cfgs {
359 rustc.arg("--cfg").arg(cfg);
360 }
361 if pass_l_flag {
362 for name in output.library_links.iter() {
363 rustc.arg("-l").arg(name);
364 }
365 }
366 if pass_cdylib_link_args {
367 for arg in output.linker_args.iter() {
368 let link_arg = format!("link-arg={}", arg);
369 rustc.arg("-C").arg(link_arg);
370 }
371 }
372 }
373 }
374 Ok(())
375 }
376
377 fn add_custom_env(
380 rustc: &mut ProcessBuilder,
381 build_script_outputs: &BuildScriptOutputs,
382 current_id: PackageId,
383 metadata: Option<Metadata>,
384 ) -> CargoResult<()> {
385 let metadata = match metadata {
386 Some(metadata) => metadata,
387 None => return Ok(()),
388 };
389 if let Some(output) = build_script_outputs.get(current_id, metadata) {
390 for &(ref name, ref value) in output.env.iter() {
391 rustc.env(name, value);
392 }
393 }
394 Ok(())
395 }
396}
397
398fn link_targets<'a, 'cfg>(
401 cx: &mut Context<'a, 'cfg>,
402 unit: &Unit<'a>,
403 fresh: bool,
404) -> CargoResult<Work> {
405 let bcx = cx.bcx;
406 let outputs = cx.outputs(unit)?;
407 let export_dir = cx.files().export_dir();
408 let package_id = unit.pkg.package_id();
409 let profile = unit.profile;
410 let unit_mode = unit.mode;
411 let features = unit.features.iter().map(|s| s.to_string()).collect();
412 let json_messages = bcx.build_config.emit_json();
413 let executable = cx.get_executable(unit)?;
414 let mut target = unit.target.clone();
415 if let TargetSourcePath::Metabuild = target.src_path() {
416 let path = unit.pkg.manifest().metabuild_path(cx.bcx.ws.target_dir());
418 target.set_src_path(TargetSourcePath::Path(path));
419 }
420
421 Ok(Work::new(move |state| {
422 let mut destinations = vec![];
427 for output in outputs.iter() {
428 let src = &output.path;
429 if !src.exists() {
432 continue;
433 }
434 let dst = match output.hardlink.as_ref() {
435 Some(dst) => dst,
436 None => {
437 destinations.push(src.clone());
438 continue;
439 }
440 };
441 destinations.push(dst.clone());
442 paths::link_or_copy(src, dst)?;
443 if let Some(ref path) = output.export_path {
444 let export_dir = export_dir.as_ref().unwrap();
445 paths::create_dir_all(export_dir)?;
446
447 paths::link_or_copy(src, path)?;
448 }
449 }
450
451 if json_messages {
452 let art_profile = machine_message::ArtifactProfile {
453 opt_level: profile.opt_level.as_str(),
454 debuginfo: profile.debuginfo,
455 debug_assertions: profile.debug_assertions,
456 overflow_checks: profile.overflow_checks,
457 test: unit_mode.is_any_test(),
458 };
459
460 let msg = machine_message::Artifact {
461 package_id,
462 target: &target,
463 profile: art_profile,
464 features,
465 filenames: destinations,
466 executable,
467 fresh,
468 }
469 .to_json_string();
470 state.stdout(msg);
471 }
472 Ok(())
473 }))
474}
475
476fn add_plugin_deps(
480 rustc: &mut ProcessBuilder,
481 build_script_outputs: &BuildScriptOutputs,
482 build_scripts: &BuildScripts,
483 root_output: &PathBuf,
484) -> CargoResult<()> {
485 let var = util::dylib_path_envvar();
486 let search_path = rustc.get_env(var).unwrap_or_default();
487 let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
488 for (pkg_id, metadata) in &build_scripts.plugins {
489 let output = build_script_outputs
490 .get(*pkg_id, *metadata)
491 .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
492 search_path.append(&mut filter_dynamic_search_path(
493 output.library_paths.iter(),
494 root_output,
495 ));
496 }
497 let search_path = join_paths(&search_path, var)?;
498 rustc.env(var, &search_path);
499 Ok(())
500}
501
502fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &PathBuf) -> Vec<PathBuf>
508where
509 I: Iterator<Item = &'a PathBuf>,
510{
511 let mut search_path = vec![];
512 for dir in paths {
513 let dir = match dir.to_str() {
514 Some(s) => {
515 let mut parts = s.splitn(2, '=');
516 match (parts.next(), parts.next()) {
517 (Some("native"), Some(path))
518 | (Some("crate"), Some(path))
519 | (Some("dependency"), Some(path))
520 | (Some("framework"), Some(path))
521 | (Some("all"), Some(path)) => path.into(),
522 _ => dir.clone(),
523 }
524 }
525 None => dir.clone(),
526 };
527 if dir.starts_with(&root_output) {
528 search_path.push(dir);
529 } else {
530 debug!(
531 "Not including path {} in runtime library search path because it is \
532 outside target root {}",
533 dir.display(),
534 root_output.display()
535 );
536 }
537 }
538 search_path
539}
540
541fn prepare_rustc<'a, 'cfg>(
542 cx: &mut Context<'a, 'cfg>,
543 crate_types: &[&str],
544 unit: &Unit<'a>,
545) -> CargoResult<ProcessBuilder> {
546 let is_primary = cx.is_primary_package(unit);
547 let is_workspace = cx.bcx.ws.is_member(unit.pkg);
548
549 let mut base = cx
550 .compilation
551 .rustc_process(unit.pkg, is_primary, is_workspace)?;
552 if cx.bcx.config.cli_unstable().jobserver_per_rustc {
553 let client = cx.new_jobserver()?;
554 base.inherit_jobserver(&client);
555 base.arg("-Zjobserver-token-requests");
556 assert!(cx.rustc_clients.insert(*unit, client).is_none());
557 } else {
558 base.inherit_jobserver(&cx.jobserver);
559 }
560 build_base_args(cx, &mut base, unit, crate_types)?;
561 build_deps_args(&mut base, cx, unit)?;
562 Ok(base)
563}
564
565fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult<Work> {
566 let bcx = cx.bcx;
567 let mut rustdoc = cx.compilation.rustdoc_process(unit.pkg, unit.target)?;
568 rustdoc.inherit_jobserver(&cx.jobserver);
569 rustdoc.arg("--crate-name").arg(&unit.target.crate_name());
570 add_path_args(bcx, unit, &mut rustdoc);
571 add_cap_lints(bcx, unit, &mut rustdoc);
572
573 if let CompileKind::Target(target) = unit.kind {
574 rustdoc.arg("--target").arg(target.rustc_target());
575 }
576
577 let doc_dir = cx.files().out_dir(unit);
578
579 paths::create_dir_all(&doc_dir)?;
583
584 rustdoc.arg("-o").arg(doc_dir);
585
586 for feat in &unit.features {
587 rustdoc.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
588 }
589
590 add_error_format_and_color(cx, &mut rustdoc, false)?;
591
592 if let Some(args) = bcx.extra_args_for(unit) {
593 rustdoc.args(args);
594 }
595
596 build_deps_args(&mut rustdoc, cx, unit)?;
597
598 rustdoc.args(bcx.rustdocflags_args(unit));
599
600 add_crate_versions_if_requested(bcx, unit, &mut rustdoc);
601
602 let name = unit.pkg.name().to_string();
603 let build_script_outputs = Arc::clone(&cx.build_script_outputs);
604 let package_id = unit.pkg.package_id();
605 let target = unit.target.clone();
606 let mut output_options = OutputOptions::new(cx, unit);
607 let pkg_id = unit.pkg.package_id();
608 let script_metadata = cx.find_build_script_metadata(*unit);
609
610 Ok(Work::new(move |state| {
611 if let Some(script_metadata) = script_metadata {
612 if let Some(output) = build_script_outputs
613 .lock()
614 .unwrap()
615 .get(pkg_id, script_metadata)
616 {
617 for cfg in output.cfgs.iter() {
618 rustdoc.arg("--cfg").arg(cfg);
619 }
620 for &(ref name, ref value) in output.env.iter() {
621 rustdoc.env(name, value);
622 }
623 }
624 }
625 state.running(&rustdoc);
626
627 rustdoc
628 .exec_with_streaming(
629 &mut |line| on_stdout_line(state, line, package_id, &target),
630 &mut |line| on_stderr_line(state, line, package_id, &target, &mut output_options),
631 false,
632 )
633 .chain_err(|| format!("Could not document `{}`.", name))?;
634 Ok(())
635 }))
636}
637
638fn add_crate_versions_if_requested<'a>(
639 bcx: &BuildContext<'a, '_>,
640 unit: &Unit<'a>,
641 rustdoc: &mut ProcessBuilder,
642) {
643 if bcx.config.cli_unstable().crate_versions && !crate_version_flag_already_present(rustdoc) {
644 append_crate_version_flag(unit, rustdoc);
645 }
646}
647
648fn crate_version_flag_already_present(rustdoc: &ProcessBuilder) -> bool {
651 rustdoc.get_args().iter().any(|flag| {
652 flag.to_str()
653 .map_or(false, |flag| flag.starts_with(RUSTDOC_CRATE_VERSION_FLAG))
654 })
655}
656
657fn append_crate_version_flag(unit: &Unit<'_>, rustdoc: &mut ProcessBuilder) {
658 rustdoc
659 .arg(RUSTDOC_CRATE_VERSION_FLAG)
660 .arg(unit.pkg.version().to_string());
661}
662
663fn path_args(bcx: &BuildContext<'_, '_>, unit: &Unit<'_>) -> (PathBuf, PathBuf) {
678 let ws_root = bcx.ws.root();
679 let src = match unit.target.src_path() {
680 TargetSourcePath::Path(path) => path.to_path_buf(),
681 TargetSourcePath::Metabuild => unit.pkg.manifest().metabuild_path(bcx.ws.target_dir()),
682 };
683 assert!(src.is_absolute());
684 if unit.pkg.package_id().source_id().is_path() {
685 if let Ok(path) = src.strip_prefix(ws_root) {
686 return (path.to_path_buf(), ws_root.to_path_buf());
687 }
688 }
689 (src, unit.pkg.root().to_path_buf())
690}
691
692fn add_path_args(bcx: &BuildContext<'_, '_>, unit: &Unit<'_>, cmd: &mut ProcessBuilder) {
693 let (arg, cwd) = path_args(bcx, unit);
694 cmd.arg(arg);
695 cmd.cwd(cwd);
696}
697
698fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit<'_>, cmd: &mut ProcessBuilder) {
699 if !bcx.show_warnings(unit.pkg.package_id()) {
702 cmd.arg("--cap-lints").arg("allow");
703
704 } else if !unit.pkg.package_id().source_id().is_path() {
707 cmd.arg("--cap-lints").arg("warn");
708 }
709}
710
711fn add_error_format_and_color(
720 cx: &Context<'_, '_>,
721 cmd: &mut ProcessBuilder,
722 pipelined: bool,
723) -> CargoResult<()> {
724 cmd.arg("--error-format=json");
725 let mut json = String::from("--json=diagnostic-rendered-ansi");
726 if pipelined {
727 json.push_str(",artifacts");
730 }
731 match cx.bcx.build_config.message_format {
732 MessageFormat::Short | MessageFormat::Json { short: true, .. } => {
733 json.push_str(",diagnostic-short");
734 }
735 _ => {}
736 }
737 cmd.arg(json);
738 Ok(())
739}
740
741fn build_base_args<'a, 'cfg>(
742 cx: &mut Context<'a, 'cfg>,
743 cmd: &mut ProcessBuilder,
744 unit: &Unit<'a>,
745 crate_types: &[&str],
746) -> CargoResult<()> {
747 assert!(!unit.mode.is_run_custom_build());
748
749 let bcx = cx.bcx;
750 let Profile {
751 ref opt_level,
752 ref lto,
753 codegen_units,
754 debuginfo,
755 debug_assertions,
756 overflow_checks,
757 rpath,
758 ref panic,
759 incremental,
760 ..
761 } = unit.profile;
762 let test = unit.mode.is_any_test();
763
764 cmd.arg("--crate-name").arg(&unit.target.crate_name());
765
766 let edition = unit.target.edition();
767 if edition != Edition::Edition2015 {
768 cmd.arg(format!("--edition={}", edition));
769 }
770
771 add_path_args(bcx, unit, cmd);
772 add_error_format_and_color(cx, cmd, cx.rmeta_required(unit))?;
773
774 if !test {
775 for crate_type in crate_types.iter() {
776 cmd.arg("--crate-type").arg(crate_type);
777 }
778 }
779
780 if unit.mode.is_check() {
781 cmd.arg("--emit=dep-info,metadata");
782 } else if !unit.requires_upstream_objects() {
783 cmd.arg("--emit=dep-info,metadata,link");
787 } else {
788 cmd.arg("--emit=dep-info,link");
789 }
790
791 let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
792 || (crate_types.contains(&"dylib") && bcx.ws.members().any(|p| p != unit.pkg));
793 if prefer_dynamic {
794 cmd.arg("-C").arg("prefer-dynamic");
795 }
796
797 if opt_level.as_str() != "0" {
798 cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
799 }
800
801 if *panic != PanicStrategy::Unwind {
802 cmd.arg("-C").arg(format!("panic={}", panic));
803 }
804
805 if unit.target.can_lto() && !unit.target.for_host() {
808 match *lto {
809 Lto::Bool(false) => {}
810 Lto::Bool(true) => {
811 cmd.args(&["-C", "lto"]);
812 }
813 Lto::Named(ref s) => {
814 cmd.arg("-C").arg(format!("lto={}", s));
815 }
816 }
817 }
818
819 if let Some(n) = codegen_units {
820 cmd.arg("-C").arg(&format!("codegen-units={}", n));
823 }
824
825 if let Some(debuginfo) = debuginfo {
826 cmd.arg("-C").arg(format!("debuginfo={}", debuginfo));
827 }
828
829 if let Some(args) = bcx.extra_args_for(unit) {
830 cmd.args(args);
831 }
832
833 if opt_level.as_str() != "0" {
837 if debug_assertions {
838 cmd.args(&["-C", "debug-assertions=on"]);
839 if !overflow_checks {
840 cmd.args(&["-C", "overflow-checks=off"]);
841 }
842 } else if overflow_checks {
843 cmd.args(&["-C", "overflow-checks=on"]);
844 }
845 } else if !debug_assertions {
846 cmd.args(&["-C", "debug-assertions=off"]);
847 if overflow_checks {
848 cmd.args(&["-C", "overflow-checks=on"]);
849 }
850 } else if !overflow_checks {
851 cmd.args(&["-C", "overflow-checks=off"]);
852 }
853
854 if test && unit.target.harness() {
855 cmd.arg("--test");
856
857 if *panic == PanicStrategy::Abort {
865 cmd.arg("-Zpanic-abort-tests");
866 }
867 } else if test {
868 cmd.arg("--cfg").arg("test");
869 }
870
871 for feat in &unit.features {
872 cmd.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
873 }
874
875 match cx.files().metadata(unit) {
876 Some(m) => {
877 cmd.arg("-C").arg(&format!("metadata={}", m));
878 cmd.arg("-C").arg(&format!("extra-filename=-{}", m));
879 }
880 None => {
881 cmd.arg("-C")
882 .arg(&format!("metadata={}", cx.files().target_short_hash(unit)));
883 }
884 }
885
886 if rpath {
887 cmd.arg("-C").arg("rpath");
888 }
889
890 cmd.arg("--out-dir").arg(&cx.files().out_dir(unit));
891
892 fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) {
893 if let Some(val) = val {
894 let mut joined = OsString::from(prefix);
895 joined.push(val);
896 cmd.arg(key).arg(joined);
897 }
898 }
899
900 if let CompileKind::Target(n) = unit.kind {
901 cmd.arg("--target").arg(n.rustc_target());
902 }
903
904 opt(
905 cmd,
906 "-C",
907 "linker=",
908 bcx.linker(unit.kind).as_ref().map(|s| s.as_ref()),
909 );
910 if incremental {
911 let dir = cx.files().layout(unit.kind).incremental().as_os_str();
912 opt(cmd, "-C", "incremental=", Some(dir));
913 }
914
915 if unit.is_std {
916 cmd.arg("-Zforce-unstable-if-unmarked")
922 .env("RUSTC_BOOTSTRAP", "1");
923 }
924
925 if unit.target.is_test() || unit.target.is_bench() {
927 for bin_target in unit
928 .pkg
929 .manifest()
930 .targets()
931 .iter()
932 .filter(|target| target.is_bin())
933 {
934 let exe_path = cx
935 .files()
936 .bin_link_for_target(bin_target, unit.kind, cx.bcx)?;
937 let key = format!("CARGO_BIN_EXE_{}", bin_target.name());
938 cmd.env(&key, exe_path);
939 }
940 }
941 Ok(())
942}
943
944fn build_deps_args<'a, 'cfg>(
945 cmd: &mut ProcessBuilder,
946 cx: &mut Context<'a, 'cfg>,
947 unit: &Unit<'a>,
948) -> CargoResult<()> {
949 let bcx = cx.bcx;
950 cmd.arg("-L").arg(&{
951 let mut deps = OsString::from("dependency=");
952 deps.push(cx.files().deps_dir(unit));
953 deps
954 });
955
956 if !unit.kind.is_host() {
959 cmd.arg("-L").arg(&{
960 let mut deps = OsString::from("dependency=");
961 deps.push(cx.files().host_deps());
962 deps
963 });
964 }
965
966 let deps = cx.unit_deps(unit);
967
968 if !deps
972 .iter()
973 .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.linkable())
974 {
975 if let Some(dep) = deps
976 .iter()
977 .find(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_lib())
978 {
979 bcx.config.shell().warn(format!(
980 "The package `{}` \
981 provides no linkable target. The compiler might raise an error while compiling \
982 `{}`. Consider adding 'dylib' or 'rlib' to key `crate-type` in `{}`'s \
983 Cargo.toml. This warning might turn into a hard error in the future.",
984 dep.unit.target.crate_name(),
985 unit.target.crate_name(),
986 dep.unit.target.crate_name()
987 ))?;
988 }
989 }
990
991 let mut unstable_opts = false;
992
993 for dep in deps {
994 if dep.unit.mode.is_run_custom_build() {
995 cmd.env("OUT_DIR", &cx.files().build_script_out_dir(&dep.unit));
996 }
997 }
998
999 for arg in extern_args(cx, unit, &mut unstable_opts)? {
1000 cmd.arg(arg);
1001 }
1002
1003 if unstable_opts {
1006 cmd.arg("-Z").arg("unstable-options");
1007 }
1008
1009 Ok(())
1010}
1011
1012pub fn extern_args<'a>(
1014 cx: &Context<'a, '_>,
1015 unit: &Unit<'a>,
1016 unstable_opts: &mut bool,
1017) -> CargoResult<Vec<OsString>> {
1018 let mut result = Vec::new();
1019 let deps = cx.unit_deps(unit);
1020
1021 let mut link_to = |dep: &UnitDep<'a>,
1023 extern_crate_name: InternedString,
1024 noprelude: bool|
1025 -> CargoResult<()> {
1026 let mut value = OsString::new();
1027 let mut opts = Vec::new();
1028 if unit
1029 .pkg
1030 .manifest()
1031 .features()
1032 .require(Feature::public_dependency())
1033 .is_ok()
1034 && !dep.public
1035 {
1036 opts.push("priv");
1037 *unstable_opts = true;
1038 }
1039 if noprelude {
1040 opts.push("noprelude");
1041 *unstable_opts = true;
1042 }
1043 if !opts.is_empty() {
1044 value.push(opts.join(","));
1045 value.push(":");
1046 }
1047 value.push(extern_crate_name.as_str());
1048 value.push("=");
1049
1050 let mut pass = |file| {
1051 let mut value = value.clone();
1052 value.push(file);
1053 result.push(OsString::from("--extern"));
1054 result.push(value);
1055 };
1056
1057 let outputs = cx.outputs(&dep.unit)?;
1058 let mut outputs = outputs.iter().filter_map(|output| match output.flavor {
1059 FileFlavor::Linkable { rmeta } => Some((output, rmeta)),
1060 _ => None,
1061 });
1062
1063 if cx.only_requires_rmeta(unit, &dep.unit) {
1064 let (output, _rmeta) = outputs
1065 .find(|(_output, rmeta)| *rmeta)
1066 .expect("failed to find rlib dep for pipelined dep");
1067 pass(&output.path);
1068 } else {
1069 for (output, rmeta) in outputs {
1070 if !rmeta {
1071 pass(&output.path);
1072 }
1073 }
1074 }
1075 Ok(())
1076 };
1077
1078 for dep in deps {
1079 if dep.unit.target.linkable() && !dep.unit.mode.is_doc() {
1080 link_to(dep, dep.extern_crate_name, dep.noprelude)?;
1081 }
1082 }
1083 if unit.target.proc_macro() {
1084 result.push(OsString::from("--extern"));
1086 result.push(OsString::from("proc_macro"));
1087 }
1088
1089 Ok(result)
1090}
1091
1092fn envify(s: &str) -> String {
1093 s.chars()
1094 .flat_map(|c| c.to_uppercase())
1095 .map(|c| if c == '-' { '_' } else { c })
1096 .collect()
1097}
1098
1099struct OutputOptions {
1100 format: MessageFormat,
1102 look_for_metadata_directive: bool,
1105 color: bool,
1107 cache_cell: Option<(PathBuf, LazyCell<File>)>,
1112}
1113
1114impl OutputOptions {
1115 fn new<'a>(cx: &Context<'a, '_>, unit: &Unit<'a>) -> OutputOptions {
1116 let look_for_metadata_directive = cx.rmeta_required(unit);
1117 let color = cx.bcx.config.shell().supports_color();
1118 let path = cx.files().message_cache_path(unit);
1119 drop(fs::remove_file(&path));
1121 let cache_cell = Some((path, LazyCell::new()));
1122 OutputOptions {
1123 format: cx.bcx.build_config.message_format,
1124 look_for_metadata_directive,
1125 color,
1126 cache_cell,
1127 }
1128 }
1129}
1130
1131fn on_stdout_line(
1132 state: &JobState<'_>,
1133 line: &str,
1134 _package_id: PackageId,
1135 _target: &Target,
1136) -> CargoResult<()> {
1137 state.stdout(line.to_string());
1138 Ok(())
1139}
1140
1141fn on_stderr_line(
1142 state: &JobState<'_>,
1143 line: &str,
1144 package_id: PackageId,
1145 target: &Target,
1146 options: &mut OutputOptions,
1147) -> CargoResult<()> {
1148 if on_stderr_line_inner(state, line, package_id, target, options)? {
1149 if let Some((path, cell)) = &mut options.cache_cell {
1151 let f = cell.try_borrow_mut_with(|| File::create(path))?;
1153 debug_assert!(!line.contains('\n'));
1154 f.write_all(line.as_bytes())?;
1155 f.write_all(&[b'\n'])?;
1156 }
1157 }
1158 Ok(())
1159}
1160
1161fn on_stderr_line_inner(
1163 state: &JobState<'_>,
1164 line: &str,
1165 package_id: PackageId,
1166 target: &Target,
1167 options: &mut OutputOptions,
1168) -> CargoResult<bool> {
1169 if !line.starts_with('{') {
1175 state.stderr(line.to_string());
1176 return Ok(true);
1177 }
1178
1179 let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
1180 Ok(msg) => msg,
1181
1182 Err(e) => {
1186 debug!("failed to parse json: {:?}", e);
1187 state.stderr(line.to_string());
1188 return Ok(true);
1189 }
1190 };
1191
1192 match options.format {
1195 MessageFormat::Human
1200 | MessageFormat::Short
1201 | MessageFormat::Json {
1202 render_diagnostics: true,
1203 ..
1204 } => {
1205 #[derive(serde::Deserialize)]
1206 struct CompilerMessage {
1207 rendered: String,
1208 }
1209 if let Ok(mut error) = serde_json::from_str::<CompilerMessage>(compiler_message.get()) {
1210 if error.rendered.ends_with('\n') {
1212 error.rendered.pop();
1213 }
1214 let rendered = if options.color {
1215 error.rendered
1216 } else {
1217 strip_ansi_escapes::strip(&error.rendered)
1220 .map(|v| String::from_utf8(v).expect("utf8"))
1221 .expect("strip should never fail")
1222 };
1223 state.stderr(rendered);
1224 return Ok(true);
1225 }
1226 }
1227
1228 MessageFormat::Json { ansi: false, .. } => {
1232 #[derive(serde::Deserialize, serde::Serialize)]
1233 struct CompilerMessage {
1234 rendered: String,
1235 #[serde(flatten)]
1236 other: std::collections::BTreeMap<String, serde_json::Value>,
1237 }
1238 if let Ok(mut error) = serde_json::from_str::<CompilerMessage>(compiler_message.get()) {
1239 error.rendered = strip_ansi_escapes::strip(&error.rendered)
1240 .map(|v| String::from_utf8(v).expect("utf8"))
1241 .unwrap_or(error.rendered);
1242 let new_line = serde_json::to_string(&error)?;
1243 let new_msg: Box<serde_json::value::RawValue> = serde_json::from_str(&new_line)?;
1244 compiler_message = new_msg;
1245 }
1246 }
1247
1248 MessageFormat::Json { ansi: true, .. } => {}
1251 }
1252
1253 if options.look_for_metadata_directive {
1262 #[derive(serde::Deserialize)]
1263 struct ArtifactNotification {
1264 artifact: String,
1265 }
1266 if let Ok(artifact) = serde_json::from_str::<ArtifactNotification>(compiler_message.get()) {
1267 log::trace!("found directive from rustc: `{}`", artifact.artifact);
1268 if artifact.artifact.ends_with(".rmeta") {
1269 log::debug!("looks like metadata finished early!");
1270 state.rmeta_produced();
1271 }
1272 return Ok(false);
1273 }
1274 }
1275
1276 #[derive(serde::Deserialize)]
1277 struct JobserverNotification {
1278 jobserver_event: Event,
1279 }
1280
1281 #[derive(Debug, serde::Deserialize)]
1282 enum Event {
1283 WillAcquire,
1284 Release,
1285 }
1286
1287 if let Ok(JobserverNotification { jobserver_event }) =
1288 serde_json::from_str::<JobserverNotification>(compiler_message.get())
1289 {
1290 log::info!(
1291 "found jobserver directive from rustc: `{:?}`",
1292 jobserver_event
1293 );
1294 match jobserver_event {
1295 Event::WillAcquire => state.will_acquire(),
1296 Event::Release => state.release_token(),
1297 }
1298 return Ok(false);
1299 }
1300
1301 let msg = machine_message::FromCompiler {
1305 package_id,
1306 target,
1307 message: compiler_message,
1308 }
1309 .to_json_string();
1310
1311 state.stdout(msg);
1315 Ok(true)
1316}
1317
1318fn replay_output_cache(
1319 package_id: PackageId,
1320 target: &Target,
1321 path: PathBuf,
1322 format: MessageFormat,
1323 color: bool,
1324) -> Work {
1325 let target = target.clone();
1326 let mut options = OutputOptions {
1327 format,
1328 look_for_metadata_directive: true,
1329 color,
1330 cache_cell: None,
1331 };
1332 Work::new(move |state| {
1333 if !path.exists() {
1334 return Ok(());
1336 }
1337 let file = fs::File::open(&path)?;
1341 let mut reader = std::io::BufReader::new(file);
1342 let mut line = String::new();
1343 loop {
1344 let length = reader.read_line(&mut line)?;
1345 if length == 0 {
1346 break;
1347 }
1348 let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
1349 on_stderr_line(state, trimmed, package_id, &target, &mut options)?;
1350 line.clear();
1351 }
1352 Ok(())
1353 })
1354}