From 4d905b163004c9e7d39b85aa5e031dedc3284f23 Mon Sep 17 00:00:00 2001 From: Jon Gjengset Date: Tue, 22 Feb 2022 14:41:34 -0800 Subject: [PATCH 1/3] Make std-lib workspace construction public This allows tools that consume Cargo as a library to locate a `std` Workspace without _also_ having to resolve it and possibly download all of its dependencies (as `resolve_ws_with_opts` does). --- src/cargo/core/compiler/standard_lib.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index bf159e1781f..4837a584f0f 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -6,7 +6,7 @@ use crate::core::compiler::{CompileKind, CompileMode, RustcTargetData, Unit}; use crate::core::profiles::{Profiles, UnitFor}; use crate::core::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures}; use crate::core::resolver::HasDevUnits; -use crate::core::{Dependency, PackageId, PackageSet, Resolve, SourceId, Workspace}; +use crate::core::{Dependency, PackageId, PackageIdSpec, PackageSet, Resolve, SourceId, Workspace}; use crate::ops::{self, Packages}; use crate::util::errors::CargoResult; use std::collections::{HashMap, HashSet}; @@ -31,13 +31,12 @@ pub fn parse_unstable_flag(value: Option<&str>) -> Vec { crates.into_iter().map(|s| s.to_string()).collect() } -/// Resolve the standard library dependencies. -pub fn resolve_std<'cfg>( +/// Make a Workspace representing the standard library. +pub fn make_std_ws<'cfg>( ws: &Workspace<'cfg>, target_data: &RustcTargetData<'cfg>, - requested_targets: &[CompileKind], crates: &[String], -) -> CargoResult<(PackageSet<'cfg>, Resolve, ResolvedFeatures)> { +) -> CargoResult<(Workspace<'cfg>, CliFeatures, Vec)> { let src_path = detect_sysroot_src_path(target_data)?; let to_patch = [ "rustc-std-workspace-core", @@ -111,6 +110,19 @@ pub fn resolve_std<'cfg>( let cli_features = CliFeatures::from_command_line( &features, /*all_features*/ false, /*uses_default_features*/ false, )?; + + Ok((std_ws, cli_features, specs)) +} + +/// Resolve the standard library dependencies. +pub fn resolve_std<'cfg>( + ws: &Workspace<'cfg>, + target_data: &RustcTargetData<'cfg>, + requested_targets: &[CompileKind], + crates: &[String], +) -> CargoResult<(PackageSet<'cfg>, Resolve, ResolvedFeatures)> { + let (std_ws, cli_features, specs) = make_std_ws(ws, target_data, crates)?; + let resolve = ops::resolve_ws_with_opts( &std_ws, target_data, From 1b5ca5bc8aea74f8f48cb511313cdccff2690c78 Mon Sep 17 00:00:00 2001 From: Jon Gjengset Date: Tue, 22 Feb 2022 14:46:59 -0800 Subject: [PATCH 2/3] Expose more install building blocks Many of these functions were already `pub`, but weren't re-exported by a `pub` module. These functions help users who consume Cargo as a library if they want to implement variants of `cargo install` themselves. --- src/cargo/core/package.rs | 4 +++- src/cargo/ops/cargo_install.rs | 5 +++-- src/cargo/ops/mod.rs | 5 ++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index e8685610aeb..5dd3873d684 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -609,7 +609,9 @@ impl<'cfg> PackageSet<'cfg> { Ok(()) } - fn filter_deps<'a>( + /// Filter the dependency closure of the given package to only those dependencies needed in the + /// given configuration. + pub fn filter_deps<'a>( pkg_id: PackageId, resolve: &'a Resolve, has_dev_units: HasDevUnits, diff --git a/src/cargo/ops/cargo_install.rs b/src/cargo/ops/cargo_install.rs index 4380d3f48c1..f20d57daf55 100644 --- a/src/cargo/ops/cargo_install.rs +++ b/src/cargo/ops/cargo_install.rs @@ -700,7 +700,8 @@ where Ok(None) } -fn make_ws_rustc_target<'cfg>( +/// Make a `Workspace` for the given to-be-installed package. +pub fn make_ws_rustc_target<'cfg>( config: &'cfg Config, opts: &ops::CompileOptions, source_id: &SourceId, @@ -725,7 +726,7 @@ fn make_ws_rustc_target<'cfg>( /// Parses x.y.z as if it were =x.y.z, and gives CLI-specific error messages in the case of invalid /// values. -fn parse_semver_flag(v: &str) -> CargoResult { +pub fn parse_semver_flag(v: &str) -> CargoResult { // If the version begins with character <, >, =, ^, ~ parse it as a // version range, otherwise parse it as a specific version let first = v diff --git a/src/cargo/ops/mod.rs b/src/cargo/ops/mod.rs index e81486f3dd5..9d573dce6b8 100644 --- a/src/cargo/ops/mod.rs +++ b/src/cargo/ops/mod.rs @@ -10,7 +10,7 @@ pub use self::cargo_fetch::{fetch, FetchOptions}; pub use self::cargo_generate_lockfile::generate_lockfile; pub use self::cargo_generate_lockfile::update_lockfile; pub use self::cargo_generate_lockfile::UpdateOptions; -pub use self::cargo_install::{install, install_list}; +pub use self::cargo_install::{install, install_list, parse_semver_flag}; pub use self::cargo_new::{init, new, NewOptions, VersionControl}; pub use self::cargo_output_metadata::{output_metadata, ExportInfo, OutputMetadataOptions}; pub use self::cargo_package::{package, package_one, PackageOpts}; @@ -19,6 +19,9 @@ pub use self::cargo_read_manifest::{read_package, read_packages}; pub use self::cargo_run::run; pub use self::cargo_test::{run_benches, run_tests, TestOptions}; pub use self::cargo_uninstall::uninstall; +pub use self::common_for_install_and_uninstall::{ + exe_names, path_source, resolve_root, select_dep_pkg, select_pkg, +}; pub use self::fix::{fix, fix_maybe_exec_rustc, FixOptions}; pub use self::lockfile::{load_pkg_lockfile, resolve_to_string, write_pkg_lockfile}; pub use self::registry::HttpTimeout; From 30fe01124ec7b0f30098c1cd362ec5d2d3e52112 Mon Sep 17 00:00:00 2001 From: Jon Gjengset Date: Tue, 22 Feb 2022 14:59:28 -0800 Subject: [PATCH 3/3] Expose finer-grained resolver machinery Previously, users consuming Cargo as a library had to either use the very low-level `resolve_with_previous`, or the "do everything" `resolve_ws_with_opts`, which includes downloading the source tarballs of most crates in the resolved dependency closure. This patch exposes a version of `resolve_ws_with_opts` that does everything _up to_ anything that requires a source tarball download, as well as `resolve_registry`, which is a building block of other resolver functions that may be of use. --- src/cargo/ops/resolve.rs | 72 +++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/src/cargo/ops/resolve.rs b/src/cargo/ops/resolve.rs index 3f96e262934..75343d15227 100644 --- a/src/cargo/ops/resolve.rs +++ b/src/cargo/ops/resolve.rs @@ -31,6 +31,19 @@ use anyhow::Context as _; use log::{debug, trace}; use std::collections::{HashMap, HashSet}; +/// Result for `just_resolve_ws_with_opts`. +pub struct PartialWorkspaceResolve<'cfg> { + pub registry: PackageRegistry<'cfg>, + /// The resolve for the entire workspace. + /// + /// This may be `None` for things like `cargo install` and `-Zavoid-dev-deps`. + /// This does not include `paths` overrides. + pub workspace_resolve: Option, + /// The narrowed resolve, with the specific features enabled, and only the + /// given package specs requested. + pub targeted_resolve: Resolve, +} + /// Result for `resolve_ws_with_opts`. pub struct WorkspaceResolve<'cfg> { /// Packages to be downloaded. @@ -79,15 +92,17 @@ pub fn resolve_ws<'a>(ws: &Workspace<'a>) -> CargoResult<(PackageSet<'a>, Resolv /// /// `specs` may be empty, which indicates it should resolve all workspace /// members. In this case, `opts.all_features` must be `true`. -pub fn resolve_ws_with_opts<'cfg>( +/// +/// This function will only consult the index, and will not download crate artifacts. It will +/// therefore not trim dependency edges that are not needed on the particular platform or given the +/// way features happen to interact, as some of that requires downloading the source of a subset of +/// the crates in the resolve. If that functionality is needed, use `resolve_ws_with_opts`. +pub fn just_resolve_ws_with_opts<'cfg>( ws: &Workspace<'cfg>, - target_data: &RustcTargetData<'cfg>, - requested_targets: &[CompileKind], cli_features: &CliFeatures, specs: &[PackageIdSpec], has_dev_units: HasDevUnits, - force_all_targets: ForceAllTargets, -) -> CargoResult> { +) -> CargoResult> { let mut registry = PackageRegistry::new(ws.config())?; let mut add_patches = true; let resolve = if ws.ignore_lock() { @@ -143,7 +158,39 @@ pub fn resolve_ws_with_opts<'cfg>( add_patches, )?; - let pkg_set = get_resolved_packages(&resolved_with_overrides, registry)?; + Ok(PartialWorkspaceResolve { + registry, + workspace_resolve: resolve, + targeted_resolve: resolved_with_overrides, + }) +} + +/// Resolves dependencies for some packages of the workspace, +/// taking into account `paths` overrides and activated features. +/// +/// This function will also write the result of resolution as a new lock file +/// (unless `Workspace::require_optional_deps` is false, such as `cargo +/// install` or `-Z avoid-dev-deps`), or it is an ephemeral workspace (`cargo +/// install` or `cargo package`). +/// +/// `specs` may be empty, which indicates it should resolve all workspace +/// members. In this case, `opts.all_features` must be `true`. +pub fn resolve_ws_with_opts<'cfg>( + ws: &Workspace<'cfg>, + target_data: &RustcTargetData<'cfg>, + requested_targets: &[CompileKind], + cli_features: &CliFeatures, + specs: &[PackageIdSpec], + has_dev_units: HasDevUnits, + force_all_targets: ForceAllTargets, +) -> CargoResult> { + let PartialWorkspaceResolve { + registry, + workspace_resolve, + targeted_resolve, + } = just_resolve_ws_with_opts(ws, cli_features, specs, has_dev_units)?; + + let pkg_set = get_resolved_packages(&targeted_resolve, registry)?; let member_ids = ws .members_with_features(specs, cli_features)? @@ -151,7 +198,7 @@ pub fn resolve_ws_with_opts<'cfg>( .map(|(p, _fts)| p.package_id()) .collect::>(); pkg_set.download_accessible( - &resolved_with_overrides, + &targeted_resolve, &member_ids, has_dev_units, requested_targets, @@ -163,7 +210,7 @@ pub fn resolve_ws_with_opts<'cfg>( let resolved_features = FeatureResolver::resolve( ws, target_data, - &resolved_with_overrides, + &targeted_resolve, &pkg_set, cli_features, specs, @@ -173,7 +220,7 @@ pub fn resolve_ws_with_opts<'cfg>( pkg_set.warn_no_lib_packages_and_artifact_libs_overlapping_deps( ws, - &resolved_with_overrides, + &targeted_resolve, &member_ids, has_dev_units, requested_targets, @@ -183,13 +230,14 @@ pub fn resolve_ws_with_opts<'cfg>( Ok(WorkspaceResolve { pkg_set, - workspace_resolve: resolve, - targeted_resolve: resolved_with_overrides, + workspace_resolve, + targeted_resolve, resolved_features, }) } -fn resolve_with_registry<'cfg>( +/// Resolve the dependency closure of the given workspace in the context of the given registry. +pub fn resolve_with_registry<'cfg>( ws: &Workspace<'cfg>, registry: &mut PackageRegistry<'cfg>, ) -> CargoResult {