你能优先考虑 makefile 目标吗?

Can you prioritize makefile targets?

我有一个非常庞大且复杂的 make 系统,需要几个小时才能完成。我正在研究潜在的优化,我对是否可以对某些目标进行优先排序提出了疑问。

这是一个简单的 Makefile 示例,它说明了这个概念:

Test%: Prog%
    #run some post build verification on Prog{n}
    #  -- takes 30min or less on failure...

Prog1:  A B
    # do some linking

Prog2:  B C
    # do some linking

A B C:
    # take 10 minutes each

然后我运行

make -j2 Prog1 Test2

那么构建它的最快方法是:B & C,然后是 Prog2 & A,然后是 Test2Prog2。当然,通常情况下,make 会首先构建 AB,这会使我的最终输出延迟 10 分钟。

如果我预知 Test2 将成为瓶颈,是否可以在 gnu make 中优先考虑该目标及其所有依赖项而不是其他目标?

为了补充问题,假设我正在构建 Test1Test2 ... Test20。那么在那种情况下,我希望其中一个目标——比如说 Test1 尽快完成,就好像它失败了一样,我想尽快知道,这样我就可以终止构建并让其他人使用构建机器。

我考虑过这样做单独的 make 实例:

make Test2 && make Prog1

但是,当测试正在构建时,Prog1 将不会构建,从而导致构建服务器上的周期被浪费。如果我尝试按优先级并行构建它们:

`make Test2; nice -n -10 make Prog1`

这可能会导致在构建 B 时出现竞争条件。

我还没有找到任何好的解决方案,但我想我会问一下,以防我遗漏了什么。 (另外,我不确定这是否应该在 SO 或 SuperUser 上——我选择这里是因为它在技术上是关于 'programming',但请随时纠正我,我可以移动它)。

GNU make 不提供向目标添加权重的语法。 IE。当 make 能够开始下一个工作并且独立的(!)目标 AB 被解锁(它们的所有依赖关系都已经完成)并且需要重新制作时,这取决于它们在内部的顺序建立首先选择执行的数据库。

但是您可以使用其他依赖项来实现您的目标。以你的问题为例:

TESTS := Test1 Test2

$(TESTS): Test%: Prog%
Prog1:  A B
Prog2:  B C

ifdef _PRIORITIZE_TEST2
# additional dependencies to make sure Prog2 -> Test2 is prioritized
A: B C
endif

A B C Prog1 Prog2 Test1 Test2 all:
    @echo $@

.PHONY: A B C Prog1 Prog2 $(TESTS) all

测试运行:

$ make --trace -j10 Prog1 Test2
Makefile:13: target 'A' does not exist
echo A
Makefile:13: target 'B' does not exist
echo B
Makefile:13: target 'C' does not exist
echo C
A
B
C
Makefile:13: update target 'Prog1' due to: A B
echo Prog1
Makefile:13: update target 'Prog2' due to: B C
echo Prog2
Prog1
Prog2
Makefile:13: update target 'Test2' due to: Prog2
echo Test2
Test2

$ make --trace -j10 _PRIORITIZE_TEST2=1 Prog1 Test2
Makefile:13: target 'B' does not exist
echo B
Makefile:13: target 'C' does not exist
echo C
B
C
Makefile:13: update target 'A' due to: B C
echo A
Makefile:13: update target 'Prog2' due to: B C
echo Prog2
A
Makefile:13: update target 'Prog1' due to: A B
echo Prog1
Prog2
Makefile:13: update target 'Test2' due to: Prog2
echo Test2
Prog1
Test2

这可能需要大量手工制作才能获得您想要的结果。您甚至可能需要编写不同的附加依赖集来涵盖不同的 make 调用场景。

您可以查看转储 GNU make 数据库 (make --no-builtin-rules --print-data-base),对其进行解析以提取所有目标及其依赖项并可视化生成的图形。以下是如何在 DOT language of Graphviz:

中生成有向图的示例
#!/usr/bin/perl
use warnings;
use strict;

BEGIN {
    print "digraph build\n";
    print "{\n";
}

my $default_goal = '???';
my @goals;

while (<STDIN>) {
    if (my($target, $dependencies) = /^([\w_-]+):\s+(.*)/) {
        #print "Node ${target}\n";
        print "  $_ -> ${target}\n"
            for (split(' ', $dependencies));

    } elsif (my($goals) = /^MAKECMDGOALS :=\s+(.+)/) {
        @goals = split(' ', $goals);

    } elsif (my($goal) = /^\.DEFAULT_GOAL :=\s+(.+)/) {
        $default_goal = $goal;
    }
}

END {
    @goals = ( $default_goal )
        unless (@goals);
    #print "Root $_\n"
    #   for (@goals);
    print "}\n";
}

exit 0;

对于上面的例子,结果是:

$ make -n --no-builtin-rules --print-data-base Prog1 Test2 2>&1 | perl dummy.pl
digraph build
{
  A -> Prog1
  B -> Prog1
  Prog1 -> Test1
  B -> Prog2
  C -> Prog2
  Prog2 -> Test2
}