子生成文件并向上传递变量

Sub-makefiles and passing variables upward

我有一个项目涉及带有子 makefile 的子目录。我知道我可以使用 export 命令通过环境将变量从父 makefile 传递到子 makefile。有没有办法将变量从子 makefile 传递到它的调用 makefile? IE。导出可以反向工作吗?我试过这个但没有成功。我猜一旦 sub-make 完成它的 shell 连同它的环境变量一起被销毁。还有另一种向上传递变量的标准方法吗?

对你的问题的简短回答是:不,你不能[直接]做你想做的递归构建(非递归构建见下文) .

Make 像任何其他命令一样将子制作过程作为配方行执行。它的 stdout/stderr 像任何其他进程一样被打印到终端。一般来说,子进程不会影响父进程的环境(显然我们在这里不是在谈论环境,但同样的原则适用)——除非你故意在父进程中构建类似的东西,但你会使用实现它的 IPC 机制。

我可以想出很多方法来实现这一点,所有这些听起来都很糟糕。例如,您可以写入文件并在 eval:

中使用 include 指令(注意:未经测试)作为源文件
some_target:
  ${MAKE} ${MFLAGS} -f /path/to/makefile

some_other_target : some_target
  $(eval include /path/to/new/file)

...尽管它必须位于如上所述的单独目标中,因为所有 $(macro statements) 都在配方开始执行之前进行评估,即使宏位于配方的后面一行。

gmake v4.x 有一项新功能,允许您直接从 makefile 指令 write out to a file。文档中的示例:

If the command required each argument to be on a separate line of the input file, you might write your recipe like this:

program: $(OBJECTS)
        $(file >$@.in) $(foreach O,$^,$(file >>$@.in,$O))
        $(CMD) $(CMDFLAGS) @$@.in
        @rm $@.in

(gnu.org)

...但是您仍然需要在单独的配方中使用 $(eval include ...) 宏来使用文件内容。

我对在食谱中使用 $(eval include ...) 持怀疑态度;在并行构建中,包含的文件会影响 make 变量,并且包含发生的时间对于并行构建的其他目标来说可能是不确定的 w/respect。

你最好找到一个更自然的解决方案来解决你的问题。我会先退后一步问问自己 "what problem am I trying to solve, and how have other people solved that problem?" 如果您没有找到试图解决该问题的人,那很有可能是因为他们没有按照您所走的道路开始。


编辑可以 为非递归构建做您想做的事。例如:

# makefile1
include makefile2

my_tool: ${OBJS}


# makefile2
OBJS := some.o list.o of.o objects.o

...尽管我提醒您对此要非常小心。我维护的构建非常大(大约 250 个 makefile)。每个级别都包含如下语句:

include ${SOME_DIRECTORY}/*/makefile

这里的危险是您不希望一棵树中的人依赖于另一棵树中的变量。有几个地方在短期内我不得不做一些你想要的事情:子 makefile 附加到一个变量,然后该变量在父 makefile 中使用。从长远来看,它会消失,因为它是 brittle/unsafe,但目前我不得不使用它。

我建议你阅读论文Recursive Make Considered Harmful (if that link doesn't work, just google the name of the paper)。

你的目录结构大概是这样的:

my_proj
|-- Makefile
|-- dir1
|   `-- Makefile
`-- dir2
    `-- Makefile

您在 parent Makefile 中所做的可能是这样的:

make -C ./dir1
make -C ./dir2

这实际上 spawns/forks 每个 make 调用一个新的 child 进程。
您要求从 children 更新 parent 进程的环境,但这在设计上是不可能的 (1, 2)。

您仍然可以通过以下方式解决此问题:

  • 使用文件作为两个进程之间的共享内存(参见 Brian 的回答)
  • 使用 child 的退出错误代码作为不同操作的触发器 [丑陋的技巧]

我认为最简单的解决方案是使用子 Makefile 中的标准输出。

父 Makefile

VAR := $(shell $(MAKE) -s -C child-directory)

all:
        echo $(VAR)

子 Makefile

all:
        @echo "MessageToTheParent"