Skip to content

Commit

Permalink
Merge branch 'main' into releases
Browse files Browse the repository at this point in the history
  • Loading branch information
UebelAndre authored Jan 10, 2025
2 parents 0034f4a + f86e01b commit b940436
Show file tree
Hide file tree
Showing 85 changed files with 1,332 additions and 313 deletions.
35 changes: 29 additions & 6 deletions .bazelci/presubmit.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
---
minimum_bazel_version: &minimum_bazel_version "7.4.1"
no_bzlmod_bazel_version: &no_bzlmod_bazel_version "7.4.1"
no_bzlmod_shell_commands: &no_bzlmod_shell_commands
- echo "common --noenable_bzlmod --enable_workspace" >> user.bazelrc
- echo "7.4.1" > .bazelversion
no_bzlmod_rbe_shell_commands: &no_bzlmod_rbe_shell_commands
- sed -i 's/^# load("@bazel_ci_rules/load("@bazel_ci_rules/' WORKSPACE.bazel
- sed -i 's/^# rbe_preconfig/rbe_preconfig/' WORKSPACE.bazel
- echo "common --noenable_bzlmod --enable_workspace" >> user.bazelrc
- echo "7.4.1" > .bazelversion
aspects_flags: &aspects_flags
- "--config=rustfmt"
- "--config=clippy"
Expand All @@ -16,12 +25,6 @@ bzlmod_flags: &bzlmod_flags
bzlmod_plus_repo_names_flags: &bzlmod_plus_repo_names_flags
# `--lockfile_mode=error` is omitted because the repo names leak into the lock file.
- "--incompatible_use_plus_in_repo_names"
no_bzlmod_shell_commands: &no_bzlmod_shell_commands
- echo "common --noenable_bzlmod --enable_workspace" >> user.bazelrc
no_bzlmod_rbe_shell_commands: &no_bzlmod_rbe_shell_commands
- sed -i 's/^# load("@bazel_ci_rules/load("@bazel_ci_rules/' WORKSPACE.bazel
- sed -i 's/^# rbe_preconfig/rbe_preconfig/' WORKSPACE.bazel
- echo "common --noenable_bzlmod --enable_workspace" >> user.bazelrc
single_rust_channel_targets: &single_rust_channel_targets
- "--"
- "//..."
Expand Down Expand Up @@ -116,6 +119,7 @@ tasks:
ubuntu2004_no_bzlmod:
name: No Bzlmod
platform: ubuntu2004
bazel: *no_bzlmod_bazel_version
shell_commands: *no_bzlmod_shell_commands
build_targets: *default_linux_targets
test_targets: *default_linux_targets
Expand All @@ -126,12 +130,14 @@ tasks:
rbe_ubuntu2004_no_bzlmod:
name: No Bzlmod
platform: rbe_ubuntu2004
bazel: *no_bzlmod_bazel_version
shell_commands: *no_bzlmod_rbe_shell_commands
build_targets: *default_linux_targets
test_targets: *default_linux_targets
macos_no_bzlmod:
name: No Bzlmod
platform: macos_arm64
bazel: *no_bzlmod_bazel_version
shell_commands: *no_bzlmod_shell_commands
build_targets: *default_macos_targets
test_targets: *default_macos_targets
Expand All @@ -140,6 +146,7 @@ tasks:
windows_no_bzlmod:
name: No Bzlmod
platform: windows
bazel: *no_bzlmod_bazel_version
shell_commands: *no_bzlmod_shell_commands
build_targets: *default_windows_targets
test_targets: *default_windows_targets
Expand Down Expand Up @@ -475,6 +482,22 @@ tasks:
working_directory: examples/crate_universe_local_path
run_targets:
- "//:vendor_edit_test_in_tree"
crate_universe_local_path_external_no_bzlmod:
name: Crate Universe Local Path External (No Bzlmod)
platform: ubuntu2004
bazel: *no_bzlmod_bazel_version
shell_commands: *no_bzlmod_shell_commands
working_directory: examples/crate_universe_local_path
run_targets:
- "//:vendor_edit_test_out_of_tree"
crate_universe_local_path_in_tree_no_bzlmod:
name: Crate Universe Local Path In Tree (No Bzlmod)
platform: ubuntu2004
bazel: *no_bzlmod_bazel_version
shell_commands: *no_bzlmod_shell_commands
working_directory: examples/crate_universe_local_path
run_targets:
- "//:vendor_edit_test_in_tree"
# See https://github.com/bazelbuild/rules_rust/issues/2186 about re-enabling these.
# crate_universe_examples_windows:
# name: Crate Universe Examples
Expand Down
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);
}
}
4 changes: 4 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ The oldest version of Bazel the `main` branch is tested against is `7.4.1`. Prev

We test these rules against the latest rolling releases of Bazel, and aim for compatibility with them, but prioritise stable releases over rolling releases where necessary.

### WORKSPACE support

WORKSPACE support is officially tested with Bazel 7 for as long as that is the min supported version. While it may work with later versions, compatibility with those versions is not guaranteed or actively verified.

## Supported platforms

We aim to support Linux and macOS.
Expand Down
8 changes: 7 additions & 1 deletion examples/all_deps_vendor/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,10 @@ register_toolchains("@rust_toolchains//:all")
###############################################################################
# R U S T C R A T E S
###############################################################################
use_extension("@rules_rust//crate_universe:extensions.bzl", "crate")

deps = use_extension("//:extensions.bzl", "rust_example")
use_repo(
deps,
"basic",
"basic__bzip2-0.3.3",
)
2 changes: 1 addition & 1 deletion examples/all_deps_vendor/basic/3rdparty/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ crates_vendor(
gen_build_script = True,
)],
},
cargo_lockfile = "Cargo.Bazel.lock",
cargo_lockfile = "Cargo.lock",
generate_build_scripts = False,
mode = "remote",
packages = {
Expand Down
23 changes: 23 additions & 0 deletions examples/all_deps_vendor/extensions.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Bzlmod module extensions"""

load("//basic/3rdparty/crates:crates.bzl", basic_crate_repositories = "crate_repositories")

def _rust_example_impl(module_ctx):
# This should contain the subset of WORKSPACE.bazel that defines
# repositories.
direct_deps = []

direct_deps.extend(basic_crate_repositories())

# is_dev_dep is ignored here. It's not relevant for internal_deps, as dev
# dependencies are only relevant for module extensions that can be used
# by other MODULES.
return module_ctx.extension_metadata(
root_module_direct_deps = [repo.repo for repo in direct_deps],
root_module_direct_dev_deps = [],
)

rust_example = module_extension(
doc = "Dependencies for the rules_rust examples.",
implementation = _rust_example_impl,
)
Loading

0 comments on commit b940436

Please sign in to comment.