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!
|
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:
|
Assignment, strings, concatination, and substitution with `{{...}}` are supported:
|
||||||
|
|
||||||
```make
|
```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."),
|
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 justfile_option = matches.value_of("justfile");
|
||||||
let working_directory_option = matches.value_of("working-directory");
|
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() {
|
let arguments = if !rest.is_empty() {
|
||||||
rest
|
rest
|
||||||
} else if let Some(recipe) = justfile.first() {
|
} 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();
|
let mut binary = env::current_dir().unwrap();
|
||||||
binary.push("./target/debug/just");
|
binary.push("./target/debug/just");
|
||||||
let output = process::Command::new(binary)
|
let output = process::Command::new(binary)
|
||||||
.current_dir(path)
|
.current_dir(path)
|
||||||
|
.args(args)
|
||||||
.output()
|
.output()
|
||||||
.expect("just invocation failed");
|
.expect("just invocation failed");
|
||||||
|
|
||||||
@ -86,7 +87,7 @@ fn test_justfile_search() {
|
|||||||
path.push("d");
|
path.push("d");
|
||||||
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
|
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
|
||||||
|
|
||||||
search_test(path);
|
search_test(path, &[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -107,7 +108,7 @@ fn test_capitalized_justfile_search() {
|
|||||||
path.push("d");
|
path.push("d");
|
||||||
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
|
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
|
||||||
|
|
||||||
search_test(path);
|
search_test(path, &[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -139,7 +140,52 @@ fn test_capitalization_priority() {
|
|||||||
path.push("d");
|
path.push("d");
|
||||||
fs::create_dir(&path).expect("test justfile search: failed to create intermediary directory");
|
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]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user