Mike
Mike

Reputation: 49403

recursive makefile not building

I have a bunch of C files in different directories and I'm getting a make: nothing to be done for 'all' error with my recursive Makefile; however if I tweak the dependences I can get it to work... but I don't understand why I have to.

Here's my original Makefile:

APP_DIRS=rescoco ressys resvm

.PHONY: all

all: $(APP_DIRS)

$(APP_DIRS):
    $(MAKE) --directory $@

clean:
    $(RM) *~

Now if I change my line: .PHONY to .PHONY: all $(APP_DIRS) it builds fine.

Another possibility is if I change the line: $(APP_DIRS): to $(APP_DIRS): clean it builds fine.

(NOTE: removing .PHONY target doesn't change anything)


So what's going on here? Is the Makefile trying to tell me I haven't listed dependencies correctly? I was thinking make would do something like:

Clearly I am wrong; but why?


FYI, if it matters my files are structured something like this:

Makefile       #top level makefile as seen above
/rescoco
    rescoco.c
    Makefile   #builds rescoco src and moves archive to ../lib directory
/ressys
    ressys.c
    Makefile   #same as above but for ressys
/resvm
    resvm.c
    Makefile   #same as above but for resvm
/lib

and my build command is simply make. When I run with make -n or make -n all I get no output at all:

:~/proj$ make -n all
make: Nothing to be done for 'all'.
:~/proj$ 

Upvotes: 0

Views: 1718

Answers (3)

Tuxdude
Tuxdude

Reputation: 49473

Things first you should be aware of:

  • If you have directories as dependencies, make is going to consider building the targets (i.e. executing the recipes for such directory targets), only if the modification timestamp of the directory gets updated. This would happen only when you add a new file in the directory but not for file modifications in the directory. Adding files in a sub-directory does not change the timestamp of the directory.
  • PHONY targets are meant to be used when executing such a target does not create a file with the name of the target. In other words, you want make to execute the rule irrespective of whether the file already exists or not.

So your Makefile esentially only tells this:

  • To build the target all, I need to build $(APP_DIRS). Since all is a PHONY target, I will always execute the recipe for all.
  • $(APP_DIRS) is not a PHONY target and does not have any dependencies. So *only if $(APP_DIRS) does not exist already (i.e. the file or directory), I'm going to execute the recipe, otherwise I'm doing nothing for this target.
  • clean has no pre-requisite and not a PHONY, so I expect to execute this rule only when explicitly invoked by make (from the command line or another Makefile). Also clean is not a PHONY, so I expect the recipe to create a file called clean after execution (which is incorrect for your case)

Hence changing the .PHONY line to:

.PHONY: all $(APP_DIRS)

makes the Makefile go and execute the recipe for $(APP_DIRS) always.

So if you would like make to always traverse into all of the $(APP_DIRS) directories and invoke make again on them, you need to add $(APP_DIRS) to .PHONY, which makes $(APP_DIRS) a PHONY target, and executes the recipe irrespective of the file's/directory's timestamp if it exists.

For your particular use-case, I think this is the Makefile you should be using:

APP_DIRS=rescoco ressys resvm

.PHONY: all clean $(APP_DIRS)

all: $(APP_DIRS)

$(APP_DIRS):
    $(MAKE) --directory $@

clean:
    $(RM) *~

BONUS:

  • Changing $(APP_DIRS): to $(APP_DIRS): clean implies that $(APP_DIRS) depends on the clean target.
  • Although clean is not marked a PHONY, make does not see a file named clean in the current directory. So it goes ahead and tries to execute the recipe for clean.
  • Since a dependency of $(APP_DIRS) (i.e. clean) was built, this makes the Makefile execute the recipe for building $(APP_DIRS).

This brings us to an interesting observation: - Any target that depends on a PHONY target will always get rebuilt (i.e. the recipe would be executed).

Take this simple Makefile:

all: target1

target1: target2
    @echo "$@"
    @touch $@

target2: target3
    @echo "$@"
    @touch $@

target3:
    @echo "$@"

.PHONY: all target3

The first time I run make, I see this output:

target3
target2
target1

After this, files target1 and target2 are created. Even then, if I run make again, I would see the output:

target3
target2
target1

As you can see, the PHONY dependencies get propagated up and not the other way down. target2 gets rebuilt just because target3 is a PHONY, and target1 gets rebuilt just because target2 got rebuilt.

Upvotes: 2

jocke-l
jocke-l

Reputation: 703

This is how you can do it, just remove the wildcard if you don't want it.
make wildcard subdirectory targets

Upvotes: 0

75inchpianist
75inchpianist

Reputation: 4102

You are defining a variable named 'APP_DIRS' with a list of directories. That is fine.

You then do

$(APP_DIRS): make blah blah, which is essentially equivalent to rescoco ressys resvm: make blah blah

which obviously isnt valid.

So you need to pretend your $(APP_DIRS) is a variable, not a target name, which seems to be what you're using it as.

having said that, think why .PHONY: all $(APP_DIRS) works

Upvotes: 0

Related Questions