How Chen
How Chen

Reputation: 1370

How to write a good and efficient makefile

I have following folder structure:

TOPDIR
|
├── a
│   ├── a.c
│   ├── a.h
│   └── a.mk
├── b
│   ├── b.c
│   ├── b.h
│   └── b.mk
├── c
│   ├── c.c
│   ├── c.h
│   └── c.mk
├── include
│   └── common.h
├── root
│    ├── main.c
│    └── root.mk
└── Makefile

per-condition

My target is to write main Makefile under TOPDIR and sub-makefile, *.mk in sub folder, the include folder contain some common defines. root folder contain my main file(main function located here). Meanwhile, in main.c, it will call function from a.c and b.c, c.c is driver related, and will be called from a.c and b.c

Problem

I wrote sub-makefile like(I use one a.mk for example, others are same, ONLY root.mk has little different):

#MODULE will be modified for each sub folder
MODULE = a
LIB = $(MAKE_DIR)/libs/lib$(MODULE).a
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c, %.o, $(SRCS))

#generate lib file from obj file
$(LIB): $(OBJS)
    @mkdir -p ../libs
    @$(AR) cr $@ $^
    @echo "    Archive    $(notdir $@)"

#compile obj file from source file
$(OBJS): $(SRCS)
    @$(CC) $(CFLAGS) -c $^
    @echo "    CC        $(OBJS)"

.PHONY: clean
clean:
    @$(RM) -f $(LIB) $(OBJS)
    @$(RM) -f *.expand
    @echo "    Remove Objects:   $(OBJS)"
    @echo "    Remove Libraries:  $(notdir $(LIB))"

I wrote root.mk like:

PROG = ../prog/DEMO

SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c, %.o, $(SRCS))

#generate finial target file for run
$(PROG): $(SRCS)
    @mkdir -p ../prog
    @$(CC) $^ $(CFLAGS) -Wl,-Map=$(PROG).map $(LIBS) -o $@
    @echo "    Generate Program $(notdir $(PROG)) from $^"

.PHONY: clean
clean:
    @$(RM) -f $(OBJS) $(PROG)
    @$(RM) -f *.expand
    @$(RM) -rf ../prog ../libs
    @echo "    Remove Objects:   $(OBJS)"
    @echo "    Remove Libraries:  $(notdir $(PROG))"

I wrote main Makefile like:

MAKE_DIR = $(PWD)

ROOT_DIR    := $(MAKE_DIR)/root 
DRV_DIR     := $(MAKE_DIR)/driver
INCLUDE_DIR := $(MAKE_DIR)/include
DEBUG_DIR   := $(MAKE_DIR)/debug

INC_SRCH_PATH := 
INC_SRCH_PATH += -I$(ROOT_DIR)
INC_SRCH_PATH += -I$(DRV_DIR) 
INC_SRCH_PATH += -I$(INCLUDE_DIR)
INC_SRCH_PATH += -I$(DEBUG_DIR)

LIB_SRCH_PATH :=
LIB_SRCH_PATH += -L$(MAKE_DIR)/libs

CC = gcc
LD = ld

#problem happan here, if I change the sequence of LIB, 
#during the finial link, it will find some function un-referenced, 
#why can I put liba first?
LIBS := -lc -lb -la

CFLAGS :=
CFLAGS += $(INC_SRCH_PATH) $(LIB_SRCH_PATH) 
CFLAGS += -Wall -O -ggdb
CFLAGS += -DDEBUG -D_REENTRANT

LDFLAGS :=

export MAKE_DIR CC LD CFLAGS LDFLAGS LIBS LINT INC_SRCH_PATH

all:
    @$(MAKE) -C a -f a.mk
    @$(MAKE) -C b -f b.mk
    @$(MAKE) -C c -f c.mk
    @$(MAKE) -C root -f root.mk

.PHONY: clean
clean:
    @$(MAKE) -C debug -f debug.mk clean
    @$(MAKE) -C driver -f driver.mk clean
    @$(MAKE) -C mw -f mw.mk clean
    @$(MAKE) -C root -f root.mk clean

Question

  1. In main Makefile, I define which LIB file I will use, if need move it to root.mk for better?

  2. In sub-makefile, I did NOT use -MM to generate depend file, if this cause the problem I can NOT change the sequence of my lib*, which I also described in Makefile comments.

  3. Seems my makefile system can NOT detect I update some head file, for example, I first compiled whole code, and then, I modified one head file, when I try to re-compile, none of source is compiled

if:

#Automatic dependency magic:
%.d: src/%.c
   $(CC) -MM -o$@ $<

-include (MYPROG_OBJECTS:%.o=%.d)

need add into each sub-makefile?

Upvotes: 3

Views: 3961

Answers (1)

MadScientist
MadScientist

Reputation: 100926

This rule is definitely wrong:

$(OBJS): $(SRCS)
        @$(CC) $(CFLAGS) -c $^
        @echo "    CC        $(OBJS)"

The target line will expand to something like:

a.o b.o c.o d.o : a.c b.c c.c d.c

That's not right. It is identical to writing this:

a.o : a.c b.c c.c d.c
        ...
b.o : a.c b.c c.c d.c
        ...
c.o : a.c b.c c.c d.c
        ...
d.o : a.c b.c c.c d.c
        ...

This means that whenever you change any source file, ALL the object files will be rebuilt. You should use a pattern rule here:

%.o : %.c
        @$(CC) $(CFLAGS) -o $@ -c $<
        @echo "    CC        $@"

to compile the object files one at a time.

As far as your questions go, I don't understand question #1.

Questions #2 and #3 (if I understand correctly) are the same thing: the reason for #3 (no files are recompiled when you change a header file) is that you're not declaring any prerequisites on header files. Make doesn't have any built-in support for this, so you either have to do it by hand (add a.o : a.c b.h c.h g.h to your makefiles) or else automatically generate the dependencies.

The dependency generation will typically use the -MM or similar flags, assuming your compiler supports these flags.

Upvotes: 4

Related Questions