mirror of
https://github.com/casey/just.git
synced 2024-11-22 02:09:44 +03:00
Merge 0c6f5e8376
into 67034cb8b4
This commit is contained in:
commit
5bc728d472
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -498,6 +498,15 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_executable"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4a1b5bad6f9072935961dfbf1cced2f3d129963d091b6f69f007fe04e758ae2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
@ -535,8 +544,10 @@ dependencies = [
|
||||
"dirs",
|
||||
"dotenvy",
|
||||
"edit-distance",
|
||||
"either",
|
||||
"executable-path",
|
||||
"heck",
|
||||
"is_executable",
|
||||
"lexiclean",
|
||||
"libc",
|
||||
"num_cpus",
|
||||
|
@ -30,7 +30,9 @@ derivative = "2.0.0"
|
||||
dirs = "5.0.1"
|
||||
dotenvy = "0.15"
|
||||
edit-distance = "2.0.0"
|
||||
either = "1.13.0"
|
||||
heck = "0.5.0"
|
||||
is_executable = "1.0.4"
|
||||
lexiclean = "0.0.1"
|
||||
libc = "0.2.0"
|
||||
num_cpus = "1.15.0"
|
||||
|
16
README.md
16
README.md
@ -1632,6 +1632,22 @@ $ just
|
||||
name `key`, returning `default` if it is not present.
|
||||
- `env(key)`<sup>1.15.0</sup> — Alias for `env_var(key)`.
|
||||
- `env(key, default)`<sup>1.15.0</sup> — Alias for `env_var_or_default(key, default)`.
|
||||
- `which(exe)`<sup>master</sup> — Retrieves the full path of `exe` according
|
||||
to the `PATH`. Returns an empty string if no executable named `exe` exists.
|
||||
|
||||
```just
|
||||
bash := which("bash")
|
||||
nexist := which("does-not-exist")
|
||||
|
||||
@test:
|
||||
echo "bash: '{{bash}}'"
|
||||
echo "nexist: '{{nexist}}'"
|
||||
```
|
||||
|
||||
```console
|
||||
bash: '/bin/bash'
|
||||
nexist: ''
|
||||
```
|
||||
|
||||
#### Invocation Information
|
||||
|
||||
|
2
justfile
2
justfile
@ -6,6 +6,8 @@ alias t := test
|
||||
|
||||
log := "warn"
|
||||
|
||||
bingus := which("bash")
|
||||
|
||||
export JUST_LOG := log
|
||||
|
||||
[group: 'dev']
|
||||
|
@ -1,5 +1,6 @@
|
||||
use {
|
||||
super::*,
|
||||
either::Either,
|
||||
heck::{
|
||||
ToKebabCase, ToLowerCamelCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnakeCase, ToTitleCase,
|
||||
ToUpperCamelCase,
|
||||
@ -110,6 +111,7 @@ pub(crate) fn get(name: &str) -> Option<Function> {
|
||||
"uppercase" => Unary(uppercase),
|
||||
"uuid" => Nullary(uuid),
|
||||
"without_extension" => Unary(without_extension),
|
||||
"which" => Unary(which),
|
||||
_ => return None,
|
||||
};
|
||||
Some(function)
|
||||
@ -667,6 +669,61 @@ fn uuid(_context: Context) -> FunctionResult {
|
||||
Ok(uuid::Uuid::new_v4().to_string())
|
||||
}
|
||||
|
||||
fn which(context: Context, s: &str) -> FunctionResult {
|
||||
use is_executable::IsExecutable;
|
||||
|
||||
let cmd = PathBuf::from(s);
|
||||
|
||||
let path_var;
|
||||
let candidates = match cmd.components().count() {
|
||||
0 => Err("empty command string".to_string())?,
|
||||
1 => {
|
||||
// cmd is a regular command
|
||||
path_var = env::var_os("PATH").ok_or("Environment variable `PATH` is not set")?;
|
||||
Either::Left(env::split_paths(&path_var).map(|path| path.join(cmd.clone())))
|
||||
}
|
||||
_ => {
|
||||
// cmd contains a path separator, treat it as a path
|
||||
Either::Right(iter::once(cmd))
|
||||
}
|
||||
};
|
||||
|
||||
for mut candidate in candidates.into_iter() {
|
||||
if candidate.is_relative() {
|
||||
// This candidate is a relative path, either because the user invoked `which("./rel/path")`,
|
||||
// or because there was a relative path in `PATH`. Resolve it to an absolute path.
|
||||
let cwd = context
|
||||
.evaluator
|
||||
.context
|
||||
.search
|
||||
.justfile
|
||||
.parent()
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Could not resolve absolute path from `{}` relative to the justfile directory. Justfile `{}` had no parent.",
|
||||
candidate.display(),
|
||||
context.evaluator.context.search.justfile.display()
|
||||
)
|
||||
})?;
|
||||
let mut cwd = PathBuf::from(cwd);
|
||||
cwd.push(candidate);
|
||||
candidate = cwd;
|
||||
}
|
||||
|
||||
if candidate.is_executable() {
|
||||
return candidate.to_str().map(str::to_string).ok_or_else(|| {
|
||||
format!(
|
||||
"Executable path is not valid unicode: {}",
|
||||
candidate.display()
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// No viable candidates; return an empty string
|
||||
Ok(String::new())
|
||||
}
|
||||
|
||||
fn without_extension(_context: Context, path: &str) -> FunctionResult {
|
||||
let parent = Utf8Path::new(path)
|
||||
.parent()
|
||||
|
@ -111,6 +111,7 @@ mod timestamps;
|
||||
mod undefined_variables;
|
||||
mod unexport;
|
||||
mod unstable;
|
||||
mod which_exec;
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
#[cfg(target_family = "windows")]
|
||||
|
42
tests/which_exec.rs
Normal file
42
tests/which_exec.rs
Normal file
@ -0,0 +1,42 @@
|
||||
use super::*;
|
||||
|
||||
fn make_path() -> TempDir {
|
||||
let tmp = temptree! {
|
||||
"hello.exe": "#!/usr/bin/env bash\necho hello\n",
|
||||
};
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
let exe = tmp.path().join("hello.exe");
|
||||
let perms = std::os::unix::fs::PermissionsExt::from_mode(0o755);
|
||||
fs::set_permissions(exe, perms).unwrap();
|
||||
}
|
||||
|
||||
tmp
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finds_executable() {
|
||||
let tmp = make_path();
|
||||
let mut path = env::current_dir().unwrap();
|
||||
path.push("bin");
|
||||
Test::new()
|
||||
.justfile(r#"p := which("hello.exe")"#)
|
||||
.env("PATH", tmp.path().to_str().unwrap())
|
||||
.args(["--evaluate", "p"])
|
||||
.stdout(format!("{}", tmp.path().join("hello.exe").display()))
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prints_empty_string_for_missing_executable() {
|
||||
let tmp = make_path();
|
||||
let mut path = env::current_dir().unwrap();
|
||||
path.push("bin");
|
||||
Test::new()
|
||||
.justfile(r#"p := which("goodbye.exe")"#)
|
||||
.env("PATH", tmp.path().to_str().unwrap())
|
||||
.args(["--evaluate", "p"])
|
||||
.stdout("")
|
||||
.run();
|
||||
}
|
Loading…
Reference in New Issue
Block a user