Skip to content

Commit e1ac615

Browse files
author
Jon Gjengset
committed
Add config-based path shorthands
When doing local development, users may wish to specify many `path` dependencies that all live in the same local directory. If that local directory is not a short distance from the `Cargo.toml`, this can get unwieldy. This patch introduces the notion of path "prefixes", which are defined in `.cargo/config.toml`: ```toml [path] devdir = "/home/jon/dev/rust/" ``` Which can then be used as the root for path dependencies. For example: ```toml foo = { path = "devdir::foo" } bar = { path = "devdir::bar" } baz = { path = "devdir::ws/baz" } ``` would fetch `foo` from `/home/jon/dev/rust/foo`, `bar` from `/home/jon/dev/rust/bar`, and `baz` from `/home/jon/dev/rust/ws/baz`. This feature also serves as a convenient way for external build systems to mask build-dependent paths from the user. Consider a build system that vendors first-party dependencies into ``` /home/user/workplace/feature-1/build/first-party-package/first-party-package-1.0/x86_64/dev/build/private/rust-vendored/ ``` Instead of requiring users to hard-code that path, or the relative equivalent, into their `Cargo.toml`, the build-system can instead produce a project-local `.cargo/config.toml` that defines that path as `path.vendored`, and the user can then use vendored dependencies using ```toml foo = { path = "vendored::foo" } ```
1 parent 56e0df6 commit e1ac615

File tree

4 files changed

+157
-2
lines changed

4 files changed

+157
-2
lines changed

src/bin/cargo/cli.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Available unstable (nightly-only) flags:
4545
-Z terminal-width -- Provide a terminal width to rustc for error truncation
4646
-Z namespaced-features -- Allow features with `dep:` prefix
4747
-Z weak-dep-features -- Allow `dep_name?/feature` feature syntax
48+
-Z path-prefixes -- Allow paths that use prefixes from config
4849
4950
Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
5051
);

src/cargo/core/features.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ pub struct CliUnstable {
444444
pub weak_dep_features: bool,
445445
pub extra_link_arg: bool,
446446
pub credential_process: bool,
447+
pub path_prefixes: bool,
447448
}
448449

449450
const STABILIZED_COMPILE_PROGRESS: &str = "The progress bar is now always \
@@ -627,6 +628,7 @@ impl CliUnstable {
627628
"weak-dep-features" => self.weak_dep_features = parse_empty(k, v)?,
628629
"extra-link-arg" => self.extra_link_arg = parse_empty(k, v)?,
629630
"credential-process" => self.credential_process = parse_empty(k, v)?,
631+
"path-prefixes" => self.path_prefixes = parse_empty(k, v)?,
630632
"compile-progress" => stabilized_warn(k, "1.30", STABILIZED_COMPILE_PROGRESS),
631633
"offline" => stabilized_err(k, "1.36", STABILIZED_OFFLINE)?,
632634
"cache-messages" => stabilized_warn(k, "1.40", STABILIZED_CACHE_MESSAGES),

src/cargo/util/toml/mod.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, Worksp
2424
use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
2525
use crate::util::errors::{CargoResult, CargoResultExt, ManifestError};
2626
use crate::util::interning::InternedString;
27-
use crate::util::{self, paths, validate_package_name, Config, IntoUrl};
27+
use crate::util::{
28+
self, config::ConfigRelativePath, paths, validate_package_name, Config, IntoUrl,
29+
};
2830

2931
mod targets;
3032
use self::targets::targets;
@@ -1765,7 +1767,36 @@ impl DetailedTomlDependency {
17651767
// always end up hashing to the same value no matter where it's
17661768
// built from.
17671769
if cx.source_id.is_path() {
1768-
let path = cx.root.join(path);
1770+
let mut with_prefix = path.splitn(2, "::");
1771+
let prefix = with_prefix.next().expect("split always yields once");
1772+
let path = if let Some(path) = with_prefix.next() {
1773+
if !cx.config.cli_unstable().path_prefixes {
1774+
bail!("Usage of path prefixes requires `-Z path-prefixes`");
1775+
}
1776+
1777+
// We have prefix::path.
1778+
// Look up the relevant prefix in the Config and use that as the root.
1779+
if let Some(prefix_path) = cx
1780+
.config
1781+
.get::<Option<ConfigRelativePath>>(&format!("path.{}", prefix))?
1782+
{
1783+
let prefix_path = prefix_path.resolve_path(cx.config);
1784+
prefix_path.join(path)
1785+
} else {
1786+
bail!(
1787+
"path prefix `{}` is undefined in path `{}::{}`. \
1788+
You must specify a path for `path.{}` in your cargo configuration.",
1789+
prefix,
1790+
prefix,
1791+
path,
1792+
prefix
1793+
);
1794+
}
1795+
} else {
1796+
// This is a standard path with no prefix.
1797+
let path = prefix;
1798+
cx.root.join(path)
1799+
};
17691800
let path = util::normalize_path(&path);
17701801
SourceId::for_path(&path)?
17711802
} else {

tests/testsuite/path.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,127 @@ Caused by:
529529
.run();
530530
}
531531

532+
#[cargo_test]
533+
fn prefix_not_stable() {
534+
let bar = project()
535+
.at("bar")
536+
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
537+
.file("src/lib.rs", "")
538+
.build();
539+
540+
fs::create_dir(&paths::root().join(".cargo")).unwrap();
541+
fs::write(
542+
&paths::root().join(".cargo/config"),
543+
&format!(
544+
"[path]\ntest = '{}'",
545+
bar.root().parent().unwrap().display()
546+
),
547+
)
548+
.unwrap();
549+
550+
let p = project()
551+
.file(
552+
"Cargo.toml",
553+
r#"
554+
[package]
555+
556+
name = "foo"
557+
version = "0.5.0"
558+
authors = ["[email protected]"]
559+
560+
[dependencies.bar]
561+
path = 'test::bar'
562+
"#,
563+
)
564+
.file("src/lib.rs", "")
565+
.build();
566+
567+
p.cargo("build")
568+
.with_status(101)
569+
.with_stderr(
570+
"\
571+
error: failed to parse manifest at `[..]/foo/Cargo.toml`
572+
573+
Caused by:
574+
Usage of path prefixes requires `-Z path-prefixes`
575+
",
576+
)
577+
.run();
578+
}
579+
580+
#[cargo_test]
581+
fn prefixed_path() {
582+
let bar = project()
583+
.at("bar")
584+
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
585+
.file("src/lib.rs", "")
586+
.build();
587+
588+
fs::create_dir(&paths::root().join(".cargo")).unwrap();
589+
fs::write(
590+
&paths::root().join(".cargo/config"),
591+
&format!(
592+
"[path]\ntest = '{}'",
593+
bar.root().parent().unwrap().display()
594+
),
595+
)
596+
.unwrap();
597+
598+
let p = project()
599+
.file(
600+
"Cargo.toml",
601+
r#"
602+
[package]
603+
604+
name = "foo"
605+
version = "0.5.0"
606+
authors = ["[email protected]"]
607+
608+
[dependencies.bar]
609+
path = 'test::bar'
610+
"#,
611+
)
612+
.file("src/lib.rs", "")
613+
.build();
614+
615+
p.cargo("build -v -Zpath-prefixes")
616+
.masquerade_as_nightly_cargo()
617+
.run();
618+
}
619+
620+
#[cargo_test]
621+
fn unknown_prefix() {
622+
let p = project()
623+
.file(
624+
"Cargo.toml",
625+
r#"
626+
[package]
627+
628+
name = "foo"
629+
version = "0.5.0"
630+
authors = ["[email protected]"]
631+
632+
[dependencies.bar]
633+
path = 'test::bar'
634+
"#,
635+
)
636+
.file("src/lib.rs", "")
637+
.build();
638+
639+
p.cargo("build -Zpath-prefixes")
640+
.masquerade_as_nightly_cargo()
641+
.with_status(101)
642+
.with_stderr(
643+
"\
644+
error: failed to parse manifest at `[..]/foo/Cargo.toml`
645+
646+
Caused by:
647+
path prefix `test` is undefined in path `test::bar`. You must specify a path for `path.test` in your cargo configuration.
648+
",
649+
)
650+
.run();
651+
}
652+
532653
#[cargo_test]
533654
fn override_relative() {
534655
let bar = project()

0 commit comments

Comments
 (0)