我作为开发团队的一员,负责一个包含许多移动部件的软件项目。我们使用 Makefile 轻松运行开发过程中经常需要的命令和脚本。示例可能是:
- 设置 Python 虚拟环境。
- 初始化本地开发数据库。
- 运行一次检查。
- ETC。
大多数这些命令不需要使用附加参数进行自定义,因此 Makefile 目标可以完美工作:
.PHONY: venv
venv:
python3 -m venv venv
venv/bin/pip install -r requirements.txt
$ make venv
但是,如果需要时不时地将额外的参数传递给命令,它就会变得非常麻烦。两种直接的方法是:
- 通过变量传递附加参数,例如
make foo FOO_ARGS="lalala"
。如果参数包含空格,则会变得麻烦且混乱。 - 在 Makefile 中找到目标,将其复制到 shell 提示符并编辑它。
解决方案的想法
我提出了一个关于我实际需要的非常简单的概念:一个命令行工具,例如称为run
,它查找runfile
在当前目录及其父目录中调用的可执行文件并执行该文件,将任何命令行参数传递给该文件。用 Python 实现它可能看起来像这样:
#! /usr/bin/env python3
import sys, os, subprocess, pathlib
cwd = pathlib.Path().resolve()
# Go through the CWD (current working directory) and all its parents and look
# for an executable file called "runfile".
for dir in [cwd, *cwd.parents]:
run_script_path = dir / 'runfile'
if os.access(run_script_path, os.X_OK):
break
else:
print(
f'No executable runfile found in {cwd} or any of its parents.',
file=sys.stderr)
sys.exit(1)
# Run the runfile, forwarding any command line arguments to it. Use the parent
# of the runfile as CWD and pass the original CWD in the environment variable
# "RUN_PWD".
subprocess.run(
[run_script_path, *sys.argv[1:]],
cwd=dir,
env=dict(os.environ, RUN_PWD=str(cwd)))
运行文件可以使用任何脚本语言。我可以是一个 Bash 脚本:
#! /usr/bin/env bash
case "$1" in
venv)
rm -rf venv
echo python3 -m venv "${@:2}" venv
;;
lint)
pycodestyle --exclude='./venv,./node_modules,./.git' "${@:2}" .
;;
*)
echo "Unknown command: $1" >&2
exit 1
;;
esac
("${@:2}"
将脚本的参数作为单独的单词插入到命令行中的第一个参数之后)。
$ run venv --copies
# Would run "python3 -m venv --copies venv".
问题
是否已经有这样的工具可能具有更多功能?如果该工具使用自己的语言来定义命令而不是简单地执行文件就可以了。
补充笔记
我知道某些构建也支持此作为次要功能,例如npm
允许定义scripts
可以从命令行调用:
npm run foo -- args...
但这已经需要在脚本名称之前有两个单词,并且--
在附加参数之前需要一个单词,这并不那么方便。
安装中控制台脚本入口点进入 virtualenv 并激活 virtualenv 有时可能是一个解决方案。但在本例中,用例之一是设置项目的 virtualenv,所以这是一个先有鸡还是先有蛋的问题。
我知道我可能可以破解$(MAKECMDGOALS)
。不。