diff --git a/src/bin/cargo/cli.rs b/src/bin/cargo/cli.rs index 243f6ac0773..10ac54016aa 100644 --- a/src/bin/cargo/cli.rs +++ b/src/bin/cargo/cli.rs @@ -45,6 +45,7 @@ Available unstable (nightly-only) flags: -Z terminal-width -- Provide a terminal width to rustc for error truncation -Z namespaced-features -- Allow features with `dep:` prefix -Z weak-dep-features -- Allow `dep_name?/feature` feature syntax + -Z path-bases -- Allow paths that resolve relatively to a base specified in the config Run with 'cargo -Z [FLAG] [SUBCOMMAND]'" ); diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 9342c12dcc1..aa570b5172c 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -446,6 +446,7 @@ pub struct CliUnstable { pub extra_link_arg: bool, pub credential_process: bool, pub configurable_env: bool, + pub path_bases: bool, } const STABILIZED_COMPILE_PROGRESS: &str = "The progress bar is now always \ @@ -631,6 +632,7 @@ impl CliUnstable { "weak-dep-features" => self.weak_dep_features = parse_empty(k, v)?, "extra-link-arg" => self.extra_link_arg = parse_empty(k, v)?, "credential-process" => self.credential_process = parse_empty(k, v)?, + "path-bases" => self.path_bases = parse_empty(k, v)?, "compile-progress" => stabilized_warn(k, "1.30", STABILIZED_COMPILE_PROGRESS), "offline" => stabilized_err(k, "1.36", STABILIZED_OFFLINE)?, "cache-messages" => stabilized_warn(k, "1.40", STABILIZED_CACHE_MESSAGES), diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 6d6b9f1e76a..696a9745cc6 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -23,7 +23,9 @@ use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, Worksp use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY}; use crate::util::errors::{CargoResult, CargoResultExt, ManifestError}; use crate::util::interning::InternedString; -use crate::util::{self, paths, validate_package_name, Config, IntoUrl}; +use crate::util::{ + self, config::ConfigRelativePath, paths, validate_package_name, Config, IntoUrl, +}; mod targets; use self::targets::targets; @@ -260,6 +262,7 @@ pub struct DetailedTomlDependency { /// crates published by other users. registry_index: Option, path: Option, + base: Option, git: Option, branch: Option, tag: Option, @@ -1004,6 +1007,7 @@ impl TomlManifest { let mut d = d.clone(); // Path dependencies become crates.io deps. d.path.take(); + d.base.take(); // Same with git dependencies. d.git.take(); d.branch.take(); @@ -1764,7 +1768,31 @@ impl DetailedTomlDependency { // always end up hashing to the same value no matter where it's // built from. if cx.source_id.is_path() { - let path = cx.root.join(path); + let path = if let Some(base) = self.base.as_ref() { + if !cx.config.cli_unstable().path_bases { + bail!("Usage of path bases requires `-Z path-bases`"); + } + + // Look up the relevant base in the Config and use that as the root. + if let Some(base_path) = cx + .config + .get::>(&format!("base.{}", base))? + { + let base_path = base_path.resolve_path(cx.config); + base_path.join(path) + } else { + bail!( + "path base `{}` is undefined for path `{}`. \ + You must specify a path for `base.{}` in your cargo configuration.", + base, + path, + base + ); + } + } else { + // This is a standard path with no prefix. + cx.root.join(path) + }; let path = util::normalize_path(&path); SourceId::for_path(&path)? } else { diff --git a/tests/testsuite/path.rs b/tests/testsuite/path.rs index 34cdffb3e13..dc3423cc1be 100644 --- a/tests/testsuite/path.rs +++ b/tests/testsuite/path.rs @@ -529,6 +529,175 @@ Caused by: .run(); } +#[cargo_test] +fn path_bases_not_stable() { + let bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("src/lib.rs", "") + .build(); + + fs::create_dir(&paths::root().join(".cargo")).unwrap(); + fs::write( + &paths::root().join(".cargo/config"), + &format!( + "[base]\ntest = '{}'", + bar.root().parent().unwrap().display() + ), + ) + .unwrap(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = 'bar' + base = 'test' + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]/foo/Cargo.toml` + +Caused by: + Usage of path bases requires `-Z path-bases` +", + ) + .run(); +} + +#[cargo_test] +fn patch_with_base() { + let bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("src/lib.rs", "pub fn hello() {}") + .build(); + Package::new("bar", "0.5.0").publish(); + + fs::create_dir(&paths::root().join(".cargo")).unwrap(); + fs::write( + &paths::root().join(".cargo/config"), + &format!( + "[base]\ntest = '{}'", + bar.root().parent().unwrap().display() + ), + ) + .unwrap(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + edition = "2018" + + [dependencies.bar] + bar = "0.5.0" + + [patch.crates-io.bar] + path = 'bar' + base = 'test' + "#, + ) + .file("src/lib.rs", "use bar::hello as _;") + .build(); + + p.cargo("build -v -Zpath-bases") + .masquerade_as_nightly_cargo() + .run(); +} + +#[cargo_test] +fn path_with_base() { + let bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("src/lib.rs", "") + .build(); + + fs::create_dir(&paths::root().join(".cargo")).unwrap(); + fs::write( + &paths::root().join(".cargo/config"), + &format!( + "[base]\ntest = '{}'", + bar.root().parent().unwrap().display() + ), + ) + .unwrap(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = 'bar' + base = 'test' + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build -v -Zpath-bases") + .masquerade_as_nightly_cargo() + .run(); +} + +#[cargo_test] +fn unknown_base() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + + [dependencies.bar] + path = 'bar' + base = 'test' + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("build -Zpath-bases") + .masquerade_as_nightly_cargo() + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[..]/foo/Cargo.toml` + +Caused by: + path base `test` is undefined for path `bar`. You must specify a path for `base.test` in your cargo configuration. +", + ) + .run(); +} + #[cargo_test] fn override_relative() { let bar = project()