Reputation: 159
I've recently picked up makefiles and am trying to automate my build process. For this makefile, I want it to find every xxx/src/xxx.c source file and build an equivalent xxx/obj/xxx.o object for each. So every obj folder mirrors the layout of a src folder.
This is working as intended but only if I clean and make. Modifying a source file and running make won't rebuild that file. I think it might have to do with my subst in the dependecy of %.o, but I don't know how to modify that and still have my automated build layout work.
CFLAGS := -std=c11 -pedantic -Wall -Wextra -O3
LIBARIES := -lm -lglut -lGL
INCDIR := include ../plib/include
SRCDIR := src ../plib/src
INC := $(foreach d, $(INCDIR),-I$d)
SRC := $(wildcard $(foreach d, $(SRCDIR),$d/*.c $d/*/*.c))
OBJ := $(subst src/,obj/, $(SRC:.c=.o))
EXE := bin/test
$(EXE): $(OBJ)
gcc -o $@ $(OBJ) $(LIBARIES)
$@
%.o: $(subst obj/,src/,$(%.c))
@mkdir -p $(@D)
gcc -o $@ -c $(subst obj/,src/,$(@:.o=.c)) $(CFLAGS) $(INC)
.PHONY: clean
clean:
rm $(EXE)
rm $(OBJ)
Upvotes: 3
Views: 884
Reputation: 6984
You can solve such a %/xxxx/%
pattern replacement by iterating over the SRCDIR
:
define genrule
_prefix := $$(subst src,obj,$1/)
$$(filter $${_prefix}%.o,$$(OBJ)):\
$${_prefix}%.o: $1/%.c
endef
$(foreach d,${SRCDIR},$(eval $(call genrule,$d)))
${OBJ}:
gcc ... -c $< -p $@
Upvotes: 3
Reputation: 16540
The posted makefile is rather 'iffy' for several different reasons
The following proposed makefile is VERY EASILY modified for other projects BUT does place the object files in the same directory as the source files. You might want to 'tweak' that feature
And now, the proposed makefile
SHELL := /bin/sh
CC := /usr/bin/gcc
RM := /usr/bin/rm
MAKE := /usr/bin/make
CFLAGS := -std=c11 -pedantic -Wall -Wextra -O3
LIBS := -lm -lglut -lGL
INC := -Iinclude/ -I../plib/
SRC := $(wildcard src/*.c) $(wildcard ../plib/src/*.c)
OBJ := $(SRC:.c=.o))
DEP := $(SRC:.c=.d)
EXE := bin/test
.PHONY: all clean
all: $(EXE)
$(EXE): $(OBJ)
#
# ======= $(EXE) Link Start =========
$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS)
# ======= $(EXE) Link Done ==========
#
#
# create dependancy files
#
%.d: %.c
#
# ========= START $< TO $@ =========
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
(RM) -f $@.$$$$
# ========= END $< TO $@ =========
#
# compile the .c files into .o files using the compiler flags
#
%.o: %.c %.d
#
# ========= START $< TO $@ =========
$(CC) $(CFLAGS) -c $< -o $@ $(INC)
# ========= END $< TO $@ =========
#
clean:
# ========== start clean activities ==========
rm -f *.o
rm -f $(EXE)
rm -f *.d
# ========== end clean activities ==========
# include the contents of all the .d files
# note: the .d files contain:
# <filename>.o:<filename>.c plus all the dependancies for that file
# I.E. the #include'd header files
# wrap with ifneg... so will not rebuild *.d files when goal is 'clean'
#
ifneq "$(MAKECMDGOALS)" "clean"
-include $(DEP)
endif
Upvotes: 1
Reputation: 99094
You can do it with secondary expansion. It's not elegant, but it works:
.SECONDEXPANSION:
%.o: $$(addsuffix .c,$$(basename $$(subst /obj/,/src/,$$@)))
@echo building $@ from $^
@mkdir -p $(@D)
gcc -o $@ -c $< $(CFLAGS) $(INC)
Upvotes: 1