Preliminaries
Generating dependencies
gcc's -MM -MG can be used to create the dependencies: gcc -MM -MG src/hellofunc.c -I./include
will create hellofunc.o: src/hellofunc.c include/hellomake.h
. However, we need to include the .d file itself into the .d file, so we use sed
to achieve that goal.
gcc -MM -MG src/hellofunc.c -I./include | sed -e 's@^\(.*\)\.o:@\1.d \1.o:@'
The results will be as follows:
hellofunc.d hellofunc.o: src/hellofunc.c include/hellomake.h
When we want to change the content to include the different directory location, we can modify the sed script.
gcc -MM -MG src/hellofunc.c -I./include | sed -e 's@^\(.*\)\.o:@obj/\1.d obj/\1.o:@'
The results will be as follows:
obj/hellofunc.d obj/hellofunc.o: src/hellofunc.c include/hellomake.h
include
-include $(DEPS)
will include the files in DEPS
directory, the -
in front of the include teaches the make to ignore when the directories does not exits.
Pattern matching and replacement
We can substitute any pattern (%) or pattern that ends with c (%.c) as follows:
OBJDIRS := $(patsubst %, $(OBJDIR)/%, $(MODULES))
OBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(SRCS))
We also can have shorthand form.
DEPS := $(OBJS:.o=.d)
filter
We can select only some of the files with filter, this is an example:
OBJ := $(patsubst %.c, %.o, $(filter %.c, $(SRC)))
Method 1
In this method, we specify the dependency (.d depends on .c), and include the created dependency file with include
.
.PHONY: clean depend
CC := gcc
CXX := g++
LD := g++
CP := cp
PROG := hellomake
MODULES := src
OBJDIR := obj
default: $(PROG)
OPTFLAGS := -g -O
CFLAGS += -Wall -Wno-unused-function $(OPTFLAGS) $(patsubst %, -I%, $(MODULES))
GARBAGE := core core.* *.stackdump ./tags $(PROG)
include $(patsubst %, %/module.make, $(MODULES))
OBJ := \
$(patsubst %.c, %.o, $(filter %.c, $(SRC)))
DEP := $(OBJ:.o=.d)
# implicit rules
%.d: %.c
./depends.sh $(CC) `dirname $*.c` $(CFLAGS) $*.c > $@
-include $(DEP)
# Actual targets
depend: $(DEP)
clean:
rm -rf $(PROG) $(OBJ) $(GARBAGE) $(DEP) depends
$(PROG): $(OBJ)
$(LD) -o $@ $^ $(LIBS)
Each module should contain the make file (module.make) to specify what files are included in the SRC variable.
`SRC += src/hellofunc.c \
src/hellomake.c`.
This is the script to generate the dependency file:
#!/bin/sh
#echo "## Got: $*"
CC="$1"
DIR="$2"
shift 2
case "$DIR" in
"" | ".")
$CC -MM -MG "$@" | sed -e 's@^\(.*\)\.o:@\1.d \1.o:@'
;;
*)
$CC -MM -MG "$@" | sed -e "s@^\(.*\)\.o:@$DIR/\1.d $DIR/\1.o:@"
;;
esac
This method is simple, but the object files and dependency files are created in the same src directory with these statements.
SRC += src/hellofunc.c src/hellomake.c
OBJ := $(patsubst %.c, %.o, $(filter %.c, $(SRC)))
DEP := $(OBJ:.o=.d)
Method 2
This method creates the object directory, and puts all the generated (intermediate) files into the directory.
It enlists all the modules to specify the actions both for object file and dependency file generation.
In the example, we have only one module, but with multiple modules, we need to duplicate the statements.
obj/src/%.o: src/%.c
$(CC) -c $< -o $@ $(CFLAGS)
obj/src/%.d: src/%.c
gcc -MM -MG $< $(CFLAGS) | sed -e 's@^\(.*\)\.o:@obj/src/\1.d obj/src/\1.o:@' > $@
This is the makefile:
.SUFFIX = .o .c
.PHONY: clean
CC := gcc
LD := gcc
PROG := hellomake
OBJDIR = obj
MODULES := src
SRCS := src/hellofunc.c src/hellomake.c
OBJDIRS := $(patsubst %, $(OBJDIR)/%, $(MODULES))
OBJS := $(patsubst %.c, $(OBJDIR)/%.o, $(SRCS))
DEPS := $(OBJS:.o=.d)
default: $(PROG)
CFLAGS += -Wall -Wno-unused-function $(OPTFLAGS) $(patsubst %, -I%, $(MODULES))
CXXFLAGS += $(CFLAGS)
obj/src/%.o: src/%.c
$(CC) -c $< -o $@ $(CFLAGS)
$(PROG): $(OBJDIRS) $(OBJS)
$(LD) $(filter %.o, $^) -o $(PROG)
-include $(DEPS)
obj/src/%.d: src/%.c
gcc -MM -MG $< $(CFLAGS) | sed -e 's@^\(.*\)\.o:@obj/src/\1.d obj/src/\1.o:@' > $@
depend: $(DEPS)
GARBAGE := core core.* *.stackdump ./tags $(PROG)
clean:
rm -rf $(PROG) obj/*
$(OBJDIRS):
mkdir -p $@
References