big_gie
big_gie

Reputation: 3009

Problem creating gnu makefile "function"

I have a big chunk of my makefile (~50 lines) that needs to be copy-pasted 5 times for different case (the different libraries used). Is it possible to create a function in a makefile and just call that function instead of copy-pasting?

This is an example of what I've tried. Basically this tries to find the right path for an installed library.

define Flags_template
$(1)_include_home      := $(HOME)/usr/include
$(1)_include_home_name := $(HOME)/usr/include/$(1)

ifneq ($$(wildcard $($(1)_include_home)/$(2)),)
    $(1)_Include := $$($(1)_include_home)
else
    ifneq ($$(wildcard $($(1)_include_home_name)/$(2)),)
        $(1)_Include := $$($(1)_include_home_name)
    endif
endif

CFLAGS += -I$$($(1)_Include)

endef

$(eval $(call Flags_template,stdcout,StdCout.hpp))

.PHONY: test
test:
    # stdcout_include_home_name = $(stdcout_include_home_name)
    # stdcout_Include = $(stdcout_Include)
    # CFLAGS: $(CFLAGS)

Typing "make", I get this output:

# stdcout_include_home_name = /home/nicolas/usr/include/stdcout
# stdcout_Include = 
# CFLAGS: -I

It's so close. But note the last "-I", I always get dupplicates, one fully expended, one empty...

I don't understant what needs to be eval'ed, escaped with two $, etc.

How can I achieve this?

Thank you very much.

Upvotes: 0

Views: 1887

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 753585

Does §8.8 of the GNU Make (3.82) manual help?

[...] Although it might seem overly complex to use eval in this example, rather than just writing out the rules, consider two things: first, the template definition (in PROGRAM_template) could need to be much more complex than it is here; and second, you might put the complex, “generic” part of this example into another makefile, then include it in all the individual makefiles. Now your individual makefiles are quite straightforward.

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)

This Works (For Me)

This is the output from the GNU makefile just below:

stdcout_include_home = /work4/jleffler/usr/include
stdcout_include_home_name = /work4/jleffler/usr/include/stdcout
stdcout_Include = /work4/jleffler/usr/include
CFLAGS: -I/work4/jleffler/include -I/work4/jleffler/usr/include

GNU Makefile

CFLAGS = -I${HOME}/include

define Flags_template
$(1)_include_home      := $(HOME)/usr/include
$(1)_include_home_name := $(HOME)/usr/include/$(1)

ifneq ($$(wildcard $$($(1)_include_home)/$(2)),)
    $(1)_Include := $$($(1)_include_home)
else
    ifneq ($$(wildcard $$($(1)_include_home_name)/$(2)),)
        $(1)_Include := $$($(1)_include_home_name)
    else
        $(1)_Include := Neither $$($(1)_include_home) nor $$($(1)_include_home_name) contains $2
    endif
endif

CFLAGS += -I$$($(1)_Include)

endef

$(eval $(call Flags_template,stdcout,StdCout.hpp))

.PHONY: test
test:
        @echo stdcout_include_home = $(stdcout_include_home)
        @echo stdcout_include_home_name = $(stdcout_include_home_name)
        @echo stdcout_Include = $(stdcout_Include)
        @echo CFLAGS: $(CFLAGS)

The difference is in the wildcard invocations:

ifneq ($$(wildcard  $($(1)_include_home)/$(2)),)    # Fails
ifneq ($$(wildcard $$($(1)_include_home)/$(2)),)    # Works

I have half an intuition about when double-dollars and when single-dollars are needed; I am not sure I can articulate the decision, though.

Upvotes: 1

Related Questions