From 2b9d8ea75d4a5bb6df1248fe825273e49ffb02a2 Mon Sep 17 00:00:00 2001 From: Binlogo Date: Thu, 30 Oct 2025 19:09:40 +0800 Subject: [PATCH] feat: add support to specify the `package_name` attribute for generation --- crate_universe/extensions.bzl | 5 ++++ crate_universe/private/crates_repository.bzl | 4 +++ crate_universe/private/crates_vendor.bzl | 7 +++++ crate_universe/private/generate_utils.bzl | 4 +++ crate_universe/src/cli/splice.rs | 1 + crate_universe/src/cli/vendor.rs | 1 + crate_universe/src/config.rs | 5 ++++ .../src/metadata/cargo_tree_resolver.rs | 30 +++++++++++++------ 8 files changed, 48 insertions(+), 9 deletions(-) diff --git a/crate_universe/extensions.bzl b/crate_universe/extensions.bzl index da2830f085..62e4ef673d 100644 --- a/crate_universe/extensions.bzl +++ b/crate_universe/extensions.bzl @@ -598,6 +598,7 @@ def _generate_hub_and_spokes( workspace_name = cfg.name, generate_binaries = cfg.generate_binaries, render_config = render_config, + package_name = cfg.package_name, repository_ctx = module_ctx, ), ) @@ -1182,6 +1183,10 @@ _FROM_COMMON_ATTRS = { "cargo_lockfile": CRATES_VENDOR_ATTRS["cargo_lockfile"], "generate_binaries": CRATES_VENDOR_ATTRS["generate_binaries"], "generate_build_scripts": CRATES_VENDOR_ATTRS["generate_build_scripts"], + "package_name": attr.string( + doc = "The name of the package being generated", + mandatory = False, + ), "host_tools": attr.label( doc = "The `rust_host_tools` repository to use.", default = "@rust_host_tools", diff --git a/crate_universe/private/crates_repository.bzl b/crate_universe/private/crates_repository.bzl index 76e862c089..1e6c3a8f29 100644 --- a/crate_universe/private/crates_repository.bzl +++ b/crate_universe/private/crates_repository.bzl @@ -283,6 +283,10 @@ CARGO_BAZEL_REPIN=1 CARGO_BAZEL_REPIN_ONLY=crate_index bazel sync --only=crate_i ), default = True, ), + "package_name": attr.string( + doc = "The name of the package being generated", + mandatory = False, + ), "generate_target_compatible_with": attr.bool( doc = "DEPRECATED: Moved to `render_config`.", default = True, diff --git a/crate_universe/private/crates_vendor.bzl b/crate_universe/private/crates_vendor.bzl index ca18c11cd6..7a7904ba13 100644 --- a/crate_universe/private/crates_vendor.bzl +++ b/crate_universe/private/crates_vendor.bzl @@ -276,6 +276,7 @@ def _write_config_file(ctx): repository_name = ctx.attr.repository_name, output_pkg = _get_output_package(ctx), workspace_name = workspace_name, + package_name = ctx.attr.package_name, render_config = dict(json.decode(ctx.attr.render_config)) if ctx.attr.render_config else None, ), ) @@ -297,6 +298,7 @@ def generate_config_file( repository_name, output_pkg, workspace_name, + package_name, render_config, repository_ctx = None): """Writes the rendering config to cargo-bazel-config.json. @@ -313,6 +315,7 @@ def generate_config_file( repository_name (str): The name of the repository to generate. output_pkg: The path to the package containing the build files. workspace_name (str): The name of the workspace. + package_name (str): The name of the package. render_config: The render config to use. repository_ctx (repository_ctx, optional): A repository context object used for enabling certain functionality. @@ -389,6 +392,7 @@ def generate_config_file( render_config = render_config, supported_platform_triples = supported_platform_triples, repository_name = repository_name or ctx.label.name, + package_name = package_name, repository_ctx = repository_ctx, ) @@ -569,6 +573,9 @@ CRATES_VENDOR_ATTRS = { ], default = "remote", ), + "package_name": attr.string( + doc = "The name of the package being generated", + ), "packages": attr.string_dict( doc = "A set of crates (packages) specifications to depend on. See [crate.spec](#crate.spec).", ), diff --git a/crate_universe/private/generate_utils.bzl b/crate_universe/private/generate_utils.bzl index 7a5d1a0513..2ff8eab079 100644 --- a/crate_universe/private/generate_utils.bzl +++ b/crate_universe/private/generate_utils.bzl @@ -249,6 +249,7 @@ def compile_config( render_config, supported_platform_triples, repository_name, + package_name = None, repository_ctx = None): """Create a config file for generating crate targets @@ -264,6 +265,7 @@ def compile_config( render_config (dict): The deserialized dict of the `render_config` function. supported_platform_triples (list): A list of platform triples repository_name (str): The name of the repository being generated + package_name (str, optional): The name of the package being generated. repository_ctx (repository_ctx, optional): A repository context object used for enabling certain functionality. @@ -305,6 +307,7 @@ def compile_config( repository_name = repository_name, ), supported_platform_triples = supported_platform_triples, + package_name = package_name, ) return config @@ -328,6 +331,7 @@ def generate_config(repository_ctx): render_config = _get_render_config(repository_ctx), supported_platform_triples = repository_ctx.attr.supported_platform_triples, repository_name = repository_ctx.name, + package_name = repository_ctx.attr.package_name, repository_ctx = repository_ctx, ) diff --git a/crate_universe/src/cli/splice.rs b/crate_universe/src/cli/splice.rs index e2deae78eb..4c6aefe157 100644 --- a/crate_universe/src/cli/splice.rs +++ b/crate_universe/src/cli/splice.rs @@ -130,6 +130,7 @@ pub fn splice(opt: SpliceOptions) -> Result<()> { .generate( manifest_path.as_path_buf(), &config.supported_platform_triples, + &config.package_name, ) .context("Failed to generate features")?; diff --git a/crate_universe/src/cli/vendor.rs b/crate_universe/src/cli/vendor.rs index f7900635a2..a0ecf77528 100644 --- a/crate_universe/src/cli/vendor.rs +++ b/crate_universe/src/cli/vendor.rs @@ -234,6 +234,7 @@ pub fn vendor(opt: VendorOptions) -> anyhow::Result<()> { let resolver_data = TreeResolver::new(cargo.clone()).generate( manifest_path.as_path_buf(), &config.supported_platform_triples, + &config.package_name, )?; // Write the registry url info to the manifest now that a lockfile has been generated diff --git a/crate_universe/src/config.rs b/crate_universe/src/config.rs index 3d4496a826..af5a90f757 100644 --- a/crate_universe/src/config.rs +++ b/crate_universe/src/config.rs @@ -692,6 +692,11 @@ impl<'de> Visitor<'de> for GenBinariesVisitor { #[derive(Debug, Default, Serialize, Deserialize, Clone)] #[serde(deny_unknown_fields)] pub(crate) struct Config { + /// The name of a package to be the root of the crate graph. + /// If not specified, the workspace members will be used. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub(crate) package_name: Option, + /// Whether to generate `rust_binary` targets for all bins by default pub(crate) generate_binaries: bool, diff --git a/crate_universe/src/metadata/cargo_tree_resolver.rs b/crate_universe/src/metadata/cargo_tree_resolver.rs index 0dfb6a62d3..71ddb9dd53 100644 --- a/crate_universe/src/metadata/cargo_tree_resolver.rs +++ b/crate_universe/src/metadata/cargo_tree_resolver.rs @@ -112,6 +112,7 @@ impl TreeResolver { host_triples: &BTreeSet, target_triples: &BTreeSet, rustc_wrapper: &Path, + package_name: &Option, ) -> Result>>> { // A collection of all stdout logs from each process let mut stdouts: BTreeMap>> = BTreeMap::new(); @@ -169,16 +170,12 @@ impl TreeResolver { let manifest_path = manifest_path.to_owned(); let rustc_wrapper = rustc_wrapper.to_owned(); let cargo_bin = self.cargo_bin.clone(); + let package_name = package_name.clone(); in_flight.push(thread::spawn( move || -> anyhow::Result<(String, String, Output)> { - // We use `cargo tree` here because `cargo metadata` doesn't report - // back target-specific features (enabled with `resolver = "2"`). - // This is unfortunately a bit of a hack. See: - // - https://github.com/rust-lang/cargo/issues/9863 - // - https://github.com/bazelbuild/rules_rust/issues/1662 - let child = cargo_bin - .command()? + let mut cargo_bin = cargo_bin.command()?; + cargo_bin // These next two environment variables are used to hack cargo into using a custom // host triple instead of the host triple detected by rustc. .env("RUSTC_WRAPPER", &rustc_wrapper) @@ -199,9 +196,22 @@ impl TreeResolver { .arg("--format=;{p};{f};") .arg("--color=never") .arg("--charset=ascii") - .arg("--workspace") .arg("--target") - .arg(&cargo_target) + .arg(&cargo_target); + + // If a package name is given, use it. + if let Some(package) = package_name.as_ref().filter(|p| !p.is_empty()) { + cargo_bin.arg("--package").arg(package); + } else { // Otherwise, assume a workspace. + cargo_bin.arg("--workspace"); + } + + // We use `cargo tree` here because `cargo metadata` doesn't report + // back target-specific features (enabled with `resolver = "2"`). + // This is unfortunately a bit of a hack. See: + // - https://github.com/rust-lang/cargo/issues/9863 + // - https://github.com/bazelbuild/rules_rust/issues/1662 + let child = cargo_bin .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped()) .spawn() @@ -353,6 +363,7 @@ impl TreeResolver { &self, pristine_manifest_path: &Utf8Path, target_triples: &BTreeSet, + package_name: &Option, ) -> Result { debug!( "Generating features for manifest {}", @@ -387,6 +398,7 @@ impl TreeResolver { &host_triples, target_triples, &rustc_wrapper, + package_name, )?; let mut metadata: BTreeMap> =