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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ cargo-platform = { path = "crates/cargo-platform", version = "0.3.3" }
cargo-test-macro = { version = "0.4.15", path = "crates/cargo-test-macro" }
cargo-test-support = { version = "0.12.0", path = "crates/cargo-test-support" }
cargo-util = { version = "0.2.33", path = "crates/cargo-util" }
cargo-util-schemas = { version = "0.14.4", path = "crates/cargo-util-schemas" }
cargo-util-schemas = { version = "0.15.0", path = "crates/cargo-util-schemas" }
cargo-util-terminal = { version = "0.1.3", path = "crates/cargo-util-terminal" }
cargo_metadata = "0.23.1"
clap = "4.6.0"
Expand Down
2 changes: 1 addition & 1 deletion crates/cargo-util-schemas/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cargo-util-schemas"
version = "0.14.4"
version = "0.15.0"
rust-version = "1.98" # MSRV:1
edition.workspace = true
license.workspace = true
Expand Down
7 changes: 7 additions & 0 deletions crates/cargo-util-schemas/src/core/source_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub enum SourceKind {
LocalRegistry,
/// A directory-based registry.
Directory,
/// Package sources distributed with the rust toolchain
Builtin,

@adamgemmell adamgemmell Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Existing comment from @epage regarding updating package_id_spec.rs #16675 (comment)

I opted to implement pkg id spec input/output in a separate PR after this sequence. We don't expect to see builtin pkg ids in cargo's output as they're not present in the resolve, and will be filtered from cargo metadata (see discussion at #16675 (comment))

View changes since the review

}

// The hash here is important for what folder packages get downloaded into.
Expand All @@ -40,6 +42,7 @@ impl SourceKind {
SourceKind::SparseRegistry => None,
SourceKind::LocalRegistry => Some("local-registry"),
SourceKind::Directory => Some("directory"),
SourceKind::Builtin => Some("builtin"),
}
}
}
Expand Down Expand Up @@ -71,6 +74,10 @@ impl Ord for SourceKind {
(_, SourceKind::Directory) => Ordering::Greater,

(SourceKind::Git(a), SourceKind::Git(b)) => a.cmp(b),
(SourceKind::Git(_), _) => Ordering::Less,
(_, SourceKind::Git(_)) => Ordering::Greater,

(SourceKind::Builtin, SourceKind::Builtin) => Ordering::Equal,
}
}
}
Expand Down
28 changes: 28 additions & 0 deletions crates/resolver-tests/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ impl<T: AsRef<str>, U: AsRef<str>> ToPkgId for (T, U) {
}
}

#[derive(Copy, Clone)]
pub struct BuiltinPid {
pub name: &'static str,
}

impl ToPkgId for BuiltinPid {
fn to_pkgid(&self) -> PackageId {
PackageId::try_new(self.name, "0.0.0", builtin_loc()).unwrap()
}
}

#[macro_export]
macro_rules! pkg {
($pkgid:expr => [$($deps:expr),* $(,)? ]) => ({
Expand All @@ -108,6 +119,13 @@ fn registry_loc() -> SourceId {
*example_dot
}

fn builtin_loc() -> SourceId {
static LOCAL_PATH: OnceLock<SourceId> = OnceLock::new();
let local_path = LOCAL_PATH
.get_or_init(|| SourceId::for_builtin(&std::env::current_dir().unwrap()).unwrap());
*local_path
}

pub fn pkg<T: ToPkgId>(name: T) -> Summary {
pkg_dep(name, Vec::new())
}
Expand Down Expand Up @@ -215,6 +233,10 @@ pub fn dep_loc(name: &str, location: &str) -> Dependency {
Dependency::parse(name, Some("1.0.0"), source_id).unwrap()
}

pub fn dep_builtin(name: &str) -> Dependency {
Dependency::parse(name, None, builtin_loc()).unwrap()
}

pub fn dep_kind(name: &str, kind: DepKind) -> Dependency {
let mut dep = dep(name);
dep.set_kind(kind);
Expand All @@ -235,6 +257,12 @@ pub fn names<P: ToPkgId>(names: &[P]) -> Vec<PackageId> {
names.iter().map(|name| name.to_pkgid()).collect()
}

/// For a set of name specifiers of varying types
#[macro_export]
macro_rules! names {
($($name:expr),* $(,)?) => {&vec![$($name.to_pkgid()),*]};
}

pub fn loc_names(names: &[(&'static str, &'static str)]) -> Vec<PackageId> {
names
.iter()
Expand Down
32 changes: 29 additions & 3 deletions crates/resolver-tests/tests/resolve.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use cargo::util::GlobalContext;
use cargo::workspace::Dependency;
use cargo::workspace::dependency::DepKind;
use resolver_tests::helpers::dep_builtin;
use snapbox::assert_data_eq;
use snapbox::str;

use resolver_tests::{
helpers::{
ToDep, ToPkgId, assert_contains, assert_same, dep, dep_kind, dep_loc, dep_req, loc_names,
names, pkg, pkg_dep, pkg_dep_with, pkg_id, pkg_loc, registry,
BuiltinPid, ToDep, ToPkgId, assert_contains, assert_same, dep, dep_kind, dep_loc, dep_req,
loc_names, names, pkg, pkg_dep, pkg_dep_with, pkg_id, pkg_loc, registry,
},
pkg, resolve, resolve_with_global_context,
names, pkg, resolve, resolve_with_global_context,
};

#[test]
Expand Down Expand Up @@ -1036,3 +1037,28 @@ failed to select a version for `F` which could resolve this conflict
"#]]
);
}

#[test]
fn test_builtin_dependency() {
let core = BuiltinPid { name: "core" };
let reg = registry(vec![pkg!(core)]);

let builtin_dep = dep_builtin("core");

let res = resolve(vec![builtin_dep], &reg).unwrap();

assert_same(&res, &names!("root", core));
}

#[test]
fn normal_dependency_is_not_satisfied_by_builtin_package() {
let core = BuiltinPid { name: "core" };
let reg = registry(vec![pkg!(core)]);

assert!(resolve(vec![dep("core")], &reg).is_err());
}

#[test]
fn missing_builtin_dependency_errors() {
assert!(resolve(vec![dep_builtin("core")], &registry(vec![])).is_err());
}
2 changes: 1 addition & 1 deletion src/resolver/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ pub fn encodable_package_id(
}

fn encodable_source_id(id: SourceId, version: ResolveVersion) -> Option<TomlLockfileSourceId> {
if id.is_path() {
if id.is_path() || id.is_builtin() {
None
} else {
Some(
Expand Down
15 changes: 15 additions & 0 deletions src/workspace/source_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,14 @@ impl SourceId {
SourceId::new(SourceKind::Path, url, None)
}

/// Creates a `SourceId` from a filesystem path representing a builtin package.
///
/// `path`: an absolute path.
pub fn for_builtin(path: &Path) -> CargoResult<SourceId> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we do path.is_absolute() check here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already checked by into_url() in this function. Should be enough unless you think we need to canonicalize here?

I've added an e2e test with a relative source path that does a conversion in detect_sysroot_src_path which I'll post in a separate PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually nevermind, I tried that with a real workflow and overriding the sysroot with a relative source path doesn't work without build-std anyway. The into_url() check should be sufficient.

let url = path.into_url()?;
SourceId::new(SourceKind::Builtin, url, None)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about fn stable_hash and impl Hash for builtin sources?

Currently in this PR, we treat builtin sources unique if they comes from different paths. However, what if they actually come form the same Rust version?

BTW, stable_hash is more for serialization and and cross-machine reproducibility , and Hash impl is for internal runtime uniqueness

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention is there's only ever one Url that builtin SourceIds are constructed with and so only one builtin source. If we can ensure this then implementation is simpler, and we cut down the RFC scope a bit to ensure assumptions like this will be possible.

I'm not sure how best to enforce that in code however other than something awkward like caching a Builtin SourceId in the gctx and ensuring we only use that. We could modify impl Hash to not hash URLs if the kind is builtin, but I'm worried that would just mask when this assumption is broken.

@adamgemmell adamgemmell Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for a bit of context not included in this PR, the packages returned by the builtin source are not built - the real Units will still come from the separate std resolve, which in theory can support patching, source replacement or whatever else the main resolve currently supports.

To ensure there's only ever one Builtin source loaded I could modify impl Hash, Eq, Ord etc to not consider the URL for builtins.

We'd then add a sanity check in PackageRegistry::ensure_loaded() whenever the cached SourceId's Url differs from the one we're trying to load, but I'm not sure what could go wrong in the meantime before this check happens.

@adamgemmell adamgemmell Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Existing comment from @epage regarding the fact that the URL in source ids is public facing: #16675 (comment)

Since then the URL used is a little more generic, and later work on the pkg id format will hide this further. The build-std goal task plan involves a thorough look at the output of each subcommand, so if this URL leaks later I expect to catch it.

View changes since the review

}

/// Creates a `SourceId` from a filesystem path.
///
/// `path`: an absolute path.
Expand Down Expand Up @@ -345,6 +353,11 @@ impl SourceId {
self.inner.kind == SourceKind::Path
}

/// Returns `true` if this source is built into Cargo
pub fn is_builtin(self) -> bool {
self.inner.kind == SourceKind::Builtin
}

/// Returns the local path if this is a path dependency.
pub fn local_path(self) -> Option<PathBuf> {
if self.inner.kind != SourceKind::Path {
Expand Down Expand Up @@ -403,6 +416,7 @@ impl SourceId {
}
Ok(Box::new(PathSource::new(&path, self, gctx)))
}
SourceKind::Builtin => todo!("builtin source"),
SourceKind::Registry | SourceKind::SparseRegistry => {
Ok(Box::new(RegistrySource::remote(self, gctx)?))
}
Expand Down Expand Up @@ -663,6 +677,7 @@ impl fmt::Display for SourceId {
Ok(())
}
SourceKind::Path => write!(f, "{}", url_display(&self.inner.url)),
SourceKind::Builtin => write!(f, "builtin {}", url_display(&self.inner.url)),
SourceKind::Registry | SourceKind::SparseRegistry => {
write!(f, "registry `{}`", self.display_registry_name())
}
Expand Down