Skip to content

Commit 769b584

Browse files
hofbiillicitonion
andauthored
Write lockfile external only (#3699)
## Description Another attempt to improve #3522 Current status/plan on the issue above was - pre-commit hook that converts the `Cargo.lock` file into a `Cargo.bazel.lock` file by removing internal deps - use `crate.from_cargo` with the `Cargo.bazel.lock` file and `skip_cargo_lockfile_overwrite = True` - commit `MODULE.bazel.lock` which should have less merge conflicts since the sha of `Cargo.bazel.lock` does not change that often This works, but we need to make sure `Cargo.lock` is up to date. This requires another step, so we replace a slow automated step (cargo splicing) with an extra manual/non-bazel step (updating the `Cargo.lock` file). New idea to iterate - use `crate.from_cargo` with the `Cargo.bazel.lock` file but instead of using `skip_cargo_lockfile_overwrite = True` - we let rules_rust do the cargo dependency resolution that is happening as part of splicing - instead of writing back the full content into the cargo lock file, we only write back the third party (making the pre-commit hook obsolete). For this, I introduced the `strip_internal_dependencies_from_cargo_lockfile` flag This would save us the pre-commit hook as well as the manual step to update the `Cargo.lock` file. I already did a quick verification and `remove_internal_dependencies_from_cargo_lockfile` achieves the same results as the python implementation I listed in #3522 ## Open Points This is an early draft to collect feedback. Once we aligned how exactly we want to proceed, I would like to - [ ] add/extend an integration test to have this tested end to end - [ ] add and example to demonstrate how this should be used --------- Co-authored-by: Daniel Wagner-Hall <[email protected]>
1 parent c6b9399 commit 769b584

File tree

19 files changed

+7538
-2689
lines changed

19 files changed

+7538
-2689
lines changed

crate_universe/extensions.bzl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ def _generate_hub_and_spokes(
559559
splicing_config,
560560
lockfile,
561561
skip_cargo_lockfile_overwrite,
562+
strip_internal_dependencies_from_cargo_lockfile,
562563
cargo_lockfile = None,
563564
manifests = {},
564565
packages = {}):
@@ -575,6 +576,11 @@ def _generate_hub_and_spokes(
575576
skip_cargo_lockfile_overwrite (bool): Whether to skip writing the cargo lockfile back after resolving.
576577
You may want to set this if your dependency versions are maintained externally through a non-trivial set-up.
577578
But you probably don't want to set this.
579+
strip_internal_dependencies_from_cargo_lockfile (bool): Whether to strip internal dependencies from the cargo lockfile.
580+
You may want to use this if you want to maintain a cargo lockfile for bazel only.
581+
Bazel only requires external dependencies to be present in the lockfile.
582+
By removing internal dependencies, the lockfile changes less frequently which reduces merge conflicts
583+
in other lockfiles where the cargo lockfile's sha is stored.
578584
cargo_lockfile (path): Path to Cargo.lock, if we have one.
579585
manifests (dict): The set of Cargo.toml manifests that apply to this closure, if any, keyed by path.
580586
packages (dict): The set of extra cargo crate tags that apply to this closure, if any, keyed by package name.
@@ -684,6 +690,7 @@ def _generate_hub_and_spokes(
684690
paths_to_track_file = paths_to_track_file,
685691
warnings_output_file = warnings_output_file,
686692
skip_cargo_lockfile_overwrite = skip_cargo_lockfile_overwrite,
693+
strip_internal_dependencies_from_cargo_lockfile = strip_internal_dependencies_from_cargo_lockfile,
687694
**kwargs
688695
)
689696

@@ -1169,6 +1176,7 @@ def _crate_impl(module_ctx):
11691176
manifests = manifests,
11701177
packages = packages,
11711178
skip_cargo_lockfile_overwrite = cfg.skip_cargo_lockfile_overwrite,
1179+
strip_internal_dependencies_from_cargo_lockfile = cfg.strip_internal_dependencies_from_cargo_lockfile,
11721180
)
11731181

11741182
metadata_kwargs = {}
@@ -1210,6 +1218,16 @@ _FROM_COMMON_ATTRS = {
12101218
),
12111219
default = False,
12121220
),
1221+
"strip_internal_dependencies_from_cargo_lockfile": attr.bool(
1222+
doc = (
1223+
"Whether to strip internal dependencies from the cargo lockfile. " +
1224+
"You may want to use this if you want to maintain a cargo lockfile for bazel only. " +
1225+
"Bazel only requires external dependencies to be present in the lockfile. " +
1226+
"By removing internal dependencies, the lockfile changes less frequently which reduces merge conflicts " +
1227+
"in other lockfiles where the cargo lockfile's sha is stored."
1228+
),
1229+
default = False,
1230+
),
12131231
"supported_platform_triples": attr.string_list(
12141232
doc = "A set of all platform triples to consider when generating dependencies.",
12151233
default = SUPPORTED_PLATFORM_TRIPLES,

crate_universe/private/crates_repository.bzl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def _crates_repository_impl(repository_ctx):
122122
paths_to_track_file = paths_to_track_file,
123123
warnings_output_file = warnings_output_file,
124124
skip_cargo_lockfile_overwrite = repository_ctx.attr.skip_cargo_lockfile_overwrite,
125+
strip_internal_dependencies_from_cargo_lockfile = repository_ctx.attr.strip_internal_dependencies_from_cargo_lockfile,
125126
# sysroot = tools.sysroot,
126127
**kwargs
127128
)
@@ -377,6 +378,16 @@ CARGO_BAZEL_REPIN=1 CARGO_BAZEL_REPIN_ONLY=crate_index bazel sync --only=crate_i
377378
"generate the value for this field. If unset, the defaults defined there will be used."
378379
),
379380
),
381+
"strip_internal_dependencies_from_cargo_lockfile": attr.bool(
382+
doc = (
383+
"Whether to strip internal dependencies from the cargo lockfile. " +
384+
"You may want to use this if you want to maintain a cargo lockfile for bazel only. " +
385+
"Bazel only requires external dependencies to be present in the lockfile. " +
386+
"By removing internal dependencies, the lockfile changes less frequently which reduces merge conflicts " +
387+
"in other lockfiles where the cargo lockfile's sha is stored."
388+
),
389+
default = False,
390+
),
380391
"supported_platform_triples": attr.string_list(
381392
doc = "A set of all platform triples to consider when generating dependencies.",
382393
default = SUPPORTED_PLATFORM_TRIPLES,

crate_universe/private/generate_utils.bzl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ def execute_generator(
441441
paths_to_track_file,
442442
warnings_output_file,
443443
skip_cargo_lockfile_overwrite,
444+
strip_internal_dependencies_from_cargo_lockfile,
444445
metadata = None,
445446
generator_label = None):
446447
"""Execute the `cargo-bazel` binary to produce `BUILD` and `.bzl` files.
@@ -458,6 +459,11 @@ def execute_generator(
458459
skip_cargo_lockfile_overwrite (bool): Whether to skip writing the cargo lockfile back after resolving.
459460
You may want to set this if your dependency versions are maintained externally through a non-trivial set-up.
460461
But you probably don't want to set this.
462+
strip_internal_dependencies_from_cargo_lockfile (bool): Whether to strip internal dependencies from the cargo lockfile.
463+
You may want to use this if you want to maintain a cargo lockfile for bazel only.
464+
Bazel only requires external dependencies to be present in the lockfile.
465+
By removing internal dependencies, the lockfile changes less frequently which reduces merge conflicts
466+
in other lockfiles where the cargo lockfile's sha is stored.
461467
generator_label (Label): The label of the `generator` parameter.
462468
metadata (path, optional): The path to a Cargo metadata json file. If this is set, it indicates to
463469
the generator that repinning is required. This file must be adjacent to a `Cargo.toml` and
@@ -499,6 +505,9 @@ def execute_generator(
499505
if skip_cargo_lockfile_overwrite:
500506
args.append("--skip-cargo-lockfile-overwrite")
501507

508+
if strip_internal_dependencies_from_cargo_lockfile:
509+
args.append("--strip-internal-dependencies-from-cargo-lockfile")
510+
502511
# Some components are not required unless re-pinning is enabled
503512
if metadata:
504513
args.extend([

crate_universe/src/cli/generate.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ pub struct GenerateOptions {
102102
/// But you probably don't want to set this.
103103
#[clap(long)]
104104
pub skip_cargo_lockfile_overwrite: bool,
105+
106+
/// Whether to strip internal dependencies from the cargo lockfile.
107+
/// You may want to use this if you want to maintain a cargo lockfile for bazel only.
108+
/// Bazel only requires external dependencies to be present in the lockfile.
109+
/// By removing internal dependencies, the lockfile changes less frequently which reduces merge conflicts
110+
/// in other lockfiles where the cargo lockfile's sha is stored.
111+
#[clap(long)]
112+
pub strip_internal_dependencies_from_cargo_lockfile: bool,
105113
}
106114

107115
pub fn generate(opt: GenerateOptions) -> Result<()> {
@@ -222,12 +230,31 @@ pub fn generate(opt: GenerateOptions) -> Result<()> {
222230
}
223231

224232
if !opt.skip_cargo_lockfile_overwrite {
225-
update_cargo_lockfile(&opt.cargo_lockfile, cargo_lockfile)?;
233+
let cargo_lockfile_to_write = if opt.strip_internal_dependencies_from_cargo_lockfile {
234+
remove_internal_dependencies_from_cargo_lockfile(cargo_lockfile)
235+
} else {
236+
cargo_lockfile
237+
};
238+
update_cargo_lockfile(&opt.cargo_lockfile, cargo_lockfile_to_write)?;
226239
}
227240

228241
Ok(())
229242
}
230243

244+
fn remove_internal_dependencies_from_cargo_lockfile(cargo_lockfile: Lockfile) -> Lockfile {
245+
let filtered_packages: Vec<_> = cargo_lockfile
246+
.packages
247+
.into_iter()
248+
// Filter packages to only keep external dependencies (those with a source)
249+
.filter(|pkg| pkg.source.is_some())
250+
.collect();
251+
252+
Lockfile {
253+
packages: filtered_packages,
254+
..cargo_lockfile
255+
}
256+
}
257+
231258
fn update_cargo_lockfile(path: &Path, cargo_lockfile: Lockfile) -> Result<()> {
232259
let old_contents = fs::read_to_string(path).ok();
233260
let new_contents = cargo_lockfile.to_string();
@@ -293,3 +320,34 @@ fn write_paths_to_track<
293320
.context("Failed to write warnings file")?;
294321
Ok(())
295322
}
323+
324+
#[cfg(test)]
325+
mod tests {
326+
use super::*;
327+
use crate::test;
328+
329+
#[test]
330+
fn test_remove_internal_dependencies_from_cargo_lockfile_workspace_build_scripts_deps_should_remove_internal_dependencies(
331+
) {
332+
let original_lockfile = test::lockfile::workspace_build_scripts_deps();
333+
334+
let filtered_lockfile =
335+
remove_internal_dependencies_from_cargo_lockfile(original_lockfile.clone());
336+
337+
assert!(filtered_lockfile.packages.len() < original_lockfile.packages.len());
338+
339+
assert!(original_lockfile
340+
.packages
341+
.iter()
342+
.any(|pkg| pkg.name.as_str() == "child"));
343+
assert!(!filtered_lockfile
344+
.packages
345+
.iter()
346+
.any(|pkg| pkg.name.as_str() == "child"));
347+
348+
assert!(filtered_lockfile
349+
.packages
350+
.iter()
351+
.any(|pkg| pkg.name.as_str() == "anyhow"));
352+
}
353+
}

examples/crate_universe/MODULE.bazel

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ local_path_override(
1616
)
1717

1818
bazel_dep(name = "platforms", version = "1.0.0")
19-
bazel_dep(name = "rules_cc", version = "0.2.4")
19+
bazel_dep(name = "rules_cc", version = "0.2.8")
2020
bazel_dep(name = "bazel_skylib", version = "1.8.2")
2121

2222
bazel_dep(name = "bazel_ci_rules", version = "1.0.0", dev_dependency = True)
23+
bazel_dep(name = "bazel_lib", version = "3.0.0", dev_dependency = True)
2324

2425
dev = use_extension("//:vendor_extensions.bzl", "dev", dev_dependency = True)
2526
use_repo(
@@ -345,6 +346,41 @@ use_repo(
345346
"crate_index_cargo_workspace",
346347
)
347348

349+
###############################################################################
350+
# R E M O V E I N T E R N A L D E P S
351+
###############################################################################
352+
353+
# https://bazelbuild.github.io/rules_rust/crate_universe_bzlmod.html
354+
# Stripping internal dependencies from the cargo lockfile
355+
crate_index_remove_internal_deps = use_extension("@rules_rust//crate_universe:extensions.bzl", "crate")
356+
crate_index_remove_internal_deps.from_cargo(
357+
name = "crate_index_remove_internal_deps",
358+
cargo_lockfile = "//remove_internal_deps:Cargo.bazel.lock",
359+
manifests = [
360+
"//remove_internal_deps:Cargo.toml",
361+
],
362+
strip_internal_dependencies_from_cargo_lockfile = True,
363+
)
364+
use_repo(
365+
crate_index_remove_internal_deps,
366+
"crate_index_remove_internal_deps",
367+
)
368+
369+
# https://bazelbuild.github.io/rules_rust/crate_universe_bzlmod.html
370+
# Not stripping internal dependencies from the cargo lockfile as baseline
371+
crate_index_no_strip_internal_deps = use_extension("@rules_rust//crate_universe:extensions.bzl", "crate")
372+
crate_index_no_strip_internal_deps.from_cargo(
373+
name = "crate_index_no_strip_internal_deps",
374+
cargo_lockfile = "//remove_internal_deps:Cargo.lock",
375+
manifests = [
376+
"//remove_internal_deps:Cargo.toml",
377+
],
378+
)
379+
use_repo(
380+
crate_index_no_strip_internal_deps,
381+
"crate_index_no_strip_internal_deps",
382+
)
383+
348384
###############################################################################
349385
# C A R G O C O N D I T I O N A L D E P S
350386
###############################################################################

0 commit comments

Comments
 (0)