user2790498
user2790498

Reputation: 41

Target's directory in prerequisites without second expansion

Lets assume, i want to call

make somepath/abc.pot

which depends on somepath/somefiles.c

My target I've created so far looks like

%.pot: $(dir $@)*.c
    @echo "it works"
ifeq (,$(wildcard $@))
#   pot-file does not exist, do something
else
#   pot-file already exists, do something else
endif

but does not work as the Automatic Variables like $@ are not available in the prerequisites.

If found, that i can enable second expansion

.SECONDEXPANSION:
%.pot: $$(dir $$@)*.c
    @echo "it works"
ifeq (,$(wildcard $@))
#   pot-file does not exist, do something
else
#   pot-file already exists, do something else
endif

which allows me to use $@ in the prerequisites but breaks my ifeq statement which then always results in the first branch. If I change the ifeq to

ifeq (,$$(wildcard $$@))

it's working again but I really don't get why.

Now there a two questions:

A) Is there another way but to enable second expansion to have the path of the target in my prerequisites?

B) Why does the ifeq (,$(wildcard $@)) statement always result in the first branch if second expansion is enabled?

Upvotes: 1

Views: 552

Answers (1)

Etan Reisner
Etan Reisner

Reputation: 80931

Don't use ifeq in the recipe at all. Just use normal shell functionality. It works better.

.SECONDEXPANSION:
%.pot: $$(dir $$@)*.c
    @echo "it works"
    if [ ! -f $@ ]; then \
        : pot-file does not exist, do something; \
    else \
        : pot-file already exists, do something else; \
    fi

That said using wildcard in prerequisite lists is generally a bad idea because the time that they are globbed is not reliable and can cause odd behaviors. See Pitfalls of Using Wildcards for one example of a problem.

If you need to write different recipe contents based on some external factor (like OS) then you need to detect that at make parse time and have two copies of your recipes/makefile that you switch between correctly. You can do that inline but you can't do that per-recipe inline.

Your original attempts (using ifeq in a recipe) do not work. They don't do what you think they do. They may appear to work but they aren't working the way you expect.

Consider this makefile:

all: c

a:
        @touch a

c: a

.SECONDEXPANSION:

c d:
ifeq (,$(wildcard a))
        @echo "a doesn't exist (make)"
else
        @echo 'a does exist (make)'
endif
        @if [ ! -f a ]; then \
            echo "a doesn't exist (sh)"; \
        else \
            echo 'a does exist (sh)'; \
        fi
ifeq (,$$(wildcard a))
        @echo "a doesn't exist (make se)"
else
        @echo 'a does exist (make se)'
endif

In an empty directory you would expect make to output (assuming ifeq works the way you want it to):

a does exist (make)
a does exist (sh)
a does exist (make se)

Right? But it doesn't. You get:

a doesn't exist (make)
a does exist (sh)
a does exist (make se)

Ok, you think, that's just things not working without secondary expansion. But the secondary expansion version is working correctly. But it isn't.

If you run make d in an empty directory (note the d target doesn't list a as a prerequisite so it won't create it) you would expect the following output:

a doesn't exist (make)
a doesn' exist (sh)
a doesn' exist (make se)

Right? But what you actually get is:

a doesn't exist (make)
a doesn't exist (sh)
a does exist (make se)

So it appears that the secondary expansion version isn't working either.

A look at the make database explains why not.

Run make -qprR | awk '/c: a/,/^$/; /d:/,/^$/' in an empty directory and you see:

c: a
#  Implicit rule search has not been done.
#  File does not exist.
#  File has been updated.
#  Needs to be updated (-q is set).
# variable set hash-table stats:
# Load=0/32=0%, Rehash=0, Collisions=0/2=0%
#  commands to execute (from `Makefile', line 12):
        @echo "a doesn't exist (make)"
        @if [ ! -f a ]; then \
        echo "a doesn't exist (sh)"; \
        else \
        echo 'a does exist (sh)'; \
        fi
        @echo 'a does exist (make se)'


d:
#  Implicit rule search has not been done.
#  Modification time never checked.
#  File has not been updated.
#  commands to execute (from `Makefile', line 12):
        @echo "a doesn't exist (make)"
        @if [ ! -f a ]; then \
        echo "a doesn't exist (sh)"; \
        else \
        echo 'a does exist (sh)'; \
        fi
        @echo 'a does exist (make se)'

Which, as you can see, doesn't contain the ifeq lines but just the "correct" branch of the ifeq logic.

And that's the problem, the ifeq conditionals are evaluated at make parse time which is well before any recipes run (and thus before any files can be created, etc.).

Upvotes: 0

Related Questions