子生成文件并向上传递变量
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"
我有一个项目涉及带有子 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"