1

I have a simple makefile.

IDIR =./include
CC=gcc
CFLAGS=-I$(IDIR)
SRCDIR = ./src
ODIR=obj
LDIR =./lib

LIBS=-lm

SRC = hellomake hellofunc
OBJ = ${SRC:%=$(ODIR)/%.o} 

_DEPS = hellomake.h
DEPS = ${_DEPS:%=$(IDIR)/%} 

$(ODIR)/%.o: $(SRCDIR)/%.c $(DEPS)
    $(CC) -c -o $@ $< $(CFLAGS)

hellomake: $(OBJ)
    gcc -o $@ $^ $(CFLAGS) $(LIBS)

.PHONY: clean

clean:
    rm -f $(ODIR)/*.o *~ hellomake

What I don't like about this Makefile is that the dependency generation.

.c.o

Is it possible to teach make file that the .c is in src directory, and .o is in obj directory to make this simple one?

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

header dependency

Is it possible to teach make file that all the c files are recompiled automatically when header files that the c file contains is changed?

prosseek
  • 182,215
  • 215
  • 566
  • 871
  • 1
    No, you cannot use old-style suffix rules if the object and source files are in different directories. You can configure automatic dependency detection using something like http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/ – MadScientist Mar 07 '15 at 16:59

2 Answers2

1

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

Community
  • 1
  • 1
prosseek
  • 182,215
  • 215
  • 566
  • 871
-1
IDIR = ./include
CC = gcc
CFLAGS = -I$(IDIR)
SRCDIR = src
ODIR = obj
LDIR = ./lib

LIBS = -lm

SRC = hellomake hellofunc
OBJ = ${SRC:%=$(ODIR)/%.o}

_DEPS = hellomake.h
DEPS = ${_DEPS:%=$(IDIR)/%}

%.o: ../$(SRCDIR)/%.c $(DEPS)
    $(CC) -c -o $@ $< $(CFLAGS)

hellomake: $(OBJ) $(DEPS)
    gcc -o $@ $^ $(CFLAGS) $(LIBS)
akond
  • 15,865
  • 4
  • 35
  • 55