Add justfile_directory() and justfile() (#569)

Add `justfile()` function, returning the current justfile, and
`justfile_directory(), returning its parent directory.
This commit is contained in:
Casey Rodarmor 2019-12-25 06:12:06 -08:00 committed by GitHub
parent eeb603160a
commit 61ab53dbc1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 109 additions and 70 deletions

View File

@ -402,13 +402,11 @@ impl Config {
match &self.subcommand { match &self.subcommand {
Dump => self.dump(justfile), Dump => self.dump(justfile),
Evaluate { overrides } => { Evaluate { overrides } => self.run(justfile, &search, overrides, &Vec::new()),
self.run(justfile, &search.working_directory, overrides, &Vec::new())
}
Run { Run {
arguments, arguments,
overrides, overrides,
} => self.run(justfile, &search.working_directory, overrides, arguments), } => self.run(justfile, &search, overrides, arguments),
List => self.list(justfile), List => self.list(justfile),
Show { ref name } => self.show(&name, justfile), Show { ref name } => self.show(&name, justfile),
Summary => self.summary(justfile), Summary => self.summary(justfile),
@ -561,7 +559,7 @@ impl Config {
fn run( fn run(
&self, &self,
justfile: Justfile, justfile: Justfile,
working_directory: &Path, search: &Search,
overrides: &BTreeMap<String, String>, overrides: &BTreeMap<String, String>,
arguments: &[String], arguments: &[String],
) -> Result<(), i32> { ) -> Result<(), i32> {
@ -569,7 +567,7 @@ impl Config {
warn!("Failed to set CTRL-C handler: {}", error) warn!("Failed to set CTRL-C handler: {}", error)
} }
let result = justfile.run(&self, working_directory, overrides, arguments); let result = justfile.run(&self, search, overrides, arguments);
if !self.quiet { if !self.quiet {
result.eprint(self.color) result.eprint(self.color)

View File

@ -6,7 +6,7 @@ pub(crate) struct Evaluator<'src: 'run, 'run> {
dotenv: &'run BTreeMap<String, String>, dotenv: &'run BTreeMap<String, String>,
scope: Scope<'src, 'run>, scope: Scope<'src, 'run>,
settings: &'run Settings<'run>, settings: &'run Settings<'run>,
working_directory: &'run Path, search: &'run Search,
} }
impl<'src, 'run> Evaluator<'src, 'run> { impl<'src, 'run> Evaluator<'src, 'run> {
@ -16,7 +16,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
dotenv: &'run BTreeMap<String, String>, dotenv: &'run BTreeMap<String, String>,
overrides: Scope<'src, 'run>, overrides: Scope<'src, 'run>,
settings: &'run Settings<'run>, settings: &'run Settings<'run>,
working_directory: &'run Path, search: &'run Search,
) -> RunResult<'src, Scope<'src, 'run>> { ) -> RunResult<'src, Scope<'src, 'run>> {
let mut evaluator = Evaluator { let mut evaluator = Evaluator {
scope: overrides, scope: overrides,
@ -24,7 +24,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
config, config,
dotenv, dotenv,
settings, settings,
working_directory, search,
}; };
for assignment in assignments.values() { for assignment in assignments.values() {
@ -67,9 +67,9 @@ impl<'src, 'run> Evaluator<'src, 'run> {
} }
Expression::Call { thunk } => { Expression::Call { thunk } => {
let context = FunctionContext { let context = FunctionContext {
invocation_directory: &self.config.invocation_directory,
working_directory: &self.working_directory,
dotenv: self.dotenv, dotenv: self.dotenv,
invocation_directory: &self.config.invocation_directory,
search: self.search,
}; };
use Thunk::*; use Thunk::*;
@ -127,7 +127,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
cmd.arg(raw); cmd.arg(raw);
cmd.current_dir(self.working_directory); cmd.current_dir(&self.search.working_directory);
cmd.export(self.dotenv, &self.scope); cmd.export(self.dotenv, &self.scope);
@ -167,15 +167,15 @@ impl<'src, 'run> Evaluator<'src, 'run> {
arguments: &[&str], arguments: &[&str],
scope: &'run Scope<'src, 'run>, scope: &'run Scope<'src, 'run>,
settings: &'run Settings, settings: &'run Settings,
working_directory: &'run Path, search: &'run Search,
) -> RunResult<'src, Scope<'src, 'run>> { ) -> RunResult<'src, Scope<'src, 'run>> {
let mut evaluator = Evaluator { let mut evaluator = Evaluator {
assignments: None, assignments: None,
scope: Scope::child(scope), scope: Scope::child(scope),
search,
settings, settings,
dotenv, dotenv,
config, config,
working_directory,
}; };
let mut scope = Scope::child(scope); let mut scope = Scope::child(scope);
@ -211,15 +211,15 @@ impl<'src, 'run> Evaluator<'src, 'run> {
dotenv: &'run BTreeMap<String, String>, dotenv: &'run BTreeMap<String, String>,
scope: &'run Scope<'src, 'run>, scope: &'run Scope<'src, 'run>,
settings: &'run Settings, settings: &'run Settings,
working_directory: &'run Path, search: &'run Search,
) -> Evaluator<'src, 'run> { ) -> Evaluator<'src, 'run> {
Evaluator { Evaluator {
assignments: None, assignments: None,
scope: Scope::child(scope), scope: Scope::child(scope),
search,
settings, settings,
dotenv, dotenv,
config, config,
working_directory,
} }
} }
} }

View File

@ -1,6 +1,7 @@
use crate::common::*; use crate::common::*;
use target; use target;
use Function::*;
pub(crate) enum Function { pub(crate) enum Function {
Nullary(fn(&FunctionContext) -> Result<String, String>), Nullary(fn(&FunctionContext) -> Result<String, String>),
@ -10,15 +11,14 @@ pub(crate) enum Function {
lazy_static! { lazy_static! {
pub(crate) static ref TABLE: BTreeMap<&'static str, Function> = vec![ pub(crate) static ref TABLE: BTreeMap<&'static str, Function> = vec![
("arch", Function::Nullary(arch)), ("arch", Nullary(arch)),
("os", Function::Nullary(os)), ("os", Nullary(os)),
("os_family", Function::Nullary(os_family)), ("os_family", Nullary(os_family)),
("env_var", Function::Unary(env_var)), ("justfile_directory", Nullary(justfile_directory)),
("env_var_or_default", Function::Binary(env_var_or_default)), ("justfile", Nullary(justfile)),
( ("invocation_directory", Nullary(invocation_directory)),
"invocation_directory", ("env_var", Unary(env_var)),
Function::Nullary(invocation_directory) ("env_var_or_default", Binary(env_var_or_default)),
),
] ]
.into_iter() .into_iter()
.collect(); .collect();
@ -26,7 +26,6 @@ lazy_static! {
impl Function { impl Function {
pub(crate) fn argc(&self) -> usize { pub(crate) fn argc(&self) -> usize {
use self::Function::*;
match *self { match *self {
Nullary(_) => 0, Nullary(_) => 0,
Unary(_) => 1, Unary(_) => 1,
@ -48,8 +47,44 @@ fn os_family(_context: &FunctionContext) -> Result<String, String> {
} }
fn invocation_directory(context: &FunctionContext) -> Result<String, String> { fn invocation_directory(context: &FunctionContext) -> Result<String, String> {
Platform::to_shell_path(context.working_directory, context.invocation_directory) Platform::to_shell_path(
.map_err(|e| format!("Error getting shell path: {}", e)) &context.search.working_directory,
context.invocation_directory,
)
.map_err(|e| format!("Error getting shell path: {}", e))
}
fn justfile(context: &FunctionContext) -> Result<String, String> {
context
.search
.justfile
.to_str()
.map(str::to_owned)
.ok_or_else(|| {
format!(
"Justfile path is not valid unicode: {}",
context.search.justfile.to_string_lossy()
)
})
}
fn justfile_directory(context: &FunctionContext) -> Result<String, String> {
let justfile_directory = context.search.justfile.parent().ok_or_else(|| {
format!(
"Could not resolve justfile directory. Justfile `{}` had no parent.",
context.search.justfile.display()
)
})?;
justfile_directory
.to_str()
.map(str::to_owned)
.ok_or_else(|| {
format!(
"Justfile directory is not valid unicode: {}",
justfile_directory.to_string_lossy()
)
})
} }
fn env_var(context: &FunctionContext, key: &str) -> Result<String, String> { fn env_var(context: &FunctionContext, key: &str) -> Result<String, String> {

View File

@ -1,7 +1,7 @@
use crate::common::*; use crate::common::*;
pub(crate) struct FunctionContext<'run> { pub(crate) struct FunctionContext<'run> {
pub(crate) invocation_directory: &'run Path,
pub(crate) working_directory: &'run Path,
pub(crate) dotenv: &'run BTreeMap<String, String>, pub(crate) dotenv: &'run BTreeMap<String, String>,
pub(crate) invocation_directory: &'run Path,
pub(crate) search: &'run Search,
} }

View File

@ -43,13 +43,13 @@ impl<'src> Justfile<'src> {
None None
} }
pub(crate) fn run( pub(crate) fn run<'run>(
&'src self, &'run self,
config: &'src Config, config: &'run Config,
working_directory: &'src Path, search: &'run Search,
overrides: &'src BTreeMap<String, String>, overrides: &'run BTreeMap<String, String>,
arguments: &'src [String], arguments: &'run [String],
) -> RunResult<'src, ()> { ) -> RunResult<'run, ()> {
let argvec: Vec<&str> = if !arguments.is_empty() { let argvec: Vec<&str> = if !arguments.is_empty() {
arguments.iter().map(|argument| argument.as_str()).collect() arguments.iter().map(|argument| argument.as_str()).collect()
} else if let Some(recipe) = self.first() { } else if let Some(recipe) = self.first() {
@ -105,7 +105,7 @@ impl<'src> Justfile<'src> {
&dotenv, &dotenv,
scope, scope,
&self.settings, &self.settings,
working_directory, search,
)? )?
}; };
@ -172,12 +172,12 @@ impl<'src> Justfile<'src> {
settings: &self.settings, settings: &self.settings,
config, config,
scope, scope,
working_directory, search,
}; };
let mut ran = BTreeSet::new(); let mut ran = BTreeSet::new();
for (recipe, arguments) in grouped { for (recipe, arguments) in grouped {
self.run_recipe(&context, recipe, arguments, &dotenv, &mut ran)? self.run_recipe(&context, recipe, arguments, &dotenv, &search, &mut ran)?
} }
Ok(()) Ok(())
@ -203,6 +203,7 @@ impl<'src> Justfile<'src> {
recipe: &Recipe<'src>, recipe: &Recipe<'src>,
arguments: &[&'run str], arguments: &[&'run str],
dotenv: &BTreeMap<String, String>, dotenv: &BTreeMap<String, String>,
search: &'run Search,
ran: &mut BTreeSet<Vec<String>>, ran: &mut BTreeSet<Vec<String>>,
) -> RunResult<'src, ()> { ) -> RunResult<'src, ()> {
let scope = Evaluator::evaluate_parameters( let scope = Evaluator::evaluate_parameters(
@ -212,16 +213,11 @@ impl<'src> Justfile<'src> {
arguments, arguments,
&context.scope, &context.scope,
context.settings, context.settings,
context.working_directory, search,
)?; )?;
let mut evaluator = Evaluator::recipe_evaluator( let mut evaluator =
context.config, Evaluator::recipe_evaluator(context.config, dotenv, &scope, context.settings, search);
dotenv,
&scope,
context.settings,
context.working_directory,
);
for Dependency { recipe, arguments } in &recipe.dependencies { for Dependency { recipe, arguments } in &recipe.dependencies {
let mut invocation = vec![recipe.name().to_owned()]; let mut invocation = vec![recipe.name().to_owned()];
@ -236,11 +232,11 @@ impl<'src> Justfile<'src> {
.skip(1) .skip(1)
.map(String::as_ref) .map(String::as_ref)
.collect::<Vec<&str>>(); .collect::<Vec<&str>>();
self.run_recipe(context, recipe, &arguments, dotenv, ran)?; self.run_recipe(context, recipe, &arguments, dotenv, search, ran)?;
} }
} }
recipe.run(context, dotenv, scope)?; recipe.run(context, dotenv, scope, search)?;
let mut invocation = Vec::new(); let mut invocation = Vec::new();
invocation.push(recipe.name().to_owned()); invocation.push(recipe.name().to_owned());

View File

@ -69,6 +69,7 @@ impl<'src, D> Recipe<'src, D> {
context: &RecipeContext<'src, 'run>, context: &RecipeContext<'src, 'run>,
dotenv: &BTreeMap<String, String>, dotenv: &BTreeMap<String, String>,
scope: Scope<'src, 'run>, scope: Scope<'src, 'run>,
search: &'run Search,
) -> RunResult<'src, ()> { ) -> RunResult<'src, ()> {
let config = &context.config; let config = &context.config;
@ -82,13 +83,8 @@ impl<'src, D> Recipe<'src, D> {
); );
} }
let mut evaluator = Evaluator::recipe_evaluator( let mut evaluator =
context.config, Evaluator::recipe_evaluator(context.config, dotenv, &scope, context.settings, search);
dotenv,
&scope,
context.settings,
context.working_directory,
);
if self.shebang { if self.shebang {
let mut evaluated_lines = vec![]; let mut evaluated_lines = vec![];
@ -166,12 +162,16 @@ impl<'src, D> Recipe<'src, D> {
})?; })?;
// create a command to run the script // create a command to run the script
let mut command = let mut command = Platform::make_shebang_command(
Platform::make_shebang_command(&path, context.working_directory, interpreter, argument) &path,
.map_err(|output_error| RuntimeError::Cygpath { &context.search.working_directory,
recipe: self.name(), interpreter,
output_error, argument,
})?; )
.map_err(|output_error| RuntimeError::Cygpath {
recipe: self.name(),
output_error,
})?;
command.export(dotenv, &scope); command.export(dotenv, &scope);
@ -248,7 +248,7 @@ impl<'src, D> Recipe<'src, D> {
let mut cmd = context.settings.shell_command(config); let mut cmd = context.settings.shell_command(config);
cmd.current_dir(context.working_directory); cmd.current_dir(&context.search.working_directory);
cmd.arg(command); cmd.arg(command);

View File

@ -3,6 +3,6 @@ use crate::common::*;
pub(crate) struct RecipeContext<'src: 'run, 'run> { pub(crate) struct RecipeContext<'src: 'run, 'run> {
pub(crate) config: &'run Config, pub(crate) config: &'run Config,
pub(crate) scope: Scope<'src, 'run>, pub(crate) scope: Scope<'src, 'run>,
pub(crate) working_directory: &'run Path, pub(crate) search: &'run Search,
pub(crate) settings: &'run Settings<'src>, pub(crate) settings: &'run Settings<'src>,
} }

View File

@ -2,7 +2,7 @@ use crate::common::*;
use std::path::Component; use std::path::Component;
const FILENAME: &str = "justfile"; pub(crate) const FILENAME: &str = "justfile";
const PROJECT_ROOT_CHILDREN: &[&str] = &[".bzr", ".git", ".hg", ".svn", "_darcs"]; const PROJECT_ROOT_CHILDREN: &[&str] = &[".bzr", ".git", ".hg", ".svn", "_darcs"];
pub(crate) struct Search { pub(crate) struct Search {

View File

@ -20,6 +20,16 @@ pub(crate) fn config(args: &[&str]) -> Config {
Config::from_matches(&matches).unwrap() Config::from_matches(&matches).unwrap()
} }
pub(crate) fn search(config: &Config) -> Search {
let working_directory = config.invocation_directory.clone();
let justfile = working_directory.join(crate::search::FILENAME);
Search {
working_directory,
justfile,
}
}
pub(crate) use test_utilities::{tempdir, unindent}; pub(crate) use test_utilities::{tempdir, unindent};
macro_rules! analysis_error { macro_rules! analysis_error {
@ -80,15 +90,15 @@ macro_rules! run_error {
} => { } => {
#[test] #[test]
fn $name() { fn $name() {
let config = &$crate::testing::config(&$args); let config = $crate::testing::config(&$args);
let current_dir = std::env::current_dir().unwrap(); let search = $crate::testing::search(&config);
if let Subcommand::Run{ overrides, arguments } = &config.subcommand { if let Subcommand::Run{ overrides, arguments } = &config.subcommand {
match $crate::compiler::Compiler::compile(&$crate::testing::unindent($src)) match $crate::compiler::Compiler::compile(&$crate::testing::unindent($src))
.expect("Expected successful compilation") .expect("Expected successful compilation")
.run( .run(
config, &config,
&current_dir, &search,
&overrides, &overrides,
&arguments, &arguments,
).expect_err("Expected runtime error") { ).expect_err("Expected runtime error") {