diff --git a/crate_universe/extensions.bzl b/crate_universe/extensions.bzl index 2ec7465f8d..7327f36f55 100644 --- a/crate_universe/extensions.bzl +++ b/crate_universe/extensions.bzl @@ -591,6 +591,9 @@ def _generate_hub_and_spokes( workspace_name = cfg.name, generate_binaries = cfg.generate_binaries, render_config = render_config, + # The hub repository's `crates.bzl` lives outside the module being + # generated for, so first-party labels need an explicit repository. + cargo_lockfile_label = str(cfg.cargo_lockfile) if cfg.cargo_lockfile else None, repository_ctx = module_ctx, ), ) diff --git a/crate_universe/private/crates_vendor.bzl b/crate_universe/private/crates_vendor.bzl index d562c482a6..bd632a68a4 100644 --- a/crate_universe/private/crates_vendor.bzl +++ b/crate_universe/private/crates_vendor.bzl @@ -264,6 +264,12 @@ def _write_config_file(ctx): output_pkg = _get_output_package(ctx), workspace_name = workspace_name, render_config = dict(json.decode(ctx.attr.render_config)) if ctx.attr.render_config else None, + # Vendored output is committed into the workspace that owns the Cargo + # workspace, so first-party labels stay repository-relative. + cargo_lockfile_label = "//{}:{}".format( + ctx.attr.cargo_lockfile.package, + ctx.attr.cargo_lockfile.name, + ) if ctx.attr.cargo_lockfile else None, ), ) @@ -285,6 +291,7 @@ def generate_config_file( output_pkg, workspace_name, render_config, + cargo_lockfile_label = None, repository_ctx = None): """Writes the rendering config to cargo-bazel-config.json. @@ -301,6 +308,9 @@ def generate_config_file( output_pkg: The path to the package containing the build files. workspace_name (str): The name of the workspace. render_config: The render config to use. + cargo_lockfile_label (str, optional): The label of the `cargo_lockfile`. Its repository + is where `all_crate_deps(first_party = True)` looks for the Cargo workspace's own + crates. repository_ctx (repository_ctx, optional): A repository context object used for enabling certain functionality. @@ -378,6 +388,7 @@ def generate_config_file( render_config = render_config, supported_platform_triples = supported_platform_triples, repository_name = repository_name or ctx.label.name, + cargo_lockfile_label = cargo_lockfile_label, repository_ctx = repository_ctx, ) diff --git a/crate_universe/private/generate_utils.bzl b/crate_universe/private/generate_utils.bzl index 8e7e46fc95..9d00eac282 100644 --- a/crate_universe/private/generate_utils.bzl +++ b/crate_universe/private/generate_utils.bzl @@ -218,12 +218,15 @@ def _read_cargo_config(repository_ctx): return repository_ctx.read(config) return None -def _update_render_config(config, repository_name): - """Add the repository name to the render config +def _update_render_config(config, repository_name, cargo_lockfile_label): + """Add rendering details that come from the rule rather than the user Args: config (dict): A `render_config` struct repository_name (str): The name of the repository that owns the config + cargo_lockfile_label (str): The label of the rule's `cargo_lockfile`, or None. Its + repository is what `all_crate_deps(first_party = True)` renders labels against, + since that is the Bazel repository holding the Cargo workspace's own crates. Returns: struct: An updated `render_config`. @@ -232,6 +235,9 @@ def _update_render_config(config, repository_name): # Add the repository name as it's very relevant to rendering. config.update({"repository_name": repository_name}) + if cargo_lockfile_label: + config.update({"cargo_lockfile_label": cargo_lockfile_label}) + return struct(**config) def _get_render_config(repository_ctx): @@ -256,6 +262,7 @@ def compile_config( render_config, supported_platform_triples, repository_name, + cargo_lockfile_label = None, repository_ctx = None): """Create a config file for generating crate targets @@ -271,6 +278,9 @@ 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 + cargo_lockfile_label (str, optional): The label of the rule's `cargo_lockfile`. Used to + locate the Bazel repository that owns the Cargo workspace's own crates when + rendering `all_crate_deps(first_party = True)`. repository_ctx (repository_ctx, optional): A repository context object used for enabling certain functionality. @@ -310,6 +320,7 @@ def compile_config( rendering = _update_render_config( config = render_config, repository_name = repository_name, + cargo_lockfile_label = cargo_lockfile_label, ), supported_platform_triples = supported_platform_triples, ) @@ -335,6 +346,9 @@ 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, + # The hub repository's `crates.bzl` lives outside the workspace being + # generated for, so first-party labels need an explicit repository. + cargo_lockfile_label = str(repository_ctx.attr.cargo_lockfile), repository_ctx = repository_ctx, ) diff --git a/crate_universe/src/api/lockfile.rs b/crate_universe/src/api/lockfile.rs index d509f37d87..e05137fb9d 100644 --- a/crate_universe/src/api/lockfile.rs +++ b/crate_universe/src/api/lockfile.rs @@ -170,6 +170,7 @@ mod test { got_pkg_a.normal_deps().values(), vec![ CrateDependency { + workspace_member: false, id: CrateId { name: String::from("anyhow"), version: Version::new(1, 0, 69), @@ -179,6 +180,7 @@ mod test { local_path: None, }, CrateDependency { + workspace_member: false, id: CrateId { name: String::from("reqwest"), version: Version::new(0, 11, 14), diff --git a/crate_universe/src/config.rs b/crate_universe/src/config.rs index 6adb6b2afd..3f8c5f52df 100644 --- a/crate_universe/src/config.rs +++ b/crate_universe/src/config.rs @@ -132,6 +132,21 @@ pub(crate) struct RenderConfig { /// continue to write subpackage `BUILD.bazel`s into the hub repo directly. #[serde(default)] pub(crate) crates_vendor_synthesizes_subpackages: bool, + + /// Internal: the label of the rule's `cargo_lockfile`. Only its repository + /// is used, to locate the Bazel repository holding the Cargo workspace's + /// own crates so `all_crate_deps(first_party = True)` can emit labels for + /// them. Injected by the rules, never set by users — a workspace member's + /// Bazel package is already known (`Context::workspace_members`); the + /// repository is the one piece the renderer cannot infer. + /// + /// Excluded from the digest by [`crate::lockfile::Digest::new`], mirroring + /// `label_injection_mapping`: canonical repository names are consumer- + /// specific, so hashing this would make a root-level + /// `single_version_override` demand a producer-side repin that a + /// registry-distributed lockfile in a read-only cache can never perform. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub(crate) cargo_lockfile_label: Option