Skip to content
Open
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
93 changes: 90 additions & 3 deletions crate_universe/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,42 @@ pub(crate) use self::crate_context::*;
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct Context {
/// The collective checksum of all inputs to the context
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) checksum: Option<Digest>,

/// The collection of all crates that make up the dependency graph
/// The collection of all crates that make up the dependency graph.
// Always emitted; the rendering templates reference `context.crates` and
// expect the key to be present.
pub(crate) crates: BTreeMap<CrateId, CrateContext>,

/// A subset of only crates with binary targets
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub(crate) binary_crates: BTreeSet<CrateId>,

/// A subset of workspace members mapping to their workspace
/// path relative to the workspace root
/// path relative to the workspace root.
// Always emitted; the rendering templates iterate `context.workspace_members`
// and expect the key to be present even when empty.
pub(crate) workspace_members: BTreeMap<CrateId, String>,

/// A mapping of `cfg` flags to platform triples supporting the configuration
#[serde(default)]
pub(crate) conditions: BTreeMap<String, BTreeSet<TargetTriple>>,

/// A list of crates visible to any bazel module.
// Always emitted; the rendering templates iterate `context.direct_deps`.
pub(crate) direct_deps: BTreeSet<CrateId>,

/// A list of crates visible to this bazel module.
// Always emitted; the rendering templates iterate `context.direct_dev_deps`.
pub(crate) direct_dev_deps: BTreeSet<CrateId>,

/// A list of `[patch]` entries from the Cargo.lock file which were not used in the resolve.
// TODO: Remove the serde(default) after this has been released for a few versions.
// This prevents previous lockfiles (from before this field) from failing to parse with the current version of rules_rust.
// After we've supported this (so serialised it in lockfiles) for a few versions,
// we can remove the default fallback because existing lockfiles should have the key present.
#[serde(default)]
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub(crate) unused_patches: BTreeSet<cargo_lock::Dependency>,
}

Expand Down Expand Up @@ -430,4 +439,82 @@ mod test {
// The data should be identical
assert_eq!(context, deserialized_context);
}

// Older cargo-bazel versions wrote every crate with the fully expanded
// form: `Select` as `{"common": [...], "selects": {}}`, `srcs` as a full
// `{"allow_empty": ..., "include": [...]}` object, explicit default
// `compile_data_glob: ["**"]`, `null` license/package_url, and a
// `common_attrs.version` copy of the parent version. New cargo-bazel
// versions must still read those verbose lockfiles losslessly (the extra
// fields that no longer exist in the schema are silently dropped by
// serde's default field handling).
#[test]
fn deserializes_legacy_verbose_lockfile() {
let legacy = r#"{
"checksum": "d94d3a74aa0e73ed1c9b8bd803bb6ecaaeaf258f7c3a937d4783aaf5891b31b0",
"crates": {
"anyhow 1.0.69": {
"name": "anyhow",
"version": "1.0.69",
"package_url": null,
"repository": null,
"targets": [
{
"Library": {
"crate_name": "anyhow",
"crate_root": "src/lib.rs",
"srcs": {
"allow_empty": false,
"include": ["**/*.rs"]
}
}
}
],
"library_target_name": "anyhow",
"common_attrs": {
"compile_data_glob": ["**"],
"crate_features": {
"common": ["default", "std"],
"selects": {}
},
"deps": {
"common": [],
"selects": {}
},
"edition": "2018",
"version": "1.0.69"
},
"license": null,
"license_ids": [],
"license_file": null
}
},
"binary_crates": [],
"workspace_members": {},
"conditions": {},
"direct_deps": [],
"direct_dev_deps": []
}"#;

let ctx: Context = serde_json::from_str(legacy).expect("legacy lockfile must parse");
let krate = ctx
.crates
.get(&CrateId::new("anyhow".to_owned(), Version::new(1, 0, 69)))
.expect("anyhow crate should be present");

assert_eq!(krate.name, "anyhow");
assert_eq!(krate.version, Version::new(1, 0, 69));
assert_eq!(krate.library_target_name.as_deref(), Some("anyhow"));
assert_eq!(krate.common_attrs.edition, "2018");
assert!(krate.common_attrs.compile_data_glob.contains("**"));
assert_eq!(
krate
.common_attrs
.crate_features
.values()
.into_iter()
.collect::<BTreeSet<_>>(),
BTreeSet::from(["default".to_owned(), "std".to_owned()]),
);
}
}
72 changes: 54 additions & 18 deletions crate_universe/src/context/crate_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ pub(crate) struct TargetAttributes {
pub(crate) crate_root: Option<String>,

/// A glob pattern of all source files required by the target
#[serde(
default = "Glob::default_rust_srcs",
skip_serializing_if = "Glob::is_default_rust_srcs"
)]
pub(crate) srcs: Glob,
}

Expand Down Expand Up @@ -89,6 +93,22 @@ impl Rule {
}
}

fn default_glob_all() -> BTreeSet<String> {
BTreeSet::from(["**".to_owned()])
}

fn is_default_glob_all(value: &BTreeSet<String>) -> bool {
*value == default_glob_all()
}

fn default_build_script_compile_data_glob_excludes() -> BTreeSet<String> {
BTreeSet::from(["**/*.rs".to_owned()])
}

fn is_default_build_script_compile_data_glob_excludes(value: &BTreeSet<String>) -> bool {
*value == default_build_script_compile_data_glob_excludes()
}

/// A set of attributes common to most `rust_library`, `rust_proc_macro`, and other
/// [core rules of `rules_rust`](https://bazelbuild.github.io/rules_rust/defs.html).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -97,7 +117,10 @@ pub(crate) struct CommonAttributes {
#[serde(skip_serializing_if = "Select::is_empty")]
pub(crate) compile_data: Select<BTreeSet<Label>>,

#[serde(skip_serializing_if = "BTreeSet::is_empty")]
#[serde(
default = "default_glob_all",
skip_serializing_if = "is_default_glob_all"
)]
pub(crate) compile_data_glob: BTreeSet<String>,

#[serde(skip_serializing_if = "BTreeSet::is_empty")]
Expand Down Expand Up @@ -147,8 +170,6 @@ pub(crate) struct CommonAttributes {
#[serde(skip_serializing_if = "Select::is_empty")]
pub(crate) rustc_flags: Select<Vec<String>>,

pub(crate) version: String,

#[serde(skip_serializing_if = "Vec::is_empty")]
pub(crate) tags: Vec<String>,
}
Expand All @@ -158,7 +179,7 @@ impl Default for CommonAttributes {
Self {
compile_data: Default::default(),
// Generated targets include all files in their package by default
compile_data_glob: BTreeSet::from(["**".to_owned()]),
compile_data_glob: default_glob_all(),
compile_data_glob_excludes: Default::default(),
crate_features: Default::default(),
data: Default::default(),
Expand All @@ -175,7 +196,6 @@ impl Default for CommonAttributes {
rustc_env: Default::default(),
rustc_env_files: Default::default(),
rustc_flags: Default::default(),
version: Default::default(),
tags: Default::default(),
}
}
Expand All @@ -189,16 +209,25 @@ pub(crate) struct BuildScriptAttributes {
#[serde(skip_serializing_if = "Select::is_empty")]
pub(crate) compile_data: Select<BTreeSet<Label>>,

#[serde(skip_serializing_if = "BTreeSet::is_empty")]
#[serde(
default = "default_glob_all",
skip_serializing_if = "is_default_glob_all"
)]
pub(crate) compile_data_glob: BTreeSet<String>,

#[serde(skip_serializing_if = "BTreeSet::is_empty")]
#[serde(
default = "default_build_script_compile_data_glob_excludes",
skip_serializing_if = "is_default_build_script_compile_data_glob_excludes"
)]
pub(crate) compile_data_glob_excludes: BTreeSet<String>,

#[serde(skip_serializing_if = "Select::is_empty")]
pub(crate) data: Select<BTreeSet<Label>>,

#[serde(skip_serializing_if = "BTreeSet::is_empty")]
#[serde(
default = "default_glob_all",
skip_serializing_if = "is_default_glob_all"
)]
pub(crate) data_glob: BTreeSet<String>,

#[serde(skip_serializing_if = "Select::is_empty")]
Expand Down Expand Up @@ -276,11 +305,11 @@ impl Default for BuildScriptAttributes {
compile_data: Default::default(),
// The build script itself also has access to all
// source files by default.
compile_data_glob: BTreeSet::from(["**".to_owned()]),
compile_data_glob_excludes: BTreeSet::from(["**/*.rs".to_owned()]),
compile_data_glob: default_glob_all(),
compile_data_glob_excludes: default_build_script_compile_data_glob_excludes(),
data: Default::default(),
// Build scripts include all sources by default
data_glob: BTreeSet::from(["**".to_owned()]),
data_glob: default_glob_all(),
deps: Default::default(),
extra_deps: Default::default(),
link_deps: Default::default(),
Expand Down Expand Up @@ -311,12 +340,15 @@ pub(crate) struct CrateContext {
pub(crate) version: semver::Version,

/// The package URL of the current crate
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) package_url: Option<String>,

/// Optional source annotations if they were discoverable in the
/// lockfile. Workspace Members will not have source annotations and
/// potentially others.
// Always emitted; rendering templates iterate `crate.repository` and
// Tera treats a missing variable inside `for`/attribute access as an
// error even when guarded by `{% if not ... %}`.
#[serde(default)]
pub(crate) repository: Option<SourceAnnotation>,

Expand All @@ -326,7 +358,7 @@ pub(crate) struct CrateContext {

/// The name of the crate's root library target. This is the target that a dependent
/// would get if they were to depend on `{crate_name}`.
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) library_target_name: Option<String>,

/// A set of attributes common to most [Rule] types or target types.
Expand All @@ -340,15 +372,15 @@ pub(crate) struct CrateContext {
pub(crate) build_script_attrs: Option<BuildScriptAttributes>,

/// The license used by the crate
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) license: Option<String>,

/// The SPDX licence IDs
/// #[serde(default)]
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub(crate) license_ids: BTreeSet<String>,

/// The license file
#[serde(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) license_file: Option<String>,

/// Additional text to add to the generated BUILD file.
Expand Down Expand Up @@ -434,15 +466,19 @@ impl CrateContext {
})
.unwrap_or_default();

// Gather all "common" attributes
// Gather all "common" attributes.
//
// Note: `version` is intentionally not populated here — the crate
// version is already captured on the parent `CrateContext` and is
// re-derived at render time. Keeping this field empty elides it from
// the serialized lockfile (see `CommonAttributes::version`).
let mut common_attrs = CommonAttributes {
crate_features,
deps,
deps_dev,
edition: package.edition.as_str().to_string(),
proc_macro_deps,
proc_macro_deps_dev,
version: package.version.to_string(),
..Default::default()
};

Expand Down
Loading
Loading