Allow path-prefixed first arguments (#139)
If the first argument to just contains a `/`, then it will be handled specially. Everything before the last `/` is treated as a directory, and just will search for the justfile starting there, instead of in the current directory.
This commit is contained in:
parent
86755582bb
commit
b267d0444e
16
README.md
16
README.md
@ -146,6 +146,22 @@ cc main.c foo.c bar.c -o main
|
||||
testing... all tests passed!
|
||||
```
|
||||
|
||||
If the first argument passed to `just` contains a `/`, then the following occurs:
|
||||
|
||||
1. The argument is split at the last `/`.
|
||||
2. The part before the last `/` is treated as a directory. Just will start its search for the justfile there, instead of the in current directory.
|
||||
3. The part after the last slash is treated as a normal argument, or ignored if it is empty.
|
||||
|
||||
This may seem a little strange, but it's useful if you wish to run a command in a justfile that is in a subdirectory.
|
||||
|
||||
For example, if you are in a directory which contains a subdirectory named `foo`, which contains justfile with the recipe `build`, which is also the default recipe, the following are all equivalent:
|
||||
|
||||
```sh
|
||||
$ (cd foo && just build)
|
||||
$ just foo/build
|
||||
$ just foo/
|
||||
```
|
||||
|
||||
Assignment, strings, concatination, and substitution with `{{...}}` are supported:
|
||||
|
||||
```make
|
||||
|
68
src/app.rs
68
src/app.rs
@ -163,6 +163,52 @@ pub fn app() {
|
||||
None => die!("Invalid argument to --color. This is a bug in just."),
|
||||
};
|
||||
|
||||
let set_count = matches.occurrences_of("set");
|
||||
let mut overrides = BTreeMap::new();
|
||||
if set_count > 0 {
|
||||
let mut values = matches.values_of("set").unwrap();
|
||||
for _ in 0..set_count {
|
||||
overrides.insert(values.next().unwrap(), values.next().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
let override_re = regex::Regex::new("^([^=]+)=(.*)$").unwrap();
|
||||
|
||||
let raw_arguments = matches.values_of("arguments").map(|values| values.collect::<Vec<_>>())
|
||||
.unwrap_or_default();
|
||||
|
||||
for argument in raw_arguments.iter().take_while(|arg| override_re.is_match(arg)) {
|
||||
let captures = override_re.captures(argument).unwrap();
|
||||
overrides.insert(captures.at(1).unwrap(), captures.at(2).unwrap());
|
||||
}
|
||||
|
||||
let rest = raw_arguments.iter().skip_while(|arg| override_re.is_match(arg))
|
||||
.enumerate()
|
||||
.flat_map(|(i, argument)| {
|
||||
if i == 0 {
|
||||
if let Some(i) = argument.rfind("/") {
|
||||
if matches.is_present("working-directory") {
|
||||
die!("--working-directory and a path prefixed recipe may not be used together.");
|
||||
}
|
||||
|
||||
let (dir, recipe) = argument.split_at(i + 1);
|
||||
|
||||
if let Err(error) = env::set_current_dir(dir) {
|
||||
die!("Error changing directory: {}", error);
|
||||
}
|
||||
|
||||
if recipe.is_empty() {
|
||||
return None;
|
||||
} else {
|
||||
return Some(recipe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(*argument)
|
||||
})
|
||||
.collect::<Vec<&str>>();
|
||||
|
||||
let justfile_option = matches.value_of("justfile");
|
||||
let working_directory_option = matches.value_of("working-directory");
|
||||
|
||||
@ -275,28 +321,6 @@ pub fn app() {
|
||||
}
|
||||
}
|
||||
|
||||
let set_count = matches.occurrences_of("set");
|
||||
let mut overrides = BTreeMap::new();
|
||||
if set_count > 0 {
|
||||
let mut values = matches.values_of("set").unwrap();
|
||||
for _ in 0..set_count {
|
||||
overrides.insert(values.next().unwrap(), values.next().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
let override_re = regex::Regex::new("^([^=]+)=(.*)$").unwrap();
|
||||
|
||||
let raw_arguments = matches.values_of("arguments").map(|values| values.collect::<Vec<_>>())
|
||||
.unwrap_or_default();
|
||||
|
||||
for argument in raw_arguments.iter().take_while(|arg| override_re.is_match(arg)) {
|
||||
let captures = override_re.captures(argument).unwrap();
|
||||
overrides.insert(captures.at(1).unwrap(), captures.at(2).unwrap());
|
||||
}
|
||||
|
||||
let rest = raw_arguments.iter().skip_while(|arg| override_re.is_match(arg))
|
||||
.cloned().collect::<Vec<_>>();
|
||||
|
||||
let arguments = if !rest.is_empty() {
|
||||
rest
|
||||
} else if let Some(recipe) = justfile.first() {
|
||||
|
@ -51,11 +51,12 @@ fn integration_test(
|
||||
}
|
||||
}
|
||||
|
||||
fn search_test<P: AsRef<path::Path>>(path: P) {
|
||||
fn search_test<P: AsRef<path::Path>>(path: P, args: &[&str]) {
|
||||
let mut binary = env::current_dir().unwrap();
|
||||
binary.push("./target/debug/just");
|
||||
let output = process::Command::new(binary)
|
||||
.current_dir(path)
|
||||
.args(args)
|
||||
.output()
|
||||
.expect("just invocation failed");
|
||||
|
||||
@ -86,7 +87,7 @@ fn test_justfile_search() {
|
||||
path.push("d");
|
||||
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
|
||||
|
||||
search_test(path);
|
||||
search_test(path, &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -107,7 +108,7 @@ fn test_capitalized_justfile_search() {
|
||||
path.push("d");
|
||||
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
|
||||
|
||||
search_test(path);
|
||||
search_test(path, &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -139,7 +140,52 @@ fn test_capitalization_priority() {
|
||||
path.push("d");
|
||||
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
|
||||
|
||||
search_test(path);
|
||||
search_test(path, &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_upwards_path_argument() {
|
||||
let tmp = TempDir::new("just-test-justfile-search")
|
||||
.expect("test justfile search: failed to create temporary directory");
|
||||
let mut path = tmp.path().to_path_buf();
|
||||
path.push("justfile");
|
||||
brev::dump(&path, "default:\n\techo ok");
|
||||
path.pop();
|
||||
|
||||
path.push("a");
|
||||
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
|
||||
|
||||
path.push("justfile");
|
||||
brev::dump(&path, "default:\n\techo bad");
|
||||
path.pop();
|
||||
|
||||
search_test(&path, &["../"]);
|
||||
search_test(&path, &["../default"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_downwards_path_argument() {
|
||||
let tmp = TempDir::new("just-test-justfile-search")
|
||||
.expect("test justfile search: failed to create temporary directory");
|
||||
let mut path = tmp.path().to_path_buf();
|
||||
path.push("justfile");
|
||||
brev::dump(&path, "default:\n\techo bad");
|
||||
path.pop();
|
||||
|
||||
path.push("a");
|
||||
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
|
||||
|
||||
path.push("justfile");
|
||||
brev::dump(&path, "default:\n\techo ok");
|
||||
path.pop();
|
||||
path.pop();
|
||||
|
||||
search_test(&path, &["a/"]);
|
||||
search_test(&path, &["a/default"]);
|
||||
search_test(&path, &["./a/"]);
|
||||
search_test(&path, &["./a/default"]);
|
||||
search_test(&path, &["./a/"]);
|
||||
search_test(&path, &["./a/default"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
Loading…
Reference in New Issue
Block a user