makefile is missing separator

ThePosey picture ThePosey · Jun 4, 2012 · Viewed 27.9k times · Source

Alright I am stuck on this and I have no idea what I am doing wrong. Everything was going great working on a more complicated makefile but then all of a sudden I got the "Missing separator" error. I was able to isolate it down to a very simple scenario:

test.mk

define push_dir
$(info ${1})
endef

define pop_dir
$(info ${1})
endef

define include_submake
$(call push_dir,${1})
$(call pop_dir,${1})
endef

Simple

include test.mk

INITIAL_SUBMAKE:= includeme.mk
$(call include_submake,${INITIAL_SUBMAKE})

process:
    @echo Processed...

And the output:

C:\project>make -f Simple process
includeme.mk
includeme.mk
Simple:4: *** missing separator.  Stop.

includeme.mk does not actually exist. I have no idea what is going wrong here I have tried a multitude of things. If I surround the call to include_submake in info like so:

$(info $(call include_submake,${INITIAL_SUBMAKE}))

The missing separator error goes away. Also If in the include_submake define I only call one of the functions it works fine. Additionally if I directly call the functions instead of calling them include_submake it works as well:

include test.mk

INITIAL_SUBMAKE:= includeme.mk
$(call push_dir,${INITIAL_SUBMAKE})
$(call pop_dir,${INITIAL_SUBMAKE})

process:
    @echo Processed...


C:\project>make -f Simple process
includeme.mk
includeme.mk
Processed...

I feel like I'm overlooking something fundamental here. Thanks for your help.

Answer

Eldar Abusalimov picture Eldar Abusalimov · Jun 4, 2012

The missing separator error happens because of a non-empty return value of include_submake, which is a single line feed character in your case. Make only permits whitespace characters (that is, a space or tab) to occur in an expression which is not assumed to be a part of some rule or another directive.

Rewrite your functions using plain-old Make variable assignment and the error should go away:

push_dir = \
    $(info $1)

pop_dir = \
    $(info $1)

include_submake = \
    $(call push_dir,$1) \
    $(call pop_dir,$1)

UPD.: define vs plain old variable assignment

Answering to a question from the first comment. Personally I would prefer using define directive in several cases.

Using with eval function

As the GNU Make manual suggests, define directive is very useful in conjunction with the eval function. Example from the manual (emphasis is mine):

PROGRAMS    = server client

server_OBJS = server.o server_priv.o server_access.o
server_LIBS = priv protocol

client_OBJS = client.o client_api.o client_mem.o
client_LIBS = protocol

# Everything after this is generic

.PHONY: all
all: $(PROGRAMS)

define PROGRAM_template
  $(1): $$($(1)_OBJS) $$($(1)_LIBS:%=-l%)
  ALL_OBJS   += $$($(1)_OBJS)
endef

$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))

$(PROGRAMS):
    $(LINK.o) $^ $(LDLIBS) -o $@

clean:
    rm -f $(ALL_OBJS) $(PROGRAMS)

Generator templates

Verbatim variables fit perfectly for cases when you want to generate a file from GNU Make. For example, consider generating a header file based on some information from Makefile.

# Args:
#   1. Header identifier.
define header_template
/* This file is generated by GNU Make $(MAKE_VERSION). */

#ifndef $(inclusion_guard)
#define $(inclusion_guard)

$(foreach inc,$($1.includes),
#include <$(inc).h>)

/* Something else... */

#endif /* $(inclusion_guard) */

endef

# 1. Unique header identifier.
inclusion_guard = \
    __GEN_$1_H

# Shell escape.
sh_quote = \
    '$(subst ','"'"',$1)'

foo.includes := bar baz

HEADERS := foo.h

$(HEADERS) : %.h :
    @printf "%s" $(call sh_quote,$(call header_template,$(*F)))> $@

Extended Make syntax

In our project we use our own build system called Mybuild, and it is implemented entirely on top of GNU Make. As one of low-level hacks that we used to improve the poor syntax of the builtin language of Make, we have developed a special script which allows one to use extended syntax for function definitions. The script itself is written in Make too, so it is a sort of meta-programming in Make.

In particular, one can use such features as:

  • Defining multiline functions without the need to use backslash
  • Using comments inside functions (in plain-old Make comments can only occur outside variable assignment directives)
  • Defining custom macros like $(assert ...) or $(lambda ...)
  • Inlining simple functions like $(eq s1,s2) (string equality check)

This is an example of how a function can be written using the extended syntax. Note that it becomes a valid Make function and can be called as usual after a call to $(def_all).

# Reverses the specified list.
#   1. The list
# Return:
#   The list with its elements in reverse order.
define reverse
    # Start from the empty list.
    $(fold ,$1,

        # Prepend each new element ($2) to
        # the result of previous computations.
        $(lambda $2 $1))
endef
$(def_all)

Using these new features we were able to implement some really cool things (well, at least for Make :-) ) including:

  • Object-Oriented layer with dynamic object allocation, class inheritance, method invocations and so on
  • LALR parser runtime engine for parsers generated by GOLD Parser Builder
  • Modelling library with runtime support for models generated with EMF

Feel free to use any part of the code in your own projects!