= `just` :toc: macro :toc-title: image:https://img.shields.io/crates/v/just.svg[crates.io version,link=https://crates.io/crates/just] image:https://travis-ci.org/casey/just.svg?branch=master[build status,link=https://travis-ci.org/casey/just] image:https://badges.gitter.im/just-because/Lobby.svg[chat on gitter,link=https://gitter.im/just-because/Lobby] image:https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg[say thanks,link=https://saythanks.io/to/casey] `just` is a handy way to save and run project-specific commands. (非官方中文文档,link:https://github.com/chinanf-boy/just-zh[这里],快看过来!) Commands are stored in a file called `justfile` with syntax inspired by `make`: ```make build: cc *.c -o main # test everything test-all: build ./test --all # run a specific test test TEST: build ./test --test {{TEST}} ``` You can then run them with `just COMMAND`: ```sh $ just test-all cc *.c -o main ./test --all Yay, all your tests passed! ``` `just` produces detailed error messages and avoids `make`’s idiosyncrasies, so debugging a justfile is easier and less surprising than debugging a makefile. If you need help with `just` please feel free to open an issue or let me know on link:https://gitter.im/just-because/Lobby[gitter]. Feature requests and bug reports are always welcome! [discrete] == Manual toc::[] == Installation `just` should run on any system with a reasonable `sh`, including Linux, MacOS, and the BSDs. On Windows, `just` works with the `sh` provided by https://git-scm.com[Git for Windows], https://desktop.github.com[GitHub Desktop], and http://www.cygwin.com[Cygwin]. If you'd rather not install `sh`, you can use the `shell` setting to use the shell of your choice. Like `cmd.exe`: ```make # use cmd.exe instead of sh: set shell := ["cmd.exe", "/c"] list: dir ``` …or Powershell: ```make # use Powershell instead of sh: set shell := ["powershell.exe", "-c"] hello: Write-Host "Hello, world!" ``` === Pre-built Binaries Pre-built binaries for Linux, MacOS, and Windows can be found on https://github.com/casey/just/releases[the releases page]. You can use the following command to download the latest binary for MacOS or Windows, just replace `DESTINATION_DIRECTORY` with the directory where you'd like to put `just`: ```sh curl -LSfs https://japaric.github.io/trust/install.sh | \ sh -s -- --git casey/just --to DESTINATION_DIRECTORY ``` On Linux, use: ```sh curl -LSfs https://japaric.github.io/trust/install.sh | \ sh -s -- --git casey/just --target x86_64-unknown-linux-musl --to DESTINATION_DIRECTORY ``` === Homebrew On MacOS, `just` can be installed using the https://brew.sh[Homebrew package manager]. Install Homebrew using the instructions https://brew.sh[here], then run: `brew install just` === Scoop On Windows, `just` can be installed using the https://scoop.sh[Scoop package manager]. Install Scoop using the instructions https://scoop.sh/[here], then run: ```powershell scoop install just ``` === Arch Linux On Arch Linux, `just` is packaged as https://aur.archlinux.org/packages/just/[just] in AUR, the https://aur.archlinux.org[Arch User Repository]. Several tools are available to install packages from AUR, including https://github.com/Jguer/yay[yay] and https://github.com/archlinuxfr/yaourt[yaourt]. === Void Linux On Void Linux, `just` can be installed with: ```sh sudo xbps-install -S just ``` === Nix On NixOS, Linux, and MacOS, `just` can be installed using the https://nixos.org/nix/[Nix package manager]. Install https://nixos.org/nix/download.html[Nix] or https://nixos.org/nixos/download.html[NixOS], then run: ```sh nix-env -i just ``` === Cargo On Windows, Linux, and macOS, `just` can be installed using Cargo, the https://www.rust-lang.org[rust language package manager]. Install Cargo using the instructions https://www.rustup.rs[here], then run: `cargo install just` (You might also need to add `~/.cargo/bin` to your shell's `$PATH`. If you can't run just after installing it, put `export PATH="$HOME/.cargo/bin:$PATH"` in your shell's configuration file.) == Quick Start See xref:Installation[] for how to install `just` on your computer. Try running `just --version` to make sure that it's installed correctly. Once `just` is installed and working, create a file named `justfile` in the root of your project with the following contents: ```make recipe-name: echo 'This is a recipe!' # this is a comment another-recipe: @echo 'This is another recipe.' ``` When you invoke `just` it looks for file `justfile` in the current directory and upwards, so you can invoke it from any subdirectory of your project. The search for a `justfile` is case insensitive, so any case, like `Justfile`, `JUSTFILE`, or `JuStFiLe`, will work. Running `just` with no arguments runs the first recipe in the `justfile`: ```sh $ just echo 'This is a recipe!' This is a recipe! ``` One or more arguments specify the recipe(s) to run: ```sh $ just another-recipe This is another recipe. ``` `just` prints each command to standard error before running it, which is why `echo 'This is a recipe!'` was printed. This is suppressed for lines starting with `@`, which is why `echo 'Another recipe.'` was not printed. Recipes stop running if a command fails. Here `cargo publish` will only run if `cargo test` succeeds: ```make publish: cargo test # tests passed, time to publish! cargo publish ``` Recipes can depend on other recipes. Here the `test` recipe depends on the `build` recipe, so `build` will run before `test`: ```make build: cc main.c foo.c bar.c -o main test: build ./test sloc: @echo "`wc -l *.c` lines of code" ``` ```sh $ just test cc main.c foo.c bar.c -o main ./test testing... all tests passed! ``` Recipes without dependencies will run in the order they're given on the command line: ```sh $ just build sloc cc main.c foo.c bar.c -o main 1337 lines of code ``` Dependencies will always run first, even if they are passed after a recipe that depends on them: ```sh $ just test build cc main.c foo.c bar.c -o main ./test testing... all tests passed! ``` == Features === Listing Available Recipes Recipes can be listed with `just --list` : ```sh $ just --list Available recipes: build test deploy lint ``` `just --summary` is more concise: ```sh $ just --summary build test deploy lint ``` === Aliases Aliases allow recipes to be invoked with alternative names: ```make alias b := build build: echo 'Building!' ``` ```sh $ just b build echo 'Building!' Building! ``` === Settings Settings control interpetation and execution. Each setting may be specified at most once, anywhere in the justfile. For example: ```make set shell := ["zsh", "-cu"] foo: # this line will be run as `zsh -cu 'ls **/*.txt'` ls **/*.txt ``` ==== Table of Settings [options="header"] |================= | Name | Value | Description |`shell` | `[COMMAND, ARGS...]` | Set the command used to invoke recipes and evaluate backticks. |================= ==== Shell The `shell` setting controls the command used to invoke recipe lines and backticks. Shebang recipes are unaffected. ```make # use python3 to execute recipe lines and backticks set shell := ["python3", "-c"] # use print to capture result of evaluation foos := `print("foo" * 4)` foo: print("Snake snake snake snake.") print("{{foos}}") ``` === Documentation Comments Comments immediately preceding a recipe will appear in `just --list`: ```make # build stuff build: ./bin/build # test stuff test: ./bin/test ``` ```sh $ just --list Available recipes: build # build stuff test # test stuff ``` === Variables and Substitution Variables, strings, concatenation, and substitution using `{{...}}` are supported: ```make version := "0.2.7" tardir := "awesomesauce-" + version tarball := tardir + ".tar.gz" publish: rm -f {{tarball}} mkdir {{tardir}} cp README.md *.c {{tardir}} tar zcvf {{tarball}} {{tardir}} scp {{tarball}} me@server.com:release/ rm -rf {{tarball}} {{tardir}} ``` ==== Escaping `{{` To write a recipe containing `{{`, use `{{ "{{" }}`: ```make braces: echo 'I {{ "{{" }}LOVE}} curly braces!' ``` (An unmatched `}}` is ignored, so it doesn't need to be escaped.) Another option is to put all the text you'd like to escape inside of an interpolation: ```make braces: echo '{{'I {{LOVE}} curly braces!'}}' ``` === Strings Double-quoted strings support escape sequences: ```make string-with-tab := "\t" string-with-newline := "\n" string-with-carriage-return := "\r" string-with-double-quote := "\"" string-with-slash := "\\" ``` ```sh $ just --evaluate "tring-with-carriage-return := " string-with-double-quote := """ string-with-newline := " " string-with-slash := "\" string-with-tab := " " ``` Single-quoted strings do not recognize escape sequences and may contain line breaks: ```make escapes := '\t\n\r\"\\' line-breaks := 'hello this is a raw string! ' ``` ```sh $ just --evaluate escapes := "\t\n\r\"\\" line-breaks := "hello this is a raw string! " ``` === Functions Just provides a few built-in functions that might be useful when writing recipes. ==== System Information - `arch()` – Instruction set architecture. Possible values are: `"aarch64"`, `"arm"`, `"asmjs"`, `"hexagon"`, `"mips"`, `"msp430"`, `"powerpc"`, `"powerpc64"`, `"s390x"`, `"sparc"`, `"wasm32"`, `"x86"`, `"x86_64"`, and `"xcore"`. - `os()` – Operating system. Possible values are: `"android"`, `"bitrig"`, `"dragonfly"`, `"emscripten"`, `"freebsd"`, `"haiku"`, `"ios"`, `"linux"`, `"macos"`, `"netbsd"`, `"openbsd"`, `"solaris"`, and `"windows"`. - `os_family()` – Operating system family; possible values are: `"unix"` and `"windows"`. For example: ```make system-info: @echo "This is an {{arch()}} machine". ``` ``` $ just system-info This is an x86_64 machine ``` ==== Environment Variables - `env_var(key)` – Retrieves the environment variable with name `key`, aborting if it is not present. - `env_var_or_default(key, default)` – Retrieves the environment variable with name `key`, returning `default` if it is not present. ==== Invocation Directory - `invocation_directory()` - Retrieves the path of the current working directory, before `just` changed it (chdir'd) prior to executing commands. For example, to call `rustfmt` on files just under the "current directory" (from the user/invoker's perspective), use the following rule: ``` rustfmt: find {{invocation_directory()}} -name \*.rs -exec rustfmt {} \; ``` Alternatively, if your command needs to be run from the current directory, you could use (e.g.): ``` build: cd {{invocation_directory()}}; ./some_script_that_needs_to_be_run_from_here ``` ==== Dotenv Integration `just` will load environment variables from a file named `.env`. This file can be located in the same directory as your justfile or in a parent directory. These variables are environment variables, not `just` variables, and so must be accessed using `$VARIABLE_NAME` in recipes and backticks. For example, if your `.env` file contains: ``` # a comment, will be ignored DATABASE_ADDRESS=localhost:6379 SERVER_PORT=1337 ``` And your justfile contains: ```make serve: @echo "Starting server with database $DATABASE_ADDRESS on port $SERVER_PORT..." ./server --database $DATABASE_ADDRESS --port $SERVER_PORT ``` `just serve` will output: ```sh $ just serve Starting server with database localhost:6379 on port 1337... ./server --database $DATABASE_ADDRESS --port $SERVER_PORT ``` === Command Evaluation Using Backticks Backticks can be used to store the result of commands: ```make localhost := `dumpinterfaces | cut -d: -f2 | sed 's/\/.*//' | sed 's/ //g'` serve: ./serve {{localhost}} 8080 ``` === Setting Variables from the Command Line Variables can be overridden from the command line. ```make os := "linux" test: build ./test --test {{os}} build: ./build {{os}} ``` ```sh $ just ./build linux ./test --test linux ``` Any number of arguments of the form `NAME=VALUE` can be passed before recipes: ```sh $ just os=plan9 ./build plan9 ./test --test plan9 ``` Or you can use the `--set` flag: ```sh $ just --set os bsd ./build bsd ./test --test bsd ``` === Environment Variables Assignments prefixed with the `export` keyword will be exported to recipes as environment variables: ```make export RUST_BACKTRACE := "1" test: # will print a stack trace if it crashes cargo test ``` === Recipe Parameters Recipes may have parameters. Here recipe `build` has a parameter called `target`: ```make build target: @echo 'Building {{target}}...' cd {{target}} && make ``` To pass arguments on the command line, put them after the recipe name: ```sh $ just build my-awesome-project Building my-awesome-project... cd my-awesome-project && make ``` To pass arguments to a dependency, put the dependency in parentheses along with the arguments: ```make default: (build "main") build target: @echo 'Building {{target}}...' cd {{target}} && make ``` Parameters may have default values: ```make default := 'all' test target tests=default: @echo 'Testing {{target}}:{{tests}}...' ./test --tests {{tests}} {{target}} ``` Parameters with default values may be omitted: ```sh $ just test server Testing server:all... ./test --tests all server ``` Or supplied: ```sh $ just test server unit Testing server:unit... ./test --tests unit server ``` Default values may be arbitrary expressions, but concatenations must be parenthesized: ```make arch := "wasm" test triple=(arch + "-unknown-unknown"): ./test {{triple}} ``` The last parameter of a recipe may be variadic, indicated with a `+` before the argument name: ```make backup +FILES: scp {{FILES}} me@server.com: ``` Variadic parameters accept one or more arguments and expand to a string containing those arguments separated by spaces: ```sh $ just backup FAQ.md GRAMMAR.md scp FAQ.md GRAMMAR.md me@server.com: FAQ.md 100% 1831 1.8KB/s 00:00 GRAMMAR.md 100% 1666 1.6KB/s 00:00 ``` A variadic parameter with a default argument will accept zero or more arguments: ```make commit MESSAGE +FLAGS='': git commit {{FLAGS}} -m "{{MESSAGE}}" ``` `{{...}}` substitutions may need to be quoted if they contains spaces. For example, if you have the following recipe: ```make search QUERY: lynx https://www.google.com/?q={{QUERY}} ``` And you type: ```sh $ just search "cat toupee" ``` Just will run the command `lynx https://www.google.com/?q=cat toupee`, which will get parsed by `sh` as `lynx`, `https://www.google.com/?q=cat`, and `toupee`, and not the intended `lynx` and `https://www.google.com/?q=cat toupee`. You can fix this by adding quotes: ```make search QUERY: lynx 'https://www.google.com/?q={{QUERY}}' ``` === Writing Recipes in Other Languages Recipes that start with a `#!` are executed as scripts, so you can write recipes in other languages: ```make polyglot: python js perl sh ruby python: #!/usr/bin/env python3 print('Hello from python!') js: #!/usr/bin/env node console.log('Greetings from JavaScript!') perl: #!/usr/bin/env perl print "Larry Wall says Hi!\n"; sh: #!/usr/bin/env sh hello='Yo' echo "$hello from a shell script!" ruby: #!/usr/bin/env ruby puts "Hello from ruby!" ``` ```sh $ just polyglot Hello from python! Greetings from JavaScript! Larry Wall says Hi! Yo from a shell script! Hello from ruby! ``` === Multi-line Constructs Recipes without an initial shebang are evaluated and run line-by-line, which means that multi-line constructs probably won't do what you want. For example, with the following justfile: ``` conditional: if true; then echo 'True!' fi ``` The extra leading whitespace before the second line of the `conditional` recipe will produce a parse error: ``` $ just conditional error: Recipe line has extra leading whitespace | 3 | echo 'True!' | ^^^^^^^^^^^^^^^^ ``` To work around this, you can write conditionals on one line, escape newlines with slashes, or add a shebang to your recipe. Some examples of multi-line constructs are provided for reference. ==== `if` statements ```make conditional: if true; then echo 'True!'; fi ``` ```make conditional: if true; then \ echo 'True!'; \ fi ``` ```make conditional: #!/usr/bin/env sh if true; then echo 'True!' fi ``` ==== `for` loops ```make for: for file in `ls .`; do echo $file; done ``` ```make for: for file in `ls .`; do \ echo $file; \ done ``` ```make for: #!/usr/bin/env sh for file in `ls .`; do echo $file done ``` ==== `while` loops ```make while: while `server-is-dead`; do ping -c 1 server; done ``` ```make while: while `server-is-dead`; do \ ping -c 1 server; \ done ``` ```make while: #!/usr/bin/env sh while `server-is-dead`; do do ping -c 1 server done ``` === Command Line Options `just` supports a number of useful command line options for listing, dumping, and debugging recipes and variable: ```sh $ just --list Available recipes: js perl polyglot python ruby $ just --show perl perl: #!/usr/bin/env perl print "Larry Wall says Hi!\n"; $ just --show polyglot polyglot: python js perl sh ruby ``` Run `just --help` to see all the options. === Private Recipes Recipes and aliases whose name starts with a `_` are omitted from `just --list`: ```make test: _test-helper ./bin/test _test-helper: ./bin/super-secret-test-helper-stuff ``` ```sh $ just --list Available recipes: test ``` And from `just --summary`: ```sh $ just --summary test ``` This is useful for helper recipes which are only meant to be used as dependencies of other recipes. === Quiet Recipes A recipe name may be prefixed with '@' to invert the meaning of '@' before each line: ```make @quiet: echo hello echo goodbye @# all done! ``` Now only the lines starting with '@' will be echoed: ```sh $ j quiet hello goodbye # all done! ``` === Invoking Justfiles in Other Directories 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 in the 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 a 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/ ``` === Just Scripts By adding a shebang line to the top of a justfile and making it executable, `just` can be used as an interpreter for scripts: ```sh $ cat > script <