ThomasMcLeod
ThomasMcLeod

Reputation: 7779

Gnu make: is it possible to delay include directive to secondary expansion?

I need to delay inclusion of dependency fragments until second expansion time because the make file I'm editing is itself an include file and I will not have a list of source files to generate the includes until secondary expansion.

.SECONDEXPANSION:
AUTO_DEPENDENCY_FILES = $(patsubst %.cc, depend/%.d, $(CC_SRC_FILES))

# the following does the work because the include argument is not a rule
# prerequisite therefore no secondary expansion occurs
include $$(AUTO_DEPENDENCY_FILES)

depend:
    -mkdir depend

all: autodepend

autodepend: depend autodepend_include

autodepend_include: $$(AUTO_DEPENDENCY_FILES)
    @echo \"$^\"

$$(AUTO_DEPENDENCY_FILES): depend

depend/%.d: | %.cc
    # generate .d files that do not exist
    $(COMPILE.cc) -E $*.cc > /dev/null

%.o: %.cc
    # update .d files that exist
    $(COMPILE.cc) -o $@ $<

Note the COMPILE.cc is a very long string that includes -MP -MMD -MFdepend/$*.d flags for auto dependency generation.

Upvotes: 1

Views: 1669

Answers (1)

Etan Reisner
Etan Reisner

Reputation: 81052

I don't know that there's a clean solution to this problem but with a bit of a hack you can get you what you want.

Given a main Makefile of:

$(info main one)

include depend.mk

$(info main two)

CC_SRC_FILES := $(addsuffix .c,a b c d e f)

$(info main three)

and a depend.mk of:

$(info depend one)

AUTO_DEPENDENCY_FILES = $(patsubst %.c,%.d,$(CC_SRC_FILES))
$(info AUTO_DEPENDENCY_FILES := $(AUTO_DEPENDENCY_FILES))
$(info MAKE_RESTARTS := $(MAKE_RESTARTS))
$(info CC_SRC_FILES := $(CC_SRC_FILES))

$(info depend two)

you get the following output when you run make:

main one
depend one
AUTO_DEPENDENCY_FILES :=
MAKE_RESTARTS :=
CC_SRC_FILES :=
depend two
main two
main three
make: `all' is up to date.

Which isn't surprising given the order of assignment and inclusion of files, etc.

Here's where the horrible hack comes in.

When make encounters an include directive that references a file that doesn't exist make sticks the file in a list of "missing include files" and continues parsing the makefile.

When it gets to the end of the makefile(s) it then tries to treat each entry in that list as a potential goal target1 and attempts to make the file.

Once the makefiles have been built make restarts itself and tries again.

You can use this to capture the value of CC_SRC_FILES in an built makefile that your makefile includes and have it be visible when you need it.

If we make depend.mk look like this:

$(info depend one)

include hack.mk

AUTO_DEPENDENCY_FILES = $(patsubst %.c,%.d,$(CC_SRC_FILES))
$(info AUTO_DEPENDENCY_FILES := $(AUTO_DEPENDENCY_FILES))
$(info MAKE_RESTARTS := $(MAKE_RESTARTS))
$(info CC_SRC_FILES := $(CC_SRC_FILES))

$(info depend two)

hack.mk: $(if $(MAKE_RESTARTS),,force)
        @echo creating hack.mk
        @echo 'CC_SRC_FILES := $(CC_SRC_FILES)' > '$@'

force: ;

Then our output from make becomes:

main one
depend one
depend.mk:3: hack.mk: No such file or directory
AUTO_DEPENDENCY_FILES :=
MAKE_RESTARTS :=
CC_SRC_FILES :=
depend two
main two
main three
creating hack.mk
main one
depend one
AUTO_DEPENDENCY_FILES := a.d b.d c.d d.d e.d f.d
MAKE_RESTARTS := 1
CC_SRC_FILES := a.c b.c c.c d.c e.c f.c
depend two
main two
main three
make: `all' is up to date.

Which gives us the value where we want it.

This isn't pretty but it does work.

Upvotes: 3

Related Questions