你能优先考虑 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
,然后是 Test2
和 Prog2
。当然,通常情况下,make 会首先构建 A
和 B
,这会使我的最终输出延迟 10 分钟。
如果我预知 Test2
将成为瓶颈,是否可以在 gnu make 中优先考虑该目标及其所有依赖项而不是其他目标?
为了补充问题,假设我正在构建 Test1
、Test2
... Test20
。那么在那种情况下,我希望其中一个目标——比如说 Test1
尽快完成,就好像它失败了一样,我想尽快知道,这样我就可以终止构建并让其他人使用构建机器。
我考虑过这样做单独的 make 实例:
make Test2 && make Prog1
但是,当测试正在构建时,Prog1
将不会构建,从而导致构建服务器上的周期被浪费。如果我尝试按优先级并行构建它们:
`make Test2; nice -n -10 make Prog1`
这可能会导致在构建 B
时出现竞争条件。
我还没有找到任何好的解决方案,但我想我会问一下,以防我遗漏了什么。 (另外,我不确定这是否应该在 SO 或 SuperUser 上——我选择这里是因为它在技术上是关于 'programming',但请随时纠正我,我可以移动它)。
GNU make 不提供向目标添加权重的语法。 IE。当 make 能够开始下一个工作并且独立的(!)目标 A
和 B
被解锁(它们的所有依赖关系都已经完成)并且需要重新制作时,这取决于它们在内部的顺序建立首先选择执行的数据库。
但是您可以使用其他依赖项来实现您的目标。以你的问题为例:
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
}
我有一个非常庞大且复杂的 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
,然后是 Test2
和 Prog2
。当然,通常情况下,make 会首先构建 A
和 B
,这会使我的最终输出延迟 10 分钟。
如果我预知 Test2
将成为瓶颈,是否可以在 gnu make 中优先考虑该目标及其所有依赖项而不是其他目标?
为了补充问题,假设我正在构建 Test1
、Test2
... Test20
。那么在那种情况下,我希望其中一个目标——比如说 Test1
尽快完成,就好像它失败了一样,我想尽快知道,这样我就可以终止构建并让其他人使用构建机器。
我考虑过这样做单独的 make 实例:
make Test2 && make Prog1
但是,当测试正在构建时,Prog1
将不会构建,从而导致构建服务器上的周期被浪费。如果我尝试按优先级并行构建它们:
`make Test2; nice -n -10 make Prog1`
这可能会导致在构建 B
时出现竞争条件。
我还没有找到任何好的解决方案,但我想我会问一下,以防我遗漏了什么。 (另外,我不确定这是否应该在 SO 或 SuperUser 上——我选择这里是因为它在技术上是关于 'programming',但请随时纠正我,我可以移动它)。
GNU make 不提供向目标添加权重的语法。 IE。当 make 能够开始下一个工作并且独立的(!)目标 A
和 B
被解锁(它们的所有依赖关系都已经完成)并且需要重新制作时,这取决于它们在内部的顺序建立首先选择执行的数据库。
但是您可以使用其他依赖项来实现您的目标。以你的问题为例:
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
}