Edward
Edward

Reputation: 353

Makefile: why makefile with % does not work?

% in my makefile didn't work.

I've tested the makefile on ubuntu 16.04 x64.

My makefile code is that: Version 1

CC=gcc
OBJ=main.o
TARGET:=main
.PHONY: clean
all : main
# ${OBJ}:%.o:%.c

%.i : %.c
        $(info Preprocess: build main.i)
        ${CC} -E -o $@ $<

%.s : %.i
        $(info Compile: build main.s)
        ${CC} -S -o $@ $<

%.o : %.s
        $(info Assemble: build main.o)
        ${CC} -c -o $@ $<

main : main.o
        $(info Link: build main.o)
        ${CC} -o $@ $^

clean:
        rm -f *.o *.out *.s *.i *.asm *.map ${OBJ} main

run make, the terminal print message:

gcc    -c -o main.o main.c
Link: build main.o
gcc -o main main.o

So only the last rule(main: main.o) run . fist step is autoderived code (gcc -c -o main.o main.c). why other rules didn't run?

And then I modify 3rd rules, add static mode: Version 2

...
%.i : %.c
        $(info Preprocess: build main.i)
        ${CC} -E -o $@ $<

%.s : %.i
        $(info Compile: build main.s)
        ${CC} -S -o $@ $<

main.o : %.o : %.s
        $(info Assemble: build main.o)
        ${CC} -c -o $@ $<

%: %.o
        $(info Link: build main.o)
        ${CC} -o $@ $^

Then all rules take effect, display message :

Preprocess: build main.i
gcc -E -o main.i main.c
Compile: build main.s
gcc -S -o main.s main.i
Assemble: build main.o
gcc -c -o main.o main.s
Link: build main.o
gcc -o main main.o
rm main.i

(why run "rm main.i"?)

I modify makefile again: Version 3

%.o:%.c
        $(info build main.o)
        ${CC} -c -o $@ $<
main : main.o
        $(info Link: build main.o)
        ${CC} -o $@ $^

It can be run correctly. Print message:

build main.o
gcc -c -o main.o main.c
Link: build main.o
gcc -o main main.o

So, why version 1 can't work correctly?

Upvotes: 1

Views: 1417

Answers (2)

Alex
Alex

Reputation: 3454

It doesn't work because make knows how to build an object file (.o) from a .c source, is a built-in implicit rule

You can disable implicit rules, if you run your version 1 with make -r it should run as expected.

The .i file is removed because is an intermediate file, by default make remove all intermediate files, you can avoid that by using .PRECIOUS: some-file-name

% rules in makefiles are referred as stem, pattern rules (not wildcards which are another thing)

You can run make with the argument --debug or --debug=all for a verbose log or a more verbose log

edit

You have two more options to disable built-in rules and get version 1 working:

  • override a specific built-in rule with an empty rule, just add %.o: %.c
  • disable all built-in rules adding an empty suffixes list .SUFFIXES:

If you modify the suffix list, the only predefined suffix rules in effect will be those named by one or two of the suffixes that are on the list you specify

edit

An additional option to disable built-in rules that I used in the past:

MAKEFLAGS += --no-builtin-rules

Upvotes: 6

Edward
Edward

Reputation: 353

Static mode with explicit object should be needed for avoiding implicit rules take effect. So i rewrite makefile with static mode. It can work correctly and don't run "rm main.i".

CC:=gcc
SRCS:=          $(wildcard *.c)
OBJ:=           $(patsubst %.c, %.o, ${SRCS})
PREFILE:=       $(patsubst %.o, %.i, ${OBJ})
ASMFILE:=       $(patsubst %.o, %.s, ${OBJ})
TARGET:=main
all: ${TARGET}
.PHONY: clean distclean

$(PREFILE):%.i:%.c
        $(info Preprocess: build main.i)
        $(CC) -E -o $@ $<

$(ASMFILE):%.s:%.i
        $(info Compile: build main.s)
        $(CC) -S -o $@ $<

$(OBJ):%.o:%.s
        $(info Assemble: build main.o)
        $(CC) -c -o $@ $<
        @objdump -DrwC -Mintel $@ > $(patsubst %.o,%.o.asm,$@)

$(TARGET):$(OBJ)
        $(info Link: build main)
        $(CC) -o $@ $^ -Wl,-Map=gcc.map
        @objdump -D $@ > $(patsubst %,%.asm,$@)

clean:
        rm -f *.o *.out *.s *.i *.asm *.map ${OBJ} ${TARGET}

distclean : clean
        rm -f *.d

Upvotes: 1

Related Questions