Skip to content

Commit

Permalink
Auto merge of #9112 - alexcrichton:split-debuginfo, r=ehuss
Browse files Browse the repository at this point in the history
Add split-debuginfo profile option

This commit adds a new `split-debuginfo` option to Cargo compilation
profiles which gets forwarded to the `-Csplit-debuginfo` codegen option
in rustc. This commit also sets the default, only on macOS, to be
`-Csplit-debuginfo=unpacked`. The purpose of this change is to leverage
rust-lang/rust#79570 to avoid running `dsymutil` on incremental builds
while also preserving a pleasant debugging experience by default. This
should lead to much faster incremental build times on macOS since
`dsymutil` isn't exactly the speediest tool in the world.

This is technically a breaking change in Cargo because we're no longer
by-default producing the `*.dSYM` folders on macOS. If those are still
desired, however, authors can always run `dsymutil` themselves or
otherwise configure `split-debuginfo = 'packed'` in their
manifest/profile configuration.
  • Loading branch information
bors committed Feb 3, 2021
2 parents 537dc09 + ffa9dbd commit aaaf296
Show file tree
Hide file tree
Showing 19 changed files with 303 additions and 216 deletions.
126 changes: 56 additions & 70 deletions crates/cargo-test-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,16 @@ impl Execs {
self
}

pub fn enable_mac_dsym(&mut self) -> &mut Self {
if cfg!(target_os = "macos") {
self.env("CARGO_PROFILE_DEV_SPLIT_DEBUGINFO", "packed")
.env("CARGO_PROFILE_TEST_SPLIT_DEBUGINFO", "packed")
.env("CARGO_PROFILE_RELEASE_SPLIT_DEBUGINFO", "packed")
.env("CARGO_PROFILE_BENCH_SPLIT_DEBUGINFO", "packed");
}
self
}

pub fn run(&mut self) {
self.ran = true;
let p = (&self.process_builder).clone().unwrap();
Expand Down Expand Up @@ -788,13 +798,17 @@ impl Execs {
match res {
Ok(out) => self.match_output(&out),
Err(e) => {
let err = e.downcast_ref::<ProcessError>();
if let Some(&ProcessError {
output: Some(ref out),
if let Some(ProcessError {
stdout: Some(stdout),
stderr: Some(stderr),
code,
..
}) = err
}) = e.downcast_ref::<ProcessError>()
{
return self.match_output(out);
return self
.match_status(*code, stdout, stderr)
.and(self.match_stdout(stdout, stderr))
.and(self.match_stderr(stdout, stderr));
}
Err(format!("could not exec process {}: {:?}", process, e))
}
Expand All @@ -803,119 +817,91 @@ impl Execs {

fn match_output(&self, actual: &Output) -> MatchResult {
self.verify_checks_output(actual);
self.match_status(actual)
.and(self.match_stdout(actual))
.and(self.match_stderr(actual))
self.match_status(actual.status.code(), &actual.stdout, &actual.stderr)
.and(self.match_stdout(&actual.stdout, &actual.stderr))
.and(self.match_stderr(&actual.stdout, &actual.stderr))
}

fn match_status(&self, actual: &Output) -> MatchResult {
fn match_status(&self, code: Option<i32>, stdout: &[u8], stderr: &[u8]) -> MatchResult {
match self.expect_exit_code {
None => Ok(()),
Some(code) if actual.status.code() == Some(code) => Ok(()),
Some(expected) if code == Some(expected) => Ok(()),
Some(_) => Err(format!(
"exited with {}\n--- stdout\n{}\n--- stderr\n{}",
actual.status,
String::from_utf8_lossy(&actual.stdout),
String::from_utf8_lossy(&actual.stderr)
"exited with {:?}\n--- stdout\n{}\n--- stderr\n{}",
code,
String::from_utf8_lossy(&stdout),
String::from_utf8_lossy(&stderr)
)),
}
}

fn match_stdout(&self, actual: &Output) -> MatchResult {
fn match_stdout(&self, stdout: &[u8], stderr: &[u8]) -> MatchResult {
self.match_std(
self.expect_stdout.as_ref(),
&actual.stdout,
stdout,
"stdout",
&actual.stderr,
stderr,
MatchKind::Exact,
)?;
for expect in self.expect_stdout_contains.iter() {
self.match_std(
Some(expect),
&actual.stdout,
"stdout",
&actual.stderr,
MatchKind::Partial,
)?;
self.match_std(Some(expect), stdout, "stdout", stderr, MatchKind::Partial)?;
}
for expect in self.expect_stderr_contains.iter() {
self.match_std(
Some(expect),
&actual.stderr,
"stderr",
&actual.stdout,
MatchKind::Partial,
)?;
self.match_std(Some(expect), stderr, "stderr", stdout, MatchKind::Partial)?;
}
for &(ref expect, number) in self.expect_stdout_contains_n.iter() {
self.match_std(
Some(expect),
&actual.stdout,
stdout,
"stdout",
&actual.stderr,
stderr,
MatchKind::PartialN(number),
)?;
}
for expect in self.expect_stdout_not_contains.iter() {
self.match_std(
Some(expect),
&actual.stdout,
stdout,
"stdout",
&actual.stderr,
stderr,
MatchKind::NotPresent,
)?;
}
for expect in self.expect_stderr_not_contains.iter() {
self.match_std(
Some(expect),
&actual.stderr,
stderr,
"stderr",
&actual.stdout,
stdout,
MatchKind::NotPresent,
)?;
}
for expect in self.expect_stderr_unordered.iter() {
self.match_std(
Some(expect),
&actual.stderr,
"stderr",
&actual.stdout,
MatchKind::Unordered,
)?;
self.match_std(Some(expect), stderr, "stderr", stdout, MatchKind::Unordered)?;
}
for expect in self.expect_neither_contains.iter() {
self.match_std(
Some(expect),
&actual.stdout,
stdout,
"stdout",
&actual.stdout,
stdout,
MatchKind::NotPresent,
)?;

self.match_std(
Some(expect),
&actual.stderr,
stderr,
"stderr",
&actual.stderr,
stderr,
MatchKind::NotPresent,
)?;
}

for expect in self.expect_either_contains.iter() {
let match_std = self.match_std(
Some(expect),
&actual.stdout,
"stdout",
&actual.stdout,
MatchKind::Partial,
);
let match_err = self.match_std(
Some(expect),
&actual.stderr,
"stderr",
&actual.stderr,
MatchKind::Partial,
);
let match_std =
self.match_std(Some(expect), stdout, "stdout", stdout, MatchKind::Partial);
let match_err =
self.match_std(Some(expect), stderr, "stderr", stderr, MatchKind::Partial);

if let (Err(_), Err(_)) = (match_std, match_err) {
return Err(format!(
Expand All @@ -928,12 +914,12 @@ impl Execs {
}

for (with, without) in self.expect_stderr_with_without.iter() {
self.match_with_without(&actual.stderr, with, without)?;
self.match_with_without(stderr, with, without)?;
}

if let Some(ref objects) = self.expect_json {
let stdout = str::from_utf8(&actual.stdout)
.map_err(|_| "stdout was not utf8 encoded".to_owned())?;
let stdout =
str::from_utf8(stdout).map_err(|_| "stdout was not utf8 encoded".to_owned())?;
let lines = stdout
.lines()
.filter(|line| line.starts_with('{'))
Expand All @@ -952,8 +938,8 @@ impl Execs {
}

if !self.expect_json_contains_unordered.is_empty() {
let stdout = str::from_utf8(&actual.stdout)
.map_err(|_| "stdout was not utf8 encoded".to_owned())?;
let stdout =
str::from_utf8(stdout).map_err(|_| "stdout was not utf8 encoded".to_owned())?;
let mut lines = stdout
.lines()
.filter(|line| line.starts_with('{'))
Expand All @@ -980,12 +966,12 @@ impl Execs {
Ok(())
}

fn match_stderr(&self, actual: &Output) -> MatchResult {
fn match_stderr(&self, stdout: &[u8], stderr: &[u8]) -> MatchResult {
self.match_std(
self.expect_stderr.as_ref(),
&actual.stderr,
stderr,
"stderr",
&actual.stdout,
stdout,
MatchKind::Exact,
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cargo/commands/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let err = ops::run_benches(&ws, &ops, &bench_args)?;
match err {
None => Ok(()),
Some(err) => Err(match err.exit.as_ref().and_then(|e| e.code()) {
Some(err) => Err(match err.code {
Some(i) => CliError::new(anyhow::format_err!("bench failed"), i),
None => CliError::new(err.into(), 101),
}),
Expand Down
3 changes: 1 addition & 2 deletions src/bin/cargo/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,14 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

// If we never actually spawned the process then that sounds pretty
// bad and we always want to forward that up.
let exit = match proc_err.exit {
let exit_code = match proc_err.code {
Some(exit) => exit,
None => return CliError::new(err, 101),
};

// If `-q` was passed then we suppress extra error information about
// a failed process, we assume the process itself printed out enough
// information about why it failed so we don't do so as well
let exit_code = exit.code().unwrap_or(101);
let is_quiet = config.shell().verbosity() == Verbosity::Quiet;
if is_quiet {
CliError::code(exit_code)
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cargo/commands/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
None => Ok(()),
Some(err) => {
let context = anyhow::format_err!("{}", err.hint(&ws, &ops.compile_opts));
let e = match err.exit.as_ref().and_then(|e| e.code()) {
let e = match err.code {
// Don't show "process didn't exit successfully" for simple errors.
Some(i) if errors::is_simple_exit_code(i) => CliError::new(context, i),
Some(i) => CliError::new(Error::from(err).context(context), i),
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cargo/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&str]) -> Cli
};

if let Some(perr) = err.downcast_ref::<ProcessError>() {
if let Some(code) = perr.exit.as_ref().and_then(|c| c.code()) {
if let Some(code) = perr.code {
return Err(CliError::code(code));
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/cargo/core/compiler/build_context/target_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pub struct TargetInfo {
pub rustflags: Vec<String>,
/// Extra flags to pass to `rustdoc`, see `env_args`.
pub rustdocflags: Vec<String>,
/// Whether or not rustc supports the `-Csplit-debuginfo` flag.
pub supports_split_debuginfo: bool,
}

/// Kind of each file generated by a Unit, part of `FileType`.
Expand Down Expand Up @@ -157,6 +159,9 @@ impl TargetInfo {
for crate_type in KNOWN_CRATE_TYPES.iter() {
process.arg("--crate-type").arg(crate_type.as_str());
}
let supports_split_debuginfo = rustc
.cached_output(process.clone().arg("-Csplit-debuginfo=packed"))
.is_ok();

process.arg("--print=sysroot");
process.arg("--print=cfg");
Expand Down Expand Up @@ -231,6 +236,7 @@ impl TargetInfo {
"RUSTDOCFLAGS",
)?,
cfg,
supports_split_debuginfo,
})
}

Expand Down
12 changes: 11 additions & 1 deletion src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
match err
.downcast_ref::<ProcessError>()
.as_ref()
.and_then(|perr| perr.exit.and_then(|e| e.code()))
.and_then(|perr| perr.code)
{
Some(n) if errors::is_simple_exit_code(n) => VerboseError::new(err).into(),
_ => err,
Expand Down Expand Up @@ -768,6 +768,7 @@ fn build_base_args(
codegen_units,
debuginfo,
debug_assertions,
split_debuginfo,
overflow_checks,
rpath,
ref panic,
Expand Down Expand Up @@ -820,6 +821,15 @@ fn build_base_args(

cmd.args(&lto_args(cx, unit));

// This is generally just an optimization on build time so if we don't pass
// it then it's ok. As of the time of this writing it's a very new flag, so
// we need to dynamically check if it's available.
if cx.bcx.target_data.info(unit.kind).supports_split_debuginfo {
if let Some(split) = split_debuginfo {
cmd.arg("-C").arg(format!("split-debuginfo={}", split));
}
}

if let Some(n) = codegen_units {
cmd.arg("-C").arg(&format!("codegen-units={}", n));
}
Expand Down
Loading

0 comments on commit aaaf296

Please sign in to comment.