Makefile 依赖关系顺序

问题描述 投票:0回答:2

我刚刚收到一个包含以下内容的 Makefile:

rpm: clean $(JARFILE)
    # commands here...

您可能已经猜到了,

clean
目标会删除该 jar(如果存在)。这个 Makefile 正确吗?是否可以保证
clean
目标将在
JARFILE
目标之前运行?我问的不是风格,而是正确性。是否有可能建造
JARFILE
但随后却被
clean
目标吹走?

我正在使用 gnu make,所以这是我最感兴趣的;我不确定这是否是为了移植到其他风格的品牌。

makefile gnu-make
2个回答
1
投票

如果您在没有启用任何并行性的情况下运行 make(没有

-j
),那么您的 makefile 将正常工作。如果串行运行,Make 确实保证先决条件按照它们在 makefile 中列出的顺序构建。

但是,如果您启用

-j
,那么两个目标可能会并行运行,然后您可能会遇到麻烦。


0
投票

是否可以保证 clean 目标将在 JARFILE 目标之前运行?

make
一般:不,没有没有这样的保证

GNU

make

  • 如果有理由以某种顺序解决先决条件,则将使用该顺序
  • 否则它们将被解析为左右 [1]

对于您的特定规则,使用先决条件

clean $(JARFILE)
,Makefile 的其余部分不太可能描述依赖关系树,从而使 GNU 有理由在
$(JARFILE)
之前解决
clean

理论上这是可能的。例如。考虑一下这个

Makefile

# --- file: Makefile ---

rpm: clean A.jar
    @$(rpm_build)

A.jar: A.java
    cp $^ $@
    @printf "[done: '$@']\n\n"

clean: A.jar
    @# Error msg if attempting to delete non-existing file, so I
    @# I made sure it will exist before rm-ing it by adding A.jar
    @# as a prerequisite to `clean` above. //consultant
    rm A.jar
    @printf "[done: '$@']\n\n"


rpm_build = @[ -e $(JARFILE) ] && echo "rpm: OK." \
                               || echo "rpm: ERR: no '$(JARFILE)'"

尝试制定

rpm
目标:

$ touch A.java
$ gmake

cp A.java A.jar
[done: 'A.jar']

rm A.jar
[done: 'clean']

rpm: ERR: no 'A.jar'

Makefile
的依赖图通过构建
A.jar
,然后做
clean
,然后构建
rpm
来解决。这样,所有先决条件都得到满足 - GNU make 并不是由于
rpm
目标失败(其他人)而导致公司构建系统崩溃的错误。


更好的选择

最好通过依赖关系图来处理对解决先决条件的顺序的任何限制。这更清晰(不是每个人都知道 GNU make 的复杂性),使 Makefile(更)可移植,并且更健壮 [2]。

下面是一个例子。我添加了中间目标

A_clean_jar
,因此我们仍然有可能在不首先触发
A.jar
的情况下构建
clean

# --- file: Makefile ---

rpm: A_clean_jar
    @$(inspect_jar)

A_clean_jar: A.java clean
    $(build_jar)
    @printf '[done: created pristine jar]\n\n'

A.jar: A.java
    $(build_jar)
    @printf '[done: created jar]\n\n'
    
clean:
    @rm -f A.jar
    @printf '[done: jar cleaned out of existence]\n\n'


build_jar = cp A.java A.jar

inspect_jar = @[ -e A.jar ] && printf "rpm: OK.\n\n" \
                            || printf "rpm: ERR: no 'A.jar'\n\n"

我们来试试:

$ touch A.java
$ gmake rpm

[done: jar cleaned out of existence]

cp A.java A.jar
[done: created pristine jar]

rpm: OK.


$ # so: first `clean`, then build A.jar, then the rpm target - all well.

$ # now: build target A.jar - no preceding `clear` expected:

$ touch A.java
$ gmake A.jar

cp A.java A.jar
[done: created jar]

$ # check.

[2]前面提到的“稳健性”好处怎么样?

几个月后,首席技术官在注意到数据中心账单略有增加后浏览了回购协议。一个看似无关的部门的测试工程师发布的提交引起了他的注意,据称该提交负责维护内部网遗留的 PHP,有时还用 Excel 脚本帮助会计助理的秘书。认识到提交是对不久前参与构建系统破坏的同一个文件进行的,导致即将到来的发布活动出现昂贵的延迟,他查看了 - 并发现了这一点:

#clean
clean: A.jar
    @# Added back my `clean: A.jar` above - this time I tested it
    @# before pushing, and now nothing crashed. // your guy
    rm A.jar
    @# rm -f A.jar  (avoid -f with rm - dangerous)
    @printf "[done: '$@']\n\n"

过了一会儿,CTO 的一位同事在他的屏幕上注意到了这一点:

$ # let's just see how this behaves
$ gmake

java.c A.java && jar cvf A.jar A.class
[done: created jar]

[done: jar cleaned out of existence]

java.c A.java && jar cvf A.jar A.class
[done: created pristine jar]

rpm: OK.

$ less Makefile

$ git revert HEAD -m "reverting: prev. commit caused an unneccessary "\
"build (jar created twice), but nothing worse than that this time"

$ git log -2 --pretty=format:'%ae'
[email protected]

$ gh api -X DELETE /repos/admin/spacex-padctrl-prod/collaborators/[email protected]

$ >> ~/notesToSelf.txt echo 'avoid long-duration consultant contracts'

脚注

[1]至少MadScientist的其他答案表明了这一点(他是GNU make的维护者)

© www.soinside.com 2019 - 2024. All rights reserved.