MAKE:如何使用文件作为目标,如果该文件已存在,则跳过目标



我的Makefile看起来像这样(删除了一些不相关的目标,例如"调试"):

release: comprel
a2s: release
libtelopa.so:
cd telop/neta/lib && make && cp libtelopa.so ../../../
comprel: libtelopa.so
go build -ldflags="-s -w" .
upx --best --lzma a2ssvr
clean:
cd telop/neta/lib && make clean
git clean -fdX
distclean: clean
rm -fr a2s_*.deb
deb: a2s libtelopa.so
rm -fr debian.deb
cp libtelopa.so debian/opt/a2s/bin
cp libjson/* debian/opt/a2s/bin
cp a2ssvr debian/opt/a2s/bin/a2s
dpkg-deb --build --root-owner-group debian
rm -fr a2s*.deb
dpkg-name debian.deb

如果我从 git 存储库签出一个新副本,它工作正常。 但是,在我使项目生成一个可执行a2ssvr后,在项目根文件夹中生成,在这种情况下,如果我再次运行make deb,我希望不要调用release目标。但是,每次我做make deb时都会调用它。 奇怪的是,如果文件已经存在,则不会调用libtelopa.so目标。

问题是什么,如果已经构建了可执行文件,如何避免构建可执行文件?

编辑

根据答案和评论,我想解释一下为什么 Make 规则是这样写的,让我先给出一个修改后的 Makefile:

BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
HASH=$(shell git log -n1 --pretty=format:%h)
REVS=$(shell git log --oneline|wc -l)
.PHONY: release comprel setver
debug: compdbg
release: comprel
a2ssvr: release
libtelopa.so:
cd telop/neta/lib && make && cp libtelopa.so ../../../
comprel: libtelopa.so
go build -ldflags="-s -w" . && mv a2ssvr a2s
upx --best --lzma a2ssvr
compdbg: libtelopa.so
go build -race -gcflags=all=-d=checkptr=0 . && mv a2ssvr a2s
deb: a2ssvr libtelopa.so
rm -fr debian.deb
cp libtelopa.so debian/opt/a2s/bin
cp libjson/* debian/opt/a2s/bin
cp a2ssvr debian/opt/a2s/bin/a2s
dpkg-deb --build --root-owner-group debian
rm -fr a2s*.deb
dpkg-name debian.deb

基本原理和工作流程是:

  1. 默认目标调试在日常开发期间使用。
  2. 定义comprelcompdbg,而不是直接在调试发布下编写代码的原因是,它们共享一个通用的规则集,该规则集将GIT 修订信息直接写入源代码。
  3. comprel/compdbg 依赖于libtelopa.so,因为程序使用 CGO。
  4. 我希望deb目标不要检查其依赖项的新鲜度,鉴于文件a2slibtelopa.so存在,它们不应该重建。

根据答案/评论,我添加了.PHONY目标,但它不起作用。 事实上,在提出这个问题之前,我已经尝试过了。

我更改了a2s目标,如下所示:

a2s: libtelopa.so
cp verinfo.tpl version.go
sed -i 's/{_BRANCH}/$(BRANCH)/' version.go
sed -i 's/{_G_HASH}/$(HASH)/' version.go
sed -i 's/{_G_REVS}/$(REVS)/' version.go
go build -ldflags="-s -w" . && mv a2ssvr a2s
upx --best --lzma a2s

这将"部分"工作,因为"a2s"目标将产生两次,然后我将规则更改为"仅订购":

a2s: |libtelopa.so

按预期工作。但是,我不想在不同的目标中重写所有构建脚本!

我的意图很明确:如果"a2s"文件不存在,make应该使用命名规则来构建它,如果确实存在,则应直接使用该文件。 为了实现这一点,我现在将release目标设置为"假的",我认为这应该告诉make这个目标不是一个真实的文件。

目前还不太清楚你在追求什么,但如果我理解正确,那么你只想构建release目标 iffa2ssvr已经过时了? (其中释放导致 comprel 运行,这反过来又会创建一个名为 a2ssvr 的工件......

如果是这种情况,您所要做的就是将触发a2ssvr的虚假目标折叠为名为a2ssvr的目标......(请注意,最好让目标名称与其配方生成的工件匹配)。 所以现在你会有:

a2ssvr: libtelopa.so
go build -ldflags="-s -w" .
upx --best --lzma a2ssvr
libtelopa.so: telop/neta/lib/libtelopa.so
cp $< $@
clean:
cd telop/neta/lib && make clean
git clean -fdX
distclean: clean
rm -fr a2s_*.deb
deb: a2ssvr
rm -fr debian.deb
cp libtelopa.so debian/opt/a2s/bin
cp libjson/* debian/opt/a2s/bin
cp a2ssvr debian/opt/a2s/bin/a2s
dpkg-deb --build --root-owner-group debian
rm -fr a2s*.deb
dpkg-name debian.deb
.PHONY: deb clean distclean
# for order only, you would need:
# deb: libtelopa.so | a2ssvr

现在,您的措辞暗示您不想重建a2ssrv如果它存在(而不是当它过时)。 如果是这种情况,您可以将a2ssvr依赖项切换为仅顺序。 然而,这意味着如果它存在,它就不会重建,但libtelopa.so已经被修改了(我猜这不是你想要的......

如果该文件已经存在,如何使用文件作为目标并跳过目标?

如果要考虑是否为给定目标运行配方的唯一标准是目标名称是否指定了现有文件,则对应于排除(普通)先决条件。 对于 GNUmake,您还应该注意不要声明目标.PHONY

a2ssvr: 
go build -ldflags="-s -w" .
upx --best --lzma $@

GNUmake提供了非标准的"仅限订单"的先决条件作为扩展。 如果您愿意专门依赖此make实现(广泛使用但不是通用的),则可以使用这些来实现配方相对于其他目标配方的运行顺序。 由于您的原始makefile表明a2ssvr构建取决于libtelopa.so,如果您不将后者至少作为仅订单的先决条件,那么您就是在自找麻烦:

a2ssvr: | libtelopa.so
go build -ldflags="-s -w" .
upx --best --lzma $@

但这设置了一种情况,即如果a2ssvr存在但就libtelopa.so而言已经过时,则不会重建。 听起来您假设 makefile 将仅用于干净的构建,因此不会出现这种情况,但这是短视的。 尤其是当它更干净、更清晰、更便携地简单地指定一个普通依赖项时:

a2ssvr: libtelopa.so
go build -ldflags="-s -w" .
upx --best --lzma $@

那么,让我们考虑一下你对观察到的行为的描述:

如果我从 git 存储库签出一个新副本,它工作正常。

是的。 这大概是一个干净的构建。

但是,在我使项目成为在项目根文件夹中生成的可执行a2ssvr后,

是的。

在这种情况下,如果我再次运行make deb,我希望不会调用release目标。

现在就等一下。 这是一个与问题标题中表达的不同问题。

但是,每次我做make deb时都会调用它。

是的。构建release目标是因为它是a2ssvr的先决条件,这是deb的先决条件,这是您请求的目标,并且因为不存在名为release的文件。

奇怪的是,如果文件已经存在,则不会调用libtelopa.so目标。

考虑到制作文件,这并不奇怪。libtelopa.so不会重新生成,因为它已经存在,并且相对于其任何先决条件而言,它不会过时(因为它没有任何先决条件)。 这个目标是使用递归make构建的,这意味着顶级make没有完整的依赖树可以依赖。 而且递归设置得很差,结果是如果libtelopa.so存在,那么即使它应该存在,它也不会被重建。 对于干净的构建来说,这不是问题,但同样,假设干净的构建是短视的。


一些一般原则:

  • 生成的每个文件都应目标与文件名匹配的规则直接生成。

  • 规则应表达其目标的(所有)真实依赖关系。

  • 规则不应具体说明其目标实际上并不依赖的先决条件。 构建文件的规则通常不应依赖于虚假目标,无论这些目标是否明确.PHONY

  • 总体上尽量减少使用虚假目标,尤其是那些没有既定传统含义的目标。

最新更新