export variables done
This commit is contained in:
parent
f925520101
commit
69f8e07a30
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -4,6 +4,7 @@ version = "0.2.8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"brev 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"brev 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clap 2.16.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.16.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"itertools 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -51,11 +52,24 @@ dependencies = [
|
|||||||
"vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"either 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kernel32-sys"
|
name = "kernel32-sys"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@ -184,7 +198,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||||
"checksum brev 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "79571b60a8aa293f43b46370d8ba96fed28a5bee1303ea0e015d175ed0c63b40"
|
"checksum brev 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "79571b60a8aa293f43b46370d8ba96fed28a5bee1303ea0e015d175ed0c63b40"
|
||||||
"checksum clap 2.16.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0ab91429d96eece6d6cf8a737105f0e255fea039fed86e2118ff8d3fd69601dd"
|
"checksum clap 2.16.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0ab91429d96eece6d6cf8a737105f0e255fea039fed86e2118ff8d3fd69601dd"
|
||||||
|
"checksum either 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8aa2c82b7e1abd89a8a59fd89c4a51576ea76a894edf5d5b28944dd46edfed8d"
|
||||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||||
|
"checksum itertools 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ef81b0a15a9e1808cfd3ebe6a87277d29ee88b34ac1197cc7547f1dd6d9f5424"
|
||||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||||
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
|
"checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f"
|
||||||
"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d"
|
"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d"
|
||||||
|
@ -13,3 +13,4 @@ clap = "^2.0.0"
|
|||||||
tempdir = "^0.3.5"
|
tempdir = "^0.3.5"
|
||||||
lazy_static = "^0.2.1"
|
lazy_static = "^0.2.1"
|
||||||
brev = "^0.1.6"
|
brev = "^0.1.6"
|
||||||
|
itertools = "^0.5.5"
|
||||||
|
6
notes
6
notes
@ -1,11 +1,7 @@
|
|||||||
todo
|
todo
|
||||||
----
|
----
|
||||||
|
|
||||||
- allow setting and exporting environment variables
|
clean up export parsing
|
||||||
. export a as "HELLO_BAR"
|
|
||||||
. export a
|
|
||||||
. export HELLO_BAR = a
|
|
||||||
. export CC_FLAGS = "-g"
|
|
||||||
|
|
||||||
- raw strings with ''
|
- raw strings with ''
|
||||||
- multi line strings (not in recipe interpolations)
|
- multi line strings (not in recipe interpolations)
|
||||||
|
@ -5,14 +5,13 @@ use tempdir::TempDir;
|
|||||||
use super::std::process::Command;
|
use super::std::process::Command;
|
||||||
|
|
||||||
fn integration_test(
|
fn integration_test(
|
||||||
name: &str,
|
|
||||||
args: &[&str],
|
args: &[&str],
|
||||||
justfile: &str,
|
justfile: &str,
|
||||||
expected_status: i32,
|
expected_status: i32,
|
||||||
expected_stdout: &str,
|
expected_stdout: &str,
|
||||||
expected_stderr: &str,
|
expected_stderr: &str,
|
||||||
) {
|
) {
|
||||||
let tmp = TempDir::new(name)
|
let tmp = TempDir::new("just-integration")
|
||||||
.unwrap_or_else(|err| panic!("tmpdir: failed to create temporary directory: {}", err));
|
.unwrap_or_else(|err| panic!("tmpdir: failed to create temporary directory: {}", err));
|
||||||
let mut path = tmp.path().to_path_buf();
|
let mut path = tmp.path().to_path_buf();
|
||||||
path.push("justfile");
|
path.push("justfile");
|
||||||
@ -53,7 +52,6 @@ fn integration_test(
|
|||||||
#[test]
|
#[test]
|
||||||
fn default() {
|
fn default() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"default",
|
|
||||||
&[],
|
&[],
|
||||||
"default:\n echo hello\nother: \n echo bar",
|
"default:\n echo hello\nother: \n echo bar",
|
||||||
0,
|
0,
|
||||||
@ -65,7 +63,6 @@ fn default() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn quiet() {
|
fn quiet() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"quiet",
|
|
||||||
&[],
|
&[],
|
||||||
"default:\n @echo hello",
|
"default:\n @echo hello",
|
||||||
0,
|
0,
|
||||||
@ -94,7 +91,6 @@ c: b
|
|||||||
echo c
|
echo c
|
||||||
@mv b c";
|
@mv b c";
|
||||||
integration_test(
|
integration_test(
|
||||||
"order",
|
|
||||||
&["a", "d"],
|
&["a", "d"],
|
||||||
text,
|
text,
|
||||||
0,
|
0,
|
||||||
@ -111,7 +107,6 @@ a:
|
|||||||
d: c
|
d: c
|
||||||
c: b";
|
c: b";
|
||||||
integration_test(
|
integration_test(
|
||||||
"list",
|
|
||||||
&["--list"],
|
&["--list"],
|
||||||
text,
|
text,
|
||||||
0,
|
0,
|
||||||
@ -132,7 +127,6 @@ d:
|
|||||||
c:
|
c:
|
||||||
@echo c";
|
@echo c";
|
||||||
integration_test(
|
integration_test(
|
||||||
"select",
|
|
||||||
&["d", "c"],
|
&["d", "c"],
|
||||||
text,
|
text,
|
||||||
0,
|
0,
|
||||||
@ -153,7 +147,6 @@ d:
|
|||||||
c:
|
c:
|
||||||
echo c";
|
echo c";
|
||||||
integration_test(
|
integration_test(
|
||||||
"select",
|
|
||||||
&["d", "c"],
|
&["d", "c"],
|
||||||
text,
|
text,
|
||||||
0,
|
0,
|
||||||
@ -171,7 +164,6 @@ bar = hello + hello
|
|||||||
recipe:
|
recipe:
|
||||||
echo {{hello + "bar" + bar}}"#;
|
echo {{hello + "bar" + bar}}"#;
|
||||||
integration_test(
|
integration_test(
|
||||||
"show",
|
|
||||||
&["--show", "recipe"],
|
&["--show", "recipe"],
|
||||||
text,
|
text,
|
||||||
0,
|
0,
|
||||||
@ -190,7 +182,6 @@ bar = hello + hello
|
|||||||
recipe:
|
recipe:
|
||||||
echo {{hello + "bar" + bar}}"#;
|
echo {{hello + "bar" + bar}}"#;
|
||||||
integration_test(
|
integration_test(
|
||||||
"debug",
|
|
||||||
&["--debug"],
|
&["--debug"],
|
||||||
text,
|
text,
|
||||||
0,
|
0,
|
||||||
@ -213,7 +204,6 @@ fn status() {
|
|||||||
recipe:
|
recipe:
|
||||||
@function f { return 100; }; f";
|
@function f { return 100; }; f";
|
||||||
integration_test(
|
integration_test(
|
||||||
"status",
|
|
||||||
&[],
|
&[],
|
||||||
text,
|
text,
|
||||||
100,
|
100,
|
||||||
@ -225,7 +215,6 @@ recipe:
|
|||||||
#[test]
|
#[test]
|
||||||
fn error() {
|
fn error() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"error",
|
|
||||||
&[],
|
&[],
|
||||||
"bar:\nhello:\nfoo: bar baaaaaaaz hello",
|
"bar:\nhello:\nfoo: bar baaaaaaaz hello",
|
||||||
255,
|
255,
|
||||||
@ -241,7 +230,6 @@ fn error() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn backtick_success() {
|
fn backtick_success() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"backtick_success",
|
|
||||||
&[],
|
&[],
|
||||||
"a = `printf Hello,`\nbar:\n printf '{{a + `printf ' world!'`}}'",
|
"a = `printf Hello,`\nbar:\n printf '{{a + `printf ' world!'`}}'",
|
||||||
0,
|
0,
|
||||||
@ -253,7 +241,6 @@ fn backtick_success() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn backtick_trimming() {
|
fn backtick_trimming() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"backtick_trimming",
|
|
||||||
&[],
|
&[],
|
||||||
"a = `echo Hello,`\nbar:\n echo '{{a + `echo ' world!'`}}'",
|
"a = `echo Hello,`\nbar:\n echo '{{a + `echo ' world!'`}}'",
|
||||||
0,
|
0,
|
||||||
@ -265,7 +252,6 @@ fn backtick_trimming() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn backtick_code_assignment() {
|
fn backtick_code_assignment() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"backtick_code_assignment",
|
|
||||||
&[],
|
&[],
|
||||||
"b = a\na = `function f { return 100; }; f`\nbar:\n echo '{{`function f { return 200; }; f`}}'",
|
"b = a\na = `function f { return 100; }; f`\nbar:\n echo '{{`function f { return 200; }; f`}}'",
|
||||||
100,
|
100,
|
||||||
@ -281,7 +267,6 @@ fn backtick_code_assignment() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn backtick_code_interpolation() {
|
fn backtick_code_interpolation() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"backtick_code_interpolation",
|
|
||||||
&[],
|
&[],
|
||||||
"b = a\na = `echo hello`\nbar:\n echo '{{`function f { return 200; }; f`}}'",
|
"b = a\na = `echo hello`\nbar:\n echo '{{`function f { return 200; }; f`}}'",
|
||||||
200,
|
200,
|
||||||
@ -297,7 +282,6 @@ fn backtick_code_interpolation() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn shebang_backtick_failure() {
|
fn shebang_backtick_failure() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"shebang_backtick_failure",
|
|
||||||
&[],
|
&[],
|
||||||
"foo:
|
"foo:
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
@ -316,7 +300,6 @@ fn shebang_backtick_failure() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn command_backtick_failure() {
|
fn command_backtick_failure() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"command_backtick_failure",
|
|
||||||
&[],
|
&[],
|
||||||
"foo:
|
"foo:
|
||||||
echo hello
|
echo hello
|
||||||
@ -334,7 +317,6 @@ fn command_backtick_failure() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn assignment_backtick_failure() {
|
fn assignment_backtick_failure() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"assignment_backtick_failure",
|
|
||||||
&[],
|
&[],
|
||||||
"foo:
|
"foo:
|
||||||
echo hello
|
echo hello
|
||||||
@ -353,7 +335,6 @@ a = `exit 222`",
|
|||||||
#[test]
|
#[test]
|
||||||
fn unknown_override_options() {
|
fn unknown_override_options() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"unknown_override_options",
|
|
||||||
&["--set", "foo", "bar", "a", "b", "--set", "baz", "bob", "--set", "a", "b"],
|
&["--set", "foo", "bar", "a", "b", "--set", "baz", "bob", "--set", "a", "b"],
|
||||||
"foo:
|
"foo:
|
||||||
echo hello
|
echo hello
|
||||||
@ -368,7 +349,6 @@ a = `exit 222`",
|
|||||||
#[test]
|
#[test]
|
||||||
fn unknown_override_args() {
|
fn unknown_override_args() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"unknown_override_args",
|
|
||||||
&["foo=bar", "baz=bob", "a=b", "a", "b"],
|
&["foo=bar", "baz=bob", "a=b", "a", "b"],
|
||||||
"foo:
|
"foo:
|
||||||
echo hello
|
echo hello
|
||||||
@ -383,7 +363,6 @@ a = `exit 222`",
|
|||||||
#[test]
|
#[test]
|
||||||
fn overrides_first() {
|
fn overrides_first() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"unknown_override_args",
|
|
||||||
&["foo=bar", "a=b", "recipe", "baz=bar"],
|
&["foo=bar", "a=b", "recipe", "baz=bar"],
|
||||||
r#"
|
r#"
|
||||||
foo = "foo"
|
foo = "foo"
|
||||||
@ -404,7 +383,6 @@ recipe arg:
|
|||||||
#[test]
|
#[test]
|
||||||
fn dry_run() {
|
fn dry_run() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"dry_run",
|
|
||||||
&["--dry-run", "shebang", "command"],
|
&["--dry-run", "shebang", "command"],
|
||||||
r#"
|
r#"
|
||||||
var = `echo stderr 1>&2; echo backtick`
|
var = `echo stderr 1>&2; echo backtick`
|
||||||
@ -436,7 +414,6 @@ echo command interpolation
|
|||||||
#[test]
|
#[test]
|
||||||
fn evaluate() {
|
fn evaluate() {
|
||||||
integration_test(
|
integration_test(
|
||||||
"evaluate",
|
|
||||||
&["--evaluate"],
|
&["--evaluate"],
|
||||||
r#"
|
r#"
|
||||||
foo = "a\t"
|
foo = "a\t"
|
||||||
@ -457,3 +434,102 @@ foo = "a "
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn export_success() {
|
||||||
|
integration_test(
|
||||||
|
&[],
|
||||||
|
r#"
|
||||||
|
export foo = "a"
|
||||||
|
baz = "c"
|
||||||
|
export bar = "b"
|
||||||
|
export abc = foo + bar + baz
|
||||||
|
|
||||||
|
wut:
|
||||||
|
echo $foo $bar $abc
|
||||||
|
"#,
|
||||||
|
0,
|
||||||
|
"a b abc\n",
|
||||||
|
"echo $foo $bar $abc\n",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn export_failure() {
|
||||||
|
integration_test(
|
||||||
|
&[],
|
||||||
|
r#"
|
||||||
|
export foo = "a"
|
||||||
|
baz = "c"
|
||||||
|
export bar = "b"
|
||||||
|
export abc = foo + bar + baz
|
||||||
|
|
||||||
|
wut:
|
||||||
|
echo $foo $bar $baz
|
||||||
|
"#,
|
||||||
|
127,
|
||||||
|
"",
|
||||||
|
r#"echo $foo $bar $baz
|
||||||
|
sh: baz: unbound variable
|
||||||
|
Recipe "wut" failed with exit code 127
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn export_shebang() {
|
||||||
|
integration_test(
|
||||||
|
&[],
|
||||||
|
r#"
|
||||||
|
export foo = "a"
|
||||||
|
baz = "c"
|
||||||
|
export bar = "b"
|
||||||
|
export abc = foo + bar + baz
|
||||||
|
|
||||||
|
wut:
|
||||||
|
#!/bin/sh
|
||||||
|
echo $foo $bar $abc
|
||||||
|
"#,
|
||||||
|
0,
|
||||||
|
"a b abc\n",
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn export_assignment_backtick() {
|
||||||
|
integration_test(
|
||||||
|
&[],
|
||||||
|
r#"
|
||||||
|
export exported_variable = "A"
|
||||||
|
b = `echo $exported_variable`
|
||||||
|
|
||||||
|
recipe:
|
||||||
|
echo {{b}}
|
||||||
|
"#,
|
||||||
|
127,
|
||||||
|
"",
|
||||||
|
"sh: exported_variable: unbound variable
|
||||||
|
backtick failed with exit code 127
|
||||||
|
|
|
||||||
|
3 | b = `echo $exported_variable`
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn export_recipe_backtick() {
|
||||||
|
integration_test(
|
||||||
|
&[],
|
||||||
|
r#"
|
||||||
|
export exported_variable = "A-IS-A"
|
||||||
|
|
||||||
|
recipe:
|
||||||
|
echo {{`echo recipe $exported_variable`}}
|
||||||
|
"#,
|
||||||
|
0,
|
||||||
|
"recipe A-IS-A\n",
|
||||||
|
"echo recipe A-IS-A\n",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
111
src/lib.rs
111
src/lib.rs
@ -12,6 +12,7 @@ pub use app::app;
|
|||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
|
extern crate itertools;
|
||||||
|
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
|
||||||
@ -148,14 +149,39 @@ fn backtick_error_from_signal(exit_status: process::ExitStatus) -> RunError<'sta
|
|||||||
RunError::BacktickUnknownFailure
|
RunError::BacktickUnknownFailure
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_backtick<'a>(raw: &str, token: &Token<'a>) -> Result<String, RunError<'a>> {
|
fn export_env<'a>(
|
||||||
let output = process::Command::new("sh")
|
command: &mut process::Command,
|
||||||
.arg("-cu")
|
scope: &Map<&'a str, String>,
|
||||||
.arg(raw)
|
exports: &Set<&'a str>,
|
||||||
.stderr(process::Stdio::inherit())
|
) -> Result<(), RunError<'a>> {
|
||||||
.output();
|
for name in exports {
|
||||||
|
if let Some(value) = scope.get(name) {
|
||||||
|
command.env(name, value);
|
||||||
|
} else {
|
||||||
|
return Err(RunError::InternalError {
|
||||||
|
message: format!("scope does not contain exported variable `{}`", name),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
match output {
|
|
||||||
|
fn run_backtick<'a>(
|
||||||
|
raw: &str,
|
||||||
|
token: &Token<'a>,
|
||||||
|
scope: &Map<&'a str, String>,
|
||||||
|
exports: &Set<&'a str>,
|
||||||
|
) -> Result<String, RunError<'a>> {
|
||||||
|
let mut cmd = process::Command::new("sh");
|
||||||
|
|
||||||
|
try!(export_env(&mut cmd, scope, exports));
|
||||||
|
|
||||||
|
cmd.arg("-cu")
|
||||||
|
.arg(raw)
|
||||||
|
.stderr(process::Stdio::inherit());
|
||||||
|
|
||||||
|
match cmd.output() {
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
if let Some(code) = output.status.code() {
|
if let Some(code) = output.status.code() {
|
||||||
if code != 0 {
|
if code != 0 {
|
||||||
@ -189,6 +215,7 @@ impl<'a> Recipe<'a> {
|
|||||||
&self,
|
&self,
|
||||||
arguments: &[&'a str],
|
arguments: &[&'a str],
|
||||||
scope: &Map<&'a str, String>,
|
scope: &Map<&'a str, String>,
|
||||||
|
exports: &Set<&'a str>,
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
) -> Result<(), RunError<'a>> {
|
) -> Result<(), RunError<'a>> {
|
||||||
let argument_map = arguments .iter().enumerate()
|
let argument_map = arguments .iter().enumerate()
|
||||||
@ -197,6 +224,7 @@ impl<'a> Recipe<'a> {
|
|||||||
let mut evaluator = Evaluator {
|
let mut evaluator = Evaluator {
|
||||||
evaluated: Map::new(),
|
evaluated: Map::new(),
|
||||||
scope: scope,
|
scope: scope,
|
||||||
|
exports: exports,
|
||||||
assignments: &Map::new(),
|
assignments: &Map::new(),
|
||||||
overrides: &Map::new(),
|
overrides: &Map::new(),
|
||||||
};
|
};
|
||||||
@ -257,8 +285,11 @@ impl<'a> Recipe<'a> {
|
|||||||
try!(fs::set_permissions(&path, perms).map_err(|error| RunError::TmpdirIoError{recipe: self.name, io_error: error}));
|
try!(fs::set_permissions(&path, perms).map_err(|error| RunError::TmpdirIoError{recipe: self.name, io_error: error}));
|
||||||
|
|
||||||
// run it!
|
// run it!
|
||||||
let status = process::Command::new(path).status();
|
let mut command = process::Command::new(path);
|
||||||
try!(match status {
|
|
||||||
|
try!(export_env(&mut command, scope, exports));
|
||||||
|
|
||||||
|
try!(match command.status() {
|
||||||
Ok(exit_status) => if let Some(code) = exit_status.code() {
|
Ok(exit_status) => if let Some(code) = exit_status.code() {
|
||||||
if code == 0 {
|
if code == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -284,11 +315,14 @@ impl<'a> Recipe<'a> {
|
|||||||
if dry_run {
|
if dry_run {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let status = process::Command::new("sh")
|
|
||||||
.arg("-cu")
|
let mut cmd = process::Command::new("sh");
|
||||||
.arg(command)
|
|
||||||
.status();
|
cmd.arg("-cu").arg(command);
|
||||||
try!(match status {
|
|
||||||
|
try!(export_env(&mut cmd, scope, exports));
|
||||||
|
|
||||||
|
try!(match cmd.status() {
|
||||||
Ok(exit_status) => if let Some(code) = exit_status.code() {
|
Ok(exit_status) => if let Some(code) = exit_status.code() {
|
||||||
if code == 0 {
|
if code == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -513,6 +547,7 @@ fn evaluate_assignments<'a>(
|
|||||||
let mut evaluator = Evaluator {
|
let mut evaluator = Evaluator {
|
||||||
evaluated: Map::new(),
|
evaluated: Map::new(),
|
||||||
scope: &Map::new(),
|
scope: &Map::new(),
|
||||||
|
exports: &Set::new(),
|
||||||
assignments: assignments,
|
assignments: assignments,
|
||||||
overrides: overrides,
|
overrides: overrides,
|
||||||
};
|
};
|
||||||
@ -527,6 +562,7 @@ fn evaluate_assignments<'a>(
|
|||||||
struct Evaluator<'a: 'b, 'b> {
|
struct Evaluator<'a: 'b, 'b> {
|
||||||
evaluated: Map<&'a str, String>,
|
evaluated: Map<&'a str, String>,
|
||||||
scope: &'b Map<&'a str, String>,
|
scope: &'b Map<&'a str, String>,
|
||||||
|
exports: &'b Set<&'a str>,
|
||||||
assignments: &'b Map<&'a str, Expression<'a>>,
|
assignments: &'b Map<&'a str, Expression<'a>>,
|
||||||
overrides: &'b Map<&'b str, &'b str>,
|
overrides: &'b Map<&'b str, &'b str>,
|
||||||
}
|
}
|
||||||
@ -593,7 +629,9 @@ impl<'a, 'b> Evaluator<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::String{ref cooked, ..} => cooked.clone(),
|
Expression::String{ref cooked, ..} => cooked.clone(),
|
||||||
Expression::Backtick{raw, ref token} => try!(run_backtick(raw, token)),
|
Expression::Backtick{raw, ref token} => {
|
||||||
|
try!(run_backtick(raw, token, &self.scope, &self.exports))
|
||||||
|
}
|
||||||
Expression::Concatination{ref lhs, ref rhs} => {
|
Expression::Concatination{ref lhs, ref rhs} => {
|
||||||
try!(self.evaluate_expression(lhs, arguments))
|
try!(self.evaluate_expression(lhs, arguments))
|
||||||
+
|
+
|
||||||
@ -790,6 +828,7 @@ impl<'a> Display for Error<'a> {
|
|||||||
struct Justfile<'a> {
|
struct Justfile<'a> {
|
||||||
recipes: Map<&'a str, Recipe<'a>>,
|
recipes: Map<&'a str, Recipe<'a>>,
|
||||||
assignments: Map<&'a str, Expression<'a>>,
|
assignments: Map<&'a str, Expression<'a>>,
|
||||||
|
exports: Set<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
||||||
@ -890,7 +929,7 @@ impl<'a, 'b> Justfile<'a> where 'a: 'b {
|
|||||||
try!(self.run_recipe(&self.recipes[dependency_name], &[], scope, ran, dry_run));
|
try!(self.run_recipe(&self.recipes[dependency_name], &[], scope, ran, dry_run));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try!(recipe.run(arguments, &scope, dry_run));
|
try!(recipe.run(arguments, &scope, &self.exports, dry_run));
|
||||||
ran.insert(recipe.name);
|
ran.insert(recipe.name);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -904,6 +943,9 @@ impl<'a> Display for Justfile<'a> {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
let mut items = self.recipes.len() + self.assignments.len();
|
let mut items = self.recipes.len() + self.assignments.len();
|
||||||
for (name, expression) in &self.assignments {
|
for (name, expression) in &self.assignments {
|
||||||
|
if self.exports.contains(name) {
|
||||||
|
try!(write!(f, "export "));
|
||||||
|
}
|
||||||
try!(write!(f, "{} = {}", name, expression));
|
try!(write!(f, "{} = {}", name, expression));
|
||||||
items -= 1;
|
items -= 1;
|
||||||
if items != 0 {
|
if items != 0 {
|
||||||
@ -1343,9 +1385,9 @@ fn tokenize(text: &str) -> Result<Vec<Token>, Error> {
|
|||||||
fn parse(text: &str) -> Result<Justfile, Error> {
|
fn parse(text: &str) -> Result<Justfile, Error> {
|
||||||
let tokens = try!(tokenize(text));
|
let tokens = try!(tokenize(text));
|
||||||
let filtered: Vec<_> = tokens.into_iter().filter(|token| token.kind != Comment).collect();
|
let filtered: Vec<_> = tokens.into_iter().filter(|token| token.kind != Comment).collect();
|
||||||
let parser = Parser{
|
let parser = Parser {
|
||||||
text: text,
|
text: text,
|
||||||
tokens: filtered.into_iter().peekable()
|
tokens: itertools::put_back(filtered),
|
||||||
};
|
};
|
||||||
let justfile = try!(parser.file());
|
let justfile = try!(parser.file());
|
||||||
Ok(justfile)
|
Ok(justfile)
|
||||||
@ -1353,12 +1395,15 @@ fn parse(text: &str) -> Result<Justfile, Error> {
|
|||||||
|
|
||||||
struct Parser<'a> {
|
struct Parser<'a> {
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
tokens: std::iter::Peekable<std::vec::IntoIter<Token<'a>>>
|
tokens: itertools::PutBack<std::vec::IntoIter<Token<'a>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
fn peek(&mut self, kind: TokenKind) -> bool {
|
fn peek(&mut self, kind: TokenKind) -> bool {
|
||||||
self.tokens.peek().unwrap().kind == kind
|
let next = self.tokens.next().unwrap();
|
||||||
|
let result = next.kind == kind;
|
||||||
|
self.tokens.put_back(next);
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accept(&mut self, kind: TokenKind) -> Option<Token<'a>> {
|
fn accept(&mut self, kind: TokenKind) -> Option<Token<'a>> {
|
||||||
@ -1560,7 +1605,28 @@ impl<'a> Parser<'a> {
|
|||||||
Some(token) => match token.kind {
|
Some(token) => match token.kind {
|
||||||
Eof => break,
|
Eof => break,
|
||||||
Eol => continue,
|
Eol => continue,
|
||||||
Name => if self.accepted(Equals) {
|
Name => if token.lexeme == "export" {
|
||||||
|
let next = self.tokens.next().unwrap();
|
||||||
|
if next.kind == Name && self.accepted(Equals) {
|
||||||
|
if assignments.contains_key(next.lexeme) {
|
||||||
|
return Err(token.error(ErrorKind::DuplicateVariable {
|
||||||
|
variable: next.lexeme,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
exports.insert(next.lexeme);
|
||||||
|
assignments.insert(next.lexeme, try!(self.expression(false)));
|
||||||
|
assignment_tokens.insert(next.lexeme, next);
|
||||||
|
} else {
|
||||||
|
self.tokens.put_back(next);
|
||||||
|
if let Some(recipe) = recipes.get(token.lexeme) {
|
||||||
|
return Err(token.error(ErrorKind::DuplicateRecipe {
|
||||||
|
recipe: recipe.name,
|
||||||
|
first: recipe.line_number
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
recipes.insert(token.lexeme, try!(self.recipe(token.lexeme, token.line)));
|
||||||
|
}
|
||||||
|
} else if self.accepted(Equals) {
|
||||||
if assignments.contains_key(token.lexeme) {
|
if assignments.contains_key(token.lexeme) {
|
||||||
return Err(token.error(ErrorKind::DuplicateVariable {
|
return Err(token.error(ErrorKind::DuplicateVariable {
|
||||||
variable: token.lexeme,
|
variable: token.lexeme,
|
||||||
@ -1569,7 +1635,7 @@ impl<'a> Parser<'a> {
|
|||||||
assignments.insert(token.lexeme, try!(self.expression(false)));
|
assignments.insert(token.lexeme, try!(self.expression(false)));
|
||||||
assignment_tokens.insert(token.lexeme, token);
|
assignment_tokens.insert(token.lexeme, token);
|
||||||
} else {
|
} else {
|
||||||
if let Some(recipe) = recipes.remove(token.lexeme) {
|
if let Some(recipe) = recipes.get(token.lexeme) {
|
||||||
return Err(token.error(ErrorKind::DuplicateRecipe {
|
return Err(token.error(ErrorKind::DuplicateRecipe {
|
||||||
recipe: recipe.name,
|
recipe: recipe.name,
|
||||||
first: recipe.line_number
|
first: recipe.line_number
|
||||||
@ -1627,6 +1693,7 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(Justfile {
|
Ok(Justfile {
|
||||||
recipes: recipes,
|
recipes: recipes,
|
||||||
assignments: assignments,
|
assignments: assignments,
|
||||||
|
exports: exports,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,6 +246,15 @@ fn parse_empty() {
|
|||||||
", "");
|
", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_export() {
|
||||||
|
parse_summary(r#"
|
||||||
|
export a = "hello"
|
||||||
|
|
||||||
|
"#, r#"export a = "hello""#);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_complex() {
|
fn parse_complex() {
|
||||||
parse_summary("
|
parse_summary("
|
||||||
|
Loading…
Reference in New Issue
Block a user