Don Weingarden
Don Weingarden

Reputation: 31

How to include GNU make auto dependecies with custom build steps

I'm trying to create a Makefile with multiple sources in multiple directories. I'm very close but the dependencies aren't working correctly. The .d files are being created correctly. It correctly lists sample.o: sample.c options.h. If I change sample.c, it re-compiles and links. If I change options.h, it says everything's up to date. I believe the problem is that the explicit rule to compile .c to .o is overriding the rule from the .d file. The question is how do I include a dependency and define my own rule to build it?

sample.c:

#include "options.h"
int main(int argc, char** argv) {return(0);}

options.h:

#define SAMPLE 1 

directory structure

     ---------- dir1
                options.h      
     ---------- dir2
                sample.c
     ---------- output
                sample.o
                sample.d

Makefile:

TARGET=output/sample
CC=gcc
CFLAGS=-Wall -O2 -g  
LDFLAGS=-g
LIBS+=-lm
PATHS=-Idir1 -Idir1/dir2
CORE_SRC += sample.c  
SRCS+= $(addprefix dir1/dir2/, $(CORE_SRC))  
OBJS:= $(addprefix output/, $(SRCS:.c=.o))
DEPS:= $(OBJS:.o=.d)

.PHONY: all
all: $(TARGET)

$(TARGET) : $(OBJS) $(DEPS)
    @echo "Linking ..."
    $(CC) $(LDFLAGS) -o $@  $(OBJS) 

output/%.d: %.c 
    @echo "generating dependency  $@"
    @mkdir -p output/$(dir $*.d)
    $(CC) -MM $(CFLAGS) $(PATHS) $^ > output/$*.d
    @mv -f output/$*.d output/$*.d.tmp
    @sed -e 's|.*:|$*.o:|' < output/$*.d.tmp > output/$*.d
    @sed -e 's/.*://' -e 's/\\$$//' < output/$*.d.tmp | fmt -1 | \
      sed -e 's/^ *//' -e 's/$$/:/' >> output/$*.d
    @rm -f output/$*.d.tmp

output/%.o: %.c output/%.d
    @echo "Compiling $@ from $*.c"
    @mkdir -p output/$(dir $*.o)
    $(CC) -c $(CFLAGS) $(PATHS) $*.c -o output/$*.o

-include $(DEPS)

Upvotes: 0

Views: 83

Answers (1)

Beta
Beta

Reputation: 99134

The problem appears to be that your sample.d has a rule for sample.o:

sample.o: sample.c options.h

but that rule doesn't actually help, since the target you try to build is not sample.o but output/dir1/dir2/sample.o.

You could tinker with your output/%.d rule, but I think it's much easier to simplify the whole scheme. Eliminate the output/%.d rule and modify these two:

$(TARGET) : $(OBJS)
    @echo "Linking ..."
    $(CC) $(LDFLAGS) -o $@ $^

output/%.o: %.c
    @echo "Compiling $@ from $*.c"
    @mkdir -p output/$(dir $*.o)
    $(CC) -c $(CFLAGS) $(PATHS) -MMD $< -o $@

This way, Make will build sample.d as a side-effect of building sample.o, which is all you really need.

Upvotes: 1

Related Questions