1

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?

1 Answers1

0

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

Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
  • I got the ifeq solution from [here](http://stackoverflow.com/questions/5553352/how-do-i-check-if-file-exists-in-makefile) and have to use it because the Makefile is also used on Windows machines where I obviously can't use shell functions at all. – user2790498 Nov 09 '15 at 18:24
  • Right but that's pre-recipe checking. I doubt your makefile was working the way you expected. I bet it was doing the `ifeq` check at make parse-time and that just happens to be the same as what you actually care about because nothing (other then the recipe being run) can make that file exist so which path is correct can't change *during* a run of make. The shell-level check is the correct solution here. If you can't use that then you can't do what you want directly. – Etan Reisner Nov 09 '15 at 18:36
  • That is not satisfactory. I can tell you that the makefile is working as expected but I am of course not sure if that's by accident. That's why I am asking question B) "whats happening under the hood". Question A) remains unanswered. Is there a better solution to access the path of the target in the prerequisites section. – user2790498 Nov 09 '15 at 19:02
  • And I'm telling you that your makefile **is not** working as expected. It is working "by accident". You happen to be getting the output you expect when the file does and doesn't exist *before* you run `make`. You will **not** get that output correct if the file comes into being while make is running. Your current code is, like a broken clock, correct twice a day. You just happen to only ask for the time at one of those two minutes currently. Your approach is, however, flawed in nature. – Etan Reisner Nov 09 '15 at 19:15
  • The answer to `A` is "no". Secondary expansion *is* how you get the target (and other automatic variables) to be available in the prerequisite list. – Etan Reisner Nov 09 '15 at 19:16
  • I didn't see your changes during my last commit. Sorry for that! Your answer gives now quite a good insight into what happens. From your outputs, I expect that make's order is 1) first expansion, 2) ifeq evaluation and 3) second expansion. If that's not true, I don't get why `ifeq (,$(wildcard a))` and `ifeq (,$$(wildcard a))` act differently. – user2790498 Nov 09 '15 at 19:44
  • The first expansion and `ifeq` evaluation occur together (at make-parse time). Secondary expansion happens immediately after the first phase (parse time) and before the targets are evaluated. But secondary expansion is a red-herring with regards to `ifeq` since `ifeq` is already handled by the first phase. So the `ifeq` sees the two literal values `` and `$(wildcard a)` (I believe) which aren't expanded any farther and obviously aren't identical. See [How `make` Reads a Makefile(http://www.gnu.org/software/make/manual/make.html#Reading-Makefiles)] and the following Secondary Expansion section. – Etan Reisner Nov 09 '15 at 19:50
  • 2
    What do you thing about the solution: `$(if $(wildcard $@),@echo "file exists", @echo "file does not exist")`? It should be evaluated at the right time and does not rely on any shell functions thus is suitable on windows as well. Also for the first question I found something about the (VPATH-Variable)[https://www.gnu.org/software/make/manual/html_node/General-Search.html#General-Search] which might be used to solve the problem without second expansion but I won't go into that direction any further. – user2790498 Nov 10 '15 at 09:42
  • That seems like it should work actually. The timing is still slightly off (that happens at recipe expansion time not at execution of that line time) but that's a much smaller window that is much less likely to be a problem for anything. – Etan Reisner Nov 10 '15 at 13:40