mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-25 11:14:46 +00:00
Implement base paths (RFC 3529) 1/n: path dep and patch support
This commit is contained in:
parent
2f738d617c
commit
502c74e0a5
@ -776,6 +776,7 @@ pub struct TomlDetailedDependency<P: Clone = String> {
|
||||
// `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to
|
||||
// that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file.
|
||||
pub path: Option<P>,
|
||||
pub base: Option<PathBaseName>,
|
||||
pub git: Option<String>,
|
||||
pub branch: Option<String>,
|
||||
pub tag: Option<String>,
|
||||
@ -815,6 +816,7 @@ impl<P: Clone> Default for TomlDetailedDependency<P> {
|
||||
registry: Default::default(),
|
||||
registry_index: Default::default(),
|
||||
path: Default::default(),
|
||||
base: Default::default(),
|
||||
git: Default::default(),
|
||||
branch: Default::default(),
|
||||
tag: Default::default(),
|
||||
@ -1413,6 +1415,16 @@ impl<T: AsRef<str>> FeatureName<T> {
|
||||
}
|
||||
}
|
||||
|
||||
str_newtype!(PathBaseName);
|
||||
|
||||
impl<T: AsRef<str>> PathBaseName<T> {
|
||||
/// Validated path base name
|
||||
pub fn new(name: T) -> Result<Self, NameValidationError> {
|
||||
restricted_names::validate_path_base_name(name.as_ref())?;
|
||||
Ok(Self(name))
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to a `target` entry, but `TomlTarget` is already used.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
|
@ -238,6 +238,10 @@ pub(crate) fn validate_feature_name(name: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn validate_path_base_name(name: &str) -> Result<()> {
|
||||
validate_name(name, "path base name")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -513,6 +513,9 @@ features! {
|
||||
|
||||
/// Allow multiple packages to participate in the same API namespace
|
||||
(unstable, open_namespaces, "", "reference/unstable.html#open-namespaces"),
|
||||
|
||||
/// Allow paths that resolve relatively to a base specified in the config.
|
||||
(unstable, path_bases, "", "reference/unstable.html#path-bases"),
|
||||
}
|
||||
|
||||
/// Status and metadata for a single unstable feature.
|
||||
|
@ -10,7 +10,9 @@ use crate::AlreadyPrintedError;
|
||||
use anyhow::{anyhow, bail, Context as _};
|
||||
use cargo_platform::Platform;
|
||||
use cargo_util::paths::{self, normalize_path};
|
||||
use cargo_util_schemas::manifest::{self, TomlManifest};
|
||||
use cargo_util_schemas::manifest::{
|
||||
self, PackageName, PathBaseName, TomlDependency, TomlDetailedDependency, TomlManifest,
|
||||
};
|
||||
use cargo_util_schemas::manifest::{RustVersion, StringOrBool};
|
||||
use itertools::Itertools;
|
||||
use lazycell::LazyCell;
|
||||
@ -296,7 +298,7 @@ fn normalize_toml(
|
||||
features: None,
|
||||
target: None,
|
||||
replace: original_toml.replace.clone(),
|
||||
patch: original_toml.patch.clone(),
|
||||
patch: None,
|
||||
workspace: original_toml.workspace.clone(),
|
||||
badges: None,
|
||||
lints: None,
|
||||
@ -310,6 +312,7 @@ fn normalize_toml(
|
||||
inherit_cell
|
||||
.try_borrow_with(|| load_inheritable_fields(gctx, manifest_file, &workspace_config))
|
||||
};
|
||||
let workspace_root = || inherit().map(|fields| fields.ws_root());
|
||||
|
||||
if let Some(original_package) = original_toml.package() {
|
||||
let package_name = &original_package.name;
|
||||
@ -390,6 +393,7 @@ fn normalize_toml(
|
||||
&activated_opt_deps,
|
||||
None,
|
||||
&inherit,
|
||||
&workspace_root,
|
||||
package_root,
|
||||
warnings,
|
||||
)?;
|
||||
@ -410,6 +414,7 @@ fn normalize_toml(
|
||||
&activated_opt_deps,
|
||||
Some(DepKind::Development),
|
||||
&inherit,
|
||||
&workspace_root,
|
||||
package_root,
|
||||
warnings,
|
||||
)?;
|
||||
@ -430,6 +435,7 @@ fn normalize_toml(
|
||||
&activated_opt_deps,
|
||||
Some(DepKind::Build),
|
||||
&inherit,
|
||||
&workspace_root,
|
||||
package_root,
|
||||
warnings,
|
||||
)?;
|
||||
@ -443,6 +449,7 @@ fn normalize_toml(
|
||||
&activated_opt_deps,
|
||||
None,
|
||||
&inherit,
|
||||
&workspace_root,
|
||||
package_root,
|
||||
warnings,
|
||||
)?;
|
||||
@ -463,6 +470,7 @@ fn normalize_toml(
|
||||
&activated_opt_deps,
|
||||
Some(DepKind::Development),
|
||||
&inherit,
|
||||
&workspace_root,
|
||||
package_root,
|
||||
warnings,
|
||||
)?;
|
||||
@ -483,6 +491,7 @@ fn normalize_toml(
|
||||
&activated_opt_deps,
|
||||
Some(DepKind::Build),
|
||||
&inherit,
|
||||
&workspace_root,
|
||||
package_root,
|
||||
warnings,
|
||||
)?;
|
||||
@ -499,6 +508,13 @@ fn normalize_toml(
|
||||
}
|
||||
normalized_toml.target = (!normalized_target.is_empty()).then_some(normalized_target);
|
||||
|
||||
normalized_toml.patch = normalize_patch(
|
||||
gctx,
|
||||
original_toml.patch.as_ref(),
|
||||
&workspace_root,
|
||||
features,
|
||||
)?;
|
||||
|
||||
let normalized_lints = original_toml
|
||||
.lints
|
||||
.clone()
|
||||
@ -519,6 +535,37 @@ fn normalize_toml(
|
||||
Ok(normalized_toml)
|
||||
}
|
||||
|
||||
fn normalize_patch<'a>(
|
||||
gctx: &GlobalContext,
|
||||
original_patch: Option<&BTreeMap<String, BTreeMap<PackageName, TomlDependency>>>,
|
||||
workspace_root: &dyn Fn() -> CargoResult<&'a PathBuf>,
|
||||
features: &Features,
|
||||
) -> CargoResult<Option<BTreeMap<String, BTreeMap<PackageName, TomlDependency>>>> {
|
||||
if let Some(patch) = original_patch {
|
||||
let mut normalized_patch = BTreeMap::new();
|
||||
for (name, packages) in patch {
|
||||
let mut normalized_packages = BTreeMap::new();
|
||||
for (pkg, dep) in packages {
|
||||
let dep = if let TomlDependency::Detailed(dep) = dep {
|
||||
let mut dep = dep.clone();
|
||||
normalize_path_dependency(gctx, &mut dep, workspace_root, features)
|
||||
.with_context(|| {
|
||||
format!("resolving path for patch of ({pkg}) for source ({name})")
|
||||
})?;
|
||||
TomlDependency::Detailed(dep)
|
||||
} else {
|
||||
dep.clone()
|
||||
};
|
||||
normalized_packages.insert(pkg.clone(), dep);
|
||||
}
|
||||
normalized_patch.insert(name.clone(), normalized_packages);
|
||||
}
|
||||
Ok(Some(normalized_patch))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn normalize_package_toml<'a>(
|
||||
original_package: &manifest::TomlPackage,
|
||||
@ -710,6 +757,7 @@ fn normalize_dependencies<'a>(
|
||||
activated_opt_deps: &HashSet<&str>,
|
||||
kind: Option<DepKind>,
|
||||
inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
|
||||
workspace_root: &dyn Fn() -> CargoResult<&'a PathBuf>,
|
||||
package_root: &Path,
|
||||
warnings: &mut Vec<String>,
|
||||
) -> CargoResult<Option<BTreeMap<manifest::PackageName, manifest::InheritableDependency>>> {
|
||||
@ -768,6 +816,8 @@ fn normalize_dependencies<'a>(
|
||||
}
|
||||
}
|
||||
}
|
||||
normalize_path_dependency(gctx, d, workspace_root, features)
|
||||
.with_context(|| format!("resolving path dependency {name_in_toml}"))?;
|
||||
}
|
||||
|
||||
// if the dependency is not optional, it is always used
|
||||
@ -786,6 +836,23 @@ fn normalize_dependencies<'a>(
|
||||
Ok(Some(deps))
|
||||
}
|
||||
|
||||
fn normalize_path_dependency<'a>(
|
||||
gctx: &GlobalContext,
|
||||
detailed_dep: &mut TomlDetailedDependency,
|
||||
workspace_root: &dyn Fn() -> CargoResult<&'a PathBuf>,
|
||||
features: &Features,
|
||||
) -> CargoResult<()> {
|
||||
if let Some(base) = detailed_dep.base.take() {
|
||||
if let Some(path) = detailed_dep.path.as_mut() {
|
||||
let new_path = lookup_path_base(&base, gctx, workspace_root, features)?.join(&path);
|
||||
*path = new_path.to_str().unwrap().to_string();
|
||||
} else {
|
||||
bail!("`base` can only be used with path dependencies");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_inheritable_fields(
|
||||
gctx: &GlobalContext,
|
||||
normalized_path: &Path,
|
||||
@ -901,13 +968,17 @@ impl InheritableFields {
|
||||
};
|
||||
let mut dep = dep.clone();
|
||||
if let manifest::TomlDependency::Detailed(detailed) = &mut dep {
|
||||
if let Some(rel_path) = &detailed.path {
|
||||
detailed.path = Some(resolve_relative_path(
|
||||
name,
|
||||
self.ws_root(),
|
||||
package_root,
|
||||
rel_path,
|
||||
)?);
|
||||
if detailed.base.is_none() {
|
||||
// If this is a path dependency without a base, then update the path to be relative
|
||||
// to the workspace root instead.
|
||||
if let Some(rel_path) = &detailed.path {
|
||||
detailed.path = Some(resolve_relative_path(
|
||||
name,
|
||||
self.ws_root(),
|
||||
package_root,
|
||||
rel_path,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(dep)
|
||||
@ -2151,6 +2222,33 @@ fn to_dependency_source_id<P: ResolveToPath + Clone>(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn lookup_path_base<'a>(
|
||||
base: &PathBaseName,
|
||||
gctx: &GlobalContext,
|
||||
workspace_root: &dyn Fn() -> CargoResult<&'a PathBuf>,
|
||||
features: &Features,
|
||||
) -> CargoResult<PathBuf> {
|
||||
features.require(Feature::path_bases())?;
|
||||
|
||||
// HACK: The `base` string is user controlled, but building the path is safe from injection
|
||||
// attacks since the `PathBaseName` type restricts the characters that can be used to exclude `.`
|
||||
let base_key = format!("path-bases.{base}");
|
||||
|
||||
// Look up the relevant base in the Config and use that as the root.
|
||||
if let Some(path_bases) = gctx.get::<Option<ConfigRelativePath>>(&base_key)? {
|
||||
Ok(path_bases.resolve_path(gctx))
|
||||
} else {
|
||||
// Otherwise, check the built-in bases.
|
||||
match base.as_str() {
|
||||
"workspace" => Ok(workspace_root()?.clone()),
|
||||
_ => bail!(
|
||||
"path base `{base}` is undefined. \
|
||||
You must add an entry for `{base}` in the Cargo configuration [path-bases] table."
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ResolveToPath {
|
||||
fn resolve(&self, gctx: &GlobalContext) -> PathBuf;
|
||||
}
|
||||
@ -2865,6 +2963,7 @@ fn prepare_toml_for_publish(
|
||||
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();
|
||||
|
@ -101,6 +101,7 @@ Each new feature described below should explain how to use it.
|
||||
* [Edition 2024](#edition-2024) — Adds support for the 2024 Edition.
|
||||
* [Profile `trim-paths` option](#profile-trim-paths-option) --- Control the sanitization of file paths in build outputs.
|
||||
* [`[lints.cargo]`](#lintscargo) --- Allows configuring lints for Cargo.
|
||||
* [path bases](#path-bases) --- Named base directories for path dependencies.
|
||||
* Information and metadata
|
||||
* [Build-plan](#build-plan) --- Emits JSON information on which commands will be run.
|
||||
* [unit-graph](#unit-graph) --- Emits JSON for Cargo's internal graph structure.
|
||||
@ -1570,6 +1571,60 @@ implicit-features = "warn"
|
||||
workspace = true
|
||||
```
|
||||
|
||||
## Path Bases
|
||||
|
||||
* Tracking Issue: [#14355](https://github.com/rust-lang/cargo/issues/14355)
|
||||
|
||||
A `path` dependency may optionally specify a base by setting the `base` key to
|
||||
the name of a path base from the `[path-bases]` table in either the
|
||||
[configuration](config.md) or one of the [built-in path bases](#built-in-path-bases).
|
||||
The value of that path base is prepended to the `path` value (along with a path
|
||||
separator if necessary) to produce the actual location where Cargo will look for
|
||||
the dependency.
|
||||
|
||||
For example, if the `Cargo.toml` contains:
|
||||
|
||||
```toml
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[dependencies]
|
||||
foo = { base = "dev", path = "foo" }
|
||||
```
|
||||
|
||||
Given a `[path-bases]` table in the configuration that contains:
|
||||
|
||||
```toml
|
||||
[path-bases]
|
||||
dev = "/home/user/dev/rust/libraries/"
|
||||
```
|
||||
|
||||
This will produce a `path` dependency `foo` located at
|
||||
`/home/user/dev/rust/libraries/foo`.
|
||||
|
||||
Path bases can be either absolute or relative. Relative path bases are relative
|
||||
to the parent directory of the configuration file that declared that path base.
|
||||
|
||||
The name of a path base must use only [alphanumeric](https://doc.rust-lang.org/std/primitive.char.html#method.is_alphanumeric)
|
||||
characters or `-` or `_`, must start with an [alphabetic](https://doc.rust-lang.org/std/primitive.char.html#method.is_alphabetic)
|
||||
character, and must not be empty.
|
||||
|
||||
If the name of path base used in a dependency is neither in the configuration
|
||||
nor one of the built-in path base, then Cargo will raise an error.
|
||||
|
||||
#### Built-in path bases
|
||||
|
||||
Cargo provides implicit path bases that can be used without the need to specify
|
||||
them in a `[path-bases]` table.
|
||||
|
||||
* `workspace` - If a project is [a workspace or workspace member](workspaces.md)
|
||||
then this path base is defined as the parent directory of the root `Cargo.toml`
|
||||
of the workspace.
|
||||
|
||||
If a built-in path base name is also declared in the configuration, then Cargo
|
||||
will prefer the value in the configuration. The allows Cargo to add new built-in
|
||||
path bases without compatibility issues (as existing uses will shadow the
|
||||
built-in name).
|
||||
|
||||
# Stabilized and removed features
|
||||
|
||||
## Compile progress
|
||||
|
@ -3028,3 +3028,49 @@ foo v0.0.0 ([ROOT]/foo)
|
||||
|
||||
assert_eq!(p.read_file("Cargo.lock"), p.read_file("Cargo.lock.orig"));
|
||||
}
|
||||
|
||||
#[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();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
".cargo/config.toml",
|
||||
&format!(
|
||||
r#"
|
||||
[path-bases]
|
||||
test = '{}'
|
||||
"#,
|
||||
bar.root().parent().unwrap().display()
|
||||
),
|
||||
)
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.5.0"
|
||||
authors = ["wycats@example.com"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bar = "0.5.0"
|
||||
|
||||
[patch.crates-io]
|
||||
bar = { base = 'test', path = 'bar' }
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "use bar::hello as _;")
|
||||
.build();
|
||||
|
||||
p.cargo("build -v")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.run();
|
||||
}
|
||||
|
@ -589,6 +589,618 @@ 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();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
".cargo/config.toml",
|
||||
&format!(
|
||||
r#"
|
||||
[path-bases]
|
||||
test = '{}'
|
||||
"#,
|
||||
bar.root().parent().unwrap().display()
|
||||
),
|
||||
)
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
|
||||
name = "foo"
|
||||
version = "0.5.0"
|
||||
authors = ["wycats@example.com"]
|
||||
|
||||
[dependencies]
|
||||
bar = { base = 'test', path = 'bar' }
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("build")
|
||||
.with_status(101)
|
||||
.with_stderr_data(
|
||||
"\
|
||||
[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml`
|
||||
|
||||
Caused by:
|
||||
resolving path dependency bar
|
||||
|
||||
Caused by:
|
||||
feature `path-bases` is required
|
||||
|
||||
The package requires the Cargo feature called `path-bases`, but that feature is not stabilized in this version of Cargo ([..]).
|
||||
Consider trying a newer version of Cargo (this may require the nightly release).
|
||||
See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#path-bases for more information about the status of this feature.
|
||||
",
|
||||
)
|
||||
.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();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
".cargo/config.toml",
|
||||
&format!(
|
||||
r#"
|
||||
[path-bases]
|
||||
test = '{}'
|
||||
"#,
|
||||
bar.root().parent().unwrap().display()
|
||||
),
|
||||
)
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.5.0"
|
||||
authors = ["wycats@example.com"]
|
||||
|
||||
[dependencies]
|
||||
bar = { base = 'test', path = 'bar' }
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("build -v")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn current_dir_with_base() {
|
||||
let bar = project()
|
||||
.at("bar")
|
||||
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
".cargo/config.toml",
|
||||
&format!(
|
||||
r#"
|
||||
[path-bases]
|
||||
test = '{}'
|
||||
"#,
|
||||
bar.root().display()
|
||||
),
|
||||
)
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.5.0"
|
||||
authors = ["wycats@example.com"]
|
||||
|
||||
[dependencies]
|
||||
bar = { base = 'test', path = '.' }
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("build -v")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn parent_dir_with_base() {
|
||||
let bar = project()
|
||||
.at("bar")
|
||||
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
".cargo/config.toml",
|
||||
&format!(
|
||||
r#"
|
||||
[path-bases]
|
||||
test = '{}/subdir'
|
||||
"#,
|
||||
bar.root().display()
|
||||
),
|
||||
)
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.5.0"
|
||||
authors = ["wycats@example.com"]
|
||||
|
||||
[dependencies]
|
||||
bar = { base = 'test', path = '..' }
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("build -v")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn inherit_dependency_using_base() {
|
||||
let bar = project()
|
||||
.at("dep_with_base")
|
||||
.file("Cargo.toml", &basic_manifest("dep_with_base", "0.5.0"))
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
".cargo/config.toml",
|
||||
&format!(
|
||||
r#"
|
||||
[path-bases]
|
||||
test = '{}'
|
||||
"#,
|
||||
bar.root().parent().unwrap().display()
|
||||
),
|
||||
)
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "parent"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
|
||||
[workspace]
|
||||
members = ["child"]
|
||||
|
||||
[workspace.dependencies]
|
||||
dep_with_base = { base = 'test', path = 'dep_with_base' }
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.file(
|
||||
"child/Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "child"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
|
||||
[dependencies]
|
||||
dep_with_base = { workspace = true }
|
||||
"#,
|
||||
)
|
||||
.file("child/src/main.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
p.cargo("build -v")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn path_with_relative_base() {
|
||||
project()
|
||||
.at("shared_proj/bar")
|
||||
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"../.cargo/config.toml",
|
||||
r#"
|
||||
[path-bases]
|
||||
test = 'shared_proj'
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.5.0"
|
||||
authors = ["wycats@example.com"]
|
||||
|
||||
[dependencies]
|
||||
bar = { base = 'test', path = 'bar' }
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("build -v")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn workspace_builtin_base() {
|
||||
project()
|
||||
.at("dep_with_base")
|
||||
.file("Cargo.toml", &basic_manifest("dep_with_base", "0.5.0"))
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "parent"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
|
||||
[workspace]
|
||||
members = ["child"]
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.file(
|
||||
"child/Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "child"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
|
||||
[dependencies]
|
||||
dep_with_base = { base = 'workspace', path = '../dep_with_base' }
|
||||
"#,
|
||||
)
|
||||
.file("child/src/main.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
p.cargo("build -v")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn workspace_builtin_base_not_a_workspace() {
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
|
||||
[dependencies]
|
||||
bar = { base = 'workspace', path = 'bar' }
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
p.cargo("build")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.with_status(101)
|
||||
.with_stderr_data(
|
||||
"\
|
||||
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
||||
|
||||
Caused by:
|
||||
resolving path dependency bar
|
||||
|
||||
Caused by:
|
||||
failed to find a workspace root
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn shadow_workspace_builtin_base() {
|
||||
let bar = project()
|
||||
.at("dep_with_base")
|
||||
.file("Cargo.toml", &basic_manifest("dep_with_base", "0.5.0"))
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
".cargo/config.toml",
|
||||
&format!(
|
||||
r#"
|
||||
[path-bases]
|
||||
workspace = '{}/subdir/anotherdir'
|
||||
"#,
|
||||
bar.root().parent().unwrap().display()
|
||||
),
|
||||
)
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "parent"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
|
||||
[workspace]
|
||||
members = ["child"]
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.file(
|
||||
"child/Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "child"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
|
||||
[dependencies]
|
||||
dep_with_base = { base = 'workspace', path = '../../dep_with_base' }
|
||||
"#,
|
||||
)
|
||||
.file("child/src/main.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
p.cargo("build -v")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn unknown_base() {
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.5.0"
|
||||
authors = ["wycats@example.com"]
|
||||
|
||||
[dependencies]
|
||||
bar = { base = 'test', path = 'bar' }
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("build")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.with_status(101)
|
||||
.with_stderr_data(
|
||||
"\
|
||||
[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml`
|
||||
|
||||
Caused by:
|
||||
resolving path dependency bar
|
||||
|
||||
Caused by:
|
||||
path base `test` is undefined. You must add an entry for `test` in the Cargo configuration [path-bases] table.
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn base_without_path() {
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.5.0"
|
||||
authors = ["wycats@example.com"]
|
||||
|
||||
[dependencies]
|
||||
bar = { version = '1.0.0', base = 'test' }
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("build")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.with_status(101)
|
||||
.with_stderr_data(
|
||||
"\
|
||||
[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml`
|
||||
|
||||
Caused by:
|
||||
resolving path dependency bar
|
||||
|
||||
Caused by:
|
||||
`base` can only be used with path dependencies
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn invalid_base() {
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.5.0"
|
||||
authors = ["wycats@example.com"]
|
||||
|
||||
[dependencies]
|
||||
bar = { base = '^^not-valid^^', path = 'bar' }
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("build")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.with_status(101)
|
||||
.with_stderr_data(
|
||||
"\
|
||||
[ERROR] invalid character `^` in path base name: `^^not-valid^^`, the first character must be a Unicode XID start character (most letters or `_`)
|
||||
|
||||
|
||||
--> Cargo.toml:10:23
|
||||
|
|
||||
10 | bar = { base = '^^not-valid^^', path = 'bar' }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn invalid_path_with_base() {
|
||||
let p = project()
|
||||
.file(
|
||||
".cargo/config.toml",
|
||||
r#"
|
||||
[path-bases]
|
||||
test = 'shared_proj'
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.5.0"
|
||||
authors = ["wycats@example.com"]
|
||||
edition = "2015"
|
||||
|
||||
[dependencies]
|
||||
bar = { base = 'test', path = '"' }
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "")
|
||||
.build();
|
||||
|
||||
p.cargo("build")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.with_status(101)
|
||||
.with_stderr_data(
|
||||
"\
|
||||
[ERROR] failed to get `bar` as a dependency of package `foo v0.5.0 ([ROOT]/foo)`
|
||||
|
||||
Caused by:
|
||||
failed to load source for dependency `bar`
|
||||
|
||||
Caused by:
|
||||
Unable to update [ROOT]/foo/shared_proj/\"
|
||||
|
||||
Caused by:
|
||||
failed to read `[ROOT]/foo/shared_proj/\"/Cargo.toml`
|
||||
|
||||
Caused by:
|
||||
[NOT_FOUND]
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn self_dependency_using_base() {
|
||||
let p = project()
|
||||
.file(
|
||||
".cargo/config.toml",
|
||||
r#"
|
||||
[path-bases]
|
||||
test = '.'
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
cargo-features = ["path-bases"]
|
||||
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
authors = []
|
||||
edition = "2015"
|
||||
|
||||
[dependencies]
|
||||
foo = { base = 'test', path = '.' }
|
||||
"#,
|
||||
)
|
||||
.file("src/main.rs", "fn main() {}")
|
||||
.build();
|
||||
|
||||
p.cargo("build")
|
||||
.masquerade_as_nightly_cargo(&["path-bases"])
|
||||
.with_status(101)
|
||||
.with_stderr_data(
|
||||
"\
|
||||
[ERROR] cyclic package dependency: package `foo v0.1.0 ([ROOT]/foo)` depends on itself. Cycle:
|
||||
package `foo v0.1.0 ([ROOT]/foo)`
|
||||
... which satisfies path dependency `foo` of package `foo v0.1.0 ([ROOT]/foo)`
|
||||
",
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn override_relative() {
|
||||
let bar = project()
|
||||
|
Loading…
x
Reference in New Issue
Block a user