Makefile:在目标依赖声明中使用特定于目标的变量

3

我正在尝试编写一个使用目标特定变量的makefile,但是虽然每个目标和前提条件中的变量都被正确设置,但前提条件列表本身没有使用该变量进行更新,从而导致检查和调用错误的前提条件。

如何在前提条件中也更新目标特定变量?

在下面的示例中,make foomake bar 都应该打印 "world",但是 make foo 打印 "hello"。

X=hello

hello:
    echo "hello"

world:
    echo "world"

foo:X=world
foo:$(X)

bar:X=world
bar:
    make $(X)

我试图实现的目标是,不同的目标将通过将文件夹作为目标特定变量来传递,构建类似的先决条件 - 相同的文件,位于不同的文件夹中。问题在于,如下面的示例所示,如果首先调用一个目标(例如示例中的foo),则调用第二个目标将不会执行任何操作。
DIR=fooDir
FILE=$(DIR)/filename

$(FILE):
    touch $(FILE)
    echo $(FILE)

foo: $(FILE)

bar:DIR=barDir
bar: $(FILE)

2
不要直接在make内部调用make,因为变量将无法传递给子make。请使用$(MAKE)代替。 https://www.gnu.org/software/make/manual/html_node/MAKE-Variable.html - undefined
2个回答

5

6.11 目标特定的变量值:

与自动变量一样,这些值仅在目标配方的上下文中(以及其他目标特定的分配)可用。

但是在先决条件列表中不可用。换句话说,在 foo:$(X) 中,X 不是目标特定的。


实现所需结果的一种方法是:

same_files := filename another_filename

# ${1} is the target name.
# ${2} is the directory.
define similar_prerequisites
${1} : $(addprefix ${2}/,${same_files})
$(addprefix ${2}/,${same_files}) : | ${2}
    touch $$@
${2} :
    mkdir $$@
endef

all : foo bar

foo bar :
    @echo "$@ prerequisites are $^"

$(eval $(call similar_prerequisites,foo,fooDir))
$(eval $(call similar_prerequisites,bar,barDir))

输出:

$ make
mkdir fooDir
touch fooDir/filename
touch fooDir/another_filename
foo prerequisites are fooDir/filename fooDir/another_filename
mkdir barDir
touch barDir/filename
touch barDir/another_filename
bar prerequisites are barDir/filename barDir/another_filename

编辑以解释我想要做什么。 - undefined
1
@nihohit 给你添加了一个解决方案。 - undefined
谢谢,这个方法可以用!问题是解决方案比仅声明两个变量要冗长得多,所以我需要检查我的代码并理解哪种方法更可取。 - undefined
1
@nihohit 如果你找到了更优雅的解决方案,我会很有兴趣看看。 - undefined

1
我需要类似的东西。这个例子是否符合您的需求?
# Basic rules can be ignored
# Use % to not take much space
.PHONY: foo bar
foo bar:
    @printf "%s depends on %s\n" "$@" "$^"

fooDir/ barDir/:
    @printf "%s\n" "Building $@"
    @mkdir "$@"

fooDir/%: fooDir
barDir/%: barDir
%/filename: %/
    @printf "%s\n" "Building $@"
    @touch "$@"

%/filename2: %/
    @printf "%s\n" "Building $@"
    @touch "$@"

%/filename3: %/
    @printf "%s\n" "Building $@"
    @touch "$@"


fooFile = fooDir/filename2
barFile = barDir/filename2

DIR = $(*)Dir
FILE = $(DIR)/filename3

.SECONDEXPANSION:
# Here the fun begin
# See full details on https://dev59.com/wTsDtIcB2Jgan1znbfp_#73679964
foo bar: %: $$(*)Dir/filename $$($$(*)File) $$(FILE)

输出:

$ make foo; make bar
Building fooDir/
Building fooDir/filename
Building fooDir/filename2
Building fooDir/filename3
foo depends on fooDir/filename fooDir/filename2 fooDir/filename3
Building barDir/
Building barDir/filename
Building barDir/filename2
Building barDir/filename3
bar depends on barDir/filename barDir/filename2 barDir/filename3

技巧在于使用.SECONDEXPANSION:,所有在其后定义的目标(这就是为什么我将其放在最后)都将获得第二次扩展。匹配规则的$(*),也就是所匹配规则的主干,在第二次扩展中才会变得可用。这使我们能够构建与目标相关联的名称。
模式targets: %: deps只允许匹配列出的目标,而不是捕获所有目标。
我更喜欢版本1或2,因为第三个版本可能会让你想在.SECONDEXPANSION:之前使用它,从而导致奇怪的错误消息。如果你真的想使用3,则将此特殊目标放在顶部。同时,如果与多个通配符规则一起使用,3可能会有错误的值(你可能想要使用MAKECMDGOALS或类似的值,而不是*)。只要记住,如果你想使用3,则在DIR或FILE定义中或在使用位置中放置$$。

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