另一个品牌?
许多人对Make不满意,否则就不会有数十个其他构建系统和Make的方言。一个重做这一个又一个替代?在一方面,当然是-只有极其简单,但能够解决的绝对所有相同的任务,制作。另一方面,我们是否有一些通用且统一的Make?
大多数“替代”构建系统之所以诞生,是因为它们缺乏本机的Make功能和灵活性。许多系统只关心生成Makefile,而不是自己生成。许多工具是针对某些编程语言的生态系统量身定制的。
下面我将尝试显示重做 是一个更加值得注意的系统,而不仅仅是另一个解决方案。
无论如何,Make总是在那里
就我个人而言,我仍然总是对整个替代方案感到困惑,因为它要么更复杂,要么特定于生态系统/语言,要么是需要设置并学习如何使用它的附加依赖项。而且Make是这样一个东西,每个人都在加或减之间熟悉并知道如何在基本级别上使用。因此,无论何时何地,我都尝试使用POSIX Make,假设这是任何情况下(POSIX)系统中每个人都可以使用的东西,例如C编译器。Make中的任务只能按预期执行:并行执行目标(命令),并考虑到它们之间的依赖性。
仅用Make编写并确保其在任何系统上都能工作有什么问题?毕竟,您可以(必须!)在POSIX shell中进行编写,而不必强迫用户安装一些巨大的GNU Bash。唯一的问题是仅POSIX Make方言可以使用,即使对于许多小型简单项目而言,这也足够稀缺。在现代BSD系统上进行制作更加复杂且功能齐全。好的,使用GNU Make,几乎没有人可以与任何人进行比较,尽管几乎没有人充分利用其功能并且不知道如何使用它们。但是GNU Make不支持现代BSD系统的方言。 BSD系统中没有GNU Make(它们是可以理解的!)。
使用BSD / GNU方言意味着潜在地迫使用户安装并非开箱即用的其他软件。在这种情况下,Make的可能优势-它在系统中的存在将被抵消。
可以在POSIX Make中使用和编写,但是很困难。我个人立即想到了两个非常烦人的案例:
- 有些Make实现在执行$(MAKE)-C时会“转到”执行新Make的目录,有些则没有。是否可以编写一个Makefile,使其在任何地方都相同?当然:
tgt: (cd subdir ; $(MAKE) -C ...)
方便吗 当然不。令人不愉快的是,人们必须不断记住这种琐事。 - 在POSIX Make中,没有执行外壳调用并将其结果存储在变量中的语句。在GNU Make up to 4.x版本中,您可以执行以下操作:
VAR = $(shell cat VERSION)
从4.x开始,以及在BSD方言中,您可以执行以下操作:
VAR != cat VERSION
可以做的动作不完全相同:
VAR = `cat VERSION`
但实际上是用目标表达式中描述的shell命令替换了该表达式。这种方法用于无精打采的项目中,但它当然是拐杖。
就个人而言,我经常在这样的地方一次为三个方言(GNU,BSD和POSIX)编写Makefile:
$ cat BSDmakefile
GOPATH != pwd
VERSION != cat VERSION
include common.mk
$ cat GNUmakefile
GOPATH = $(shell pwd)
VERSION = $(shell cat VERSION)
include common.mk
方便吗 离得很远!尽管任务非常简单和常见。因此事实证明:
- 并行编写多个Make方言。交易开发时间以方便用户。
- 记住很多细微差别和琐事,也许是用低效的替换(`cmd ...`),尝试用POSIX Make编写。对我个人而言,凭借多年使用GNU / BSD Make的经验,此选项最耗时(用多种方言书写更容易)。
- 输入Make方言之一,强制用户安装第三方软件。
产生技术问题
但是一切都变得更糟,因为任何Make都没有说(很好)应付分配给它的任务。
- mtime , Make mtime, . , , Make . mtime ! mtime , , ! mtime — , . FUSE mtime . mmap mtime… -, msync ( POSIX ). NFS? , Make : ( ), , FUSE/NFS/mmap/VCS.
- . ? Make . :
tgt-zstd: zstd -d < tgt-zstd.zst > tgt tgt-fetch: fetch -o tgt-fetch SOME://URL
, , Make , , , , Make, .
:
tgt-zstd: zstd -d < tgt-zstd.zst > tgt-zstd.tmp fsync tgt-zstd.tmp mv tgt-zstd.tmp tgt-zstd
tmp/fsync/mv ? , Make-, tgt.tmp. - . ( ) Makefile, Make ? . - $(CFLAGS)? .
Makefile! . Makefile , , . , , - , .
Makefile :
$ cat Makefile include tgt1.mk include tgt2.mk ...
. ? !
- , . Recursive Make Considered Harmful , Makefile-, Makefile- - , , Make , . Makefile — . ? , Makefile.
? , . FreeBSD , , , , .
- . , #include «tgt.h», .c tgt.h, .c - sed .
tgt.o: tgt.c `sed s/.../ tgt.c`
. .mk Makefile include. ? Make, : .mk , , Makefile- include-.
- Makefile- shell, , - , \\$, , .sh , Make. Make /, shell shell, . ?
老实说,让我们坦白地说:由于某些东西不完整或未按预期进行重建,您需要多少次清理或重建而不进行并行化?当然,在一般情况下,这并不是由于理想情况下正确,正确和完整地编写的Makefile而引起的,Makefile证明了其有效而有效的编写的复杂性。该工具应该有所帮助。
重做要求
在继续介绍redo之前,我将首先向您介绍实现的含义以及“用户”(描述目标和目标之间的依赖关系的开发人员)必须学习的内容。
- redo, , - . redo . POSIX shell . Python . : , , .
- redo : POSIX shell, GNU bash, Python, Haskell, Go, C++, Inferno Shell. .
- C , SHA256, 27KB. POSIX shell 100 . , POSIX shell redo tarball- .
- Make-, ( ).
redo
目标构建规则是target_name.do中的常规POSIX Shell脚本。让我最后一次提醒您,它可以是任何其他语言(如果添加了shebang)或只是可执行的二进制文件,但是默认情况下,它是POSIX shell。该脚本使用set -e和三个参数运行:
- $1 —
$2 — ( )
$3 —
redo . stdout $3 . ? - , - stdout. redo:
$ cat tgt-zstd.do zstd -d < $1.zst $ cat tgt-fetch.do fetch -o $3 SOME://URL
, fetch stdout. stdout , $3. , fsync . ! , fsync — .
, (make) clean, , . redo , . , all .
default
. POSIX Make .c:
.c: $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
redo default.do , default.---.do. Make :
$ cat default.c.do $CC $CLFAGS $LDFLAGS -o $3 $1
— $2 , $1 «» redo . default- :
a.b.c.do -> $2=a.b.c default.do -> $2=a.b.c default.c.do -> $2=a.b default.b.c.do -> $2=a
, , . cd dir; redo tgt redo dir/tgt. .do . , .
-.do , default.do . , .do ../a/b/xtarget.y :
./../a/b/xtarget.y.do ./../a/b/default.y.do ./../a/b/default.do ./../a/default.y.do ./../a/default.do ./../default.y.do ./../default.do
2/3 redo .
redo-ifchange :
$ cat hello-world.do redo-ifchange hello-world.o ../config . ../config $CC $CFLAGS -o $3 hello-world.o $ cat hello-world.o.do redo-ifchange hw.c hw.h ../config . ../config $CC $CFLAGS -c -o $3 hw.c $ cat ../config CC=cc CFLAGS=-g $ cat ../all.do # , , <em>redo</em>, # hw/hello-world redo-ifchange hw/hello-world # $ cat ../clean.do redo hw/clean $ cat clean.do rm -f *.o hello-world
redo : state. . redo-ifchange , - , - , , , , . .do . , config hello-world .
state? . - TSV-like -.do.state, - , .redo , - SQLite3 .redo .
stderr - , - state, « - ».
state? redo : , FUSE/mmap/NFS/VCS, . ctime, inode number, — , .
state lock- Make — . ( ) state lock- . .
, redo-ifchange - , . — . redo-ifchange , :
redo-ifchange $2.c gcc -o $3 -c $2.c -MMD -MF $2.deps read deps < $2.deps redo-ifchange ${deps#*:}
, include-:
$ cat default.o.do deps=`sed -n 's/^#include "\(.*\)"$/\1/p' < $2.c` redo-ifchange ../config $deps [...]
*.c?
for f in *.c ; do echo ${f%.c}.o ; done | xargs redo-ifchange
.do (....do.do ) . .do $CC $CFLAGS..., « »:
$ cat tgt.do redo-ifchange $1.c cc ./cc $3 $1.c $ cat cc.do redo-ifchange ../config . ../config cat > $3 <<EOF #!/bin/sh -e $CC $CFLAGS $LDFLAGS -o \$1 \$@ $LDLIBS EOF chmod +x $3
compile_flags.txt Clang LSP ?
$ cat compile_flags.txt.do redo-ifchange ../config . ../config echo "$PCSC_CFLAGS $TASN1_CFLAGS $CRYPTO_CFLAGS $WHATEVER_FLAGS $CFLAGS" | tr " " "\n" | sed "/^$/d" | sort | uniq
$PCSC_CFLAGS, $TASN1_CFLAGS? , pkg-config, autotools!
$ cat config.do cat <<EOF [...] PKG_CONFIG="${PKG_CONFIG:-pkgconf}" PCSC_CFLAGS="${PCSC_CFLAGS:-`$PKG_CONFIG --cflags libpcsclite`}" PCSC_LDFLAGS="${PCSC_LDFLAGS:-`$PKG_CONFIG --libs-only-L libpcsclite`}" PCSC_LDLIBS="${PCSC_LDLIBS:-`$PKG_CONFIG --libs-only-l libpcsclite`}" TASN1_CFLAGS="${TASN1_CFLAGS:-`$PKG_CONFIG --cflags libtasn1`}" TASN1_LDFLAGS="${TASN1_LDFLAGS:-`$PKG_CONFIG --libs-only-L libtasn1`}" TASN1_LDLIBS="${TASN1_LDLIBS:-`$PKG_CONFIG --libs-only-l libtasn1`}" [...] EOF
- .do , Makefile:
foo: bar baz hello world .c: $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
:
$ cat default.do case $1 in foo) redo-ifchange bar baz hello world ;; *.c) $CC $CFLAGS $LDFLAGS -o $3 $1 ;; esac
, default.do . .o ? special.o.do, fallback default.o.do default.do .
redo , , « , !?» ( default ). , , , , . suckless ( , CMake, GCC, pure-C redo — ).
- - .
- (*BSD vs GNU) — POSIX shell , (Python, C, shell) redo .
- / Makefile-.
- .
- ( ) , , .
- — , , l **.do.
/?
- Make , .
- 我花了一个多月的时间才没有学会做反射做重做清洁,因为在Make之后,这已经成为一种习惯,有些东西不会(重新)聚集起来。
我推荐apenwarr / redo实施文档,其中包含大量示例和说明。
Sergey Matveev,cypherpunk,Python / Go / C开发人员,FGUP STC Atlas首席专家。