Search code examples
makefilegnu-makeinclude-guards

ifndef include guard in Gnu Make breaks on nested conditional


I’m trying to implement include guards in Gnu Make. In this Makefile, the first inclusion is OK, while the second one fails with an error.

ifndef INCLUDED
INCLUDED = 1
$(info Including)

define macro
ifneq ($(1),)
define inner_macro
macro content...
endef
else
define inner_macro
endef
endif
endef

endif

The same effect can be simulated by explicitly giving INCLUDED = 1 before the inclusion, e.g. on command line.

Gnu Make 4.1 under Gentoo says Makefile:14: *** missing separator. Stop., while Gnu Make 3.81 under Debian Wheezy says Makefile:14: *** extraneous `endef'. Stop.. On the first inclusion, they both say:

Including
make: *** No targets.  Stop.

If I try $(eval $(call macro,whatever)) after the first inclusion, it defines inner_macro as expected.

I used make INCLUDED=1 and make commands respectively to get the described behavior.

The same happens when I clear the environment and disable built-in rules and variables: env -i make -rR INCLUDE=1. When I use -p to dump the database, without INCLUDED=1, the macro is defined as it should be, but with INCLUDED=1, empty inner_macro is defined. This is consistent across both the versions of Make. This hints me that when the condition is false, Make parses the Makefile differently and thinks the else inside macro’s definition belongs to the ifndef. Other condition types behave all the same.

If I remove both the definitions of inner_macro, the problem goes away.

I read the manual pages info make conditional\ syntax and info make multi-line (formerly defining), but I found no caveat there and I still think I am doing nothing wrong.

  1. Am I correct with my conclusions?
  2. Is this a bug in Make, or am I invoking undefined behavior?
  3. How should I implement include guards in Gnu Make?

Solution

  • That's a bug. Report it on Savannah.

    There's something wrong with the tracking of nested define/endef inside a not-taken ifdef/ifndef condition. If you don't use nested define/endef then it works; for example (obviously you may not be able to do this in your environment):

    ifndef INCLUDED
    INCLUDED = 1
    $(info Including)
    
    define macro
    ifneq ($(1),)
    inner_macro = macro content...
    else
    inner_macro =
    endif
    endef
    
    endif