Working
This commit is contained in:
parent
a0d5b83a80
commit
114f6b7bdc
38
Cargo.lock
generated
38
Cargo.lock
generated
@ -1,4 +1,42 @@
|
|||||||
[root]
|
[root]
|
||||||
name = "j"
|
name = "j"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"brev 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "brev"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempdir"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
@ -4,3 +4,5 @@ version = "0.1.0"
|
|||||||
authors = ["Casey Rodarmor <casey@rodarmor.com>"]
|
authors = ["Casey Rodarmor <casey@rodarmor.com>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
|
brev = "0.1.6"
|
||||||
|
31
README.md
Normal file
31
README.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
j
|
||||||
|
=
|
||||||
|
|
||||||
|
`j` is a handy way to run project-specific commands.
|
||||||
|
|
||||||
|
`j` looks upward from the current directory for a file called `justfile` and then runs make with that file as the makefile. `j` also sets the current working directory to where it found the justfile, so your commands are executed from the root of your project and not from whatever subdirectory you happen to be in.
|
||||||
|
|
||||||
|
With no arguments it runs the default recipe:
|
||||||
|
|
||||||
|
`j`
|
||||||
|
|
||||||
|
Adding one argument specifies the recipe:
|
||||||
|
|
||||||
|
`j compile`
|
||||||
|
|
||||||
|
Arguments after `--` are exported as `ARG0, ARG1, ..., ARGN`, which can be used in the justfile. To run recipe `compile` and export `ARG0=bar` and `ARG1=baz`:
|
||||||
|
|
||||||
|
`just compile -- bar baz`
|
||||||
|
|
||||||
|
By way of example, the included justfile has a pinch of fanciful fluff.
|
||||||
|
|
||||||
|
getting j
|
||||||
|
---------
|
||||||
|
|
||||||
|
J is distributed via `cargo`, rust's package manager.
|
||||||
|
|
||||||
|
1. Get cargo at [rustup.rs](https://www.rustup.rs)
|
||||||
|
2. Run `cargo install j`
|
||||||
|
3. Add `~/.cargo/bin` to your PATH
|
||||||
|
|
||||||
|
`j` depends on make to actually run commands, but hopefully if you're on a unix, make is already installed. If not, you can get it from friendly local package manager.
|
35
justfile
35
justfile
@ -1,2 +1,33 @@
|
|||||||
default:
|
# just a makefile with no special magic
|
||||||
cargo run hello -- -- bar a b c
|
|
||||||
|
test:
|
||||||
|
cargo build
|
||||||
|
./target/debug/j args -- a b c
|
||||||
|
|
||||||
|
# list all recipies
|
||||||
|
list:
|
||||||
|
@$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$' | xargs
|
||||||
|
|
||||||
|
args:
|
||||||
|
@echo "I got some arguments: ARG0=${ARG0} ARG1=${ARG1} ARG2=${ARG2}"
|
||||||
|
|
||||||
|
# make a quine and compile it
|
||||||
|
quine: create compile
|
||||||
|
|
||||||
|
# create our quine
|
||||||
|
create:
|
||||||
|
mkdir -p tmp
|
||||||
|
echo 'int printf(const char*, ...); int main() { char *s = "int printf(const char*, ...); int main() { char *s = %c%s%c; printf(s, 34, s, 34); return 0; }"; printf(s, 34, s, 34); return 0; }' > tmp/gen0.c
|
||||||
|
|
||||||
|
# make sure it's really a quine
|
||||||
|
compile:
|
||||||
|
cc tmp/gen0.c -o tmp/gen0
|
||||||
|
./tmp/gen0 > tmp/gen1.c
|
||||||
|
cc tmp/gen1.c -o tmp/gen1
|
||||||
|
./tmp/gen1 > tmp/gen2.c
|
||||||
|
diff tmp/gen1.c tmp/gen2.c
|
||||||
|
@echo 'It was a quine!'
|
||||||
|
|
||||||
|
# clean up
|
||||||
|
clean:
|
||||||
|
rm -r tmp
|
||||||
|
141
src/main.rs
141
src/main.rs
@ -1,84 +1,107 @@
|
|||||||
use std::io::prelude::*;
|
#[macro_use]
|
||||||
|
extern crate brev;
|
||||||
|
|
||||||
fn can(command: &str) -> bool {
|
#[derive(PartialEq, Clone, Copy)]
|
||||||
if let Ok(paths) = std::env::var("PATH") {
|
enum Make {
|
||||||
for path in paths.split(":") {
|
GNU, // GNU Make installed as `gmake`
|
||||||
let candidate = format!("{}/{}", path, command);
|
GNUStealthy, // GNU Make installed as `make`
|
||||||
if isfile(&candidate) {
|
Other, // Another make installed as `make`
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
impl Make {
|
||||||
|
fn command(self) -> &'static str {
|
||||||
|
if self == Make::GNU {
|
||||||
|
"gmake"
|
||||||
|
} else {
|
||||||
|
"make"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn isfile(path: &str) -> bool {
|
fn gnu(self) -> bool {
|
||||||
if let Ok(metadata) = std::fs::metadata(path) {
|
self != Make::Other
|
||||||
metadata.is_file()
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cwd() -> String {
|
fn status(command: &mut std::process::Command) -> std::io::Result<std::process::ExitStatus> {
|
||||||
match std::env::current_dir() {
|
command
|
||||||
Ok(pathbuf) => pathbuf.to_str().unwrap_or_else(|| panic!("cwd: cwd was not a valid utf8 string")).to_string(),
|
.stdin(std::process::Stdio::null())
|
||||||
Err(err) => panic!("cwd: {}", err),
|
.stdout(std::process::Stdio::null())
|
||||||
}
|
.stderr(std::process::Stdio::null())
|
||||||
|
.status()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cd(path: &str) {
|
fn which_make() -> Option<Make> {
|
||||||
if let Err(err) = std::env::set_current_dir(path) {
|
// check `gmake`
|
||||||
panic!("cd: {}", err)
|
let result = status(std::process::Command::new("gmake").arg("-v"));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn say(s: &str) {
|
if let Ok(exit_status) = result {
|
||||||
println!("{}", s)
|
if exit_status.success() {
|
||||||
}
|
return Some(Make::GNU);
|
||||||
|
}
|
||||||
fn warn(s: &str) {
|
|
||||||
if let Err(err) = std::io::stderr().write(s.as_bytes()) {
|
|
||||||
panic!("warn: could not write to stderr: {}", err);
|
|
||||||
}
|
}
|
||||||
if let Err(err) = std::io::stderr().write("\n".as_bytes()) {
|
|
||||||
panic!("warn: could not write to stderr: {}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn die(s: &str) {
|
// check `make`. pass gmake specific flags to see if it's actually gmake
|
||||||
warn(s);
|
let result = status(std::process::Command::new("make").arg("-v").arg("--always-make"));
|
||||||
std::process::exit(-1);
|
|
||||||
|
if let Ok(exit_status) = result {
|
||||||
|
return if exit_status.success() {
|
||||||
|
Some(Make::GNUStealthy)
|
||||||
|
} else {
|
||||||
|
Some(Make::Other)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let can_make = can("make");
|
let make = match which_make() {
|
||||||
let can_gmake = can("gmake");
|
None => die!("Could not execute `make` or `gmake`."),
|
||||||
if !(can_make || can_gmake) {
|
Some(make) => make,
|
||||||
die("cannot find \"make\" or \"gmake\" in $PATH");
|
};
|
||||||
}
|
|
||||||
|
|
||||||
println!("can make: {}", can_make);
|
|
||||||
println!("can gmake: {}", can_gmake);
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if isfile("justfile") {
|
match std::fs::metadata("justfile") {
|
||||||
break;
|
Ok(metadata) => if metadata.is_file() { break; },
|
||||||
|
Err(error) => die!("Error fetching justfile metadata: {}", error),
|
||||||
}
|
}
|
||||||
if cwd() == "/" {
|
|
||||||
die("No justfile found.")
|
|
||||||
}
|
|
||||||
cd("..");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
match std::env::current_dir() {
|
||||||
|
Ok(pathbuf) => if pathbuf.as_os_str() == "/" { die!("No justfile found"); },
|
||||||
|
Err(error) => die!("Error getting current dir: {}", error),
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(error) = std::env::set_current_dir("..") {
|
||||||
|
die!("Error changing directory: {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let recipes: Vec<String> = std::env::args().skip(1).take_while(|arg| arg != "--").collect();
|
let recipes: Vec<String> = std::env::args().skip(1).take_while(|arg| arg != "--").collect();
|
||||||
let arguments: Vec<String> = std::env::args().skip(1 + recipes.len() + 1).collect();
|
let arguments: Vec<String> = std::env::args().skip(1 + recipes.len() + 1).collect();
|
||||||
|
|
||||||
print!("{:?}", recipes);
|
for (i, argument) in arguments.into_iter().enumerate() {
|
||||||
print!("{:?}", arguments);
|
std::env::set_var(format!("ARG{}", i), argument);
|
||||||
|
}
|
||||||
|
|
||||||
// export as $ARG0 -> ARGX
|
let mut command = std::process::Command::new(make.command());
|
||||||
// start at 0 or 1?
|
|
||||||
// exec $MAKE MAKEFLAGS='' --always-make --no-print-directory -f justfile ${RECIPES[*]}
|
command.arg("MAKEFLAGS=");
|
||||||
|
|
||||||
|
if make.gnu() {
|
||||||
|
command.arg("--always-make").arg("--no-print-directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
command.arg("-f").arg("justfile");
|
||||||
|
|
||||||
|
for recipe in recipes {
|
||||||
|
command.arg(recipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
match command.status() {
|
||||||
|
Err(error) => die!("Failed to execute `{:?}`: {}", command, error),
|
||||||
|
Ok(exit_status) => match exit_status.code() {
|
||||||
|
Some(code) => std::process::exit(code),
|
||||||
|
None => std::process::exit(-1),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user