mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-25 11:14:46 +00:00
Add resolver
opt-in for new feature resolver.
This commit is contained in:
parent
f044cf9fb0
commit
787e75b797
@ -473,6 +473,27 @@ impl Package {
|
||||
}
|
||||
|
||||
fn make_archive(&self) {
|
||||
let dst = self.archive_dst();
|
||||
t!(fs::create_dir_all(dst.parent().unwrap()));
|
||||
let f = t!(File::create(&dst));
|
||||
let mut a = Builder::new(GzEncoder::new(f, Compression::default()));
|
||||
|
||||
if !self.files.iter().any(|(name, _)| name == "Cargo.toml") {
|
||||
self.append_manifest(&mut a);
|
||||
}
|
||||
if self.files.is_empty() {
|
||||
self.append(&mut a, "src/lib.rs", "");
|
||||
} else {
|
||||
for &(ref name, ref contents) in self.files.iter() {
|
||||
self.append(&mut a, name, contents);
|
||||
}
|
||||
}
|
||||
for &(ref name, ref contents) in self.extra_files.iter() {
|
||||
self.append_extra(&mut a, name, contents);
|
||||
}
|
||||
}
|
||||
|
||||
fn append_manifest<W: Write>(&self, ar: &mut Builder<W>) {
|
||||
let mut manifest = format!(
|
||||
r#"
|
||||
[package]
|
||||
@ -508,21 +529,7 @@ impl Package {
|
||||
manifest.push_str("[lib]\nproc-macro = true\n");
|
||||
}
|
||||
|
||||
let dst = self.archive_dst();
|
||||
t!(fs::create_dir_all(dst.parent().unwrap()));
|
||||
let f = t!(File::create(&dst));
|
||||
let mut a = Builder::new(GzEncoder::new(f, Compression::default()));
|
||||
self.append(&mut a, "Cargo.toml", &manifest);
|
||||
if self.files.is_empty() {
|
||||
self.append(&mut a, "src/lib.rs", "");
|
||||
} else {
|
||||
for &(ref name, ref contents) in self.files.iter() {
|
||||
self.append(&mut a, name, contents);
|
||||
}
|
||||
}
|
||||
for &(ref name, ref contents) in self.extra_files.iter() {
|
||||
self.append_extra(&mut a, name, contents);
|
||||
}
|
||||
self.append(ar, "Cargo.toml", &manifest);
|
||||
}
|
||||
|
||||
fn append<W: Write>(&self, ar: &mut Builder<W>, file: &str, contents: &str) {
|
||||
|
@ -71,6 +71,7 @@ pub fn resolve_std<'cfg>(
|
||||
ws_config,
|
||||
/*profiles*/ None,
|
||||
crate::core::Features::default(),
|
||||
None,
|
||||
);
|
||||
|
||||
let config = ws.config();
|
||||
|
@ -211,6 +211,9 @@ features! {
|
||||
|
||||
// Allow to specify profiles other than 'dev', 'release', 'test', etc.
|
||||
[unstable] named_profiles: bool,
|
||||
|
||||
// Opt-in new-resolver behavior.
|
||||
[unstable] resolver: bool,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ use serde::Serialize;
|
||||
use url::Url;
|
||||
|
||||
use crate::core::interning::InternedString;
|
||||
use crate::core::resolver::ResolveBehavior;
|
||||
use crate::core::{Dependency, PackageId, PackageIdSpec, SourceId, Summary};
|
||||
use crate::core::{Edition, Feature, Features, WorkspaceConfig};
|
||||
use crate::util::errors::*;
|
||||
@ -44,6 +45,7 @@ pub struct Manifest {
|
||||
im_a_teapot: Option<bool>,
|
||||
default_run: Option<String>,
|
||||
metabuild: Option<Vec<String>>,
|
||||
resolve_behavior: Option<ResolveBehavior>,
|
||||
}
|
||||
|
||||
/// When parsing `Cargo.toml`, some warnings should silenced
|
||||
@ -66,6 +68,7 @@ pub struct VirtualManifest {
|
||||
profiles: Option<TomlProfiles>,
|
||||
warnings: Warnings,
|
||||
features: Features,
|
||||
resolve_behavior: Option<ResolveBehavior>,
|
||||
}
|
||||
|
||||
/// General metadata about a package which is just blindly uploaded to the
|
||||
@ -410,6 +413,7 @@ impl Manifest {
|
||||
default_run: Option<String>,
|
||||
original: Rc<TomlManifest>,
|
||||
metabuild: Option<Vec<String>>,
|
||||
resolve_behavior: Option<ResolveBehavior>,
|
||||
) -> Manifest {
|
||||
Manifest {
|
||||
summary,
|
||||
@ -432,6 +436,7 @@ impl Manifest {
|
||||
default_run,
|
||||
publish_lockfile,
|
||||
metabuild,
|
||||
resolve_behavior,
|
||||
}
|
||||
}
|
||||
|
||||
@ -501,6 +506,13 @@ impl Manifest {
|
||||
&self.features
|
||||
}
|
||||
|
||||
/// The style of resolver behavior to use, declared with the `resolver` field.
|
||||
///
|
||||
/// Returns `None` if it is not specified.
|
||||
pub fn resolve_behavior(&self) -> Option<ResolveBehavior> {
|
||||
self.resolve_behavior
|
||||
}
|
||||
|
||||
pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Manifest {
|
||||
Manifest {
|
||||
summary: self.summary.map_source(to_replace, replace_with),
|
||||
@ -564,6 +576,7 @@ impl VirtualManifest {
|
||||
workspace: WorkspaceConfig,
|
||||
profiles: Option<TomlProfiles>,
|
||||
features: Features,
|
||||
resolve_behavior: Option<ResolveBehavior>,
|
||||
) -> VirtualManifest {
|
||||
VirtualManifest {
|
||||
replace,
|
||||
@ -572,6 +585,7 @@ impl VirtualManifest {
|
||||
profiles,
|
||||
warnings: Warnings::new(),
|
||||
features,
|
||||
resolve_behavior,
|
||||
}
|
||||
}
|
||||
|
||||
@ -602,6 +616,13 @@ impl VirtualManifest {
|
||||
pub fn features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
|
||||
/// The style of resolver behavior to use, declared with the `resolver` field.
|
||||
///
|
||||
/// Returns `None` if it is not specified.
|
||||
pub fn resolve_behavior(&self) -> Option<ResolveBehavior> {
|
||||
self.resolve_behavior
|
||||
}
|
||||
}
|
||||
|
||||
impl Target {
|
||||
|
@ -23,13 +23,27 @@ use crate::core::interning::InternedString;
|
||||
use crate::core::resolver::{HasDevUnits, Resolve};
|
||||
use crate::core::source::MaybePackage;
|
||||
use crate::core::{Dependency, Manifest, PackageId, SourceId, Target};
|
||||
use crate::core::{FeatureMap, SourceMap, Summary};
|
||||
use crate::core::{FeatureMap, SourceMap, Summary, Workspace};
|
||||
use crate::ops;
|
||||
use crate::util::config::PackageCacheLock;
|
||||
use crate::util::errors::{CargoResult, CargoResultExt, HttpNot200};
|
||||
use crate::util::network::Retry;
|
||||
use crate::util::{self, internal, Config, Progress, ProgressStyle};
|
||||
|
||||
pub const MANIFEST_PREAMBLE: &str = "\
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# \"normalize\" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
";
|
||||
|
||||
/// Information about a package that is available somewhere in the file system.
|
||||
///
|
||||
/// A package is a `Cargo.toml` file plus all the files that are part of it.
|
||||
@ -209,29 +223,13 @@ impl Package {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_registry_toml(&self, config: &Config) -> CargoResult<String> {
|
||||
pub fn to_registry_toml(&self, ws: &Workspace<'_>) -> CargoResult<String> {
|
||||
let manifest = self
|
||||
.manifest()
|
||||
.original()
|
||||
.prepare_for_publish(config, self.root())?;
|
||||
.prepare_for_publish(ws, self.root())?;
|
||||
let toml = toml::to_string(&manifest)?;
|
||||
Ok(format!(
|
||||
"# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO\n\
|
||||
#\n\
|
||||
# When uploading crates to the registry Cargo will automatically\n\
|
||||
# \"normalize\" Cargo.toml files for maximal compatibility\n\
|
||||
# with all versions of Cargo and also rewrite `path` dependencies\n\
|
||||
# to registry (e.g., crates.io) dependencies\n\
|
||||
#\n\
|
||||
# If you believe there's an error in this file please file an\n\
|
||||
# issue against the rust-lang/cargo repository. If you're\n\
|
||||
# editing this file be aware that the upstream Cargo.toml\n\
|
||||
# will likely look very different (and much more reasonable)\n\
|
||||
\n\
|
||||
{}\
|
||||
",
|
||||
toml
|
||||
))
|
||||
Ok(format!("{}\n{}", MANIFEST_PREAMBLE, toml))
|
||||
}
|
||||
|
||||
/// Returns if package should include `Cargo.lock`.
|
||||
|
@ -41,9 +41,9 @@
|
||||
use crate::core::compiler::{CompileKind, RustcTargetData};
|
||||
use crate::core::dependency::{DepKind, Dependency};
|
||||
use crate::core::resolver::types::FeaturesSet;
|
||||
use crate::core::resolver::Resolve;
|
||||
use crate::core::resolver::{Resolve, ResolveBehavior};
|
||||
use crate::core::{FeatureValue, InternedString, PackageId, PackageIdSpec, PackageSet, Workspace};
|
||||
use crate::util::{CargoResult, Config};
|
||||
use crate::util::CargoResult;
|
||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -110,9 +110,9 @@ impl FeaturesFor {
|
||||
}
|
||||
|
||||
impl FeatureOpts {
|
||||
fn new(config: &Config, has_dev_units: HasDevUnits) -> CargoResult<FeatureOpts> {
|
||||
fn new(ws: &Workspace<'_>, has_dev_units: HasDevUnits) -> CargoResult<FeatureOpts> {
|
||||
let mut opts = FeatureOpts::default();
|
||||
let unstable_flags = config.cli_unstable();
|
||||
let unstable_flags = ws.config().cli_unstable();
|
||||
opts.package_features = unstable_flags.package_features;
|
||||
let mut enable = |feat_opts: &Vec<String>| {
|
||||
opts.new_resolver = true;
|
||||
@ -136,6 +136,12 @@ impl FeatureOpts {
|
||||
if let Some(feat_opts) = unstable_flags.features.as_ref() {
|
||||
enable(feat_opts)?;
|
||||
}
|
||||
match ws.resolve_behavior() {
|
||||
ResolveBehavior::V1 => {}
|
||||
ResolveBehavior::V2 => {
|
||||
enable(&vec!["all".to_string()]).unwrap();
|
||||
}
|
||||
}
|
||||
// This env var is intended for testing only.
|
||||
if let Ok(env_opts) = std::env::var("__CARGO_FORCE_NEW_FEATURES") {
|
||||
if env_opts == "1" {
|
||||
@ -146,6 +152,7 @@ impl FeatureOpts {
|
||||
}
|
||||
}
|
||||
if let HasDevUnits::Yes = has_dev_units {
|
||||
// Dev deps cannot be decoupled when they are in use.
|
||||
opts.decouple_dev_deps = false;
|
||||
}
|
||||
Ok(opts)
|
||||
@ -268,7 +275,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
|
||||
use crate::util::profile;
|
||||
let _p = profile::start("resolve features");
|
||||
|
||||
let opts = FeatureOpts::new(ws.config(), has_dev_units)?;
|
||||
let opts = FeatureOpts::new(ws, has_dev_units)?;
|
||||
if !opts.new_resolver {
|
||||
// Legacy mode.
|
||||
return Ok(ResolvedFeatures {
|
||||
|
@ -71,7 +71,7 @@ pub use self::encode::{EncodableDependency, EncodablePackageId, EncodableResolve
|
||||
pub use self::errors::{ActivateError, ActivateResult, ResolveError};
|
||||
pub use self::features::HasDevUnits;
|
||||
pub use self::resolve::{Resolve, ResolveVersion};
|
||||
pub use self::types::ResolveOpts;
|
||||
pub use self::types::{ResolveBehavior, ResolveOpts};
|
||||
|
||||
mod conflict_cache;
|
||||
mod context;
|
||||
|
@ -97,6 +97,35 @@ impl ResolverProgress {
|
||||
/// optimized comparison operators like `is_subset` at the interfaces.
|
||||
pub type FeaturesSet = Rc<BTreeSet<InternedString>>;
|
||||
|
||||
/// Resolver behavior, used to opt-in to new behavior that is
|
||||
/// backwards-incompatible via the `resolver` field in the manifest.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum ResolveBehavior {
|
||||
/// V1 is the original resolver behavior.
|
||||
V1,
|
||||
/// V2 adds the new feature resolver.
|
||||
V2,
|
||||
}
|
||||
|
||||
impl ResolveBehavior {
|
||||
pub fn from_manifest(resolver: &str) -> CargoResult<ResolveBehavior> {
|
||||
match resolver {
|
||||
"2" => Ok(ResolveBehavior::V2),
|
||||
s => anyhow::bail!(
|
||||
"`resolver` setting `{}` is not valid, only valid option is \"2\"",
|
||||
s
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_manifest(&self) -> Option<String> {
|
||||
match self {
|
||||
ResolveBehavior::V1 => None,
|
||||
ResolveBehavior::V2 => Some("2".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for how the resolve should work.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct ResolveOpts {
|
||||
|
@ -12,6 +12,7 @@ use url::Url;
|
||||
use crate::core::features::Features;
|
||||
use crate::core::registry::PackageRegistry;
|
||||
use crate::core::resolver::features::RequestedFeatures;
|
||||
use crate::core::resolver::ResolveBehavior;
|
||||
use crate::core::{Dependency, InternedString, PackageId, PackageIdSpec};
|
||||
use crate::core::{EitherManifest, Package, SourceId, VirtualManifest};
|
||||
use crate::ops;
|
||||
@ -84,6 +85,9 @@ pub struct Workspace<'cfg> {
|
||||
// If `true`, then the resolver will ignore any existing `Cargo.lock`
|
||||
// file. This is set for `cargo install` without `--locked`.
|
||||
ignore_lock: bool,
|
||||
|
||||
/// The resolver behavior specified with the `resolver` field.
|
||||
resolve_behavior: ResolveBehavior,
|
||||
}
|
||||
|
||||
// Separate structure for tracking loaded packages (to avoid loading anything
|
||||
@ -143,6 +147,11 @@ impl<'cfg> Workspace<'cfg> {
|
||||
ws.target_dir = config.target_dir()?;
|
||||
ws.root_manifest = ws.find_root(manifest_path)?;
|
||||
ws.find_members()?;
|
||||
ws.resolve_behavior = match ws.root_maybe() {
|
||||
MaybePackage::Package(p) => p.manifest().resolve_behavior(),
|
||||
MaybePackage::Virtual(vm) => vm.resolve_behavior(),
|
||||
}
|
||||
.unwrap_or(ResolveBehavior::V1);
|
||||
ws.validate()?;
|
||||
Ok(ws)
|
||||
}
|
||||
@ -164,6 +173,7 @@ impl<'cfg> Workspace<'cfg> {
|
||||
require_optional_deps: true,
|
||||
loaded_packages: RefCell::new(HashMap::new()),
|
||||
ignore_lock: false,
|
||||
resolve_behavior: ResolveBehavior::V1,
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,6 +186,7 @@ impl<'cfg> Workspace<'cfg> {
|
||||
let mut ws = Workspace::new_default(current_manifest, config);
|
||||
ws.root_manifest = Some(root_path.join("Cargo.toml"));
|
||||
ws.target_dir = config.target_dir()?;
|
||||
ws.resolve_behavior = manifest.resolve_behavior().unwrap_or(ResolveBehavior::V1);
|
||||
ws.packages
|
||||
.packages
|
||||
.insert(root_path, MaybePackage::Virtual(manifest));
|
||||
@ -203,6 +214,10 @@ impl<'cfg> Workspace<'cfg> {
|
||||
let mut ws = Workspace::new_default(package.manifest_path().to_path_buf(), config);
|
||||
ws.is_ephemeral = true;
|
||||
ws.require_optional_deps = require_optional_deps;
|
||||
ws.resolve_behavior = package
|
||||
.manifest()
|
||||
.resolve_behavior()
|
||||
.unwrap_or(ResolveBehavior::V1);
|
||||
let key = ws.current_manifest.parent().unwrap();
|
||||
let id = package.package_id();
|
||||
let package = MaybePackage::Package(package);
|
||||
@ -578,6 +593,18 @@ impl<'cfg> Workspace<'cfg> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_behavior(&self) -> ResolveBehavior {
|
||||
self.resolve_behavior
|
||||
}
|
||||
|
||||
pub fn allows_unstable_package_features(&self) -> bool {
|
||||
self.config().cli_unstable().package_features
|
||||
|| match self.resolve_behavior() {
|
||||
ResolveBehavior::V1 => false,
|
||||
ResolveBehavior::V2 => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates a workspace, ensuring that a number of invariants are upheld:
|
||||
///
|
||||
/// 1. A workspace only has one root.
|
||||
@ -769,6 +796,12 @@ impl<'cfg> Workspace<'cfg> {
|
||||
if !manifest.patch().is_empty() {
|
||||
emit_warning("patch")?;
|
||||
}
|
||||
if let Some(behavior) = manifest.resolve_behavior() {
|
||||
// Only warn if they don't match.
|
||||
if behavior != self.resolve_behavior {
|
||||
emit_warning("resolver")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -878,7 +911,7 @@ impl<'cfg> Workspace<'cfg> {
|
||||
.map(|m| (m, RequestedFeatures::new_all(true)))
|
||||
.collect());
|
||||
}
|
||||
if self.config().cli_unstable().package_features {
|
||||
if self.allows_unstable_package_features() {
|
||||
self.members_with_features_pf(specs, requested_features)
|
||||
} else {
|
||||
self.members_with_features_stable(specs, requested_features)
|
||||
|
@ -156,7 +156,7 @@ fn build_ar_list(
|
||||
rel_str: "Cargo.toml.orig".to_string(),
|
||||
contents: FileContents::OnDisk(src_file),
|
||||
});
|
||||
let generated = pkg.to_registry_toml(ws.config())?;
|
||||
let generated = pkg.to_registry_toml(ws)?;
|
||||
result.push(ArchiveFile {
|
||||
rel_path,
|
||||
rel_str,
|
||||
@ -267,7 +267,7 @@ fn build_lock(ws: &Workspace<'_>) -> CargoResult<String> {
|
||||
orig_pkg
|
||||
.manifest()
|
||||
.original()
|
||||
.prepare_for_publish(config, orig_pkg.root())?,
|
||||
.prepare_for_publish(ws, orig_pkg.root())?,
|
||||
);
|
||||
let package_root = orig_pkg.root();
|
||||
let source_id = orig_pkg.package_id().source_id();
|
||||
|
@ -301,7 +301,7 @@ pub trait ArgMatchesExt {
|
||||
if config.cli_unstable().avoid_dev_deps {
|
||||
ws.set_require_optional_deps(false);
|
||||
}
|
||||
if ws.is_virtual() && !config.cli_unstable().package_features {
|
||||
if ws.is_virtual() && !ws.allows_unstable_package_features() {
|
||||
// --all-features is actually honored. In general, workspaces and
|
||||
// feature flags are a bit of a mess right now.
|
||||
for flag in &["features", "no-default-features"] {
|
||||
|
@ -15,8 +15,9 @@ use url::Url;
|
||||
|
||||
use crate::core::dependency::DepKind;
|
||||
use crate::core::manifest::{LibKind, ManifestMetadata, TargetSourcePath, Warnings};
|
||||
use crate::core::resolver::ResolveBehavior;
|
||||
use crate::core::{Dependency, InternedString, Manifest, PackageId, Summary, Target};
|
||||
use crate::core::{Edition, EitherManifest, Feature, Features, VirtualManifest};
|
||||
use crate::core::{Edition, EitherManifest, Feature, Features, VirtualManifest, Workspace};
|
||||
use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, WorkspaceRootConfig};
|
||||
use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
|
||||
use crate::util::errors::{CargoResult, CargoResultExt, ManifestError};
|
||||
@ -805,6 +806,7 @@ pub struct TomlProject {
|
||||
license_file: Option<String>,
|
||||
repository: Option<String>,
|
||||
metadata: Option<toml::Value>,
|
||||
resolver: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
@ -813,6 +815,7 @@ pub struct TomlWorkspace {
|
||||
#[serde(rename = "default-members")]
|
||||
default_members: Option<Vec<String>>,
|
||||
exclude: Option<Vec<String>>,
|
||||
resolver: Option<String>,
|
||||
}
|
||||
|
||||
impl TomlProject {
|
||||
@ -836,9 +839,10 @@ struct Context<'a, 'b> {
|
||||
impl TomlManifest {
|
||||
pub fn prepare_for_publish(
|
||||
&self,
|
||||
config: &Config,
|
||||
ws: &Workspace<'_>,
|
||||
package_root: &Path,
|
||||
) -> CargoResult<TomlManifest> {
|
||||
let config = ws.config();
|
||||
let mut package = self
|
||||
.package
|
||||
.as_ref()
|
||||
@ -846,6 +850,19 @@ impl TomlManifest {
|
||||
.unwrap()
|
||||
.clone();
|
||||
package.workspace = None;
|
||||
let mut cargo_features = self.cargo_features.clone();
|
||||
package.resolver = ws.resolve_behavior().to_manifest();
|
||||
if package.resolver.is_some() {
|
||||
// This should be removed when stabilizing.
|
||||
match &mut cargo_features {
|
||||
None => cargo_features = Some(vec!["resolver".to_string()]),
|
||||
Some(feats) => {
|
||||
if !feats.iter().any(|feat| feat == "resolver") {
|
||||
feats.push("resolver".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(license_file) = &package.license_file {
|
||||
let license_path = Path::new(&license_file);
|
||||
let abs_license_path = paths::normalize_path(&package_root.join(license_path));
|
||||
@ -927,7 +944,7 @@ impl TomlManifest {
|
||||
patch: None,
|
||||
workspace: None,
|
||||
badges: self.badges.clone(),
|
||||
cargo_features: self.cargo_features.clone(),
|
||||
cargo_features,
|
||||
});
|
||||
|
||||
fn map_deps(
|
||||
@ -1015,6 +1032,25 @@ impl TomlManifest {
|
||||
features.require(Feature::metabuild())?;
|
||||
}
|
||||
|
||||
if project.resolver.is_some()
|
||||
|| me
|
||||
.workspace
|
||||
.as_ref()
|
||||
.map_or(false, |ws| ws.resolver.is_some())
|
||||
{
|
||||
features.require(Feature::resolver())?;
|
||||
}
|
||||
let resolve_behavior = match (
|
||||
project.resolver.as_ref(),
|
||||
me.workspace.as_ref().and_then(|ws| ws.resolver.as_ref()),
|
||||
) {
|
||||
(None, None) => None,
|
||||
(Some(s), None) | (None, Some(s)) => Some(ResolveBehavior::from_manifest(s)?),
|
||||
(Some(_), Some(_)) => {
|
||||
bail!("cannot specify `resolver` field in both `[workspace]` and `[package]`")
|
||||
}
|
||||
};
|
||||
|
||||
// If we have no lib at all, use the inferred lib, if available.
|
||||
// If we have a lib with a path, we're done.
|
||||
// If we have a lib with no path, use the inferred lib or else the package name.
|
||||
@ -1256,6 +1292,7 @@ impl TomlManifest {
|
||||
project.default_run.clone(),
|
||||
Rc::clone(me),
|
||||
project.metabuild.clone().map(|sov| sov.0),
|
||||
resolve_behavior,
|
||||
);
|
||||
if project.license_file.is_some() && project.license.is_some() {
|
||||
manifest.warnings_mut().add_warning(
|
||||
@ -1347,6 +1384,19 @@ impl TomlManifest {
|
||||
if let Some(profiles) = &profiles {
|
||||
profiles.validate(&features, &mut warnings)?;
|
||||
}
|
||||
if me
|
||||
.workspace
|
||||
.as_ref()
|
||||
.map_or(false, |ws| ws.resolver.is_some())
|
||||
{
|
||||
features.require(Feature::resolver())?;
|
||||
}
|
||||
let resolve_behavior = me
|
||||
.workspace
|
||||
.as_ref()
|
||||
.and_then(|ws| ws.resolver.as_deref())
|
||||
.map(|r| ResolveBehavior::from_manifest(r))
|
||||
.transpose()?;
|
||||
let workspace_config = match me.workspace {
|
||||
Some(ref config) => WorkspaceConfig::Root(WorkspaceRootConfig::new(
|
||||
root,
|
||||
@ -1359,7 +1409,14 @@ impl TomlManifest {
|
||||
}
|
||||
};
|
||||
Ok((
|
||||
VirtualManifest::new(replace, patch, workspace_config, profiles, features),
|
||||
VirtualManifest::new(
|
||||
replace,
|
||||
patch,
|
||||
workspace_config,
|
||||
profiles,
|
||||
features,
|
||||
resolve_behavior,
|
||||
),
|
||||
nested_paths,
|
||||
))
|
||||
}
|
||||
|
@ -574,6 +574,40 @@ make feature flags behave in a more intuitive manner.
|
||||
The ability to set features for non-workspace members is no longer allowed, as
|
||||
the resolver fundamentally does not support that ability.
|
||||
|
||||
### Resolver
|
||||
* Tracking Issue: [#8088](https://github.com/rust-lang/cargo/issues/8088)
|
||||
|
||||
The `resolver` feature allows the resolver version to be specified in the
|
||||
`Cargo.toml` manifest. This allows a project to opt-in to
|
||||
backwards-incompatible changes in the resolver.
|
||||
|
||||
```toml
|
||||
cargo-features = ["resolver"]
|
||||
|
||||
[package]
|
||||
name = "my-package"
|
||||
version = "1.0.0"
|
||||
resolver = "2"
|
||||
```
|
||||
|
||||
Currently the only allowed value is `"2"`. This declaration enables all of the
|
||||
new feature behavior of [`-Zfeatures=all`](#features) and
|
||||
[`-Zpackage-features`](#package-features).
|
||||
|
||||
This flag is global for a workspace. If using a virtual workspace, the root
|
||||
definition should be in the `[workspace]` table like this:
|
||||
|
||||
```toml
|
||||
cargo-features = ["resolver"]
|
||||
|
||||
[workspace]
|
||||
members = ["member1", "member2"]
|
||||
resolver = "2"
|
||||
```
|
||||
|
||||
The `resolver` field is ignored in dependencies, only the top-level project or
|
||||
workspace can control the new behavior.
|
||||
|
||||
### crate-versions
|
||||
* Tracking Issue: [#7907](https://github.com/rust-lang/cargo/issues/7907)
|
||||
|
||||
|
@ -2,8 +2,10 @@
|
||||
|
||||
use cargo_test_support::cross_compile::{self, alternate};
|
||||
use cargo_test_support::paths::CargoPathExt;
|
||||
use cargo_test_support::publish::validate_crate_contents;
|
||||
use cargo_test_support::registry::{Dependency, Package};
|
||||
use cargo_test_support::{basic_manifest, project, rustc_host};
|
||||
use cargo_test_support::{basic_manifest, cargo_process, project, rustc_host};
|
||||
use std::fs::File;
|
||||
|
||||
#[cargo_test]
|
||||
fn inactivate_targets() {
|
||||
@ -1289,3 +1291,421 @@ fn build_dep_activated() {
|
||||
.masquerade_as_nightly_cargo()
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn resolver_gated() {
|
||||
// Check that `resolver` field is feature gated.
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
resolver = "2"
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("build")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_status(101)
|
||||
.with_stderr(
|
||||
"\
|
||||
error: failed to parse manifest at `[..]/foo/Cargo.toml`
|
||||
|
||||
Caused by:
|
||||
feature `resolver` is required
|
||||
|
||||
consider adding `cargo-features = [\"resolver\"]` to the manifest
|
||||
",
|
||||
)
|
||||
.run();
|
||||
|
||||
// Test with virtual ws.
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[workspace]
|
||||
members = ["a"]
|
||||
resolver = "2"
|
||||
"#,
|
||||
)
|
||||
.file("a/Cargo.toml", &basic_manifest("a", "0.1.0"))
|
||||
.file("a/src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("build")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_status(101)
|
||||
.with_stderr(
|
||||
"\
|
||||
error: failed to parse manifest at `[..]/foo/Cargo.toml`
|
||||
|
||||
Caused by:
|
||||
feature `resolver` is required
|
||||
|
||||
consider adding `cargo-features = [\"resolver\"]` to the manifest
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn resolver_bad_setting() {
|
||||
// Unknown setting in `resolver`
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["resolver"]
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
resolver = "1"
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("build")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_status(101)
|
||||
.with_stderr(
|
||||
"\
|
||||
error: failed to parse manifest at `[..]/foo/Cargo.toml`
|
||||
|
||||
Caused by:
|
||||
`resolver` setting `1` is not valid, only valid option is \"2\"
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn resolver_not_both() {
|
||||
// Can't specify resolver in both workspace and package.
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["resolver"]
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
resolver = "2"
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("build")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_status(101)
|
||||
.with_stderr(
|
||||
"\
|
||||
error: failed to parse manifest at `[..]/foo/Cargo.toml`
|
||||
|
||||
Caused by:
|
||||
cannot specify `resolver` field in both `[workspace]` and `[package]`
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn resolver_ws_member() {
|
||||
// Can't specify `resolver` in a ws member.
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[workspace]
|
||||
members = ["a"]
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"a/Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["resolver"]
|
||||
[package]
|
||||
name = "a"
|
||||
version = "0.1.0"
|
||||
resolver = "2"
|
||||
"#,
|
||||
)
|
||||
.file("a/src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("check")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stderr(
|
||||
"\
|
||||
warning: resolver for the non root package will be ignored, specify resolver at the workspace root:
|
||||
package: [..]/foo/a/Cargo.toml
|
||||
workspace: [..]/foo/Cargo.toml
|
||||
[CHECKING] a v0.1.0 [..]
|
||||
[FINISHED] [..]
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn resolver_ws_root_and_member() {
|
||||
// Check when specified in both ws root and member.
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["resolver"]
|
||||
[workspace]
|
||||
members = ["a"]
|
||||
resolver = "2"
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"a/Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["resolver"]
|
||||
[package]
|
||||
name = "a"
|
||||
version = "0.1.0"
|
||||
resolver = "2"
|
||||
"#,
|
||||
)
|
||||
.file("a/src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
// Ignores if they are the same.
|
||||
p.cargo("check")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stderr(
|
||||
"\
|
||||
[CHECKING] a v0.1.0 [..]
|
||||
[FINISHED] [..]
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn resolver_enables_new_features() {
|
||||
// resolver="2" enables all the things.
|
||||
Package::new("common", "1.0.0")
|
||||
.feature("normal", &[])
|
||||
.feature("build", &[])
|
||||
.feature("dev", &[])
|
||||
.feature("itarget", &[])
|
||||
.file(
|
||||
"src/lib.rs",
|
||||
r#"
|
||||
pub fn feats() -> u32 {
|
||||
let mut res = 0;
|
||||
if cfg!(feature="normal") { res |= 1; }
|
||||
if cfg!(feature="build") { res |= 2; }
|
||||
if cfg!(feature="dev") { res |= 4; }
|
||||
if cfg!(feature="itarget") { res |= 8; }
|
||||
res
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.publish();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["resolver"]
|
||||
[workspace]
|
||||
members = ["a", "b"]
|
||||
resolver = "2"
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"a/Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "a"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
common = {version = "1.0", features=["normal"]}
|
||||
|
||||
[dev-dependencies]
|
||||
common = {version = "1.0", features=["dev"]}
|
||||
|
||||
[build-dependencies]
|
||||
common = {version = "1.0", features=["build"]}
|
||||
|
||||
[target.'cfg(whatever)'.dependencies]
|
||||
common = {version = "1.0", features=["itarget"]}
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"a/src/main.rs",
|
||||
r#"
|
||||
fn main() {
|
||||
expect();
|
||||
}
|
||||
|
||||
fn expect() {
|
||||
let expected: u32 = std::env::var("EXPECTED_FEATS").unwrap().parse().unwrap();
|
||||
assert_eq!(expected, common::feats());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_test() {
|
||||
expect();
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"b/Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "b"
|
||||
version = "0.1.0"
|
||||
|
||||
[features]
|
||||
ping = []
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"b/src/main.rs",
|
||||
r#"
|
||||
fn main() {
|
||||
if cfg!(feature="ping") {
|
||||
println!("pong");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.build();
|
||||
|
||||
// Only normal.
|
||||
p.cargo("run --bin a")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.env("EXPECTED_FEATS", "1")
|
||||
.run();
|
||||
|
||||
// only normal+dev
|
||||
p.cargo("test")
|
||||
.cwd("a")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.env("EXPECTED_FEATS", "5")
|
||||
.run();
|
||||
|
||||
// -Zpackage-features is enabled.
|
||||
p.cargo("run -p b --features=ping")
|
||||
.cwd("a")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stdout("pong")
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn install_resolve_behavior() {
|
||||
// install honors the resolver behavior.
|
||||
Package::new("common", "1.0.0")
|
||||
.feature("f1", &[])
|
||||
.file(
|
||||
"src/lib.rs",
|
||||
r#"
|
||||
#[cfg(feature = "f1")]
|
||||
compile_error!("f1 should not activate");
|
||||
"#,
|
||||
)
|
||||
.publish();
|
||||
|
||||
Package::new("bar", "1.0.0").dep("common", "1.0").publish();
|
||||
|
||||
Package::new("foo", "1.0.0")
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["resolver"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "1.0.0"
|
||||
resolver = "2"
|
||||
|
||||
[target.'cfg(whatever)'.dependencies]
|
||||
common = {version="1.0", features=["f1"]}
|
||||
|
||||
[dependencies]
|
||||
bar = "1.0"
|
||||
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.publish();
|
||||
|
||||
cargo_process("install foo")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn package_includes_resolve_behavior() {
|
||||
// `cargo package` will inherit the correct resolve behavior.
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["resolver"]
|
||||
[workspace]
|
||||
members = ["a"]
|
||||
resolver = "2"
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"a/Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "a"
|
||||
version = "0.1.0"
|
||||
authors = ["Zzz"]
|
||||
description = "foo"
|
||||
license = "MIT"
|
||||
homepage = "https://example.com/"
|
||||
"#,
|
||||
)
|
||||
.file("a/src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("package")
|
||||
.cwd("a")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.run();
|
||||
|
||||
let rewritten_toml = format!(
|
||||
r#"{}
|
||||
cargo-features = ["resolver"]
|
||||
|
||||
[package]
|
||||
name = "a"
|
||||
version = "0.1.0"
|
||||
authors = ["Zzz"]
|
||||
description = "foo"
|
||||
homepage = "https://example.com/"
|
||||
license = "MIT"
|
||||
resolver = "2"
|
||||
"#,
|
||||
cargo::core::package::MANIFEST_PREAMBLE
|
||||
);
|
||||
|
||||
let f = File::open(&p.root().join("target/package/a-0.1.0.crate")).unwrap();
|
||||
validate_crate_contents(
|
||||
f,
|
||||
"a-0.1.0.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"],
|
||||
&[("Cargo.toml", &rewritten_toml)],
|
||||
);
|
||||
}
|
||||
|
@ -854,18 +854,7 @@ fn generated_manifest() {
|
||||
|
||||
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
|
||||
let rewritten_toml = format!(
|
||||
r#"# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
r#"{}
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.0.1"
|
||||
@ -889,6 +878,7 @@ registry-index = "{}"
|
||||
[dependencies.ghi]
|
||||
version = "1.0"
|
||||
"#,
|
||||
cargo::core::package::MANIFEST_PREAMBLE,
|
||||
registry::alt_registry_url()
|
||||
);
|
||||
|
||||
@ -935,28 +925,20 @@ fn ignore_workspace_specifier() {
|
||||
p.cargo("package --no-verify").cwd("bar").run();
|
||||
|
||||
let f = File::open(&p.root().join("target/package/bar-0.1.0.crate")).unwrap();
|
||||
let rewritten_toml = r#"# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
let rewritten_toml = format!(
|
||||
r#"{}
|
||||
[package]
|
||||
name = "bar"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
"#;
|
||||
"#,
|
||||
cargo::core::package::MANIFEST_PREAMBLE
|
||||
);
|
||||
validate_crate_contents(
|
||||
f,
|
||||
"bar-0.1.0.crate",
|
||||
&["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"],
|
||||
&[("Cargo.toml", rewritten_toml)],
|
||||
&[("Cargo.toml", &rewritten_toml)],
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user