Skip to content

Commit

Permalink
Run bazel mod tidy after vendoring crate_universe outputs (#3177)
Browse files Browse the repository at this point in the history
`bazel mod tidy` will now run at the end of any `crates_vendor`
executions if the current workspace contains a `MODULE.bazel` file and
the current Bazel version is greater than `7.0.0`.

closes #2368
  • Loading branch information
UebelAndre authored Jan 9, 2025
1 parent 5367387 commit f86e01b
Show file tree
Hide file tree
Showing 69 changed files with 1,206 additions and 291 deletions.
2 changes: 1 addition & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ use_repo(
"libc",
"rtra",
"rtra__serde-1.0.217",
"rtra__serde_json-1.0.134",
"rtra__serde_json-1.0.135",
"rules_rust_test_load_arbitrary_tool",
"rules_rust_toolchain_test_target_json",
)
Expand Down
6 changes: 6 additions & 0 deletions cargo/private/cargo_toml_info/3rdparty/crates/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ filegroup(
)

# Workspace Member Dependencies
alias(
name = "cargo_toml-0.20.5",
actual = "@rrcti__cargo_toml-0.20.5//:cargo_toml",
tags = ["manual"],
)

alias(
name = "cargo_toml",
actual = "@rrcti__cargo_toml-0.20.5//:cargo_toml",
Expand Down
2 changes: 1 addition & 1 deletion cargo/private/cargo_toml_info/3rdparty/crates/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ def aliases(
_NORMAL_DEPENDENCIES = {
"": {
_COMMON_CONDITION: {
"cargo_toml": Label("@rrcti__cargo_toml-0.20.5//:cargo_toml"),
"cargo_toml": Label("@rrcti//:cargo_toml-0.20.5"),
},
},
}
Expand Down
2 changes: 1 addition & 1 deletion crate_universe/extensions.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,7 @@ def _crate_impl(module_ctx):

for mod in module_ctx.modules:
if not mod.tags.from_cargo and not mod.tags.from_specs:
fail("`.from_cargo` or `.from_specs` are required. Please update {}", mod.name)
fail("`.from_cargo` or `.from_specs` are required. Please update {}".format(mod.name))

local_repos = []
module_annotations = {}
Expand Down
170 changes: 148 additions & 22 deletions crate_universe/src/cli/vendor.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
//! The cli entrypoint for the `vendor` subcommand
use std::collections::BTreeSet;
use std::collections::{BTreeSet, HashMap};
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{self, ExitStatus};
use std::sync::Arc;

use anyhow::{bail, Context as AnyhowContext, Result};
use anyhow::{anyhow, bail, Context as AnyhowContext};
use camino::Utf8PathBuf;
use clap::Parser;

Expand Down Expand Up @@ -85,7 +85,7 @@ pub struct VendorOptions {
}

/// Run buildifier on a given file.
fn buildifier_format(bin: &Path, file: &Path) -> Result<ExitStatus> {
fn buildifier_format(bin: &Path, file: &Path) -> anyhow::Result<ExitStatus> {
let status = process::Command::new(bin)
.args(["-lint=fix", "-mode=fix", "-warnings=all"])
.arg(file)
Expand All @@ -99,35 +99,106 @@ fn buildifier_format(bin: &Path, file: &Path) -> Result<ExitStatus> {
Ok(status)
}

/// Query the Bazel output_base to determine the location of external repositories.
fn locate_bazel_output_base(bazel: &Path, workspace_dir: &Path) -> Result<PathBuf> {
// Allow a predefined environment variable to take precedent. This
// solves for the specific needs of Bazel CI on Github.
if let Ok(output_base) = env::var("OUTPUT_BASE") {
return Ok(PathBuf::from(output_base));
/// Run `bazel mod tidy` in a workspace.
fn bzlmod_tidy(bin: &Path, workspace_dir: &Path) -> anyhow::Result<ExitStatus> {
let status = process::Command::new(bin)
.current_dir(workspace_dir)
.arg("mod")
.arg("tidy")
.status()
.context("Failed to spawn Bazel process")?;

if !status.success() {
bail!(status)
}

let output = process::Command::new(bazel)
.current_dir(workspace_dir)
.args(["info", "output_base"])
.output()
.context("Failed to query the Bazel workspace's `output_base`")?;
Ok(status)
}

if !output.status.success() {
bail!(output.status)
/// Info about a Bazel workspace
struct BazelInfo {
/// The version of Bazel being used
release: semver::Version,

/// The location of the output_user_root.
output_base: PathBuf,
}

impl BazelInfo {
/// Construct a new struct based on the current binary and workspace paths provided.
fn try_new(bazel: &Path, workspace_dir: &Path) -> anyhow::Result<Self> {
let output = process::Command::new(bazel)
.current_dir(workspace_dir)
.arg("info")
.arg("release")
.arg("output_base")
.output()
.context("Failed to query the Bazel workspace's `output_base`")?;

if !output.status.success() {
bail!(output.status)
}

let output = String::from_utf8_lossy(output.stdout.as_slice());
let mut bazel_info: HashMap<String, String> = output
.trim()
.split('\n')
.map(|line| {
let (k, v) = line.split_at(
line.find(':')
.ok_or_else(|| anyhow!("missing `:` in bazel info output: `{}`", line))?,
);
Ok((k.to_string(), (v[1..]).trim().to_string()))
})
.collect::<anyhow::Result<HashMap<_, _>>>()?;

// Allow a predefined environment variable to take precedent. This
// solves for the specific needs of Bazel CI on Github.
if let Ok(path) = env::var("OUTPUT_BASE") {
bazel_info.insert("output_base".to_owned(), format!("output_base: {}", path));
};

BazelInfo::try_from(bazel_info)
}
}

Ok(PathBuf::from(
String::from_utf8_lossy(&output.stdout).trim(),
))
impl TryFrom<HashMap<String, String>> for BazelInfo {
type Error = anyhow::Error;

fn try_from(value: HashMap<String, String>) -> Result<Self, Self::Error> {
Ok(BazelInfo {
release: value
.get("release")
.map(|s| {
let mut r = s
.split_whitespace()
.last()
.ok_or_else(|| anyhow!("Unexpected release value: {}", s))?
.to_owned();

// Force release candidates to conform to semver.
if r.contains("rc") {
let (v, c) = r.split_once("rc").unwrap();
r = format!("{}-rc{}", v, c);
}

semver::Version::parse(&r).context("Failed to parse release version")
})
.ok_or(anyhow!("Failed to query Bazel release"))??,
output_base: value
.get("output_base")
.map(Into::into)
.ok_or(anyhow!("Failed to query Bazel output_base"))?,
})
}
}

pub fn vendor(opt: VendorOptions) -> Result<()> {
let output_base = locate_bazel_output_base(&opt.bazel, &opt.workspace_dir)?;
pub fn vendor(opt: VendorOptions) -> anyhow::Result<()> {
let bazel_info = BazelInfo::try_new(&opt.bazel, &opt.workspace_dir)?;

// Load the all config files required for splicing a workspace
let splicing_manifest = SplicingManifest::try_from_path(&opt.splicing_manifest)?
.resolve(&opt.workspace_dir, &output_base);
.resolve(&opt.workspace_dir, &bazel_info.output_base);

let temp_dir = tempfile::tempdir().context("Failed to create temporary directory")?;
let temp_dir_path = Utf8PathBuf::from_path_buf(temp_dir.as_ref().to_path_buf())
Expand Down Expand Up @@ -231,5 +302,60 @@ pub fn vendor(opt: VendorOptions) -> Result<()> {
}
}

// Optionally perform bazel mod tidy to update the MODULE.bazel file
if bazel_info.release >= semver::Version::new(7, 0, 0) {
let module_bazel = opt.workspace_dir.join("MODULE.bazel");
if module_bazel.exists() {
bzlmod_tidy(&opt.bazel, &opt.workspace_dir)?;
}
}

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_bazel_info() {
let raw_info = HashMap::from([
("release".to_owned(), "8.0.0".to_owned()),
("output_base".to_owned(), "/tmp/output_base".to_owned()),
]);

let info = BazelInfo::try_from(raw_info).unwrap();

assert_eq!(semver::Version::new(8, 0, 0), info.release);
assert_eq!(PathBuf::from("/tmp/output_base"), info.output_base);
}

#[test]
fn test_bazel_info_release_candidate() {
let raw_info = HashMap::from([
("release".to_owned(), "8.0.0rc1".to_owned()),
("output_base".to_owned(), "/tmp/output_base".to_owned()),
]);

let info = BazelInfo::try_from(raw_info).unwrap();

assert_eq!(semver::Version::parse("8.0.0-rc1").unwrap(), info.release);
assert_eq!(PathBuf::from("/tmp/output_base"), info.output_base);
}

#[test]
fn test_bazel_info_pre_release() {
let raw_info = HashMap::from([
("release".to_owned(), "9.0.0-pre.20241208.2".to_owned()),
("output_base".to_owned(), "/tmp/output_base".to_owned()),
]);

let info = BazelInfo::try_from(raw_info).unwrap();

assert_eq!(
semver::Version::parse("9.0.0-pre.20241208.2").unwrap(),
info.release
);
assert_eq!(PathBuf::from("/tmp/output_base"), info.output_base);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ rust_library(
"@rules_rust//rust/platform:x86_64-unknown-none": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
version = "1.2.6",
version = "1.2.7",
deps = [
"@rmdbi__shlex-1.3.0//:shlex",
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ rust_library(
"@rules_rust//rust/platform:x86_64-unknown-none": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
version = "4.5.23",
version = "4.5.26",
deps = [
"@rmdbi__clap_builder-4.5.23//:clap_builder",
"@rmdbi__clap_builder-4.5.26//:clap_builder",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ rust_library(
"@rules_rust//rust/platform:x86_64-unknown-none": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
version = "4.5.23",
version = "4.5.26",
deps = [
"@rmdbi__anstream-0.6.18//:anstream",
"@rmdbi__anstyle-1.0.10//:anstyle",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ rust_library(
"@rules_rust//rust/platform:x86_64-unknown-none": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
version = "4.5.40",
version = "4.5.42",
deps = [
"@rmdbi__clap-4.5.23//:clap",
"@rmdbi__clap-4.5.26//:clap",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
###############################################################################
# @generated
# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To
# regenerate this file, run the following:
#
# bazel run @@//private/3rdparty:crates_vendor
###############################################################################

load("@rules_rust//rust:defs.bzl", "rust_library")

package(default_visibility = ["//visibility:public"])

rust_library(
name = "darling",
srcs = glob(
include = ["**/*.rs"],
allow_empty = True,
),
compile_data = glob(
include = ["**"],
allow_empty = True,
exclude = [
"**/* *",
".tmp_git_root/**/*",
"BUILD",
"BUILD.bazel",
"WORKSPACE",
"WORKSPACE.bazel",
],
),
crate_features = [
"default",
"suggestions",
],
crate_root = "src/lib.rs",
edition = "2021",
proc_macro_deps = [
"@rmdbi__darling_macro-0.20.10//:darling_macro",
],
rustc_flags = [
"--cap-lints=allow",
],
tags = [
"cargo-bazel",
"crate-name=darling",
"manual",
"noclippy",
"norustfmt",
],
target_compatible_with = select({
"@rules_rust//rust/platform:aarch64-apple-darwin": [],
"@rules_rust//rust/platform:aarch64-apple-ios": [],
"@rules_rust//rust/platform:aarch64-apple-ios-sim": [],
"@rules_rust//rust/platform:aarch64-linux-android": [],
"@rules_rust//rust/platform:aarch64-pc-windows-msvc": [],
"@rules_rust//rust/platform:aarch64-unknown-fuchsia": [],
"@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [],
"@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [],
"@rules_rust//rust/platform:aarch64-unknown-nto-qnx710": [],
"@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [],
"@rules_rust//rust/platform:armv7-linux-androideabi": [],
"@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [],
"@rules_rust//rust/platform:i686-apple-darwin": [],
"@rules_rust//rust/platform:i686-linux-android": [],
"@rules_rust//rust/platform:i686-pc-windows-msvc": [],
"@rules_rust//rust/platform:i686-unknown-freebsd": [],
"@rules_rust//rust/platform:i686-unknown-linux-gnu": [],
"@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [],
"@rules_rust//rust/platform:riscv32imc-unknown-none-elf": [],
"@rules_rust//rust/platform:riscv64gc-unknown-none-elf": [],
"@rules_rust//rust/platform:s390x-unknown-linux-gnu": [],
"@rules_rust//rust/platform:thumbv7em-none-eabi": [],
"@rules_rust//rust/platform:thumbv8m.main-none-eabi": [],
"@rules_rust//rust/platform:wasm32-unknown-unknown": [],
"@rules_rust//rust/platform:wasm32-wasip1": [],
"@rules_rust//rust/platform:x86_64-apple-darwin": [],
"@rules_rust//rust/platform:x86_64-apple-ios": [],
"@rules_rust//rust/platform:x86_64-linux-android": [],
"@rules_rust//rust/platform:x86_64-pc-windows-msvc": [],
"@rules_rust//rust/platform:x86_64-unknown-freebsd": [],
"@rules_rust//rust/platform:x86_64-unknown-fuchsia": [],
"@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [],
"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [],
"@rules_rust//rust/platform:x86_64-unknown-none": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
version = "0.20.10",
deps = [
"@rmdbi__darling_core-0.20.10//:darling_core",
],
)
Loading

0 comments on commit f86e01b

Please sign in to comment.