2

I'm updating the title and content here to make it clear that this particular question was asking something that I didn't see answered plainly elsewhere. The key notion is understanding that something that looks like a single target doing multiple things in a Makefile is actually multiple targets doing one thing each.

I will also remove some extraneous material since that ended up not being relevant.

Original Content

My problem is that I have a Makefile that is (apparently) not calling one of my sub-directory Makefiles correctly. I have a project structure like this:

quendor
  src
    cheap
      cheap_init.c
      Makefile

    zmachine
      main.c
      Makefile

  Makefile  

The Makefile in the project root will refer to the Makefiles in the individual directories. Here is that core Makefile:

CC ?= gcc

CFLAGS += -Wall -std=c99
CFLAGS += -D_POSIX_C_SOURCE=200809L
CFLAGS += -O2 -fomit-frame-pointer

RANLIB ?= $(shell which ranlib)
AR ?= $(shell which ar)

export CC
export AR
export CFLAGS
export RANLIB

SRC_DIR = src

ZMACHINE_DIR = $(SRC_DIR)/zmachine
ZMACHINE_LIB = $(ZMACHINE_DIR)/quendor_zmachine.a

CHEAP_DIR = $(SRC_DIR)/cheap
CHEAP_LIB = $(CHEAP_DIR)/quendor_cheap.a

SUB_DIRS = $(ZMACHINE_DIR) $(CHEAP_DIR)

SUB_CLEAN = $(SUB_DIRS:%=%-clean)

$(SUB_DIRS):
    @echo $(SUB_DIRS)   # src/zmachine src/cheap
    @echo "DIR:"
    @echo $@            # src/zmachine
    $(MAKE) -C $@

$(SUB_CLEAN):
    -$(MAKE) -C $(@:%-clean=%) clean

clean: $(SUB_CLEAN)

help:
    @echo "Quendor"

.PHONY: $(SUB_DIRS) $(SUB_CLEAN) clean help

A key problem for me is this bit from the above:

$(SUB_DIRS):
    @echo $(SUB_DIRS)   # src/zmachine src/cheap
    @echo "DIR:"
    @echo $@            # src/zmachine
    $(MAKE) -C $@

I put the echo statements in just to show what's happening. Notice the $SUB_DIRS is correctly showing both directories, but when the Makefile runs it only shows src/zmachine. (The comments there indicate what I see during runtime.) The Makefile (apparently) doesn't process src/cheap.

The full output of the Makefile running is this (the first three lines there being my echo statements):

src/zmachine src/cheap
DIR:
src/zmachine
/Applications/Xcode.app/Contents/Developer/usr/bin/make -C src/zmachine
cc -Wall -std=c99 -D_POSIX_C_SOURCE=200809L -O2 -fomit-frame-pointer -fPIC -fpic -o main.o -c main.c
ar rc quendor_zmachine.a main.o
/usr/bin/ranlib quendor_zmachine.a
** Done with Quendor Z-Machine.

The only thing I could think of initially was that perhaps after running the sub-makefile in src/zmachine, the Make process was either erroring out or thinking it was done. But the $(SUB_DIRS) part should have iterated through both directories, I would have thought.

So I'm a bit stuck as to how to proceed.

Extra Note: The "I would have thought" part of what I said was where I was incorrect. $(SUB_DIRS) was not being executed as I thought it was; the accepted answer has clarified this.

mlp
  • 809
  • 7
  • 21
Jeff Nyman
  • 870
  • 2
  • 12
  • 31
  • 1
    Add `all: $(SUB_DIRS)` above `$(SUB_DIRS):`, make will only process the first target it encounters in a makefile if none are specified on the command line. – user657267 Jan 19 '19 at 15:23
  • Possible duplicate of [How does "make" app know default target to build if no target is specified?](https://stackoverflow.com/questions/2057689/how-does-make-app-know-default-target-to-build-if-no-target-is-specified) – user657267 Jan 19 '19 at 15:24
  • Interesting. I will try. But then why does the one sub-directory get processed at all? In my case, I guess this means that `$(SUB_DIRS):` is being processed as the first target. But wouldn't that still force it to go through both directories, not just one? Obviously not, but I guess I don't know why. – Jeff Nyman Jan 19 '19 at 15:26
  • That does work indeed. Again, it's not clear _why_ this was the case. I had targets and the first target that would have been found was my `$(SUB_DIRS):` But apparently that wasn't enough to iterate. So for someone new coming to Make, I'm not sure the first instinct would have been to think about asking for a default target. I'm not sure if that makes an argument for this being a duplicate or not. – Jeff Nyman Jan 19 '19 at 15:36
  • I'd recommend to use non-recursive approach to writing `makefile`s and use https://github.com/igagis/prorab for easily doing that – igagis Jan 20 '19 at 13:51

1 Answers1

3

The way make works is, if you don't provide an argument, it will start by scanning the Makefile looking for the "default goal". The default goal is simply the first target it encounters (notice it's the first target, not targets).

In your case, the rule:

$(SUB_DIRS):
    $(MAKE) -C $@

Is equivalent to:

src/zmachine src/cheap:
    $(MAKE) -C $@

Which is equivalent to:

src/zmachine:
    $(MAKE) -C $@
src/cheap:
    $(MAKE) -C $@

So the first target make encounters is src/zmachine, and that's its default goal and the one that gets processed. The way to fix this is, as user657267 said in the comments, to add one target that you know will be processed first that would have the other targets (that you really want to build) as its prerequisites.

mnistic
  • 10,866
  • 2
  • 19
  • 33
  • Perfect. That clarification of first target, as opposed to targets is exactly the part I was missing. Thank you! – Jeff Nyman Jan 19 '19 at 16:31