views:

237

answers:

2

I have a question regarding the GNU makefile example below:

.PHONY: $(subdirs) build x y

subdirs =  a b c

build: x y

x: target=prepare
x: $(subdirs)

y: target=build
y: $(subdirs)

$(subdirs):
    $(make) -f $@/makefile $(target)

When I run make, I expect make to be called for each sub-directory specifying the target 'prepare' then the target 'build'. Unfortunately, the $(subdirs) target executes once (making the 'prepare' target), but doesn't execute again for the 'build' target.

It appears that for rule x, make determines the $(subdirs) target needs to be run, but for rule y, the $(subdirs) target is up-to-date.

Is there any way to fix this?

Thanks!

+2  A: 

The makefile you posted seems to assume that the commands for the $(subdirs) target would be run two times: once for x and a second time for y. Unfortunately, that's not how makefiles and dependencies work. The commands for a target are run at most once per make invocation (barring unusual circumstances, such as when makefiles are modified during the make run).

The following will work on UNIX -style systems. It simply runs the subdirectory makes in a loop, one after each other:

subdirs = a b c

.PHONY: build
build:
    for dir in $(subdirs); do \
      $(MAKE) -f $$dir/makefile prepare; \
      $(MAKE) -f $$dir/makefile build; \
    done

If you need to build the subdirectories in parallel, you can use the following:

subdirs = a b c

.PHONY: build
build: $(addprefix build-,$(subdirs))

define submake-rule
  .PHONY: build-$(1)
  build-$(1):
    $(MAKE) -f $(1)/makefile prepare
    $(MAKE) -f $(1)/makefile build
endef

$(foreach dir,$(subdirs),$(eval $(call submake-rule,$(dir))))

This defines a rule build-<dirname> for each item in $(subdirs), and makes the build target depend on all of them. As an added bonus, this is portable to Windows as well, without requiring using a Cygwin bash shell or similar. The downside is that it's a bit harder to understand.

Ville Laurikari
+1  A: 

I prefer Ville's method over your attempt.
There are generally two ways to get a multiple run with varying arguments.

  1. Write an outer script (or, like Ville's idea iterate within the outer Makefile)
  2. Write an all rule in each sub-Makefile to run prepare followed with build
    • this will integrate the prepare+build combination within the sub-Makefiles and,
      you would be just running an all from the outer Makefile -- much more modular.

This is one example where you can easily land up with an unnecessarily complex Makefile

nik
+1, definitely a good idea to make the prepare+build logic local to each subdirectory.
Ville Laurikari