「 just
是保存和运行项目的特定命令的简便方法. 」
翻译的原文 | 与日期 | 最新更新 | 更多 |
---|---|---|---|
commit | ⏰ 2019-10-07 | 中文翻译 |
欢迎 👏 勘误/校对/更新贡献 😊 具体贡献请看
If help, buy me coffee —— 营养跟不上了,给我来瓶营养快线吧! 💰
just
是保存和运行项目的特定命令的简便方法。
命令存储在一个名为justfile
,语法启发自make
:
build:
cc *.c -o main
# test everything
test-all: build
./test --all
# run a specific test
test TEST: build
./test --test {{TEST}}
然后可以使用just <COMMAND>
运行它们:
$ just test-all
cc *.c -o main
./test --all
Yay, all your tests passed!
有些困惑,没事,下面就帮你理清一切
just
产生详细的错误消息,并避免make
不良癖好,因此调试一个 justfile 比调试一个 markfile 文件更容易,也更少惊吓。
如果你需要帮助just
,请随时打开一个问题或让我知道gitter。 始终欢迎功能请求和错误报告!
just
应该能在,具有合理sh
的任何系统上运行,包括 Linux、MACOS 和 BSD。
在 Windows 上,just
能与由Git for Windows,GitHub Desktop和Cygwin提供的sh
一起工作。
Linux、MacOS 和 Windows 的预构建二进制文件,在releases 页面。
您可以使用以下命令,下载 Mac OS 或 Windows 的最新二进制文件,只需将DESTINATION_DIRECTORY
替换成你想放置just
的目录:
curl -LSfs https://japaric.github.io/trust/install.sh | \
sh -s -- --git casey/just --to DESTINATION_DIRECTORY
在 Linux 上,使用:
curl -LSfs https://japaric.github.io/trust/install.sh | \
sh -s -- --git casey/just --target x86_64-unknown-linux-musl --to DESTINATION_DIRECTORY
关于 MacOS,just
可以使用Homebrew 包管理器安装。 若还没有 Homebrew 可使用这里的命令安装,然后运行:
brew install just
在 Windows 上,just
可以使用Scoop 包管理器安装。 若还没有 Scoop 可使用这里的指令安装,然后运行:
scoop install just
在 Arch Linux, just
作为 AUR包
just, Arch User
Repository。几个可以从 AUR 下载包的工具, 包括 yay
和 yaourt.
在 Void Linux, just
can be installed with:
sudo xbps-install -S just
On NixOS, Linux, and MacOS, just
的安装,可使用Nix
package manager. Install
Nix or
NixOS, then run:
nix-env -i just
在 Windows、Linux 和 MACOS 上,just
可使用 Cargo 安装, 它是rust 语言 包管理器. 可使用这里的说明书安装 Cargo,然后运行:
cargo install just
(您可能还需要添加~/.cargo/bin
到你的$PATH
。 如果安装后不能运行,请在 shell 配置文件中运行export PATH="$HOME/.cargo/bin:$PATH"
)
在安装章节中安装了just
后。试运行just --version
确保安装正确.
一旦just
成功安装和工作, 在项目的根目录中,创建一个名为justfile
的文件,具有以下内容:
recipe-name:
echo 'This is a recipe!'
# 这是一个注释
another-recipe:
@echo 'This is another recipe.'
当你调用just
,它会在当前目录和向上层目录,寻找一个justfile
,因此您可以在项目的任何子目录调用它(它就是那么方便)。
若在没有参数的情况下运行just
,那当你选择运行justfile
的第一个配方(recipe
/或者叫‘食谱’也行,反正听上去都好吃):
$ just
echo 'This is a recipe!'
This is a recipe!
一个或多个参数,就会指定要运行的配方(们):
$ just another-recipe
This is another recipe.
just
会在运行真正命令之前,将每个命令打印到标准错误(stderr),这就是为什么echo 'This is a recipe!'
会被打印。当然你可以使用@
作为行开头,这样会抑制打印。echo 'Another recipe.'
也就没有打印。
如果命令失败,配方将停止运行。下面的cargo publish
只在cargo test
成功后运行:
publish:
cargo test
# tests passed, time to publish!
cargo publish
配方可以依赖其他配方。这里test
配方依赖build
配方,所以build
会在test
之前运行:
build:
cc main.c foo.c bar.c -o main
test: build
./test
sloc:
@echo "`wc -l *.c` lines of code"
$ just test
cc main.c foo.c bar.c -o main
./test
testing... all tests passed!
若是没有依赖关系的配方,将按照命令行上的顺序运行:
$ just build sloc
cc main.c foo.c bar.c -o main
1337 lines of code
依赖关系,总是先运行,即使它们(build
)在依赖于它们的配方(test
)之后传递:
$ just test build
cc main.c foo.c bar.c -o main
./test
testing... all tests passed!
just --list
可以列出配方:
$ just --list
Available recipes:
build
test
deploy
lint
just --summary
则更简洁:
$ just --summary
build test deploy lint
Aliases 让配方拥有替代名称:
alias b := build
build:
echo 'Building!'
$ just b
build
echo 'Building!'
Building!
just --list
会让配方之前的相近注释,出现:
# build stuff
build:
./bin/build
# test stuff
test:
./bin/test
$ just --list
Available recipes:
build # build stuff
test # test stuff
变量、字符串、concatenation 和 substitution ,都通过使用{{…}}
支持:
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}}
写一份包含{{
的配方,要使用{{ "{{" }}
:
braces:
echo 'I {{ "{{" }}LOVE}} curly braces!'
(上面 LOVE 旁边的,未闭合的}}
会忽略,因此不需要转义).
另一个选择是把所有你想转义的文本,插入到插槽({{中}}
)中:
braces:
echo '{{'I {{LOVE}} curly braces!'}}'
双引号字符串支持转义序列:
string-with-tab := "\t"
string-with-newline := "\n"
string-with-carriage-return := "\r"
string-with-double-quote := "\""
string-with-slash := "\\"
$ just --evaluate
"tring-with-carriage-return := "
string-with-double-quote := """
string-with-newline := "
"
string-with-slash := "\"
string-with-tab := " "
单引号字符串不识别转义序列,可能包含空格行:
escapes := '\t\n\r\"\\'
line-breaks := 'hello
this
is
a
raw
string!
'
$ just --evaluate
escapes := "\t\n\r\"\\"
line-breaks := "hello
this
is
a
raw
string!
"
只提供一些内置函数,在编写配方时可能有用.
-
arch()
- 系统指令体系结构,可能的值是:"aarch64"
,"arm"
,"asmjs"
,"hexagon"
,"mips"
,"msp430"
,"powerpc"
,"powerpc64"
,"s390x"
,"sparc"
,"wasm32"
,"x86"
,"x86_64"
和"xcore"
. -
os()
- 操作系统,可能的值是:"android"
,"bitrig"
,"dragonfly"
,"emscripten"
,"freebsd"
,"haiku"
,"ios"
,"linux"
,"macos"
,"netbsd"
,"openbsd"
,"solaris"
和"windows"
. -
os_family()
操作系统家族; 可能的价值是:"unix"
和"windows"
.
例如:
system-info:
@echo "This is an {{arch()}} machine".
$ just system-info
This is an x86_64 machine
-
env_var(key)
- 用名称key
检索环境变量,如果不存在会中止. -
env_var_or_default(key, default)
- 用名称key
检索环境变量,如果它不存在则返回default
值.
invocation_directory()
- 检索当前工作目录的路径, 在just
执行命令更改它(用 chdir)之前。
例如,调用rustfmt
只作用于"当前目录"下的文件(从用户/调用方的角度),使用以下规则:
rustfmt:
find {{invocation_directory()}} -name \*.rs -exec rustfmt {} \;
或者,如果您的命令需要从当前目录运行,您可以使用(例如):
build:
cd {{invocation_directory()}}; ./some_script_that_needs_to_be_run_from_here
just
将从名为.env
文件中,加载环境变量。 此文件可以与您的 justfile 位于同一个目录中, 或是父目录。这些变量都是环境变量, 因不是just
变量,所以在配方和反引号中必须要使用$VARIABLE_NAME
获取。
例如,如果你.env
文件包含:
# 注释,会被忽略
DATABASE_ADDRESS=localhost:6379
SERVER_PORT=1337
你的 justfile 包含:
serve:
@echo "Starting server with database $DATABASE_ADDRESS on port $SERVER_PORT..."
./server --database $DATABASE_ADDRESS --port $SERVER_PORT
just serve
会输出:
$ just serve
Starting server with database localhost:6379 on port 1337...
./server --database $DATABASE_ADDRESS --port $SERVER_PORT
反引号可用来存储命令的结果:
localhost := `dumpinterfaces | cut -d: -f2 | sed 's/\/.*//' | sed 's/ //g'`
serve:
./serve {{localhost}} 8080
变量可以通过命令行重写.
os := "linux"
test: build
./test --test {{os}}
build:
./build {{os}}
$ just
./build linux
./test --test linux
在配方之前的任意数量参数,可用过NAME=VALUE
设置:
$ just os=plan9
./build plan9
./test --test plan9
或者你可以使用 --set
:
$ just --set os bsd
./build bsd
./test --test bsd
export
关键字会将作为环境变量,导入到配方中:
export RUST_BACKTRACE := "1"
test:
# will print a stack trace if it crashes
cargo test
配方可能有参数。这里的build
配方有一个参数叫做target
:
build target:
@echo 'Building {{target}}...'
cd {{target}} && make
其他配方可能不依赖带有参数的配方.
为了传递参数,把它们放在配方名称后面:
$ just build my-awesome-project
Building my-awesome-project...
cd my-awesome-project && make
参数也可具有默认值:
default := 'all'
test target tests=default:
@echo 'Testing {{target}}:{{tests}}...'
./test --tests {{tests}} {{target}}
可以省略,具有默认值的参数:
$ just test server
Testing server:all...
./test --tests all server
或提供:
$ just test server unit
Testing server:unit...
./test --tests unit server
默认值可以是任意表达式,但 concatenations 必须被圆括号包裹:
arch := "wasm"
test triple=(arch + "-unknown-unknown"):
./test {{triple}}
配方的最后一个参数可以是变量。这在参数名称之前,用+
表示:
backup +FILES:
scp {{FILES}} me@server.com:
变量参数接受一个或多个参数,并由包含空格分隔的参数字符串,扩展:
$ 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
具有默认参数的变量参数,可接受零个或多个参数:
commit MESSAGE +FLAGS='':
git commit {{FLAGS}} -m "{{MESSAGE}}"
{{…}}
替换可能需要引号,如果它们包含空格。例如,如果你有以下配方:
search QUERY:
lynx https://www.google.com/?q={{QUERY}}
你的类型是:
$ just search "cat toupee"
Just
会运行命令lynx [https://www.google.com/?q=cat](https://www.google.com/?q=cat) toupee
,将被sh
解析为lynx
,[https://www.google.com/?q=cat](https://www.google.com/?q=cat)
和toupee
,而不是你想的lynx
和[https://www.google.com/?q=cat](https://www.google.com/?q=cat) toupee
。
你可以通过添加引号来修正这个问题:
search QUERY:
lynx 'https://www.google.com/?q={{QUERY}}'
以一个#!
开始的配方会作为脚本执行,所以你可以用其他语言编写:
polyglot: python js perl sh ruby
python:
#!/usr/bin/env python3
print('Hello from python!')
js:
#!/usr/bin/env node
console.log('Greetings from justfile!')
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!"
$ just polyglot
Hello from python!
Greetings from justfile!
Larry Wall says Hi!
Yo from a shell script!
Hello from ruby!
配方没有执行一个初始 shebang,并且是一行一行运行的。这意味着,多行结构语句的运行,可能不是你想的那样。
例如, 下面的 justfile:
conditional:
if true; then
echo 'True!'
fi
conditional
语句下的缩进空格,
会给出解析错误:
$ just conditional
error: Recipe line has extra leading whitespace
|
3 | echo 'True!'
| ^^^^^^^^^^^^^^^^
要使这个工作,你要把条件语句放在同一行,用\
转为新行,或是添加一个 shebang 到你的配方中。下面有些参考。
conditional:
if true; then echo 'True!'; fi
conditional:
if true; then \
echo 'True!'; \
fi
conditional:
#!/usr/bin/env sh
if true; then
echo 'True!'
fi
for:
for file in `ls .`; do echo $file; done
for:
for file in `ls .`; do \
echo $file; \
done
for:
#!/usr/bin/env sh
for file in `ls .`; do
echo $file
done
while:
while `server-is-dead`; do ping -c 1 server; done
while:
while `server-is-dead`; do \
ping -c 1 server; \
done
while:
#!/usr/bin/env sh
while `server-is-dead`; do
do ping -c 1 server
done
just
支持许多有用的命令行选项,用于列表,倒腾和调试配方和变量:
$ 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
运行just --help
查看所有选项.
名称以一个_
开头的配方和 aliases,just --list
会省略:
test: _test-helper
./bin/test
_test-helper:
./bin/super-secret-test-helper-stuff
$ just --list
Available recipes:
test
just --summary
也是:
$ just --summary
test
这对于配方帮手来说是有用的,因为它们只是用来作为其他配方的依赖.
配方名称可以用"@"前缀,在每行之前反转"@"的含义:
@quiet:
echo hello
echo goodbye
@# all done!
现在,只有用"@"开头的台词会回响:
$ j quiet
hello
goodbye
# all done!
如果第一个参数传递给just
包含一个/
,那会发生以下情况:
-
参数以最后一个
/
,拆分. -
拆分后的,前半部分被视为目录。
just
会开始在那里搜索 justfile,而不是在当前目录中搜索。 -
后部分被当作一个普通的参数,或者,若它是空的,则被忽略。
这看起来有点奇怪,但是如果您希望运行子目录中的 justfile 中的命令,这是很有用的。
例如,如果您在包含名为foo
子目录,其中包含一个带有build
(也是默认配方)配方的 justfile,以下每条命令都是相等的:
$ (cd foo && just build)
$ just foo/build
$ just foo/
通过添加一个 shebang 到一个 justfile 文件的顶部,就能让它可执行化。just
就作为了脚本执行器:
$ cat > script <<EOF
#!/usr/bin/env just --justfile
foo:
echo foo
EOF
$ chmod +x script
$ ./script foo
echo foo
foo
若一个执行脚本带有一个 shebang, 系统会提供一个脚本的路径,作为命令参数。 所以,#!/usr/bin/env just --justfile
, 这个命令完整是
/usr/bin/env just --justfile PATH_TO_SCRIPT
。
使用上面的 shebang, just
会将工作目录改为脚本所在的目录。 如果不想改变工作目录, 使用 #!/usr/bin/env just --working-directory . --justfile
。
好'译'名
与just
很好搭配的工具,包括:
watchexec
-一个简单的工具,它监视路径,并在检测到修改时,运行命令.
为了闪电般的命令运行,把alias j=just
放在 shell 的配置文件中.
justfile
语法足够接近make
,您可能希望告诉编辑器使用 make 语法高亮显示。
对于 Vim,您可以将以下内容放入~/.vim/filetype.vim
:
if exists("did_load_filetypes")
finish
endif
augroup filetypedetect
au BufNewFile,BufRead Justfile,justfile setf make
augroup END
在一个justfile
中包括以下内容,能在 Vim 和 Emacs 中启用语法高亮显示:
# Local Variables:
# mode: makefile
# End:
# vim: set ft=make :
VS 代码的skellock扩展是可用的哦. (github 库)
您可以通过运行命令安装它:
code --install-extension skellock.just
Kakoune 支持justfile
语法高亮出笼啦,感谢 TeddyDD.
请随时给我发送必要的命令,使语法高亮工作在您的编辑器的选择,以便我可以在这里包括他们.
justfile 的非规范语法可以在GRAMMAR.md.
以前的just
是一个花哨的 Rust 程序,它是一个小小的 shell 脚本,其调用make
. 您可以在extras/just.sh中找到旧版本。
如果你想要一些命令在任何地方都可用,把它们放进去.~/.justfile
,并将下列内容添加到 shell 初始化文件中:
alias .j='just --justfile ~/.justfile --working-directory ~'
或者,如果你宁愿他们运行在当前目录:
alias .j='just --justfile ~/.justfile --working-directory .'
我敢肯定没有人真正使用这个功能,但它在那里.
¯\_(ツ)_/¯
Janus 是收集和分析 justfiles 的工具,并确定新版 just
更新与变化,对现有 justfiles 的解析。
在合并一个格外大的变更之前, 要运行 Janus 确保没有破坏。 不要担心 Janus 的运行有多麻烦, Casey(原作者) 会为你,在需要它时,对变更运行 Janus。
make 有一些混淆或复杂的行为,或者说,使它不适合用作一般的命令运行器.
一个例子是,有时 make 不会在配方中运行命令。例如,如果有一个文件叫test
,例子如下且运行它:
test:
./test
Make 实际上,会拒绝运行它:
$ make test
make: `test' is up to date.
Make 发觉这个test
配方,并假定它产生一个名为test
的文件。 然后, 该文件已存在,因此假定配方不需要运行。
公平地说,当使用 make 作为构建系统时,这种行为是可取的,但是当使用它作为命令运行程序时,则不是那么回事。
其他一些例子,包括必须理解=
和:=
赋值两者之间的区别;弄乱 makefile 的话,产生混乱的错误消息;不得不通过$$
,在配方中使用环境变量;以及 Make 不同风格的配方之间的不相容性。
Cargo build 脚本有一个非常具体的用途,这是控制 Cargo , 正确建立你的 Rust 项目。这可能包括将命令行参数添加到rustc
中调用,建立外部依赖,或运行某种代码生成步骤.
just
,则控制另一方面,是开发过程中部分,需要运行的所有其他杂项命令。比如在不同的配置中运行测试、链接代码、将构建工件推送到服务器、删除临时文件等等.
而且,虽然just
写于 Rust 语言,但它可以被使用在许多地方,而不在意你的项目使用的语言或构建系统.
我个人觉得写了justfile
很有用,它几乎用到每一个项目,无论大小.
在一个有多个贡献者的大型项目中,拥有一个包含所有命令的文件来处理项目是非常有用的.
可能需要测试、构建、lint、部署等不同的命令,将它们全部放在一个地方很有用,并且减少了告诉人们要运行哪些命令,以及如何键入命令的时间.
而且,有了一个易于放置命令的地方,您很可能会想到其他有用的,项目集体智慧的部分东西,但智慧被集中在一起,没有到处扔,像:神秘命令需要修订控制工作流程的某些部分一样;安装所有项目的依赖项,或者您可能需要传递给构建系统的所有随机参数.
配方的一些想法:
-
部署/发布项目
-
发布模式与调试模式的构建
-
运行调试模式或启用日志记录
-
复杂的 git 工作流
-
更新依赖关系
-
运行不同的测试集,例如快速测试和完整测试,或者用详细的输出来运行它们.
-
任何复杂的命令集,如果只是为了能够记得它们,你就应该写在某个地方(当然是 justfile 啦).
即使对于小型的个人项目,能够按名称记住命令也是很好的,而不是^反向搜索
shell 历史记录。也许在神秘(莫名其妙)的构建系统,使用未知语言编写的旧项目, 若是使用just
,那justfile
可是会知道您需要执行,还是想要的所有命令,这对于您来说是巨大的好处。如果你输入just
可能会发生一些有用(或至少有趣!)的事情.
有关配方的建议,请查看这个项目的justfile
或者一些justfile
野花.
无论如何,这是我认为的赞,且详细的 README 文件.
我希望你喜欢用just
,并在您的所有计算体系工作中找到巨大的成功和满足感!
😸