Skip to content

Commit d861dcf

Browse files
committed
Allow named debuginfo options in Cargo.toml
Rustc supports these since rust-lang/rust#109808. It's technically possible to set a named debuginfo level through `RUSTFLAGS`, but in practice cargo always passes its own opinion of what the debuginfo level is, so allow it to be configured through cargo too.
1 parent 0e474cf commit d861dcf

File tree

7 files changed

+174
-39
lines changed

7 files changed

+174
-39
lines changed

src/cargo/core/compiler/mod.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ use crate::core::{Feature, PackageId, Target, Verbosity};
9292
use crate::util::errors::{CargoResult, VerboseError};
9393
use crate::util::interning::InternedString;
9494
use crate::util::machine_message::{self, Message};
95+
use crate::util::toml::TomlDebugInfo;
9596
use crate::util::{add_path_args, internal, iter_join_onto, profile};
9697
use cargo_util::{paths, ProcessBuilder, ProcessError};
9798
use rustfix::diagnostics::Applicability;
@@ -603,9 +604,20 @@ fn link_targets(cx: &mut Context<'_, '_>, unit: &Unit, fresh: bool) -> CargoResu
603604
}
604605

605606
if json_messages {
607+
let debuginfo = profile.debuginfo.to_option().map(|d| match d {
608+
TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0),
609+
TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1),
610+
TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2),
611+
TomlDebugInfo::LineDirectivesOnly => {
612+
machine_message::ArtifactDebuginfo::Named("line-directives-only")
613+
}
614+
TomlDebugInfo::LineTablesOnly => {
615+
machine_message::ArtifactDebuginfo::Named("line-tables-only")
616+
}
617+
});
606618
let art_profile = machine_message::ArtifactProfile {
607619
opt_level: profile.opt_level.as_str(),
608-
debuginfo: profile.debuginfo.to_option(),
620+
debuginfo,
609621
debug_assertions: profile.debug_assertions,
610622
overflow_checks: profile.overflow_checks,
611623
test: unit_mode.is_any_test(),

src/cargo/core/profiles.rs

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ use crate::core::dependency::Artifact;
2626
use crate::core::resolver::features::FeaturesFor;
2727
use crate::core::{PackageId, PackageIdSpec, Resolve, Shell, Target, Workspace};
2828
use crate::util::interning::InternedString;
29-
use crate::util::toml::{ProfilePackageSpec, StringOrBool, TomlProfile, TomlProfiles, U32OrBool};
29+
use crate::util::toml::{
30+
ProfilePackageSpec, StringOrBool, TomlDebugInfo, TomlProfile, TomlProfiles,
31+
};
3032
use crate::util::{closest_msg, config, CargoResult, Config};
3133
use anyhow::{bail, Context as _};
3234
use std::collections::{BTreeMap, HashMap, HashSet};
@@ -276,15 +278,13 @@ impl Profiles {
276278
// platform which has a stable `-Csplit-debuginfo` option for rustc,
277279
// and it's typically much faster than running `dsymutil` on all builds
278280
// in incremental cases.
279-
if let Some(debug) = profile.debuginfo.to_option() {
280-
if profile.split_debuginfo.is_none() && debug > 0 {
281-
let target = match &kind {
282-
CompileKind::Host => self.rustc_host.as_str(),
283-
CompileKind::Target(target) => target.short_name(),
284-
};
285-
if target.contains("-apple-") {
286-
profile.split_debuginfo = Some(InternedString::new("unpacked"));
287-
}
281+
if profile.debuginfo.is_turned_on() && profile.split_debuginfo.is_none() {
282+
let target = match &kind {
283+
CompileKind::Host => self.rustc_host.as_str(),
284+
CompileKind::Target(target) => target.short_name(),
285+
};
286+
if target.contains("-apple-") {
287+
profile.split_debuginfo = Some(InternedString::new("unpacked"));
288288
}
289289
}
290290

@@ -528,11 +528,8 @@ fn merge_profile(profile: &mut Profile, toml: &TomlProfile) {
528528
if toml.codegen_units.is_some() {
529529
profile.codegen_units = toml.codegen_units;
530530
}
531-
match toml.debug {
532-
Some(U32OrBool::U32(debug)) => profile.debuginfo = DebugInfo::Explicit(debug),
533-
Some(U32OrBool::Bool(true)) => profile.debuginfo = DebugInfo::Explicit(2),
534-
Some(U32OrBool::Bool(false)) => profile.debuginfo = DebugInfo::None,
535-
None => {}
531+
if let Some(debuginfo) = toml.debug {
532+
profile.debuginfo = DebugInfo::Explicit(debuginfo);
536533
}
537534
if let Some(debug_assertions) = toml.debug_assertions {
538535
profile.debug_assertions = debug_assertions;
@@ -683,7 +680,7 @@ impl Profile {
683680
Profile {
684681
name: InternedString::new("dev"),
685682
root: ProfileRoot::Debug,
686-
debuginfo: DebugInfo::Explicit(2),
683+
debuginfo: DebugInfo::Explicit(TomlDebugInfo::Full),
687684
debug_assertions: true,
688685
overflow_checks: true,
689686
incremental: true,
@@ -743,7 +740,7 @@ pub enum DebugInfo {
743740
/// No debuginfo level was set.
744741
None,
745742
/// A debuginfo level that is explicitly set, by a profile or a user.
746-
Explicit(u32),
743+
Explicit(TomlDebugInfo),
747744
/// For internal purposes: a deferred debuginfo level that can be optimized
748745
/// away, but has this value otherwise.
749746
///
@@ -753,22 +750,22 @@ pub enum DebugInfo {
753750
/// faster to build (see [DebugInfo::weaken]).
754751
///
755752
/// In all other situations, this level value will be the one to use.
756-
Deferred(u32),
753+
Deferred(TomlDebugInfo),
757754
}
758755

759756
impl DebugInfo {
760757
/// The main way to interact with this debuginfo level, turning it into an Option.
761-
pub fn to_option(&self) -> Option<u32> {
758+
pub fn to_option(self) -> Option<TomlDebugInfo> {
762759
match self {
763760
DebugInfo::None => None,
764-
DebugInfo::Explicit(v) | DebugInfo::Deferred(v) => Some(*v),
761+
DebugInfo::Explicit(v) | DebugInfo::Deferred(v) => Some(v),
765762
}
766763
}
767764

768765
/// Returns true if the debuginfo level is high enough (at least 1). Helper
769766
/// for a common operation on the usual `Option` representation.
770767
pub(crate) fn is_turned_on(&self) -> bool {
771-
self.to_option().unwrap_or(0) != 0
768+
!matches!(self.to_option(), None | Some(TomlDebugInfo::None))
772769
}
773770

774771
pub(crate) fn is_deferred(&self) -> bool {

src/cargo/util/machine_message.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,20 @@ impl<'a> Message for Artifact<'a> {
5555
#[derive(Serialize)]
5656
pub struct ArtifactProfile {
5757
pub opt_level: &'static str,
58-
pub debuginfo: Option<u32>,
58+
pub debuginfo: Option<ArtifactDebuginfo>,
5959
pub debug_assertions: bool,
6060
pub overflow_checks: bool,
6161
pub test: bool,
6262
}
6363

64+
/// Internally this is an enum with different variants, but keep using 0/1/2 as integers for compatibility.
65+
#[derive(Serialize)]
66+
#[serde(untagged)]
67+
pub enum ArtifactDebuginfo {
68+
Int(u32),
69+
Named(&'static str),
70+
}
71+
6472
#[derive(Serialize)]
6573
pub struct BuildScript<'a> {
6674
pub package_id: PackageId,

src/cargo/util/toml/mod.rs

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::collections::{BTreeMap, BTreeSet, HashMap};
2-
use std::fmt;
2+
use std::fmt::{self, Display, Write};
33
use std::marker::PhantomData;
44
use std::path::{Path, PathBuf};
55
use std::rc::Rc;
@@ -12,8 +12,8 @@ use itertools::Itertools;
1212
use lazycell::LazyCell;
1313
use log::{debug, trace};
1414
use semver::{self, VersionReq};
15-
use serde::de;
1615
use serde::de::IntoDeserializer as _;
16+
use serde::de::{self, Unexpected};
1717
use serde::ser;
1818
use serde::{Deserialize, Serialize};
1919
use url::Url;
@@ -442,11 +442,96 @@ impl ser::Serialize for TomlOptLevel {
442442
}
443443
}
444444

445-
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
446-
#[serde(untagged, expecting = "expected a boolean or an integer")]
447-
pub enum U32OrBool {
448-
U32(u32),
449-
Bool(bool),
445+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
446+
pub enum TomlDebugInfo {
447+
None,
448+
LineDirectivesOnly,
449+
LineTablesOnly,
450+
Limited,
451+
Full,
452+
}
453+
454+
impl ser::Serialize for TomlDebugInfo {
455+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
456+
where
457+
S: ser::Serializer,
458+
{
459+
match self {
460+
Self::None => 0.serialize(serializer),
461+
Self::LineDirectivesOnly => "line-directives-only".serialize(serializer),
462+
Self::LineTablesOnly => "line-tables-only".serialize(serializer),
463+
Self::Limited => 1.serialize(serializer),
464+
Self::Full => 2.serialize(serializer),
465+
}
466+
}
467+
}
468+
469+
impl<'de> de::Deserialize<'de> for TomlDebugInfo {
470+
fn deserialize<D>(d: D) -> Result<TomlDebugInfo, D::Error>
471+
where
472+
D: de::Deserializer<'de>,
473+
{
474+
struct Visitor;
475+
476+
impl<'de> de::Visitor<'de> for Visitor {
477+
type Value = TomlDebugInfo;
478+
479+
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
480+
formatter
481+
.write_str("a boolean, 0-2, \"line-tables-only\", or \"line-directives-only\"")
482+
}
483+
484+
fn visit_i64<E>(self, value: i64) -> Result<TomlDebugInfo, E>
485+
where
486+
E: de::Error,
487+
{
488+
let debuginfo = match value {
489+
0 => TomlDebugInfo::None,
490+
1 => TomlDebugInfo::Limited,
491+
2 => TomlDebugInfo::Full,
492+
_ => return Err(de::Error::invalid_value(Unexpected::Signed(value), &self)),
493+
};
494+
Ok(debuginfo)
495+
}
496+
497+
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
498+
where
499+
E: de::Error,
500+
{
501+
Ok(if v {
502+
TomlDebugInfo::Full
503+
} else {
504+
TomlDebugInfo::None
505+
})
506+
}
507+
508+
fn visit_str<E>(self, value: &str) -> Result<TomlDebugInfo, E>
509+
where
510+
E: de::Error,
511+
{
512+
let debuginfo = match value {
513+
"line-directives-only" => TomlDebugInfo::LineDirectivesOnly,
514+
"line-tables-only" => TomlDebugInfo::LineTablesOnly,
515+
_ => return Err(de::Error::invalid_value(Unexpected::Str(value), &self)),
516+
};
517+
Ok(debuginfo)
518+
}
519+
}
520+
521+
d.deserialize_any(Visitor)
522+
}
523+
}
524+
525+
impl Display for TomlDebugInfo {
526+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
527+
match self {
528+
TomlDebugInfo::None => f.write_char('0'),
529+
TomlDebugInfo::Limited => f.write_char('1'),
530+
TomlDebugInfo::Full => f.write_char('2'),
531+
TomlDebugInfo::LineDirectivesOnly => f.write_str("line-directives-only"),
532+
TomlDebugInfo::LineTablesOnly => f.write_str("line-tables-only"),
533+
}
534+
}
450535
}
451536

452537
#[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
@@ -456,7 +541,7 @@ pub struct TomlProfile {
456541
pub lto: Option<StringOrBool>,
457542
pub codegen_backend: Option<InternedString>,
458543
pub codegen_units: Option<u32>,
459-
pub debug: Option<U32OrBool>,
544+
pub debug: Option<TomlDebugInfo>,
460545
pub split_debuginfo: Option<String>,
461546
pub debug_assertions: Option<bool>,
462547
pub rpath: Option<bool>,

tests/testsuite/bad_config.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1313,14 +1313,46 @@ fn bad_debuginfo() {
13131313
.file("src/lib.rs", "")
13141314
.build();
13151315

1316+
p.cargo("check")
1317+
.with_status(101)
1318+
.with_stderr(
1319+
"\
1320+
error: failed to parse manifest [..]
1321+
1322+
Caused by:
1323+
invalid value: string \"a\", expected a boolean, 0-2, \"line-tables-only\", or \"line-directives-only\"
1324+
in `profile.dev.debug`
1325+
",
1326+
)
1327+
.run();
1328+
}
1329+
1330+
#[cargo_test]
1331+
fn bad_debuginfo2() {
1332+
let p = project()
1333+
.file(
1334+
"Cargo.toml",
1335+
r#"
1336+
[package]
1337+
name = "foo"
1338+
version = "0.0.0"
1339+
authors = []
1340+
1341+
[profile.dev]
1342+
debug = 3.6
1343+
"#,
1344+
)
1345+
.file("src/lib.rs", "")
1346+
.build();
1347+
13161348
p.cargo("check")
13171349
.with_status(101)
13181350
.with_stderr(
13191351
"\
13201352
error: failed to parse manifest at `[..]`
13211353
13221354
Caused by:
1323-
expected a boolean or an integer
1355+
invalid type: floating point `3.6`, expected a boolean, 0-2, \"line-tables-only\", or \"line-directives-only\"
13241356
in `profile.dev.debug`
13251357
",
13261358
)

tests/testsuite/config.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ lto = false
401401
opt_level: Some(cargo_toml::TomlOptLevel("s".to_string())),
402402
lto: Some(cargo_toml::StringOrBool::Bool(true)),
403403
codegen_units: Some(5),
404-
debug: Some(cargo_toml::U32OrBool::Bool(true)),
404+
debug: Some(cargo_toml::TomlDebugInfo::Full),
405405
debug_assertions: Some(true),
406406
rpath: Some(true),
407407
panic: Some("abort".to_string()),
@@ -444,15 +444,15 @@ fn profile_env_var_prefix() {
444444
.build();
445445
let p: cargo_toml::TomlProfile = config.get("profile.dev").unwrap();
446446
assert_eq!(p.debug_assertions, None);
447-
assert_eq!(p.debug, Some(cargo_toml::U32OrBool::U32(1)));
447+
assert_eq!(p.debug, Some(cargo_toml::TomlDebugInfo::Limited));
448448

449449
let config = ConfigBuilder::new()
450450
.env("CARGO_PROFILE_DEV_DEBUG_ASSERTIONS", "false")
451451
.env("CARGO_PROFILE_DEV_DEBUG", "1")
452452
.build();
453453
let p: cargo_toml::TomlProfile = config.get("profile.dev").unwrap();
454454
assert_eq!(p.debug_assertions, Some(false));
455-
assert_eq!(p.debug, Some(cargo_toml::U32OrBool::U32(1)));
455+
assert_eq!(p.debug, Some(cargo_toml::TomlDebugInfo::Limited));
456456
}
457457

458458
#[cargo_test]
@@ -1511,7 +1511,7 @@ fn all_profile_options() {
15111511
lto: Some(cargo_toml::StringOrBool::String("thin".to_string())),
15121512
codegen_backend: Some(InternedString::new("example")),
15131513
codegen_units: Some(123),
1514-
debug: Some(cargo_toml::U32OrBool::U32(1)),
1514+
debug: Some(cargo_toml::TomlDebugInfo::Limited),
15151515
split_debuginfo: Some("packed".to_string()),
15161516
debug_assertions: Some(true),
15171517
rpath: Some(true),

tests/testsuite/profile_config.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Tests for profiles defined in config files.
22
3+
use cargo::util::toml::TomlDebugInfo;
34
use cargo_test_support::paths::CargoPathExt;
45
use cargo_test_support::registry::Package;
56
use cargo_test_support::{basic_lib_manifest, paths, project};
@@ -436,7 +437,7 @@ fn named_config_profile() {
436437
assert_eq!(p.name, "foo");
437438
assert_eq!(p.codegen_units, Some(2)); // "foo" from config
438439
assert_eq!(p.opt_level, "1"); // "middle" from manifest
439-
assert_eq!(p.debuginfo.to_option(), Some(1)); // "bar" from config
440+
assert_eq!(p.debuginfo.to_option(), Some(TomlDebugInfo::Limited)); // "bar" from config
440441
assert_eq!(p.debug_assertions, true); // "dev" built-in (ignore build-override)
441442
assert_eq!(p.overflow_checks, true); // "dev" built-in (ignore package override)
442443

@@ -445,7 +446,7 @@ fn named_config_profile() {
445446
assert_eq!(bo.name, "foo");
446447
assert_eq!(bo.codegen_units, Some(6)); // "foo" build override from config
447448
assert_eq!(bo.opt_level, "0"); // default to zero
448-
assert_eq!(bo.debuginfo.to_option(), Some(1)); // SAME as normal
449+
assert_eq!(bo.debuginfo.to_option(), Some(TomlDebugInfo::Limited)); // SAME as normal
449450
assert_eq!(bo.debug_assertions, false); // "foo" build override from manifest
450451
assert_eq!(bo.overflow_checks, true); // SAME as normal
451452

@@ -454,7 +455,7 @@ fn named_config_profile() {
454455
assert_eq!(po.name, "foo");
455456
assert_eq!(po.codegen_units, Some(7)); // "foo" package override from config
456457
assert_eq!(po.opt_level, "1"); // SAME as normal
457-
assert_eq!(po.debuginfo.to_option(), Some(1)); // SAME as normal
458+
assert_eq!(po.debuginfo.to_option(), Some(TomlDebugInfo::Limited)); // SAME as normal
458459
assert_eq!(po.debug_assertions, true); // SAME as normal
459460
assert_eq!(po.overflow_checks, false); // "middle" package override from manifest
460461
}

0 commit comments

Comments
 (0)