From 7c6d245ae1bd99eef4698a6cbc2c1fe9124b2a64 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Fri, 5 Dec 2025 16:06:34 -0800 Subject: [PATCH] Support more of Cargo's debug levels with Build::debug_str ...at least when using GCC or Clang. --- src/lib.rs | 45 +++++++++++++++++++++++++++++++++++++++++---- src/tool.rs | 41 +++++++++++++++++++++++++++++++++++------ tests/test.rs | 31 +++++++++++++++++++++++++++++-- 3 files changed, 105 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 48730e0af..d74daf561 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -405,7 +405,7 @@ pub struct Build { host: Option>, out_dir: Option>, opt_level: Option>, - debug: Option, + debug: Option>, force_frame_pointer: Option, env: Vec<(Arc, Arc)>, compiler: Option>, @@ -1180,7 +1180,23 @@ impl Build { /// This option is automatically scraped from the `DEBUG` environment /// variable by build scripts, so it's not required to call this function. pub fn debug(&mut self, debug: bool) -> &mut Build { - self.debug = Some(debug); + self.debug = Some(debug.to_string().into()); + self + } + + /// Configures whether the compiler will emit debug information when + /// generating object files. + /// + /// This should be one of the values accepted by Cargo's [`debug`][] + /// profile setting, which cc-rs will try to map to the appropriate C + /// compiler flag. + /// + /// This option is automatically scraped from the `DEBUG` environment + /// variable by build scripts, so it's not required to call this function. + /// + /// [debuginfo]: https://doc.rust-lang.org/cargo/reference/profiles.html#debug + pub fn debug_str(&mut self, debug: &str) -> &mut Build { + self.debug = Some(debug.into()); self } @@ -2161,7 +2177,11 @@ impl Build { cmd.args.push("-G".into()); } let family = cmd.family; - family.add_debug_flags(cmd, self.get_dwarf_version()); + family.add_debug_flags( + cmd, + self.get_debug_str().as_deref().unwrap_or_default(), + self.get_dwarf_version(), + ); } if self.get_force_frame_pointer() { @@ -3707,8 +3727,25 @@ impl Build { } } + /// Returns true if *any* debug info is enabled. + /// + /// [`get_debug_str`] provides more detail. fn get_debug(&self) -> bool { - self.debug.unwrap_or_else(|| self.getenv_boolean("DEBUG")) + match self.get_debug_str() { + Err(_) => false, + Ok(d) => match &*d { + // From https://doc.rust-lang.org/cargo/reference/profiles.html#debug + "" | "0" | "false" | "none" => false, + _ => true, + }, + } + } + + fn get_debug_str(&self) -> Result, Error> { + match &self.debug { + Some(d) => Ok(Cow::Borrowed(d)), + None => self.getenv_unwrap_str("DEBUG").map(Cow::Owned), + } } fn get_shell_escaped_flags(&self) -> bool { diff --git a/src/tool.rs b/src/tool.rs index 5cfc63322..c2bcd5b6d 100644 --- a/src/tool.rs +++ b/src/tool.rs @@ -509,17 +509,46 @@ pub enum ToolFamily { impl ToolFamily { /// What the flag to request debug info for this family of tools look like - pub(crate) fn add_debug_flags(&self, cmd: &mut Tool, dwarf_version: Option) { + pub(crate) fn add_debug_flags( + &self, + cmd: &mut Tool, + debug_opt: &str, + dwarf_version: Option, + ) { match *self { ToolFamily::Msvc { .. } => { cmd.push_cc_arg("-Z7".into()); } ToolFamily::Gnu | ToolFamily::Clang { .. } => { - cmd.push_cc_arg( - dwarf_version - .map_or_else(|| "-g".into(), |v| format!("-gdwarf-{v}")) - .into(), - ); + match debug_opt { + // From https://doc.rust-lang.org/cargo/reference/profiles.html#debug + "" | "0" | "false" | "none" => { + debug_assert!( + false, + "earlier check should have avoided calling add_debug_flags" + ); + } + + // line-directives-only is LLVM-specific; for GCC we have to treat it like "1" + "line-directives-only" if cmd.is_like_clang() => { + cmd.push_cc_arg("-gline-directives-only".into()); + } + // Clang has -gline-tables-only, but it's an alias for -g1 anyway. + // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-gline-tables-only + "1" | "limited" | "line-tables-only" | "line-directives-only" => { + cmd.push_cc_arg("-g1".into()); + } + "2" | "true" | "full" => { + cmd.push_cc_arg("-g".into()); + } + _ => { + // Err on the side of including too much info rather than too little. + cmd.push_cc_arg("-g".into()); + } + } + if let Some(v) = dwarf_version { + cmd.push_cc_arg(format!("-gdwarf-{v}").into()); + } } } } diff --git a/tests/test.rs b/tests/test.rs index 698688057..eae9c332a 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -63,7 +63,10 @@ fn gnu_debug() { .debug(true) .file("foo.c") .compile("foo"); - test.cmd(0).must_have("-gdwarf-4"); + test.cmd(0) + .must_have("-g") + .must_not_have("-g1") + .must_have("-gdwarf-4"); let test = Test::gnu(); test.gcc() @@ -71,7 +74,31 @@ fn gnu_debug() { .debug(true) .file("foo.c") .compile("foo"); - test.cmd(0).must_have("-gdwarf-2"); + test.cmd(0) + .must_have("-g") + .must_not_have("-g1") + .must_have("-gdwarf-2"); +} + +#[test] +fn gnu_debug_limited() { + let test = Test::gnu(); + test.gcc().debug_str("limited").file("foo.c").compile("foo"); + test.cmd(0).must_not_have("-g").must_have("-g1"); +} + +#[test] +fn gnu_debug_none() { + let test = Test::gnu(); + test.gcc().debug_str("none").file("foo.c").compile("foo"); + test.cmd(0).must_not_have("-g").must_not_have("-g1"); +} + +#[test] +fn gnu_debug_unknown() { + let test = Test::gnu(); + test.gcc().debug_str("99").file("foo.c").compile("foo"); + test.cmd(0).must_have("-g").must_not_have("-g1"); } #[test]