help-make
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Possible to get prerequisite path with secondary expansion when VPAT


From: Kaz Kylheku
Subject: Re: Possible to get prerequisite path with secondary expansion when VPATH is employed?
Date: Thu, 07 Apr 2022 17:55:41 -0700
User-agent: Roundcube Webmail/1.4.13

On 2022-04-06 23:36, Glen Huang wrote:
Source files
```
foo/1.tpl
foo/1.data
bar/2.tpl
bar/2.data
baz/3
```

Makefile
```
.SECONDEXPANSION:
VPATH := foo bar baz
build/%: %.tpl $$(patsubst %.tpl,%.data,$$<)
       render --data $(word 2,$^) $< $@
build/%: %
       cp $< $@
```

Sorry, the source files are actually structured like this:

Source files
```
foo/1.tpl
foo/data
bar/2.tpl
bar/data
baz/3
```
And I wish the Makefile could be like this:

Makefile
```
.SECONDEXPANSION:
VPATH := foo bar baz
build/%: %.tpl $$(dir $$<)data
       render --data $(word 2,$^) $< $@
build/%: %
       cp $< $@
```

Previously, the pattern rule could simply be "build/%: %.tpl %.data”,
but it won’t work for my actual case.

Might you be over-complicating things with this double expansion?

I have:

$ find .
.
./bar
./bar/2.data
./bar/2.tpl
./foo
./foo/1.tpl
./foo/1.data
./baz
./baz/3.tpl
./baz/3.data
./Makefile


All files are empty dummies, other than Makefile, which contains:

$ cat Makefile
tpl := $(wildcard */*.tpl)
builds = $(addprefix build/,$(foreach i,$(tpl),$(basename $(notdir $i))))
tpl_dirs = $(foreach i,$(tpl),$(dir $i))

$(info tpl := $(tpl))
$(info tpl_dirs := $(tpl_dirs))
$(info builds := $(builds))

VPATH := $(tpl_dirs)

build/%: %.tpl %.data
        @echo making $@ from $^

.PHONY: all
all: $(builds)


Run:

tpl_dirs := foo/ baz/ bar/
builds := build/1 build/3 build/2
making build/1 from foo/1.tpl foo/1.data
making build/3 from baz/3.tpl baz/3.data
making build/2 from bar/2.tpl bar/2.data


The actual recipes are simpler: we have a simple pattern rule with
no secondary expansions. It just says that build/% is made
from %.tpl and %.data.  The VPATH takes care of finding these.

All the calculations at the top are just to find the build directories
for VPATH automatically, as well as the three build numbers.

We could easily hard-code this:

$ make -f Makefile2
render --data foo/1.data foo/1.tpl build/1
render --data bar/2.data bar/2.tpl build/2
render --data baz/3.data baz/3.tpl build/3

$ cat Makefile2
tpl_dirs = foo bar baz
builds := $(addprefix build/,1 2 3)

VPATH := $(tpl_dirs)

build/%: %.tpl %.data
        @echo  render --data $(word 2,$^) $< $@

.PHONY: all
all: $(builds)


(P.S. I did not overlook  the build/%: % rule; that seems tangential.
You seem to want it so that if for build/4 we cannot find a 4.tpl
file somewhere, but a file called 4 exists, we just copy it.)


Double expansion or eval tricks might be useful if we wanted
to avoid using VPATH.  We know that, for instance,
the prerequisite for build/3 is 3.tpl, which is
found in the baz directory. We could generate the
exact dependency for that; i.e.

   build/3: baz/3.tpl baz/3.data

Or perhaps a series of pattern rules, one for each directory:

   build/%: foo/%.tpl foo/%.data\
            ...

   build/%: bar/%.tpl bar/%.data
             ...

   build/%: baz/%.tpl baz/%.data
            ...


Here is what that might look like. Like before in the first
Makefile, we calculate tpl_dirs and builds.


tpl := $(wildcard */*.tpl)
builds = $(addprefix build/,$(foreach i,$(tpl),$(basename $(notdir $i))))
tpl_dirs = $(foreach i,$(tpl),$(dir $i))

$(info tpl := $(tpl))
$(info tpl_dirs := $(tpl_dirs))
$(info builds := $(builds))


Then we use $(eval) to generate the above pattern rules,
by putting them into a multi-line template variable.
The $(1) is the one and only parameter, the directory
to insert into the template.


define rule_template
build/%: $(1)%.tpl $(1)%.data
        echo render --data $$(word 2,$$^) $$< $$@

endef

$(eval $(foreach d,$(tpl_dirs),$(call rule_template,$d)))


all rule, same as before:

.PHONY: all
all: $(builds)


For me that is easier to think about than the second-expansion thing.
The $(foreach ...) is just generating Make source code, which
eval processes right there, as if it appeared in the Makefile
literally.

We don't have any VPATH involved; everything is pinned down;
the $(wildcard ...)  expression does all the searching up-front,
while the Makefile is being read.

Cheers ...



reply via email to

[Prev in Thread] Current Thread [Next in Thread]