]> git.proxmox.com Git - cargo.git/blame - src/cargo/core/compiler/mod.rs
Fix BuildScriptOutput when a build script is run multiple times.
[cargo.git] / src / cargo / core / compiler / mod.rs
CommitLineData
f7c91ba6
AR
1mod build_config;
2mod build_context;
3mod build_plan;
4mod compilation;
ef425b77 5mod compile_kind;
f7c91ba6
AR
6mod context;
7mod custom_build;
8mod fingerprint;
9mod job;
10mod job_queue;
11mod layout;
1f14fa31 12mod links;
f7c91ba6 13mod output_depinfo;
1f14fa31 14pub mod standard_lib;
06644845 15mod timings;
e4544466 16mod unit;
1f14fa31 17pub mod unit_dependencies;
f7c91ba6 18
a6dad622 19use std::env;
5a04a4d4 20use std::ffi::{OsStr, OsString};
dcd4999d 21use std::fs::{self, File};
5e64197f 22use std::io::{BufRead, Write};
674150e4 23use std::path::PathBuf;
55321111 24use std::sync::Arc;
c2b23512 25
3a18c89a 26use anyhow::Error;
dcd4999d 27use lazycell::LazyCell;
9ed82b57 28use log::debug;
9f98208d 29
77ee608d 30pub use self::build_config::{BuildConfig, CompileMode, MessageFormat};
381251aa 31pub use self::build_context::{BuildContext, FileFlavor, TargetInfo};
f7c91ba6 32use self::build_plan::BuildPlan;
812fba62 33pub use self::compilation::{Compilation, Doctest};
ef425b77 34pub use self::compile_kind::{CompileKind, CompileTarget};
2296af27 35pub use self::context::{Context, Metadata};
bd31c081 36pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts};
e9428cba 37pub use self::job::Freshness;
e4544466 38use self::job::{Job, Work};
1a6e4524 39use self::job_queue::{JobQueue, JobState};
6633e290 40pub use self::layout::is_bad_artifact_name;
f7c91ba6 41use self::output_depinfo::output_depinfo;
1f14fa31 42use self::unit_dependencies::UnitDep;
e4544466 43pub use crate::core::compiler::unit::{Unit, UnitInterner};
0389edc6
JL
44use crate::core::manifest::TargetSourcePath;
45use crate::core::profiles::{Lto, PanicStrategy, Profile};
9aa65c55 46use crate::core::{Edition, Feature, InternedString, PackageId, Target};
78b14005 47use crate::util::errors::{self, CargoResult, CargoResultExt, Internal, ProcessError};
6b28a0c0 48use crate::util::machine_message::Message;
ed1b61ca 49use crate::util::{self, machine_message, ProcessBuilder};
381251aa 50use crate::util::{internal, join_paths, paths, profile};
ef28cd2d 51
8c3b3605 52/// A glorified callback for executing calls to rustc. Rather than calling rustc
f7c91ba6 53/// directly, we'll use an `Executor`, giving clients an opportunity to intercept
8c3b3605 54/// the build calls.
1ab19a5a 55pub trait Executor: Send + Sync + 'static {
2fd78db3
IM
56 /// Called after a rustc process invocation is prepared up-front for a given
57 /// unit of work (may still be modified for runtime-known dependencies, when
58 /// the work is actually executed).
1212c91a 59 fn init<'a, 'cfg>(&self, _cx: &Context<'a, 'cfg>, _unit: &Unit<'a>) {}
2fd78db3
IM
60
61 /// In case of an `Err`, Cargo will not continue with the build process for
62 /// this package.
90a5ba3c 63 fn exec(
641f7ff2 64 &self,
65 cmd: ProcessBuilder,
dae87a26 66 id: PackageId,
641f7ff2 67 target: &Target,
68 mode: CompileMode,
1a6e4524
AC
69 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
70 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
71 ) -> CargoResult<()>;
c7b2df1f
NC
72
73 /// Queried when queuing each unit of work. If it returns true, then the
74 /// unit will always be rebuilt, independent of whether it needs to be.
b8b7faee 75 fn force_rebuild(&self, _unit: &Unit<'_>) -> bool {
c7b2df1f
NC
76 false
77 }
8c3b3605
NC
78}
79
23591fe5 80/// A `DefaultExecutor` calls rustc without doing anything else. It is Cargo's
8c3b3605
NC
81/// default behaviour.
82#[derive(Copy, Clone)]
83pub struct DefaultExecutor;
84
641f7ff2 85impl Executor for DefaultExecutor {
1a6e4524 86 fn exec(
641f7ff2 87 &self,
88 cmd: ProcessBuilder,
dae87a26 89 _id: PackageId,
641f7ff2 90 _target: &Target,
91 _mode: CompileMode,
1a6e4524
AC
92 on_stdout_line: &mut dyn FnMut(&str) -> CargoResult<()>,
93 on_stderr_line: &mut dyn FnMut(&str) -> CargoResult<()>,
641f7ff2 94 ) -> CargoResult<()> {
1a6e4524
AC
95 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
96 .map(drop)
641f7ff2 97 }
98}
8c3b3605 99
1e682848
AC
100fn compile<'a, 'cfg: 'a>(
101 cx: &mut Context<'a, 'cfg>,
78ba9486 102 jobs: &mut JobQueue<'a, 'cfg>,
72e6b9d3 103 plan: &mut BuildPlan,
1e682848 104 unit: &Unit<'a>,
b8b7faee 105 exec: &Arc<dyn Executor>,
3db193ea 106 force_rebuild: bool,
1e682848 107) -> CargoResult<()> {
c32e395c 108 let bcx = cx.bcx;
72e6b9d3 109 let build_plan = bcx.build_config.build_plan;
659f8244 110 if !cx.compiled.insert(*unit) {
1e682848 111 return Ok(());
659f8244 112 }
9e779198 113
659f8244
AC
114 // Build up the work to be done to compile this unit, enqueuing it once
115 // we've got everything constructed.
1e682848 116 let p = profile::start(format!("preparing: {}/{}", unit.pkg, unit.target.name()));
82655b46 117 fingerprint::prepare_init(cx, unit)?;
659f8244 118
e9428cba 119 let job = if unit.mode.is_run_custom_build() {
82655b46 120 custom_build::prepare(cx, unit)?
935adb31 121 } else if unit.mode.is_doc_test() {
f7c91ba6 122 // We run these targets later, so this is just a no-op for now.
e9428cba 123 Job::new(Work::noop(), Freshness::Fresh)
72e6b9d3 124 } else if build_plan {
e9428cba 125 Job::new(rustc(cx, unit, &exec.clone())?, Freshness::Dirty)
659f8244 126 } else {
e9428cba
AC
127 let force = exec.force_rebuild(unit) || force_rebuild;
128 let mut job = fingerprint::prepare_target(cx, unit, force)?;
129 job.before(if job.freshness() == Freshness::Dirty {
130 let work = if unit.mode.is_doc() {
131 rustdoc(cx, unit)?
132 } else {
133 rustc(cx, unit, exec)?
134 };
135 work.then(link_targets(cx, unit, false)?)
3f110840 136 } else {
bd73e8da 137 let work = if cx.bcx.show_warnings(unit.pkg.package_id()) {
dcd4999d
EH
138 replay_output_cache(
139 unit.pkg.package_id(),
140 unit.target,
141 cx.files().message_cache_path(unit),
142 cx.bcx.build_config.message_format,
143 cx.bcx.config.shell().supports_color(),
144 )
145 } else {
146 Work::noop()
147 };
e9428cba 148 // Need to link targets on both the dirty and fresh.
dcd4999d 149 work.then(link_targets(cx, unit, true)?)
e9428cba 150 });
c7b2df1f 151
e9428cba 152 job
659f8244 153 };
e9428cba 154 jobs.enqueue(cx, unit, job)?;
659f8244 155 drop(p);
79768eb0 156
659f8244 157 // Be sure to compile all dependencies of this target as well.
cc9f5a87
EH
158 let deps = Vec::from(cx.unit_deps(unit)); // Create vec due to mutable borrow.
159 for dep in deps {
160 compile(cx, jobs, plan, &dep.unit, exec, false)?;
72e6b9d3
MS
161 }
162 if build_plan {
163 plan.add(cx, unit)?;
9e779198 164 }
4b82fdc0 165
2bcf2926 166 Ok(())
d70ce6b6 167}
5d7ece5b 168
1e682848 169fn rustc<'a, 'cfg>(
7691deb3 170 cx: &mut Context<'a, 'cfg>,
1e682848 171 unit: &Unit<'a>,
b8b7faee 172 exec: &Arc<dyn Executor>,
1e682848 173) -> CargoResult<Work> {
6117f526 174 let mut rustc = prepare_rustc(cx, &unit.target.rustc_crate_types(), unit)?;
72e6b9d3 175 let build_plan = cx.bcx.build_config.build_plan;
659f8244
AC
176
177 let name = unit.pkg.name().to_string();
72e6b9d3 178 let buildkey = unit.buildkey();
684790f8 179
c3b477d4 180 add_cap_lints(cx.bcx, unit, &mut rustc);
9e779198 181
7bfddeeb 182 let outputs = cx.outputs(unit)?;
cf0bb13d 183 let root = cx.files().out_dir(unit);
9e779198 184
f7c91ba6 185 // Prepare the native lib state (extra `-L` and `-l` flags).
bd31c081 186 let build_script_outputs = Arc::clone(&cx.build_script_outputs);
dae87a26 187 let current_id = unit.pkg.package_id();
bd31c081 188 let build_scripts = cx.build_scripts.get(unit).cloned();
4e6c3bd1 189
659f8244
AC
190 // If we are a binary and the package also contains a library, then we
191 // don't pass the `-l` flags.
1e682848 192 let pass_l_flag = unit.target.is_lib() || !unit.pkg.targets().iter().any(|t| t.is_lib());
f608ed61 193 let pass_cdylib_link_args = unit.target.is_cdylib();
575d6e81 194 let do_rename = unit.target.allows_underscores() && !unit.mode.is_any_test();
659f8244
AC
195 let real_name = unit.target.name().to_string();
196 let crate_name = unit.target.crate_name();
607b3d94 197
f7c91ba6 198 // Rely on `target_filenames` iterator as source of truth rather than rederiving filestem.
cf0bb13d 199 let rustc_dep_info_loc = if do_rename && cx.files().metadata(unit).is_none() {
659f8244
AC
200 root.join(&crate_name)
201 } else {
cf0bb13d 202 root.join(&cx.files().file_stem(unit))
dae87a26
E
203 }
204 .with_extension("d");
7691deb3 205 let dep_info_loc = fingerprint::dep_info_loc(cx, unit);
659f8244 206
bd8253fd 207 rustc.args(cx.bcx.rustflags_args(unit));
c6e626b3
EH
208 if cx.bcx.config.cli_unstable().binary_dep_depinfo {
209 rustc.arg("-Zbinary-dep-depinfo");
210 }
342f8608 211 let mut output_options = OutputOptions::new(cx, unit);
dae87a26 212 let package_id = unit.pkg.package_id();
9f98208d 213 let target = unit.target.clone();
90a5ba3c 214 let mode = unit.mode;
8c3b3605 215
23591fe5 216 exec.init(cx, unit);
24ba7c80 217 let exec = exec.clone();
8c3b3605 218
8df842f5 219 let root_output = cx.files().host_root().to_path_buf();
aa99e9f2 220 let target_dir = cx.bcx.ws.target_dir().into_path_unlocked();
f688e9c2 221 let pkg_root = unit.pkg.root().to_path_buf();
1e682848
AC
222 let cwd = rustc
223 .get_cwd()
c32e395c 224 .unwrap_or_else(|| cx.bcx.config.cwd())
1e682848 225 .to_path_buf();
035913ff 226 let fingerprint_dir = cx.files().fingerprint_dir(unit);
2296af27 227 let script_metadata = cx.find_build_script_metadata(*unit);
b8158cfd 228
26690d33 229 return Ok(Work::new(move |state| {
659f8244
AC
230 // Only at runtime have we discovered what the extra -L and -l
231 // arguments are for native libraries, so we process those here. We
232 // also need to be sure to add any -L paths for our plugins to the
233 // dynamic library load path as a plugin's dynamic library may be
234 // located somewhere in there.
8b744a05
KK
235 // Finally, if custom environment variables have been produced by
236 // previous build scripts, we include them in the rustc invocation.
bd31c081
EH
237 if let Some(build_scripts) = build_scripts {
238 let script_outputs = build_script_outputs.lock().unwrap();
72e6b9d3
MS
239 if !build_plan {
240 add_native_deps(
241 &mut rustc,
bd31c081
EH
242 &script_outputs,
243 &build_scripts,
72e6b9d3 244 pass_l_flag,
f608ed61 245 pass_cdylib_link_args,
dae87a26 246 current_id,
72e6b9d3 247 )?;
bd31c081 248 add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?;
72e6b9d3 249 }
2296af27 250 add_custom_env(&mut rustc, &script_outputs, current_id, script_metadata)?;
659f8244 251 }
f888b4b7 252
7bfddeeb 253 for output in outputs.iter() {
4b82fdc0
NC
254 // If there is both an rmeta and rlib, rustc will prefer to use the
255 // rlib, even if it is older. Therefore, we must delete the rlib to
256 // force using the new rmeta.
7bfddeeb
AK
257 if output.path.extension() == Some(OsStr::new("rmeta")) {
258 let dst = root.join(&output.path).with_extension("rlib");
8b68e590 259 if dst.exists() {
c933673e 260 paths::remove_file(&dst)?;
4b82fdc0 261 }
afe88e0b 262 }
659f8244 263 }
afe88e0b 264
b15dc1ac 265 fn internal_if_simple_exit_code(err: Error) -> Error {
f7c91ba6
AR
266 // If a signal on unix (`code == None`) or an abnormal termination
267 // on Windows (codes like `0xC0000409`), don't hide the error details.
6bc5e712 268 match err
b15dc1ac
EH
269 .downcast_ref::<ProcessError>()
270 .as_ref()
271 .and_then(|perr| perr.exit.and_then(|e| e.code()))
b15dc1ac 272 {
78b14005 273 Some(n) if errors::is_simple_exit_code(n) => Internal::new(err).into(),
6bc5e712 274 _ => err,
b15dc1ac 275 }
b15dc1ac
EH
276 }
277
26690d33 278 state.running(&rustc);
035913ff 279 let timestamp = paths::set_invocation_time(&fingerprint_dir)?;
1a6e4524
AC
280 if build_plan {
281 state.build_plan(buildkey, rustc.clone(), outputs.clone());
282 } else {
283 exec.exec(
1e682848 284 rustc,
dae87a26 285 package_id,
1e682848 286 &target,
90a5ba3c 287 mode,
1a6e4524 288 &mut |line| on_stdout_line(state, line, package_id, &target),
6117f526 289 &mut |line| on_stderr_line(state, line, package_id, &target, &mut output_options),
b15dc1ac
EH
290 )
291 .map_err(internal_if_simple_exit_code)
26229cd8 292 .chain_err(|| format!("could not compile `{}`.", name))?;
c5619cba 293 }
48420965 294
c5619cba 295 if do_rename && real_name != crate_name {
7bfddeeb 296 let dst = &outputs[0].path;
1e682848
AC
297 let src = dst.with_file_name(
298 dst.file_name()
299 .unwrap()
300 .to_str()
301 .unwrap()
302 .replace(&real_name, &crate_name),
303 );
54b968e5 304 if src.exists() && src.file_name() != dst.file_name() {
1e682848
AC
305 fs::rename(&src, &dst)
306 .chain_err(|| internal(format!("could not rename crate {:?}", src)))?;
8c3b3605 307 }
659f8244 308 }
f888b4b7 309
f688e9c2 310 if rustc_dep_info_loc.exists() {
34fd5cc8
MR
311 fingerprint::translate_dep_info(
312 &rustc_dep_info_loc,
313 &dep_info_loc,
314 &cwd,
315 &pkg_root,
aa99e9f2 316 &target_dir,
b1b9b79c
EH
317 // Do not track source files in the fingerprint for registry dependencies.
318 current_id.source_id().is_path(),
34fd5cc8
MR
319 )
320 .chain_err(|| {
321 internal(format!(
322 "could not parse/generate dep info at: {}",
323 rustc_dep_info_loc.display()
324 ))
325 })?;
eba43232 326 filetime::set_file_times(dep_info_loc, timestamp, timestamp)?;
c5619cba
NC
327 }
328
659f8244
AC
329 Ok(())
330 }));
50e00c97 331
f7c91ba6
AR
332 // Add all relevant `-L` and `-l` flags from dependencies (now calculated and
333 // present in `state`) to the command provided.
1e682848
AC
334 fn add_native_deps(
335 rustc: &mut ProcessBuilder,
bd31c081 336 build_script_outputs: &BuildScriptOutputs,
1e682848
AC
337 build_scripts: &BuildScripts,
338 pass_l_flag: bool,
f608ed61 339 pass_cdylib_link_args: bool,
dae87a26 340 current_id: PackageId,
1e682848 341 ) -> CargoResult<()> {
659f8244 342 for key in build_scripts.to_link.iter() {
2296af27 343 let output = build_script_outputs.get(key.0, key.1).ok_or_else(|| {
1e682848 344 internal(format!(
2296af27 345 "couldn't find build script output for {}/{}",
1e682848
AC
346 key.0, key.1
347 ))
82655b46 348 })?;
50e00c97 349 for path in output.library_paths.iter() {
a6dad622 350 rustc.arg("-L").arg(path);
50e00c97 351 }
dae87a26 352 if key.0 == current_id {
61a0ace4
AC
353 for cfg in &output.cfgs {
354 rustc.arg("--cfg").arg(cfg);
355 }
356 if pass_l_flag {
357 for name in output.library_links.iter() {
358 rustc.arg("-l").arg(name);
359 }
50e00c97 360 }
f608ed61 361 if pass_cdylib_link_args {
d426f124
LB
362 for arg in output.linker_args.iter() {
363 let link_arg = format!("link-arg={}", arg);
364 rustc.arg("-C").arg(link_arg);
365 }
55205ef8 366 }
50e00c97
AC
367 }
368 }
659f8244 369 Ok(())
50e00c97 370 }
8b744a05
KK
371
372 // Add all custom environment variables present in `state` (after they've
373 // been put there by one of the `build_scripts`) to the command provided.
1e682848
AC
374 fn add_custom_env(
375 rustc: &mut ProcessBuilder,
bd31c081 376 build_script_outputs: &BuildScriptOutputs,
dae87a26 377 current_id: PackageId,
2296af27 378 metadata: Option<Metadata>,
1e682848 379 ) -> CargoResult<()> {
2296af27
EH
380 let metadata = match metadata {
381 Some(metadata) => metadata,
382 None => return Ok(()),
383 };
384 if let Some(output) = build_script_outputs.get(current_id, metadata) {
f1ae9f84
KK
385 for &(ref name, ref value) in output.env.iter() {
386 rustc.env(name, value);
8b744a05
KK
387 }
388 }
389 Ok(())
390 }
50e00c97
AC
391}
392
23591fe5 393/// Link the compiled target (often of form `foo-{metadata_hash}`) to the
f7c91ba6 394/// final target. This must happen during both "Fresh" and "Compile".
1e682848
AC
395fn link_targets<'a, 'cfg>(
396 cx: &mut Context<'a, 'cfg>,
397 unit: &Unit<'a>,
398 fresh: bool,
399) -> CargoResult<Work> {
c32e395c 400 let bcx = cx.bcx;
7bfddeeb 401 let outputs = cx.outputs(unit)?;
d0c29393 402 let export_dir = cx.files().export_dir();
dae87a26 403 let package_id = unit.pkg.package_id();
73660740 404 let profile = unit.profile;
575d6e81 405 let unit_mode = unit.mode;
a7efa91e 406 let features = unit.features.iter().map(|s| s.to_string()).collect();
dcd4999d 407 let json_messages = bcx.build_config.emit_json();
282f238d 408 let executable = cx.get_executable(unit)?;
ecc87b17
EH
409 let mut target = unit.target.clone();
410 if let TargetSourcePath::Metabuild = target.src_path() {
411 // Give it something to serialize.
412 let path = unit.pkg.manifest().metabuild_path(cx.bcx.ws.target_dir());
413 target.set_src_path(TargetSourcePath::Path(path));
414 }
7bfd7dc7 415
1a6e4524 416 Ok(Work::new(move |state| {
f7c91ba6 417 // If we're a "root crate", e.g., the target of this compilation, then we
76ff7baa
NK
418 // hard link our outputs out of the `deps` directory into the directory
419 // above. This means that `cargo build` will produce binaries in
420 // `target/debug` which one probably expects.
7bfd7dc7 421 let mut destinations = vec![];
7bfddeeb
AK
422 for output in outputs.iter() {
423 let src = &output.path;
76ff7baa
NK
424 // This may have been a `cargo rustc` command which changes the
425 // output, so the source may not actually exist.
7bfd7dc7 426 if !src.exists() {
1e682848 427 continue;
76ff7baa 428 }
7bfddeeb 429 let dst = match output.hardlink.as_ref() {
7bfd7dc7
AK
430 Some(dst) => dst,
431 None => {
982b9e8f 432 destinations.push(src.clone());
7bfd7dc7
AK
433 continue;
434 }
435 };
982b9e8f 436 destinations.push(dst.clone());
674150e4 437 paths::link_or_copy(src, dst)?;
fa0787aa
EH
438 if let Some(ref path) = output.export_path {
439 let export_dir = export_dir.as_ref().unwrap();
5102de2b 440 paths::create_dir_all(export_dir)?;
fa0787aa 441
674150e4 442 paths::link_or_copy(src, path)?;
76ff7baa 443 }
76ff7baa 444 }
7bfd7dc7
AK
445
446 if json_messages {
575d6e81
EH
447 let art_profile = machine_message::ArtifactProfile {
448 opt_level: profile.opt_level.as_str(),
449 debuginfo: profile.debuginfo,
450 debug_assertions: profile.debug_assertions,
451 overflow_checks: profile.overflow_checks,
452 test: unit_mode.is_any_test(),
453 };
454
205bab90 455 let msg = machine_message::Artifact {
dae87a26 456 package_id,
7bfd7dc7 457 target: &target,
575d6e81 458 profile: art_profile,
0247dc42 459 features,
7bfd7dc7 460 filenames: destinations,
282f238d 461 executable,
0247dc42 462 fresh,
6b28a0c0
AC
463 }
464 .to_json_string();
dcd4999d 465 state.stdout(msg);
7bfd7dc7 466 }
76ff7baa
NK
467 Ok(())
468 }))
469}
470
bd31c081
EH
471// For all plugin dependencies, add their -L paths (now calculated and present
472// in `build_script_outputs`) to the dynamic library load path for the command
473// to execute.
1e682848
AC
474fn add_plugin_deps(
475 rustc: &mut ProcessBuilder,
bd31c081 476 build_script_outputs: &BuildScriptOutputs,
1e682848
AC
477 build_scripts: &BuildScripts,
478 root_output: &PathBuf,
479) -> CargoResult<()> {
540c5a77 480 let var = util::dylib_path_envvar();
23591fe5 481 let search_path = rustc.get_env(var).unwrap_or_default();
a6dad622 482 let mut search_path = env::split_paths(&search_path).collect::<Vec<_>>();
2296af27 483 for (pkg_id, metadata) in &build_scripts.plugins {
bd31c081 484 let output = build_script_outputs
2296af27
EH
485 .get(*pkg_id, *metadata)
486 .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?;
1e682848
AC
487 search_path.append(&mut filter_dynamic_search_path(
488 output.library_paths.iter(),
489 root_output,
490 ));
50e00c97 491 }
82655b46 492 let search_path = join_paths(&search_path, var)?;
a6dad622
AC
493 rustc.env(var, &search_path);
494 Ok(())
21322f07
YK
495}
496
b8158cfd
JM
497// Determine paths to add to the dynamic search path from -L entries
498//
499// Strip off prefixes like "native=" or "framework=" and filter out directories
f7c91ba6 500// **not** inside our output directory since they are likely spurious and can cause
b8158cfd 501// clashes with system shared libraries (issue #3366).
1e682848
AC
502fn filter_dynamic_search_path<'a, I>(paths: I, root_output: &PathBuf) -> Vec<PathBuf>
503where
504 I: Iterator<Item = &'a PathBuf>,
505{
b8158cfd
JM
506 let mut search_path = vec![];
507 for dir in paths {
508 let dir = match dir.to_str() {
509 Some(s) => {
510 let mut parts = s.splitn(2, '=');
511 match (parts.next(), parts.next()) {
1e682848
AC
512 (Some("native"), Some(path))
513 | (Some("crate"), Some(path))
514 | (Some("dependency"), Some(path))
515 | (Some("framework"), Some(path))
516 | (Some("all"), Some(path)) => path.into(),
b8158cfd
JM
517 _ => dir.clone(),
518 }
519 }
520 None => dir.clone(),
521 };
522 if dir.starts_with(&root_output) {
523 search_path.push(dir);
524 } else {
1e682848
AC
525 debug!(
526 "Not including path {} in runtime library search path because it is \
527 outside target root {}",
528 dir.display(),
529 root_output.display()
530 );
b8158cfd
JM
531 }
532 }
533 search_path
534}
535
1e682848
AC
536fn prepare_rustc<'a, 'cfg>(
537 cx: &mut Context<'a, 'cfg>,
538 crate_types: &[&str],
539 unit: &Unit<'a>,
6117f526 540) -> CargoResult<ProcessBuilder> {
46b48f6e 541 let is_primary = cx.is_primary_package(unit);
03feb61f 542
9aa65c55 543 let mut base = cx.compilation.rustc_process(unit.pkg, is_primary)?;
6117f526 544 if cx.bcx.config.cli_unstable().jobserver_per_rustc {
494b3c0a
MR
545 let client = cx.new_jobserver()?;
546 base.inherit_jobserver(&client);
547 base.arg("-Zjobserver-token-requests");
6117f526 548 assert!(cx.rustc_clients.insert(*unit, client).is_none());
494b3c0a
MR
549 } else {
550 base.inherit_jobserver(&cx.jobserver);
6117f526 551 }
45cc30bc 552 build_base_args(cx, &mut base, unit, crate_types)?;
82655b46 553 build_deps_args(&mut base, cx, unit)?;
6117f526 554 Ok(base)
5accea9f
CL
555}
556
1e682848 557fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult<Work> {
c32e395c 558 let bcx = cx.bcx;
d4ee7953 559 let mut rustdoc = cx.compilation.rustdoc_process(unit.pkg, unit.target)?;
cbf25a9b 560 rustdoc.inherit_jobserver(&cx.jobserver);
8647a87d 561 rustdoc.arg("--crate-name").arg(&unit.target.crate_name());
ecc87b17 562 add_path_args(bcx, unit, &mut rustdoc);
c3b477d4 563 add_cap_lints(bcx, unit, &mut rustdoc);
a4f58993 564
ef425b77
AC
565 if let CompileKind::Target(target) = unit.kind {
566 rustdoc.arg("--target").arg(target.rustc_target());
87c010b6 567 }
0b256d34 568
cf0bb13d 569 let doc_dir = cx.files().out_dir(unit);
2a6143ac
AC
570
571 // Create the documentation directory ahead of time as rustdoc currently has
572 // a bug where concurrent invocations will race to create this directory if
573 // it doesn't already exist.
5102de2b 574 paths::create_dir_all(&doc_dir)?;
2a6143ac 575
014765f7 576 rustdoc.arg("-o").arg(doc_dir);
87c010b6 577
a7efa91e 578 for feat in &unit.features {
50f1c172 579 rustdoc.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
a4f58993
SF
580 }
581
daa1bce2 582 add_error_format_and_color(cx, &mut rustdoc, false)?;
f3faa976 583
ef0b4776 584 if let Some(args) = bcx.extra_args_for(unit) {
768f5739 585 rustdoc.args(args);
4816a2a5
MG
586 }
587
82655b46 588 build_deps_args(&mut rustdoc, cx, unit)?;
412d3c3b 589
bd8253fd 590 rustdoc.args(bcx.rustdocflags_args(unit));
f639d381 591
659f8244 592 let name = unit.pkg.name().to_string();
bd31c081 593 let build_script_outputs = Arc::clone(&cx.build_script_outputs);
dae87a26 594 let package_id = unit.pkg.package_id();
f3faa976 595 let target = unit.target.clone();
342f8608 596 let mut output_options = OutputOptions::new(cx, unit);
2296af27
EH
597 let pkg_id = unit.pkg.package_id();
598 let script_metadata = cx.find_build_script_metadata(*unit);
3656bec8 599
26690d33 600 Ok(Work::new(move |state| {
2296af27
EH
601 if let Some(script_metadata) = script_metadata {
602 if let Some(output) = build_script_outputs
603 .lock()
604 .unwrap()
605 .get(pkg_id, script_metadata)
606 {
607 for cfg in output.cfgs.iter() {
608 rustdoc.arg("--cfg").arg(cfg);
609 }
610 for &(ref name, ref value) in output.env.iter() {
611 rustdoc.env(name, value);
612 }
cb1e6157 613 }
807da6b1 614 }
26690d33 615 state.running(&rustdoc);
641f7ff2 616
1a6e4524
AC
617 rustdoc
618 .exec_with_streaming(
619 &mut |line| on_stdout_line(state, line, package_id, &target),
6117f526 620 &mut |line| on_stderr_line(state, line, package_id, &target, &mut output_options),
1a6e4524
AC
621 false,
622 )
623 .chain_err(|| format!("Could not document `{}`.", name))?;
37cffbe0 624 Ok(())
157d639a 625 }))
3f110840 626}
79768eb0 627
4a91d015 628// The path that we pass to rustc is actually fairly important because it will
8647a87d
AC
629// show up in error messages (important for readability), debug information
630// (important for caching), etc. As a result we need to be pretty careful how we
631// actually invoke rustc.
4a91d015 632//
8647a87d 633// In general users don't expect `cargo build` to cause rebuilds if you change
3492a390
ZL
634// directories. That could be if you just change directories in the package or
635// if you literally move the whole package wholesale to a new directory. As a
8647a87d
AC
636// result we mostly don't factor in `cwd` to this calculation. Instead we try to
637// track the workspace as much as possible and we update the current directory
4a64d05e 638// of rustc/rustdoc where appropriate.
8647a87d
AC
639//
640// The first returned value here is the argument to pass to rustc, and the
641// second is the cwd that rustc should operate in.
b8b7faee 642fn path_args(bcx: &BuildContext<'_, '_>, unit: &Unit<'_>) -> (PathBuf, PathBuf) {
ecc87b17 643 let ws_root = bcx.ws.root();
9a7fadf6
EH
644 let src = match unit.target.src_path() {
645 TargetSourcePath::Path(path) => path.to_path_buf(),
646 TargetSourcePath::Metabuild => unit.pkg.manifest().metabuild_path(bcx.ws.target_dir()),
2be857af 647 };
8647a87d 648 assert!(src.is_absolute());
6e57be51
AC
649 if unit.pkg.package_id().source_id().is_path() {
650 if let Ok(path) = src.strip_prefix(ws_root) {
651 return (path.to_path_buf(), ws_root.to_path_buf());
652 }
3b09cd54 653 }
2be857af 654 (src, unit.pkg.root().to_path_buf())
4a91d015
AC
655}
656
b8b7faee 657fn add_path_args(bcx: &BuildContext<'_, '_>, unit: &Unit<'_>, cmd: &mut ProcessBuilder) {
ecc87b17 658 let (arg, cwd) = path_args(bcx, unit);
8647a87d 659 cmd.arg(arg);
f688e9c2 660 cmd.cwd(cwd);
4a91d015
AC
661}
662
b8b7faee 663fn add_cap_lints(bcx: &BuildContext<'_, '_>, unit: &Unit<'_>, cmd: &mut ProcessBuilder) {
c3b477d4
EH
664 // If this is an upstream dep we don't want warnings from, turn off all
665 // lints.
666 if !bcx.show_warnings(unit.pkg.package_id()) {
667 cmd.arg("--cap-lints").arg("allow");
668
669 // If this is an upstream dep but we *do* want warnings, make sure that they
670 // don't fail compilation.
671 } else if !unit.pkg.package_id().source_id().is_path() {
672 cmd.arg("--cap-lints").arg("warn");
673 }
674}
675
9ba68125
EH
676/// Add error-format flags to the command.
677///
bd73e8da
EH
678/// Cargo always uses JSON output. This has several benefits, such as being
679/// easier to parse, handles changing formats (for replaying cached messages),
680/// ensures atomic output (so messages aren't interleaved), allows for
681/// intercepting messages like rmeta artifacts, etc. rustc includes a
682/// "rendered" field in the JSON message with the message properly formatted,
683/// which Cargo will extract and display to the user.
daa1bce2 684fn add_error_format_and_color(
dcd4999d
EH
685 cx: &Context<'_, '_>,
686 cmd: &mut ProcessBuilder,
687 pipelined: bool,
dcd4999d 688) -> CargoResult<()> {
bd73e8da
EH
689 cmd.arg("--error-format=json");
690 let mut json = String::from("--json=diagnostic-rendered-ansi");
691 if pipelined {
692 // Pipelining needs to know when rmeta files are finished. Tell rustc
693 // to emit a message that cargo will intercept.
694 json.push_str(",artifacts");
695 }
696 match cx.bcx.build_config.message_format {
697 MessageFormat::Short | MessageFormat::Json { short: true, .. } => {
698 json.push_str(",diagnostic-short");
45699e9f 699 }
bd73e8da 700 _ => {}
99ed7ea8 701 }
bd73e8da 702 cmd.arg(json);
dcd4999d 703 Ok(())
99ed7ea8
DW
704}
705
1e682848
AC
706fn build_base_args<'a, 'cfg>(
707 cx: &mut Context<'a, 'cfg>,
708 cmd: &mut ProcessBuilder,
709 unit: &Unit<'a>,
710 crate_types: &[&str],
711) -> CargoResult<()> {
575d6e81
EH
712 assert!(!unit.mode.is_run_custom_build());
713
c32e395c 714 let bcx = cx.bcx;
9e779198 715 let Profile {
1e682848
AC
716 ref opt_level,
717 ref lto,
718 codegen_units,
1e682848
AC
719 debuginfo,
720 debug_assertions,
721 overflow_checks,
722 rpath,
1e682848 723 ref panic,
2b6fd6f0 724 incremental,
1e682848 725 ..
73660740 726 } = unit.profile;
575d6e81 727 let test = unit.mode.is_any_test();
7b49ebe0 728
1562ebfc
IB
729 cmd.arg("--crate-name").arg(&unit.target.crate_name());
730
9aa65c55
DT
731 let edition = unit.target.edition();
732 if edition != Edition::Edition2015 {
733 cmd.arg(format!("--edition={}", edition));
734 }
735
ecc87b17 736 add_path_args(bcx, unit, cmd);
daa1bce2 737 add_error_format_and_color(cx, cmd, cx.rmeta_required(unit))?;
76a48a9b 738
655decc0 739 if !test {
b74fd0a3
AC
740 for crate_type in crate_types.iter() {
741 cmd.arg("--crate-type").arg(crate_type);
742 }
bc1dea82 743 }
13eb1232 744
575d6e81 745 if unit.mode.is_check() {
655decc0 746 cmd.arg("--emit=dep-info,metadata");
0c51d71c 747 } else if !unit.requires_upstream_objects() {
34f2d471 748 // Always produce metadata files for rlib outputs. Metadata may be used
127fdfeb 749 // in this session for a pipelined compilation, or it may be used in a
205bab90 750 // future Cargo session as part of a pipelined compile.
127fdfeb 751 cmd.arg("--emit=dep-info,metadata,link");
655decc0
NC
752 } else {
753 cmd.arg("--emit=dep-info,link");
754 }
755
1e682848 756 let prefer_dynamic = (unit.target.for_host() && !unit.target.is_custom_build())
c32e395c 757 || (crate_types.contains(&"dylib") && bcx.ws.members().any(|p| p != unit.pkg));
9879a0a5 758 if prefer_dynamic {
a6dad622 759 cmd.arg("-C").arg("prefer-dynamic");
f9b2f5e1
AC
760 }
761
575d6e81 762 if opt_level.as_str() != "0" {
9e779198 763 cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
a1980dc7 764 }
8bff726e 765
34b77b58 766 if *panic != PanicStrategy::Unwind {
86489946 767 cmd.arg("-C").arg(format!("panic={}", panic));
75848a2a
AC
768 }
769
8bff726e
MP
770 // Disable LTO for host builds as prefer_dynamic and it are mutually
771 // exclusive.
f52f4896
AC
772 if unit.target.can_lto() && !unit.target.for_host() {
773 match *lto {
774 Lto::Bool(false) => {}
775 Lto::Bool(true) => {
776 cmd.args(&["-C", "lto"]);
777 }
778 Lto::Named(ref s) => {
779 cmd.arg("-C").arg(format!("lto={}", s));
780 }
781 }
782 }
783
784 if let Some(n) = codegen_units {
442de299
AC
785 // There are some restrictions with LTO and codegen-units, so we
786 // only add codegen units when LTO is not used.
23591fe5 787 cmd.arg("-C").arg(&format!("codegen-units={}", n));
e0cdd376
SP
788 }
789
6d864a81
SF
790 if let Some(debuginfo) = debuginfo {
791 cmd.arg("-C").arg(format!("debuginfo={}", debuginfo));
9e779198
AC
792 }
793
ef0b4776 794 if let Some(args) = bcx.extra_args_for(unit) {
768f5739 795 cmd.args(args);
b177d2ab
SL
796 }
797
f7c91ba6
AR
798 // `-C overflow-checks` is implied by the setting of `-C debug-assertions`,
799 // so we only need to provide `-C overflow-checks` if it differs from
800 // the value of `-C debug-assertions` we would provide.
575d6e81 801 if opt_level.as_str() != "0" {
803d748a
NF
802 if debug_assertions {
803 cmd.args(&["-C", "debug-assertions=on"]);
804 if !overflow_checks {
805 cmd.args(&["-C", "overflow-checks=off"]);
806 }
807 } else if overflow_checks {
808 cmd.args(&["-C", "overflow-checks=on"]);
809 }
23591fe5
LL
810 } else if !debug_assertions {
811 cmd.args(&["-C", "debug-assertions=off"]);
812 if overflow_checks {
813 cmd.args(&["-C", "overflow-checks=on"]);
803d748a 814 }
23591fe5
LL
815 } else if !overflow_checks {
816 cmd.args(&["-C", "overflow-checks=off"]);
a12a909c
SF
817 }
818
659f8244 819 if test && unit.target.harness() {
a6dad622 820 cmd.arg("--test");
f37f3aea
AC
821
822 // Cargo has historically never compiled `--test` binaries with
823 // `panic=abort` because the `test` crate itself didn't support it.
824 // Support is now upstream, however, but requires an unstable flag to be
825 // passed when compiling the test. We require, in Cargo, an unstable
826 // flag to pass to rustc, so register that here. Eventually this flag
827 // will simply not be needed when the behavior is stabilized in the Rust
828 // compiler itself.
829 if *panic == PanicStrategy::Abort {
830 cmd.arg("-Zpanic-abort-tests");
831 }
b74fd0a3
AC
832 } else if test {
833 cmd.arg("--cfg").arg("test");
13eb1232
TCS
834 }
835
a7efa91e 836 for feat in &unit.features {
50f1c172 837 cmd.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
2b46d039
AC
838 }
839
cf0bb13d 840 match cx.files().metadata(unit) {
083ff01e 841 Some(m) => {
0cc39aae
AC
842 cmd.arg("-C").arg(&format!("metadata={}", m));
843 cmd.arg("-C").arg(&format!("extra-filename=-{}", m));
083ff01e
AC
844 }
845 None => {
1e682848 846 cmd.arg("-C")
cf0bb13d 847 .arg(&format!("metadata={}", cx.files().target_short_hash(unit)));
083ff01e 848 }
7b49ebe0 849 }
dd2c5980 850
9e779198 851 if rpath {
a6dad622 852 cmd.arg("-C").arg("rpath");
bd9a9b02 853 }
7b49ebe0 854
cf0bb13d 855 cmd.arg("--out-dir").arg(&cx.files().out_dir(unit));
15e4cf37 856
1e682848 857 fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) {
e2c0a9d8 858 if let Some(val) = val {
5a04a4d4
MB
859 let mut joined = OsString::from(prefix);
860 joined.push(val);
861 cmd.arg(key).arg(joined);
e2c0a9d8
JA
862 }
863 }
864
ef425b77
AC
865 if let CompileKind::Target(n) = unit.kind {
866 cmd.arg("--target").arg(n.rustc_target());
1946c447 867 }
e2c0a9d8 868
1e682848
AC
869 opt(
870 cmd,
871 "-C",
872 "linker=",
381251aa 873 bcx.linker(unit.kind).as_ref().map(|s| s.as_ref()),
1e682848 874 );
2b6fd6f0
EH
875 if incremental {
876 let dir = cx.files().layout(unit.kind).incremental().as_os_str();
877 opt(cmd, "-C", "incremental=", Some(dir));
878 }
e9dd3066
EH
879
880 if unit.is_std {
881 // -Zforce-unstable-if-unmarked prevents the accidental use of
882 // unstable crates within the sysroot (such as "extern crate libc" or
883 // any non-public crate in the sysroot).
884 //
885 // RUSTC_BOOTSTRAP allows unstable features on stable.
886 cmd.arg("-Zforce-unstable-if-unmarked")
887 .env("RUSTC_BOOTSTRAP", "1");
888 }
45cc30bc 889 Ok(())
5cda0797
CL
890}
891
1e682848
AC
892fn build_deps_args<'a, 'cfg>(
893 cmd: &mut ProcessBuilder,
894 cx: &mut Context<'a, 'cfg>,
895 unit: &Unit<'a>,
896) -> CargoResult<()> {
c32e395c 897 let bcx = cx.bcx;
a6dad622 898 cmd.arg("-L").arg(&{
964e72ff 899 let mut deps = OsString::from("dependency=");
cf0bb13d 900 deps.push(cx.files().deps_dir(unit));
a6dad622
AC
901 deps
902 });
903
f7c91ba6 904 // Be sure that the host path is also listed. This'll ensure that proc macro
a298346d 905 // dependencies are correctly found (for reexported macros).
f745ca70 906 if !unit.kind.is_host() {
a298346d
AC
907 cmd.arg("-L").arg(&{
908 let mut deps = OsString::from("dependency=");
cf0bb13d 909 deps.push(cx.files().host_deps());
a298346d
AC
910 deps
911 });
912 }
913
1c779ac5 914 let deps = cx.unit_deps(unit);
554f3339
LL
915
916 // If there is not one linkable target but should, rustc fails later
917 // on if there is an `extern crate` for it. This may turn into a hard
f7c91ba6 918 // error in the future (see PR #4797).
1f14fa31 919 if !deps
1e682848 920 .iter()
1f14fa31 921 .any(|dep| !dep.unit.mode.is_doc() && dep.unit.target.linkable())
1e682848 922 {
1f14fa31 923 if let Some(dep) = deps
1e682848 924 .iter()
1f14fa31 925 .find(|dep| !dep.unit.mode.is_doc() && dep.unit.target.is_lib())
1e682848 926 {
c32e395c 927 bcx.config.shell().warn(format!(
1e682848
AC
928 "The package `{}` \
929 provides no linkable target. The compiler might raise an error while compiling \
930 `{}`. Consider adding 'dylib' or 'rlib' to key `crate-type` in `{}`'s \
931 Cargo.toml. This warning might turn into a hard error in the future.",
1f14fa31 932 dep.unit.target.crate_name(),
1e682848 933 unit.target.crate_name(),
1f14fa31 934 dep.unit.target.crate_name()
1e682848
AC
935 ))?;
936 }
554f3339
LL
937 }
938
87f1a4b2
AH
939 let mut unstable_opts = false;
940
1f14fa31
EH
941 for dep in deps {
942 if dep.unit.mode.is_run_custom_build() {
943 cmd.env("OUT_DIR", &cx.files().build_script_out_dir(&dep.unit));
41579bad 944 }
1c779ac5
EH
945 }
946
947 for arg in extern_args(cx, unit, &mut unstable_opts)? {
948 cmd.arg(arg);
8960dd18 949 }
ed3bb05b 950
b4cd6095 951 // This will only be set if we're already using a feature
87f1a4b2
AH
952 // requiring nightly rust
953 if unstable_opts {
954 cmd.arg("-Z").arg("unstable-options");
955 }
956
a6a395c6 957 Ok(())
1c779ac5 958}
ed3bb05b 959
1c779ac5
EH
960/// Generates a list of `--extern` arguments.
961pub fn extern_args<'a>(
962 cx: &Context<'a, '_>,
963 unit: &Unit<'a>,
964 unstable_opts: &mut bool,
965) -> CargoResult<Vec<OsString>> {
966 let mut result = Vec::new();
967 let deps = cx.unit_deps(unit);
968
969 // Closure to add one dependency to `result`.
970 let mut link_to = |dep: &UnitDep<'a>,
971 extern_crate_name: InternedString,
972 noprelude: bool|
973 -> CargoResult<()> {
6b28a0c0 974 let mut value = OsString::new();
1c779ac5
EH
975 let mut opts = Vec::new();
976 if unit
977 .pkg
978 .manifest()
979 .features()
980 .require(Feature::public_dependency())
981 .is_ok()
982 && !dep.public
983 {
984 opts.push("priv");
985 *unstable_opts = true;
986 }
987 if noprelude {
988 opts.push("noprelude");
989 *unstable_opts = true;
990 }
991 if !opts.is_empty() {
992 value.push(opts.join(","));
993 value.push(":");
994 }
995 value.push(extern_crate_name.as_str());
6b28a0c0
AC
996 value.push("=");
997
998 let mut pass = |file| {
999 let mut value = value.clone();
1000 value.push(file);
1c779ac5
EH
1001 result.push(OsString::from("--extern"));
1002 result.push(value);
6b28a0c0
AC
1003 };
1004
1f14fa31 1005 let outputs = cx.outputs(&dep.unit)?;
6b28a0c0
AC
1006 let mut outputs = outputs.iter().filter_map(|output| match output.flavor {
1007 FileFlavor::Linkable { rmeta } => Some((output, rmeta)),
1008 _ => None,
1009 });
1010
1c779ac5 1011 if cx.only_requires_rmeta(unit, &dep.unit) {
6b28a0c0
AC
1012 let (output, _rmeta) = outputs
1013 .find(|(_output, rmeta)| *rmeta)
1014 .expect("failed to find rlib dep for pipelined dep");
1015 pass(&output.path);
1016 } else {
1017 for (output, rmeta) in outputs {
1018 if !rmeta {
1019 pass(&output.path);
1020 }
1021 }
8defed61 1022 }
a6dad622 1023 Ok(())
1c779ac5
EH
1024 };
1025
1026 for dep in deps {
1027 if dep.unit.target.linkable() && !dep.unit.mode.is_doc() {
239ebf4f 1028 link_to(dep, dep.extern_crate_name, dep.noprelude)?;
1c779ac5 1029 }
e1434913 1030 }
f8fafbc3 1031 if unit.target.proc_macro() {
4d64eb99
EH
1032 // Automatically import `proc_macro`.
1033 result.push(OsString::from("--extern"));
1034 result.push(OsString::from("proc_macro"));
1035 }
1c779ac5
EH
1036
1037 Ok(result)
5cda0797 1038}
a12beb61 1039
63915360
AC
1040fn envify(s: &str) -> String {
1041 s.chars()
1e682848
AC
1042 .flat_map(|c| c.to_uppercase())
1043 .map(|c| if c == '-' { '_' } else { c })
1044 .collect()
63915360 1045}
bcc6c5c3 1046
342f8608 1047struct OutputOptions {
45699e9f
AC
1048 /// What format we're emitting from Cargo itself.
1049 format: MessageFormat,
342f8608
EH
1050 /// Look for JSON message that indicates .rmeta file is available for
1051 /// pipelined compilation.
1052 look_for_metadata_directive: bool,
1053 /// Whether or not to display messages in color.
1054 color: bool,
1055 /// Where to write the JSON messages to support playback later if the unit
1056 /// is fresh. The file is created lazily so that in the normal case, lots
bd73e8da
EH
1057 /// of empty files are not created. If this is None, the output will not
1058 /// be cached (such as when replaying cached messages).
342f8608
EH
1059 cache_cell: Option<(PathBuf, LazyCell<File>)>,
1060}
1061
1062impl OutputOptions {
1063 fn new<'a>(cx: &Context<'a, '_>, unit: &Unit<'a>) -> OutputOptions {
342f8608
EH
1064 let look_for_metadata_directive = cx.rmeta_required(unit);
1065 let color = cx.bcx.config.shell().supports_color();
bd73e8da
EH
1066 let path = cx.files().message_cache_path(unit);
1067 // Remove old cache, ignore ENOENT, which is the common case.
1068 drop(fs::remove_file(&path));
1069 let cache_cell = Some((path, LazyCell::new()));
342f8608 1070 OutputOptions {
45699e9f 1071 format: cx.bcx.build_config.message_format,
342f8608
EH
1072 look_for_metadata_directive,
1073 color,
1074 cache_cell,
1075 }
1076 }
1077}
1078
1a6e4524
AC
1079fn on_stdout_line(
1080 state: &JobState<'_>,
1081 line: &str,
1082 _package_id: PackageId,
1083 _target: &Target,
1084) -> CargoResult<()> {
dcd4999d 1085 state.stdout(line.to_string());
1a6e4524 1086 Ok(())
f3faa976
EH
1087}
1088
1a6e4524
AC
1089fn on_stderr_line(
1090 state: &JobState<'_>,
1091 line: &str,
1092 package_id: PackageId,
1093 target: &Target,
342f8608 1094 options: &mut OutputOptions,
1a6e4524 1095) -> CargoResult<()> {
6117f526 1096 if on_stderr_line_inner(state, line, package_id, target, options)? {
bd73e8da
EH
1097 // Check if caching is enabled.
1098 if let Some((path, cell)) = &mut options.cache_cell {
1099 // Cache the output, which will be replayed later when Fresh.
1100 let f = cell.try_borrow_mut_with(|| File::create(path))?;
1101 debug_assert!(!line.contains('\n'));
1102 f.write_all(line.as_bytes())?;
1103 f.write_all(&[b'\n'])?;
1104 }
dcd4999d 1105 }
bd73e8da
EH
1106 Ok(())
1107}
dcd4999d 1108
bd73e8da
EH
1109/// Returns true if the line should be cached.
1110fn on_stderr_line_inner(
1111 state: &JobState<'_>,
1112 line: &str,
1113 package_id: PackageId,
1114 target: &Target,
1115 options: &mut OutputOptions,
1116) -> CargoResult<bool> {
569e973a
AC
1117 // We primarily want to use this function to process JSON messages from
1118 // rustc. The compiler should always print one JSON message per line, and
1119 // otherwise it may have other output intermingled (think RUST_LOG or
1120 // something like that), so skip over everything that doesn't look like a
1121 // JSON message.
1122 if !line.starts_with('{') {
dcd4999d 1123 state.stderr(line.to_string());
bd73e8da 1124 return Ok(true);
569e973a
AC
1125 }
1126
dcd4999d 1127 let mut compiler_message: Box<serde_json::value::RawValue> = match serde_json::from_str(line) {
8f032b3b
AC
1128 Ok(msg) => msg,
1129
1130 // If the compiler produced a line that started with `{` but it wasn't
1131 // valid JSON, maybe it wasn't JSON in the first place! Forward it along
1132 // to stderr.
dcd4999d
EH
1133 Err(e) => {
1134 debug!("failed to parse json: {:?}", e);
1135 state.stderr(line.to_string());
bd73e8da 1136 return Ok(true);
8f032b3b
AC
1137 }
1138 };
569e973a 1139
45699e9f
AC
1140 // Depending on what we're emitting from Cargo itself, we figure out what to
1141 // do with this JSON message.
1142 match options.format {
1143 // In the "human" output formats (human/short) or if diagnostic messages
1144 // from rustc aren't being included in the output of Cargo's JSON
1145 // messages then we extract the diagnostic (if present) here and handle
1146 // it ourselves.
1147 MessageFormat::Human
1148 | MessageFormat::Short
1149 | MessageFormat::Json {
1150 render_diagnostics: true,
1151 ..
1152 } => {
1153 #[derive(serde::Deserialize)]
1154 struct CompilerMessage {
1155 rendered: String,
1156 }
1157 if let Ok(mut error) = serde_json::from_str::<CompilerMessage>(compiler_message.get()) {
1158 // state.stderr will add a newline
1159 if error.rendered.ends_with('\n') {
1160 error.rendered.pop();
1161 }
1162 let rendered = if options.color {
1163 error.rendered
1164 } else {
1165 // Strip only fails if the the Writer fails, which is Cursor
1166 // on a Vec, which should never fail.
1167 strip_ansi_escapes::strip(&error.rendered)
1168 .map(|v| String::from_utf8(v).expect("utf8"))
1169 .expect("strip should never fail")
1170 };
1171 state.stderr(rendered);
bd73e8da 1172 return Ok(true);
45699e9f 1173 }
569e973a 1174 }
45699e9f 1175
bd73e8da
EH
1176 // Remove color information from the rendered string if color is not
1177 // enabled. Cargo always asks for ANSI colors from rustc. This allows
1178 // cached replay to enable/disable colors without re-invoking rustc.
45699e9f
AC
1179 MessageFormat::Json { ansi: false, .. } => {
1180 #[derive(serde::Deserialize, serde::Serialize)]
1181 struct CompilerMessage {
1182 rendered: String,
1183 #[serde(flatten)]
1184 other: std::collections::BTreeMap<String, serde_json::Value>,
dcd4999d 1185 }
45699e9f
AC
1186 if let Ok(mut error) = serde_json::from_str::<CompilerMessage>(compiler_message.get()) {
1187 error.rendered = strip_ansi_escapes::strip(&error.rendered)
dcd4999d 1188 .map(|v| String::from_utf8(v).expect("utf8"))
45699e9f
AC
1189 .unwrap_or(error.rendered);
1190 let new_line = serde_json::to_string(&error)?;
1191 let new_msg: Box<serde_json::value::RawValue> = serde_json::from_str(&new_line)?;
1192 compiler_message = new_msg;
1193 }
dcd4999d 1194 }
45699e9f
AC
1195
1196 // If ansi colors are desired then we should be good to go! We can just
1197 // pass through this message as-is.
1198 MessageFormat::Json { ansi: true, .. } => {}
569e973a
AC
1199 }
1200
4617f78d 1201 // In some modes of execution we will execute rustc with `-Z
95e24044 1202 // emit-artifact-notifications` to look for metadata files being produced. When this
4617f78d
AC
1203 // happens we may be able to start subsequent compilations more quickly than
1204 // waiting for an entire compile to finish, possibly using more parallelism
1205 // available to complete a compilation session more quickly.
1206 //
1207 // In these cases look for a matching directive and inform Cargo internally
1208 // that a metadata file has been produced.
342f8608 1209 if options.look_for_metadata_directive {
4617f78d 1210 #[derive(serde::Deserialize)]
95e24044
AC
1211 struct ArtifactNotification {
1212 artifact: String,
4617f78d 1213 }
95e24044
AC
1214 if let Ok(artifact) = serde_json::from_str::<ArtifactNotification>(compiler_message.get()) {
1215 log::trace!("found directive from rustc: `{}`", artifact.artifact);
1216 if artifact.artifact.ends_with(".rmeta") {
4617f78d
AC
1217 log::debug!("looks like metadata finished early!");
1218 state.rmeta_produced();
4617f78d 1219 }
bd73e8da 1220 return Ok(false);
4617f78d
AC
1221 }
1222 }
1223
ec80cf90
MR
1224 #[derive(serde::Deserialize)]
1225 struct JobserverNotification {
1226 jobserver_event: Event,
1227 }
1228
1229 #[derive(Debug, serde::Deserialize)]
1230 enum Event {
1231 WillAcquire,
1232 Release,
1233 }
1234
1235 if let Ok(JobserverNotification { jobserver_event }) =
1236 serde_json::from_str::<JobserverNotification>(compiler_message.get())
1237 {
4a8237f6 1238 log::info!(
ec80cf90
MR
1239 "found jobserver directive from rustc: `{:?}`",
1240 jobserver_event
1241 );
6117f526
MR
1242 match jobserver_event {
1243 Event::WillAcquire => state.will_acquire(),
1244 Event::Release => state.release_token(),
ec80cf90
MR
1245 }
1246 return Ok(false);
1247 }
1248
569e973a
AC
1249 // And failing all that above we should have a legitimate JSON diagnostic
1250 // from the compiler, so wrap it in an external Cargo JSON message
1251 // indicating which package it came from and then emit it.
205bab90 1252 let msg = machine_message::FromCompiler {
569e973a
AC
1253 package_id,
1254 target,
1255 message: compiler_message,
6b28a0c0
AC
1256 }
1257 .to_json_string();
569e973a 1258
205bab90
AC
1259 // Switch json lines from rustc/rustdoc that appear on stderr to stdout
1260 // instead. We want the stdout of Cargo to always be machine parseable as
1261 // stderr has our colorized human-readable messages.
dcd4999d 1262 state.stdout(msg);
bd73e8da 1263 Ok(true)
f3faa976 1264}
dcd4999d
EH
1265
1266fn replay_output_cache(
1267 package_id: PackageId,
1268 target: &Target,
1269 path: PathBuf,
1270 format: MessageFormat,
1271 color: bool,
1272) -> Work {
1273 let target = target.clone();
342f8608 1274 let mut options = OutputOptions {
45699e9f 1275 format,
bd73e8da 1276 look_for_metadata_directive: true,
342f8608
EH
1277 color,
1278 cache_cell: None,
1279 };
dcd4999d 1280 Work::new(move |state| {
f6d30e91
EH
1281 if !path.exists() {
1282 // No cached output, probably didn't emit anything.
1283 return Ok(());
1284 }
5e64197f
MR
1285 // We sometimes have gigabytes of output from the compiler, so avoid
1286 // loading it all into memory at once, as that can cause OOM where
1287 // otherwise there would be none.
1288 let file = fs::File::open(&path)?;
1289 let mut reader = std::io::BufReader::new(file);
1290 let mut line = String::new();
1291 loop {
1292 let length = reader.read_line(&mut line)?;
1293 if length == 0 {
1294 break;
1295 }
7be3c2f0 1296 let trimmed = line.trim_end_matches(&['\n', '\r'][..]);
6117f526 1297 on_stderr_line(state, trimmed, package_id, &target, &mut options)?;
5e64197f 1298 line.clear();
dcd4999d
EH
1299 }
1300 Ok(())
1301 })
1302}