Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add split-debuginfo profile option #9112

Merged
merged 3 commits into from
Feb 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -773,6 +773,7 @@ fn build_base_args(
codegen_units,
debuginfo,
debug_assertions,
split_debuginfo,
overflow_checks,
rpath,
ref panic,
Expand Down Expand Up @@ -825,6 +826,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