Runscript
Runscript is a tool to manage project-specific commands. In essence, it's a run.sh
file for each of your projects, but
much fancier and much more pretty.
Installation
Currently, Runscript does not support any package managers.
Pre-built binaries
Executable binaries are available for download on the GitHub Releases page.
Download the binary for your platform (Windows, macOS, or Linux) and extract the archive. The archive contains the run
executable.
To make it easier to run, put the path to the binary into your PATH
.
Build from source using rust
Alternatively, you can use the cargo
package manager to build the master branch of Runscript. To do this, you will need
to install Rust and Cargo. Follow the instructions on the Rust installation page.
Once you have installed Rust, the following command can be used to build and install Runscript:
cargo install --git https://github.com/TheOnlyMrCat/runscript runscript
Basic Usage
Once you have the run
binary installed, you can execute runscripts with it.
A basic runscript
The syntax of a runscript is inspired by the TOML configuration language. Runscript looks for a file called
run
or .run
in the current directory, and any parent directory. Here is an example for executing a simple C program:
# Saved as `run` in the current directory
[prog:build]
make
[prog:run]
::default-phase build run
./prog
There are two things of note here. First are the script headers: [prog:build]
and [prog:run]
. These define targets in
your runscript. Similar to make
, the first target in a file is chosen as the default target, but unlike in make
, targets
can have multiple phases. In this case (and in most cases), there is a build
phase for building the target and a run
phase for running the target.
If the phase is omitted from a script header, e.g. [prog]
, the phase is assumed to be run
. You cannot define the same
[target:phase]
twice in the same runscript.
We'll get to the ::default-phase
in a bit.
This runscript can be executed by simply executing run
in the same directory as the runscript.
$ run
Build prog
> make
make: `prog' is up to date
Run prog
> ./prog
Hello, world!
You can also choose to only build or only run the target with flags: run -b
or run -r
.
Now, back to the ::default-phase build run
. This line tells Runscript to execute both the build
and run
phases
sequentially, by default. If this line were not included, the command run
would only execute the run
phase.
Shell scripting
Runscripts support most basic shell features, such as parameter substitution, flow control, and background jobs.
Here's an example runscript which takes positional parameters:
[target]
cargo run -q -- $@
Arguments can be passed to this script by separating them from the main command with a --
:
$ run -- Arguments
Run target
> cargo run -q -- Arguments
Found 1 positional parameter
Multiple targets
A runscript can, of course, define multiple targets. You could, for example, have a default target for testing your program, and a separate target for packaging it for release.
In addition to this, any identifier can be used as a phase name, not just build
, run
, and test
. Here's an example
runscript showcasing all of these features:
[program:run]
cargo run -- $@
[program:bench]
cargo bench
[pkg]
cargo build --release
tar czf program.tar.gz -C target/release program
In this runscript, the three targets can be executed in the following ways:
$ run -- Argument # Executes [program:run] with arguments {"Argument"}
$ run program:bench # Executes [program:bench]
$ run pkg # Executes [pkg]
External script invocation
If Runscript's builtin shell is inadequate, you can tell the script to use a different shell, such as bash
or
zsh
. This is done by putting the path to the shell on the end of the header line:
[external] /bin/bash
echo Foo
$ run
Run external
Foo
This can be done on a runscript-wide basis with the ::default-shell
option:
::default-shell /bin/bash
[external]
echo Foo
Comparison with casey/just
run
and just
were written to accomplish, in essence, the same task of managing project-specific commands. Here is a
breakdown of the main differences between the two command runners:
Disclaimers:
- I have not actually used
just
. This feature breakdown is based solely on the features listed in its documentation. - This comparison is currently incomplete. Expect more headings to be added in the future.
Command execution
just
always delegates script execution to an external shell. By default, each line of a recipe is executed by a individual
sh
instances, but a shell an be supplied for an entire recipe by placing a shebang #!/bin/sh
line at the start of the
recipe, or per-Justfile with a setting set shell := /bin/sh
.
run
, by default, tries to execute scripts itself, and provide output while doing so. It can be instructed to delegate
to an external shell in the script header [script] /bin/sh
or a file-wide option ::default-shell /bin/sh
.
For both tools, this allows recipes/scripts to be written in any language, not just shell language.
Variables, positional parameters, and substitution
just
allows variables to be defined at the top of a Justfile, which are evaluated before the recipe is executed and can be
substituted into a recipe. Recipes can also define positional parameters build TARGET:
that can be substituted in the same
way. Variable substitutions are done with double curly braces cc -o {{TARGET}} {{TARGET}}.c
, and positional parameters are
passed on the command-line in the same way as a recipe target just build my-target
.
run
does not have the same concept of variables or script-specified positional parameters. These designs are handled by
the shell language instead. Positional parameters are passed on the command-line separated from the main command by a --
:
run target -- argument
, and can be read by the script using $1
, $2
, etc. parameter substitutions.