Dipanshu
Dipanshu

Reputation: 33

Makefile Compilation using Automatic Variables

I'm trying simple thing in makefile but unable to make it work. Following is the example file which is related to my requirement:

Makefile:

OBJDIR=obj

dummy_build_folder := $(shell mkdir -p $(OBJDIR))

CC = gcc

SRCS =  1/print.c \
        2/add.c

INCLUDE =   -I "./1/" \
          -I "./2/" \

OBJS = $(addprefix $(OBJDIR)/,$(patsubst %.c,%.o,$(notdir $(SRCS))))

$(OBJDIR)/%.o: %.c
  @-if not exist $(OBJDIR) mkdir $(OBJDIR) 1>NUL 2>NUL  
  $(CC) -c $(INCLUDE) $< -o $@

all: $(OBJS)
  @-if not exist $(OBJDIR) mkdir $(OBJDIR) 1>NUL 2>NUL
  @echo $(OBJS)

.PHONY : clean

clean:
    rm $(OBJS)

When I'm trying to run the same I'm getting following error: "make: *** No rule to make target 'obj/print.o', needed by 'all'. Stop."

I tried of using "$(OBJDIR)/%.o: $(SRCS)" also, but here $< is pointing only to print.c, whereas $@ is incrementing to next file (i.e. print.o & add.o). Above is trying to compiling the print.c file twice and placing the output in both print.o and add.o

gcc -c -I "./1/" -I "./2/"  1/print.c -o obj/print.o

gcc -c -I "./1/" -I "./2/"  1/print.c -o obj/add.o

My Directory structure is:

RootFolder:

    1/print.c

    2/add.c

    3/xyz.c (Don't want to compile this file)

    Makefile

Also, I want to keep all the .o files in a different folder (./obj/*o) in above location.

Upvotes: 1

Views: 1238

Answers (1)

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136495

There are no dependencies between .o files and the corresponding .c files and no matching rules, this is what make tells you by make: *** No rule to make target 'obj/print.o', needed by 'all'. Stop..

The pattern rule $(OBJDIR)/%.o : %.c cannot match a .c to .o because the object files do not have the directory level present in the paths to .c. That directory level is removed by that $(notdir ...) call.

The % (the stem) parts in the pattern rule must match. E.g. in $(OBJDIR)/1/print.o : 1/print.c the stem is 1/print. Whereas in $(OBJDIR)/print.o : 1/print.c print does not match 1/print.

A good way to avoid this problem is to build the same directory structure for the output files as that of the source files.

Fixed Makefile:

OBJDIR := obj
CC := gcc

SRCS := 1/print.c 2/add.c

INCLUDE := -I "./1/" -I "./2/"

OBJS := $(patsubst %.c,${OBJDIR}/%.o,${SRCS})
DEPS := $(patsubst %.c,${OBJDIR}/%.d,${SRCS})

all: $(OBJS)
    @echo "Object files are $(OBJS)"

.SECONDEXPANSION:

# This rule creates the object file and its directory.
$(OBJS) : ${OBJDIR}/%.o : %.c | $$(dir $$@)
    $(CC) -c $(INCLUDE) $< -MD -MP -o $@

${OBJDIR}/%.d : ;

${OBJDIR}/% :
    mkdir -p $@

clean :
    rm -rf ${OBJDIR}

.PHONY: all clean

-include ${DEPS}

Example usage:

$ mkdir 1 2
$ touch 1/print.c 2/add.c
$ make
mkdir -p obj/1/
gcc -c -I "./1/" -I "./2/" 1/print.c -MD -MP -o obj/1/print.o
mkdir -p obj/2/
gcc -c -I "./1/" -I "./2/" 2/add.c -MD -MP -o obj/2/add.o
Object files are obj/1/print.o obj/2/add.o

Notes:

  1. The output directories get created automatically for you using the order-only dependencies | $$(dir $$@).
  2. Header file dependencies get created automatically for you using -MD -MP and -include ${DEPS} bits.

Upvotes: 2

Related Questions