nihohit
nihohit

Reputation: 578

Makefile: target-specific variables in target dependencies declaration

I'm trying to write a makefile that uses target-specific variables, but while the variables are set correctly in each target and prerequisite body, the prerequisites list itself isn't updated with the variable, thus causing the wrong prerequisite to be checked and called.

How can I update target-specific variables in the prerequisites too?

In the example below, both make foo and make bar should print "world", but make foo prints "hello".

X=hello

hello:
    echo "hello"

world:
    echo "world"

foo:X=world
foo:$(X)

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

The goal I'm trying to achieve is that different targets will build similar prerequisites - the same files, in different folders - by passing the folders as a target-specific variable. The issue is that as in the example below, if one target is called first (foo, in the example), calling the second will not do anything.

DIR=fooDir
FILE=$(DIR)/filename

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

foo: $(FILE)

bar:DIR=barDir
bar: $(FILE)

Upvotes: 3

Views: 2866

Answers (2)

Et7f3XIV
Et7f3XIV

Reputation: 619

I needed something similar. Does this exemple suit your needs ?

# 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://stackoverflow.com/a/73679964/7227940
foo bar: %: $$(*)Dir/filename $$($$(*)File) $$(FILE)

output:

$ 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

The tricks is in .SECONDEXPANSION: all target defined after (that's why I put it at the end) will get a second expansion. The $(*) aka the stem of matched rules will only become available in the second expansion. This allow us to build name that is linked to the target.

The pattern targets: %: deps allow to match only on listed target and not catch all target.

I prefer the version 1 or 2 because the third version you might be tempted to use in place before .SECONDEXPANSION: and thus you will get weird error messages. If you really want to use 3 then put this special target at the top. Also 3 might have wrong value (you might want MAKECMDGOALS or something like that instead of *) if used with multiple wildcard rule. Just remember if you want to use 3 then put $$ either in DIR in FILE definition or in the use site.

Upvotes: 1

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136208

6.11 Target-specific Variable Values:

As with automatic variables, these values are only available within the context of a target’s recipe (and in other target-specific assignments).

But not in the prerequisite list. In other words, in foo:$(X) X is not target specific.


One way to achieve the desired results is:

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))

Output:

$ 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

Upvotes: 5

Related Questions