Expand tilde in import and module paths (#1792)
This commit is contained in:
parent
b21819dcf2
commit
f039701f71
70
Cargo.lock
generated
70
Cargo.lock
generated
@ -156,6 +156,27 @@ dependencies = [
|
|||||||
"crypto-common",
|
"crypto-common",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs"
|
||||||
|
version = "5.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"option-ext",
|
||||||
|
"redox_users",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "doc-comment"
|
name = "doc-comment"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@ -326,6 +347,7 @@ dependencies = [
|
|||||||
"cradle",
|
"cradle",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"derivative",
|
"derivative",
|
||||||
|
"dirs",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"edit-distance",
|
"edit-distance",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
@ -372,6 +394,17 @@ version = "0.2.151"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libredox"
|
||||||
|
version = "0.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.4.1",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linked-hash-map"
|
name = "linked-hash-map"
|
||||||
version = "0.5.6"
|
version = "0.5.6"
|
||||||
@ -423,6 +456,12 @@ version = "1.19.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "option-ext"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pretty_assertions"
|
name = "pretty_assertions"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@ -505,6 +544,17 @@ dependencies = [
|
|||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"libredox",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ref-type"
|
name = "ref-type"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
@ -785,6 +835,26 @@ dependencies = [
|
|||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.52"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.52"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.43",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typed-arena"
|
name = "typed-arena"
|
||||||
version = "2.0.2"
|
version = "2.0.2"
|
||||||
|
@ -24,6 +24,7 @@ camino = "1.0.4"
|
|||||||
clap = { version = "2.33.0", features = ["wrap_help"] }
|
clap = { version = "2.33.0", features = ["wrap_help"] }
|
||||||
ctrlc = { version = "3.1.1", features = ["termination"] }
|
ctrlc = { version = "3.1.1", features = ["termination"] }
|
||||||
derivative = "2.0.0"
|
derivative = "2.0.0"
|
||||||
|
dirs = "5.0.1"
|
||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
edit-distance = "2.0.0"
|
edit-distance = "2.0.0"
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
|
17
README.md
17
README.md
@ -2358,7 +2358,8 @@ A
|
|||||||
```
|
```
|
||||||
|
|
||||||
The `import` path can be absolute or relative to the location of the justfile
|
The `import` path can be absolute or relative to the location of the justfile
|
||||||
containing it.
|
containing it. A leading `~/` in the import path is replaced with the current
|
||||||
|
users home directory.
|
||||||
|
|
||||||
Justfiles are insensitive to order, so included files can reference variables
|
Justfiles are insensitive to order, so included files can reference variables
|
||||||
and recipes defined after the `import` statement.
|
and recipes defined after the `import` statement.
|
||||||
@ -2406,7 +2407,19 @@ If a module is named `foo`, just will search for the module file in `foo.just`,
|
|||||||
`foo/mod.just`, `foo/justfile`, and `foo/.justfile`. In the latter two cases,
|
`foo/mod.just`, `foo/justfile`, and `foo/.justfile`. In the latter two cases,
|
||||||
the module file may have any capitalization.
|
the module file may have any capitalization.
|
||||||
|
|
||||||
Environment files are loaded for the root justfile.
|
Module statements may be of the form:
|
||||||
|
|
||||||
|
```mf
|
||||||
|
mod foo 'PATH'
|
||||||
|
```
|
||||||
|
|
||||||
|
Which loads the module's source file from `PATH`, instead of from the usual
|
||||||
|
locations. A leading `~/` in `PATH` is replaced with the current user's home
|
||||||
|
directory.
|
||||||
|
|
||||||
|
Environment files are only loaded for the root justfile, and loaded environment
|
||||||
|
variables are available in submodules. Settings in submodules that affect
|
||||||
|
enviroment file loading are ignored.
|
||||||
|
|
||||||
Recipes in submodules without the `[no-cd]` attribute run with the working
|
Recipes in submodules without the `[no-cd]` attribute run with the working
|
||||||
directory set to the directory containing the submodule source file.
|
directory set to the directory containing the submodule source file.
|
||||||
|
@ -41,7 +41,7 @@ impl Compiler {
|
|||||||
let parent = current.parent().unwrap();
|
let parent = current.parent().unwrap();
|
||||||
|
|
||||||
let import = if let Some(path) = path {
|
let import = if let Some(path) = path {
|
||||||
parent.join(&path.cooked)
|
parent.join(Self::expand_tilde(&path.cooked)?)
|
||||||
} else {
|
} else {
|
||||||
Self::find_module_file(parent, *name)?
|
Self::find_module_file(parent, *name)?
|
||||||
};
|
};
|
||||||
@ -53,7 +53,11 @@ impl Compiler {
|
|||||||
stack.push((import, depth + 1));
|
stack.push((import, depth + 1));
|
||||||
}
|
}
|
||||||
Item::Import { relative, absolute } => {
|
Item::Import { relative, absolute } => {
|
||||||
let import = current.parent().unwrap().join(&relative.cooked).lexiclean();
|
let import = current
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join(Self::expand_tilde(&relative.cooked)?)
|
||||||
|
.lexiclean();
|
||||||
if srcs.contains_key(&import) {
|
if srcs.contains_key(&import) {
|
||||||
return Err(Error::CircularImport { current, import });
|
return Err(Error::CircularImport { current, import });
|
||||||
}
|
}
|
||||||
@ -117,6 +121,16 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expand_tilde(path: &str) -> RunResult<'static, PathBuf> {
|
||||||
|
Ok(if let Some(path) = path.strip_prefix("~/") {
|
||||||
|
dirs::home_dir()
|
||||||
|
.ok_or(Error::Homedir)?
|
||||||
|
.join(path.trim_start_matches('/'))
|
||||||
|
} else {
|
||||||
|
PathBuf::from(path)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn test_compile(src: &str) -> CompileResult<Justfile> {
|
pub(crate) fn test_compile(src: &str) -> CompileResult<Justfile> {
|
||||||
let tokens = Lexer::test_lex(src)?;
|
let tokens = Lexer::test_lex(src)?;
|
||||||
|
@ -95,6 +95,7 @@ pub(crate) enum Error<'src> {
|
|||||||
GetConfirmation {
|
GetConfirmation {
|
||||||
io_error: io::Error,
|
io_error: io::Error,
|
||||||
},
|
},
|
||||||
|
Homedir,
|
||||||
InitExists {
|
InitExists {
|
||||||
justfile: PathBuf,
|
justfile: PathBuf,
|
||||||
},
|
},
|
||||||
@ -347,6 +348,9 @@ impl<'src> ColorDisplay for Error<'src> {
|
|||||||
GetConfirmation { io_error } => {
|
GetConfirmation { io_error } => {
|
||||||
write!(f, "Failed to read confirmation from stdin: {io_error}")?;
|
write!(f, "Failed to read confirmation from stdin: {io_error}")?;
|
||||||
}
|
}
|
||||||
|
Homedir => {
|
||||||
|
write!(f, "Failed to get homedir")?;
|
||||||
|
}
|
||||||
InitExists { justfile } => {
|
InitExists { justfile } => {
|
||||||
write!(f, "Justfile `{}` already exists", justfile.display())?;
|
write!(f, "Justfile `{}` already exists", justfile.display())?;
|
||||||
}
|
}
|
||||||
|
@ -152,3 +152,20 @@ fn recipes_in_import_are_overridden_by_recipes_in_parent() {
|
|||||||
.stdout("ROOT\n")
|
.stdout("ROOT\n")
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
#[test]
|
||||||
|
fn import_paths_beginning_with_tilde_are_expanded_to_homdir() {
|
||||||
|
Test::new()
|
||||||
|
.write("foobar/mod.just", "foo:\n @echo FOOBAR")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
import '~/mod.just'
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.arg("foo")
|
||||||
|
.stdout("FOOBAR\n")
|
||||||
|
.env("HOME", "foobar")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
@ -529,3 +529,22 @@ fn submodule_shebang_recipes_run_in_submodule_directory() {
|
|||||||
.stdout("BAR")
|
.stdout("BAR")
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
#[test]
|
||||||
|
fn module_paths_beginning_with_tilde_are_expanded_to_homdir() {
|
||||||
|
Test::new()
|
||||||
|
.write("foobar/mod.just", "foo:\n @echo FOOBAR")
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
mod foo '~/mod.just'
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.test_round_trip(false)
|
||||||
|
.arg("--unstable")
|
||||||
|
.arg("foo")
|
||||||
|
.arg("foo")
|
||||||
|
.stdout("FOOBAR\n")
|
||||||
|
.env("HOME", "foobar")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user