Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .bazelci/presubmit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,12 @@ tasks:
build_targets:
- "@rust_toolchains//:all"
- "//..."
external_relative_path_deps:
name: External relative path deps (issue 3089)
platform: ubuntu2204
working_directory: test/integration/external_relative_path_deps
build_targets:
- "//..."
android_examples_ubuntu2204:
name: Android Examples
platform: ubuntu2204
Expand Down
8 changes: 7 additions & 1 deletion crate_universe/extensions.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,8 @@ def _generate_hub_and_spokes(
strip_internal_dependencies_from_cargo_lockfile,
cargo_lockfile = None,
manifests = {},
packages = {}):
packages = {},
main_manifest = None):
"""Generates repositories for the transitive closure of crates defined by manifests and packages.

Args:
Expand Down Expand Up @@ -618,6 +619,7 @@ def _generate_hub_and_spokes(
cargo_config = cfg.cargo_config,
manifests = manifests,
manifest_to_path = module_ctx.path,
main_manifest = main_manifest,
),
)

Expand Down Expand Up @@ -1155,10 +1157,13 @@ def _crate_impl(module_ctx):

manifests = {}
packages = {}
main_manifest = None

# Only `from_cargo` instances will have `manifests`.
if hasattr(cfg, "manifests"):
manifests = {str(module_ctx.path(m)): str(m) for m in cfg.manifests}
if cfg.manifests:
main_manifest = str(module_ctx.path(cfg.manifests[0]))

packages = {
p.package_alias or p.package: _package_to_json(p)
Expand All @@ -1178,6 +1183,7 @@ def _crate_impl(module_ctx):
packages = packages,
skip_cargo_lockfile_overwrite = cfg.skip_cargo_lockfile_overwrite,
strip_internal_dependencies_from_cargo_lockfile = cfg.strip_internal_dependencies_from_cargo_lockfile,
main_manifest = main_manifest,
)

metadata_kwargs = {}
Expand Down
4 changes: 3 additions & 1 deletion crate_universe/private/crates_vendor.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ def _write_splicing_manifest(ctx):
cargo_config = ctx.attr.cargo_config,
manifests = manifests,
manifest_to_path = _prepare_manifest_path,
main_manifest = _prepare_manifest_path(ctx.attr.manifests[0]) if ctx.attr.manifests else None,
),
)

Expand All @@ -240,7 +241,7 @@ def _write_splicing_manifest(ctx):
runfiles = [manifest] + ctx.files.manifests + ([ctx.file.cargo_config] if ctx.attr.cargo_config else [])
return args, env, runfiles

def generate_splicing_manifest(*, packages, splicing_config, cargo_config, manifests, manifest_to_path):
def generate_splicing_manifest(*, packages, splicing_config, cargo_config, manifests, manifest_to_path, main_manifest = None):
# Deserialize information about direct packages
direct_packages_info = {
# Ensure the data is using kebab-case as that's what `cargo_toml::DependencyDetail` expects.
Expand All @@ -252,6 +253,7 @@ def generate_splicing_manifest(*, packages, splicing_config, cargo_config, manif
"cargo_config": str(manifest_to_path(cargo_config)) if cargo_config else None,
"direct_packages": direct_packages_info,
"manifests": manifests,
"main_manifest": main_manifest,
}

return json.encode_indent(
Expand Down
16 changes: 13 additions & 3 deletions crate_universe/private/local_crate_mirror.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ load("//crate_universe/private:urls.bzl", "CARGO_BAZEL_SHA256S", "CARGO_BAZEL_UR
load("//rust/platform:triple.bzl", "get_host_triple")

def _local_crate_mirror_impl(repository_ctx):
path = repository_ctx.path(repository_ctx.attr.path)
raw_path = repository_ctx.attr.path
if raw_path.startswith("/"):
# Absolute path (backward compatibility)
path = repository_ctx.path(raw_path)
else:
# Workspace-relative path — resolve using anchor
workspace_root = repository_ctx.path(repository_ctx.attr._workspace_root_anchor).dirname
path = workspace_root.get_child(raw_path)

host_triple = get_host_triple(repository_ctx)

Expand Down Expand Up @@ -53,8 +60,11 @@ This is effectively a `local_repository` rule implementation, but where the `BUI
doc = "JSON serialized instance of a crate_universe::context::SingleBuildFileRenderContext",
),
"path": attr.string(
# TODO: Verify what happens if this is not an absolute path.
doc = "Absolute path to the BUILD.bazel file to generate.",
doc = "Path to the crate directory. Can be absolute or workspace-relative.",
),
"_workspace_root_anchor": attr.label(
default = "@@//:MODULE.bazel",
allow_single_file = True,
),
"quiet": attr.bool(
doc = "If stdout and stderr should not be printed to the terminal.",
Expand Down
9 changes: 8 additions & 1 deletion crate_universe/src/cli/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,14 @@ fn write_paths_to_track<
let source_annotation_manifests: BTreeSet<_> = source_annotations
.filter_map(|v| {
if let SourceAnnotation::Path { path } = v {
Some(path.join("Cargo.toml"))
let abs = if path.is_relative() {
nonhermetic_root_bazel_workspace_dir
.join(path)
.join("Cargo.toml")
} else {
path.join("Cargo.toml")
};
Some(abs)
} else {
None
}
Expand Down
29 changes: 29 additions & 0 deletions crate_universe/src/metadata/cargo_tree_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use url::Url;
use crate::config::CrateId;
use crate::metadata::cargo_bin::Cargo;
use crate::select::{Select, SelectableScalar};
use crate::splicing::splicer::symlink_external_path_deps;
use crate::utils::symlink::symlink;
use crate::utils::target_triple::TargetTriple;

Expand Down Expand Up @@ -526,6 +527,34 @@ impl TreeResolver {
)
})?;

// Symlink any path dependencies that live outside the workspace root
symlink_external_path_deps(
pristine_manifest_path.as_std_path(),
pristine_root.as_std_path(),
output_dir,
)?;

// Also handle workspace members' external path deps
let manifest = cargo_toml::Manifest::from_path(pristine_manifest_path.as_std_path())
.with_context(|| format!("Failed to parse manifest at {}", pristine_manifest_path))?;
if let Some(ref workspace) = manifest.workspace {
for member_pattern in &workspace.members {
let glob_pattern = pristine_root.join(member_pattern).to_string();
if let Ok(entries) = glob::glob(&glob_pattern) {
for entry in entries.flatten() {
let member_manifest = entry.join("Cargo.toml");
if member_manifest.exists() {
symlink_external_path_deps(
&member_manifest,
pristine_root.as_std_path(),
output_dir,
)?;
}
}
}
}
}

let cargo_metadata = self
.cargo_bin
.metadata_command_with_options(
Expand Down
31 changes: 27 additions & 4 deletions crate_universe/src/metadata/metadata_annotation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,8 @@ impl LockfileAnnotation {
new_path.push(relative_lockfile_path);
} else {
// If path in lockfile is not under Bazel root, we are
// likely in a temporary directory, so rebase to Bazel
// root.
new_path.push(nonhermetic_root_bazel_workspace_dir);
// likely in a temporary directory, so use
// workspace_prefix to get the workspace-relative path.
if let Some(prefix) =
workspace_metadata.workspace_prefix.as_ref()
{
Expand All @@ -318,7 +317,31 @@ impl LockfileAnnotation {
new_path.push(suffix);
new_path
}
Err(_) => Utf8PathBuf::from(path_in_lockfile),
Err(_) => {
// The path dep is outside the Cargo workspace root
// (e.g. an external path dep). When we nest the
// workspace at its repo-relative depth inside the
// temp dir, external deps are also at their correct
// repo-relative positions. Compute the temp dir root
// by stripping workspace_prefix from workspace_root,
// then rebase the path to the real Bazel workspace.
let temp_dir_root = workspace_metadata
.workspace_prefix
.as_ref()
.and_then(|prefix| {
metadata
.workspace_root
.as_str()
.strip_suffix(prefix.as_str())
});
if let Some(repo_relative) = temp_dir_root.and_then(|root| {
Utf8Path::new(path_in_lockfile).strip_prefix(root).ok()
}) {
Utf8PathBuf::from(repo_relative)
} else {
Utf8PathBuf::from(path_in_lockfile)
}
}
};
return Ok(SourceAnnotation::Path { path });
}
Expand Down
19 changes: 18 additions & 1 deletion crate_universe/src/splicing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

pub(crate) mod cargo_config;
mod crate_index_lookup;
mod splicer;
pub(crate) mod splicer;

use std::collections::{BTreeMap, BTreeSet};
use std::fs;
Expand Down Expand Up @@ -41,6 +41,11 @@ pub(crate) struct SplicingManifest {

/// The Cargo resolver version to use for splicing
pub(crate) resolver_version: cargo_toml::Resolver,

/// The path of the "main" manifest (first in the manifests list).
/// Used to disambiguate when manifests come from multiple Cargo workspaces.
#[serde(default)]
pub(crate) main_manifest: Option<Utf8PathBuf>,
}

impl FromStr for SplicingManifest {
Expand All @@ -61,6 +66,7 @@ impl SplicingManifest {
let Self {
manifests,
cargo_config,
main_manifest,
..
} = self;

Expand Down Expand Up @@ -88,9 +94,19 @@ impl SplicingManifest {
Utf8PathBuf::from(resolved_path)
});

// Resolve the main manifest path
let main_manifest = main_manifest.map(|path| {
let resolved_path = path
.to_string()
.replace("${build_workspace_directory}", &workspace_dir_str)
.replace("${output_base}", &output_base_str);
Utf8PathBuf::from(resolved_path)
});

Self {
manifests,
cargo_config,
main_manifest,
..self
}
}
Expand Down Expand Up @@ -678,6 +694,7 @@ mod test {
]),
cargo_config: None,
resolver_version: cargo_toml::Resolver::V2,
main_manifest: None,
};
let metadata = SplicingMetadata::try_from(manifest).unwrap();
let metadata = serde_json::to_string(&metadata).unwrap();
Expand Down
Loading