从makefile中获取makefile的名称

30
如何在makefile中获取makefile的名称?
谢谢。
注意:
我需要这样做是因为我希望我的makefile能够调用自身,但是makefile的名称不是Makefile,所以我想写类似这样的代码:
target:
    $(MAKE) -f $(MAKEFILENAME) other_target
7个回答

16
location = $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
WHERE_ART_THOU := $(location)
$(warning $(WHERE_ART_THOU))

我也认为这是GNU make特有的,但我不太确定。


4
如果在包含另一个makefile之后检查makefile的名称,则此解决方案无效!(复制粘贴的评论) - P Shved
如果您在包含另一个makefile后再检查makefile名称,则此解决方案将无法正常工作!这不是一个大问题。我可以在我的Makefile开头编写这段代码的前两行,以正确设置WHERE_ART_THOU变量。然后我可以在Makefile中的任何地方使用该变量,它将正确包含我的Makefile的名称。 - hcs42
7
对于包含的makefile,您需要使用不同的名称:一个makefile使用WHERE_ART_THOU,另一个使用WHERE_ART_THOU_2等。我旨在引入一个适用于所有makefile的名称,不仅在顶部使用时有效。 - P Shved
你是对的,Pavel。现在我明白了你的解决方案 :) - hcs42

12

如果你有任何问题,请参考写得非常好的GNU make手册。但是请记住,就像Makefile一样,在实践这些概念之前,必须完整地阅读这个手册。

我无法轻松地想出解决方法。据我所知,你需要手动完成一些工作。

稍后我将描述如何做到这一点,并展示引入current_makefile变量的脚本。但我想强调一个重要的概念。

你应该理解,如果我们有某种变量current_makefile,它会扩展成当前的Makefile名称,那么它将在读取Makefile的过程中发生更改。这意味着它必须在“立即”扩展上下文内使用——也就是在读取Makefile期间执行的命令内。然而,大多数命令都是在读取Makefile之后执行的。因此,在某些情况下,其中使用了“延迟”扩展的地方,某些命令将顺利打印出正确的值,而在某些地方,它将始终扩展为根Makefile名称。

如果你想在规则文本中使用这个变量,例如,你将不得不做一些技巧,因为规则文本总是具有延迟扩展。因此,如果你有这样的规则:

rule:
        echo In makefile $(current_makefile):
        echo Making target $@

它将始终打印根makefile的名称。相反,为了强制立即扩展,您必须创建另一个具有特定于makefile的名称的变量(即,在每个makefile中这些变量的名称应不同):

this_makefile_unique_name := $(current_makefile)
rule:
        echo In makefile $(current_makefile):
        echo Making target $@

或者使用eval:

define make_rule
rule:
        echo In makefile $(1):
        echo Making target $$@

$(eval $(call make_rule,$(current_makefile)))

如果您只想出于调试目的使用当前makefile的名称,请考虑使用特殊的调试函数,例如warning或info:

$(warning We're in makefile $(current_makefile))

这些函数使用“立即”展开并将打印正确的值。


如何定义这样的$(current_makefile)?

您需要手动维护makefile包含的堆栈。当您包含一个makefile时,它的名称将被放置在堆栈顶部; 当您从包含的makefile返回到外部makefile时,最上面的名称将从堆栈中弹出。这是通过在makefile的开头和结尾插入特殊调用来实现的:

# Beginning of makefile
$(eval $(makefile_names_push))
#... makefile text
$(warning $(current_makefile))
#...
$(eval $(makefile_names_pop))
#End of file

现在在你的根Makefile的开头定义这些函数。

lastword=$(word $(words $(1)),$(1))

define makefile_names_push
current_makefile := $$(CURDIR)/$$(call lastword,$$(MAKEFILE_LIST))
makefile_stack :=$$(makefile_stack) $$(current_makefile)
endef

define makefile_names_pop
makefile_stack := $$(filter-out $$(current_makefile),$$(makefile_stack))
current_makefile := $$(call lastword,$$(makefile_stack))
endef

如果您确定您的 make 版本足够新(3.81+),请将 lastword 的调用替换为内置函数:

#inctead of $$(call lastword,$$(MAKEFILE_LIST))
$$(lastword $$(MAKEFILE_LIST))

这有用吗?

完全没有用。唯一可能有用的方法是创建100个符号链接到一个makefile中,这些makefile中的规则依赖于它们的名称。但是,可以在一个makefile中实现并使用手册中描述的foreach-eval技术。因此,我的帖子完全浪费了时间,尽管我有些开心 :-)


11

这将返回第一个被调用的Makefile的名称,也就是在调用堆栈底部的Makefile:

MAKEFILE_JUSTNAME := $(firstword $(MAKEFILE_LIST))
MAKEFILE_COMPLETE := $(CURDIR)/$(MAKEFILE_JUSTNAME)

当在非交叉递归的情况下使用时(例如用于makedepend),它只是当前Makefile的名称。


3

我想为使用Make管理简单重复任务时的内容回显做类似的事情。我发现了这个页面,发现它正是我所需要的,对于我有限的make理解非常有用。

阅读此页面后我的结果:

    # Makefile - 'make' and 'make help' now echo the makefile.
    help:
        cat $(lastword $(MAKEFILE_LIST))
    start:
        sudo -u www /path/to/webapp/myhttpd restart
    stop:
        sudo kill `cat /path/to/webapp/data/httpd.pid`

1
一个快速的 Google 之旅表明 this 网站有答案。

仅适用于GNU Make - 通常无法与非GNU make一起使用。 - Jonathan Leffler
1
如果在包含另一个 makefile 之后检查 makefile 名称,则此解决方案无法正常工作! - P Shved
这篇文章有一个建议的编辑。由于编辑内容太多,我无法确认您的目标是否得到保留。最好您自己审核一下。 - Roshana Pitigala
由于建议的编辑仅留下了一个链接,而链接的博客现在重定向到其主页内容,因此此答案不再是一个答案。 - jwd630

0

你好,

如果你复制了原始的 makefile 文件,比如叫做 makefile_test,然后输入以下命令:

make -np -f makefile_test 2>&1 | tee output

这将评估Makefile和您的make环境,但不会执行任何命令。查看输出文件中关于Makefile_test的引用,可以显示设置在make环境中的内容以及该值被设置的位置。

注意:这可能会生成大量信息!不要添加-d(调试)开关,它会生成有关make决策过程的大量额外输出,但仅提供有关make环境的最少附加信息。

希望能帮到你


这很有趣,但它如何回答实际问题? - Keith M
环境变量列表包含各种信息。$(MAKEFILE_LIST) 包含 make 解析的 Makefile 的名称。希望对你有所帮助。 - Rob Wells

0
这里的解决方案涉及以下内容:1)带有2)调用的POSIX make,3)类Unix平台上未包含的makefile。
OP所要求的是:
target:
    @pid=$$$$; \
    while test `ps -ocomm= $$pid` != make; do \
        pid=`ps -oppid= $$pid`; \
    done; \
    MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
    test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
    test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
    export MAKEFILENAME; \
    $(MAKE) -e -f $$MAKEFILENAME other_target

目标取决于Makefile,有点臃肿:

TARGET1_MAKEFILENAME = target1_preamble

all: target1 target2...

target1: $(TARGET1_MAKEFILENAME) other_dependencies...
    @test $(TARGET1_MAKEFILENAME) == target1_preamble && exit 0; \
    built_instructions_for_target1;

target1_preamble:
    @pid=$$$$; \
    while test `ps -ocomm= $$pid` != make; do \
        pid=`ps -oppid= $$pid`; \
    done; \
    MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
    test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
    test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
    export MAKEFILENAME; \
    $(MAKE) -e -f $$MAKEFILENAME target1;

如果 make 仅针对所有目标调用,则可以简化一些。

MAKEFILENAME = invoked_makefile_placeholder

all: target1 target2...

target1: $(MAKEFILENAME) other_dependencies...
    @test $(MAKEFILENAME) == invoked_makefile_placeholder && exit 0; \
    built_instructions_for_target1;

invoked_makefile_placeholder:
    @pid=$$$$; \
    while test `ps -ocomm= $$pid` != make; do \
        pid=`ps -oppid= $$pid`; \
    done; \
    MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
    test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
    test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
    export MAKEFILENAME; \
    $(MAKE) -e -f $$MAKEFILENAME

使用先前的方法,基于grep和包含在makefile中的唯一模式,实现包含makefiles的解决方案是微不足道的。

当我觉得问题得到了适当的解决方案时,我从不回答。


非常好的通用答案,尽管有些晦涩难懂。也许可以提供一个高层次的分解来解释为什么这样做是有效的,以帮助理解? - Jay M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接