Reputation: 69
Need help in writing a Makefile for below source tree. I have tried out simple examples for Makefile and those worked fine. But not able to figure out how do I write a Makefile for below kind of source tree.
I am organizing my code as below
root_dir:
Makefile
component1:
dir1:
file1.c file1.h
dir2:
file2.c file2.h
dir3:
file3.c file3.h
component2:
dir4:
file4.c file4.h
dir5:
file5.c file5.h
common:
debug.c debug.h
utils.c utils.h
main.c main.h
Here, main.c uses some functions declared in debug.h, utils.h, file1.h and file4.h. These file1.c and file4.c use the debug.h and utils.h. I have started writing the Makefile rules as below.
CC=gcc
CFlags=-c -g3 -Wall
myexec: main.o
$(CC) main.o -o myexec
main.o: common/main.c common/main.h common/utils.h
$(CC) $(CFLAGS) common/main.c
This Makefile gives "undefined reference" error messages for the functions declared in utils.h. It can crib about the functions declared in debug.h, file1.h or file4.h, but shouldn't have given the error message for functions from utils.h
Please help me in finding out what is wrong with the Makefile here.
Upvotes: 0
Views: 590
Reputation: 99154
First let's fix the main.o
rule:
main.o: common/main.c common/main.h common/utils.h
$(CC) $(CFLAGS) common/main.c
If, as you say main.c uses some functions declared in debug.h, utils.h, file1.h and file4.h
, then those headers should be prerequisites of the rule (so that if you modify a header, Make will rebuild main.o
):
main.o: common/main.c common/main.h common/debug.h common/utils.h component1/dir1/file1.h component2/dir4/file4.h
$(CC) $(CFLAGS) common/main.c
Now look at the myexec
rule:
myexec: main.o
$(CC) main.o -o myexec
It's no surprise that you get "undefined reference" errors; the functions declared in debug.h
, utils.h
, file1.h
and file4.h
are defined in (I presume) debug.c
, utils.c
, file1.c
and file4.c
, which this makefile never mentions. Do not proceed until you understand this.
The way to handle this is tho have the myexec
rule link all of the relevant object files:
myexec: main.o debug.o utils.o file1.o file4.o
$(CC) main.o debug.o utils.o file1.o file4.o -o myexec
Naturally you must have rules for debug.o
, utils.o
, file1.o
and file4.o
, similar to the one for main.o
.
Once that is done, you have a makefile that works but is needlessly long and redundant. "Effective but crude". It can be made much shorter and more elegant, but this answer is getting long; just get it working, then we can work on making it cleaner.
Upvotes: 2
Reputation: 16540
here is a long example: it is for a linux embedded system, but will work with only minor changes for your application
These two makefiles will create an executable in each sub directory
it has a directory tree something like the following:
top
utility .h and .c files
makefile.top
makefile.bot
/executable1
exec1 .h and .c files
/executable2
exec2 .h and .c files
contents of makefile.mak
SHELL = /bin/sh
# note: this makefile.mak needs to be run from the ./src directory
# of the GOT4 directory tree
SRC := $(wildcard *.c)
OBJ := $(SRC:.c=.o)
DEP := $(SRC:.c=.d)
INC := $(SRC:.c=.h)
MAKE := /usr/bin/make
CC := /usr/bin/gcc
CP := cp
MV := mv
LDFLAGS := -L/usr/local/lib -L/usr/lib -L/lib
DEBUG := -ggdb3
CCFLAGS := $(DEBUG) -Wall -Wextra -pedantic
#CPPFLAGS += =MD
LIBS := -lssl -ldl -lrt -lz -lc -lm
.PHONY: AllDirectories
AllDirectories := \
executable1 \
executable2
.PHONY: all
all: $(OBJ) $(AllDirectories)
$(foreach d,$(AllDirectories), \
( cd $d && $(MAKE) -f ../makefile.bot name=Tsk_$d all ); )
#
# create dependancy files
#
%.d: %.c
#
# ========= START $< TO $@ =========
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
# ========= END $< TO $@ =========
#
# compile the .c file into .o files using the compiler flags
#
%.o: %.c %.d
#
# ========= START $< TO $@ =========
$(CC) $(CCFLAGS) -c $< -o $@ -I.
# ========= END $< TO $@ =========
#
.PHONY: clean
#clean: $(AllDirectories)
# # ========== start clean activities ==========
# rm -f *.o
# rm -f $(name).map
# rm -f $(name)
# rm -f *.d
# $(foreach d,$(AllDirectories), \
# ( cd $d && $(MAKE) -f makefile.mak clean ); )
# # ========== end clean activities ==========
clean: $(AllDirectories)
# ========== start clean activities ==========
rm -f *.o
rm -f $(name).map
rm -f $(name)
rm -f *.d
rm -f ../bin/Tsk_*
$(foreach d,$(AllDirectories), \
( cd $d && $(MAKE) -f ../makefile.bot name=Tsk_$d clean ); )
# ========== end clean activities ==========
.PHONY: install
#install: $(AllDirectories)
# # ========== start install activities ==========
# $(foreach d,$(AllDirectories), \
# ( cd $d && $(MAKE) -f makefile.mak clean ); )
# # ========== end install activities ==========
install: $(AllDirectories)
# ========== start install activities ==========
$(foreach d,$(AllDirectories), \
( cd $d && $(MAKE) -f ../makefile.bot name=Tsk_$d install ); )
# ========== end install 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
and the makefile.bot
SHELL = /bin/sh
BINDIR := /home/user/bin
.PHONY: all
all : $(BINDIR)/$(name) ../makefile.mak ../makefile.bot
#
# macro of all *.c files
# (NOTE:
# (the following 'wildcard' will pick up ALL .c files
# (like FileHeader.c and FunctionHeader.c
# (which should not be part of the build
# (so be sure no unwanted .c files in directory
# (or change the extension
#
SRC := $(wildcard *.c)
OBJ := $(SRC:.c=.o)
DEP := $(SRC:.c=.d)
INC := $(SRC:.c=.h)
COMMON_OBJ := $(wildcard ../*.o)
#COMMON_SRC := $(wildcard ../*.c)
#COMMON_OBJ := $(COMMON_SRC:.c=.o)
#COMMON_DEP := $(COMMON_SRC:.c=.d)
#COMMON_INC := $(COMMON_SRC:.c=.h)
MAKE := /usr/bin/make
CC := /usr/bin/gcc
CP := cp
MV := mv
LDFLAGS := -L/usr/local/lib
DEBUG := -ggdb3
CCFLAGS := $(DEBUG) -Wall -Wextra -pedantic -std=c99
#CPPFLAGS += =MD
LIBS := -lssl -ldl -lrt -lz -lc -lm
#
# link the .o files into the executable
# using the linker flags
# -- explicit rule
#
$(name): $(OBJ) $(COMMON_OBJ) ../makefile.mak ../makefile.bot
#
# ======= $(name) Link Start =========
$(CC) $(LDFLAGS) -o $@ $(OBJ) $(COMMON_OBJ) $(LIBS)
# ======= $(name) Link Done ==========
#
# note:
# using MV rather than CP results in all executables being re-made everytime
$(BINDIR)/$(name): $(name)
#
# ======= $(name) Copy Start =========
sudo $(CP) $(name) $(BINDIR)/.
# ======= $(name) Copy Done ==========
#
#
#create dependancy files -- inference rule
# list makefile.mak as dependancy so changing makfile forces rebuild
#
%.d: %.c
#
# ========= START $< TO $@ =========
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
# ========= END $< TO $@ =========
#
# compile the .c file into .o files using the compiler flags
# -- inference rule
#
%.o: %.c %.d
#
# ========= START $< TO $@ =========
$(CC) $(CCFLAGS) -c $< -o $@ -I.
# ========= END $< TO $@ =========
#
.PHONY: clean
clean:
# ========== CLEANING UP ==========
rm -f *.o
rm -f $(name).map
rm -f $(name)
rm -f *.d
# ========== DONE ==========
.PHONY: install
install: all
# include the contents of all the .d files
# note: the .d files contain:
# <filename>.o:<filename>.c plus all the dependancies for that .c 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: 0