当 eval 创建规则时 Makefile 中的目标顺序

Order of targets in Makefile when rule created by eval

我写了一个 Makefile,它使用了与 here 类似的想法。也就是说,我定义了一组目录并使用 foreachdefine 在每个目录中构建所有对象。这是我的 Makefile 大致的样子

all_dirs := $(shell ...) # long shell command which finds all directories
all_objs := $(shell ...) # long shell command which finds all objects

define compile_rule
$(BUILDDIR)/$(1)/%.o :  $(1)/%.cpp
    $(CXX) $(CXXFLAGS) -c $< -o $@ -MD -MF $(@:.o=.dep)
endef

$(foreach dir,$(all_dirs:$(BUILDDIR)/%=%),$(eval $(call compile_rule,$(dir))))

all : program_name

program_name: $(all_objs)
    $(LD) -o $@ $(all_objs) $(LDFLAGS)

我总是 运行 带有 -j32 参数的 make 命令。通常它工作正常但在某些情况下我从 Makefile 中得到一个错误它自己说 No rule to make target$(all_objs) 列表中的第一个目标文件名 needed by program_name.

here问题就清楚了。由于我使用的是 -j,make 命令似乎在扩展 foreach 创建的规则之前开始计算 program_name。我试图在规则中编写 foreach 这样我就可以保证 program_nameforeach 之间的执行顺序但是后来我得到了错误 prerequisites cannot be defined in recipes.

知道如何解决这个问题吗?我也许可以使用两步制作来解决这个问题,即首先使用 make objs 之类的东西构建 $(all_objs),然后使用 make prog 之类的东西将它们链接在一起,但我更喜欢这一切发生在一个命令中,我觉得我错过了一些简单的事情。请指教

这可能有效,但我还没有测试过:

# ...

prereq: $(foreach dir ...)

program_name: $(all_objs) | prereq
  $(LD) ...

似乎 在我的测试设置上工作,但我只测试了几个目录和对象。管道是一个 order-only prerequisite,看起来可以将 for 循环放在目标中。

建议

如果你真的想要,你可以下载 GNU Make 3.82 source,用 patch < function.c.patch 应用下面的补丁,然后用 make --debug=b -j32 ... 调用 make 并观察 CallEvaluatingExecuting 行。

--- function.c  2010-07-13 03:20:39.000000000 +0200
+++ function.c.patched  2015-04-18 20:41:15.000000000 +0200
@@ -1371,6 +1371,8 @@

   install_variable_buffer (&buf, &len);

+   DB (DB_BASIC, (_("Evaluating: %s\n"), argv[0]));
+
   eval_buffer (argv[0]);

   restore_variable_buffer (buf, len);
@@ -1652,6 +1654,11 @@
       return o;
     }

+   DB (DB_BASIC, (_("Executing shell command:")));
+  for ( char **p=command_argv; *p!=NULL; ++p )
+    DB (DB_BASIC, (_(" %s"), *p));
+  DB (DB_BASIC, (_("\n")));
+
 # ifdef __EMX__
   /* close some handles that are unnecessary for the child process */
   CLOSE_ON_EXEC(pipedes[1]);
@@ -2311,6 +2318,12 @@
     --cp;
   cp[1] = '[=11=]';

+   DB (DB_BASIC, (_("Call %s:"), fname));
+  for ( char **p=argv; *p!=NULL; ++p )
+    DB (DB_BASIC, (_(" %s"), *p));
+  DB (DB_BASIC, (_("\n")));
+
+
   /* Calling nothing is a no-op */
   if (*fname == '[=11=]')
     return o;

这样你实际上可以 看到 如果 call compile_rule 是 运行 而它不应该。在我的例子中,输出是:

GNU Make 3.82
Built for x86_64-apple-darwin14.3.0
Copyright (C) 2010  Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Reading makefiles...
Executing shell command: find . -type d
Call compile_rule: compile_rule .
Evaluating: ./%.foo: ./%.txt
    @echo build $@ from $<
Call compile_rule: compile_rule ./1
Evaluating: ./1/%.foo: ./1/%.txt
    @echo build $@ from $<
Call compile_rule: compile_rule ./2
Evaluating: ./2/%.foo: ./2/%.txt
    @echo build $@ from $<
Updating goal targets....
 File `program' does not exist.
   File `1/1.foo' does not exist.
  Must remake target `1/1.foo'.
Invoking recipe from Makefile:11 to update target `1/1.foo'.
   File `2/2.foo' does not exist.
  Must remake target `2/2.foo'.
Invoking recipe from Makefile:11 to update target `2/2.foo'.
   File `prereq' does not exist.
  Must remake target `prereq'.
  Successfully remade target file `prereq'.
build 1/1.foo from 1/1.txt
 File `program' does not exist.
build 2/2.foo from 2/2.txt
 File `program' does not exist.
Must remake target `program'.
Invoking recipe from Makefile:9 to update target `program'.
link program w/args 1/1.foo 2/2.foo