CS代考计算机代写 ECS 150 – Makefile tutorial

ECS 150 – Makefile tutorial
Prof. Joël Porquet-Lupine
UC Davis – 2020/2021
Copyright © 2017-2021 Joël Porquet-Lupine – CC BY-NC-SA 4.0 International License /
1 / 22

Manual approach
Code example
main.c fact.h
#include #include
#include “fact.h”
int main(int argc, char **argv)
{
int n;
if (argc < 2) { fprintf(stderr, "Usage: myfact number\n"); exit(1); } n = atoi(argv[1]); printf("fact(%d) = %d\n", n, fact(n)); return 0; } main.c #ifndef FACT_H_ #define FACT_H_ int fact(int n); #endif /* FACT_H_ */ fact.h fact.c #include "fact.h" int fact(int n) { if (n == 0) return 1; return n * fact(n - 1); } fact.c README.md # Overview This program computes the factorial of a number README.md 2 / 22 / Manual approach Compilation $ gcc -Wall -Wextra -Werror -c -o fact.o fact.c $ gcc -Wall -Wextra -Werror -c -o main.o main.c $ gcc -Wall -Wextra -Werror -o myfact main.o fact.o $ ./myfact Usage: myfact number $ ./myfact 5 fact(5) = 120 $ pandoc -o README.html README.md $ firefox README.html On the long run... Now, what if: fact.c changes? main.c changes? fact.h changes? I want to change the compilation options? I want to recompile this code on another computer? I want to share this code? Solution is to automate the build process! 3 / 22 / Introduction Definition A Makefile is a file containing a set of rules used with the make build automation tool. The two following commands are equivalent: $ ls Makefile ... $ make $ make -f Makefile The set of Makefile rules usually represents the various steps to follow in order to build a program: it's the building recipe. 4 / 22 / Introduction Anatomy of a rule For target to be generated, the prerequisites must all exists (or be generated if necessary) is generated by executing the specified command is generated only if it does not exist, or if one of the prerequisites is more recent Prevents from building everything each time, but only what is necessary Commenting Lines prefixed with # are not evaluated # This is a comment target: [list of prerequisites] [ command ]
target
target
5 / 22
/

Version 0.1
Basic rules
myfact: main.o fact.o
gcc -Wall -Wextra -Werror -o myfact main.o fact.o
main.o: main.c fact.h
gcc -Wall -Wextra -Werror -c -o main.o main.c
fact.o: fact.c fact.h
gcc -Wall -Wextra -Werror -c -o fact.o fact.c
README.html: README.md
pandoc -o README.html README.md
Makefile_v0.1
$ make
gcc -c -o main.o main.c gcc -c -o fact.o fact.c gcc -o myfact main.o fact.o
$ make README.html
pandoc -o README.html README.md
6 / 22
/

Version 0.1
all rule
all: myfact README.html
myfact: main.o fact.o
gcc -Wall -Wextra -Werror -o myfact main.o fact.o
main.o: main.c fact.h
gcc -Wall -Wextra -Werror -c -o main.o main.c
fact.o: fact.c fact.h
gcc -Wall -Wextra -Werror -c -o fact.o fact.c
README.html: README.md
pandoc -o README.html README.md
Makefile_v0.1
$ make
gcc -c -o main.o main.c
gcc -c -o fact.o fact.c
gcc -o myfact main.o fact.o pandoc -o README.html README.md
7 / 22
/

Version 0.1
clean rule
all: myfact README.html

clean:
rm -f myfact README.html main.o fact.o
Makefile_v0.1
$ make
gcc -c -o main.o main.c
gcc -c -o fact.o fact.c
gcc -o myfact main.o fact.o pandoc -o README.html README.md
$ make clean
rm -f myfact README.html main.o fact.o
8 / 22
/

Version 0.1
A first and basic Makefile
all: myfact README.html
myfact: main.o fact.o
gcc -Wall -Wextra -Werror -o myfact main.o fact.o
main.o: main.c fact.h
gcc -Wall -Wextra -Werror -c -o main.o main.c
fact.o: fact.c fact.h
gcc -Wall -Wextra -Werror -c -o fact.o fact.c
README.html: README.md
pandoc -o README.html README.md
clean:
rm -f myfact README.html main.o fact.o
Makefile_v0.1
Was good enough for Project #1
(No need to generate html out of markdown –pandoc is not installed on CSIF, and also it’s just for the example)
9 / 22
/

Version 1.0
How to avoid redundancy…?
A good programmer is a lazy programmer!
all: myfact README.html
myfact: main.o fact.o
gcc -Wall -Wextra -Werror -o myfact main.o fact.o
main.o: main.c fact.h
gcc -Wall -Wextra -Werror -c -o main.o main.c
fact.o: fact.c fact.h
gcc -Wall -Wextra -Werror -c -o fact.o fact.c
README.html: README.md
pandoc -o README.html README.md
clean:
rm -f myfact README.html main.o fact.o
Makefile_v0.1
10 / 22
/

Version 1.0
Automatic variables in commands
: replaced by name of target
: replaced by name of first prerequisite : replaced by names of all prerequisites
$@
$< $^ all: myfact README.html myfact: main.o fact.o gcc -Wall -Wextra -Werror -o $@ $^ main.o: main.c fact.h gcc -Wall -Wextra -Werror -c -o $@ $< fact.o: fact.c fact.h gcc -Wall -Wextra -Werror -c -o $@ $< README.html: README.md pandoc -o $@ $< clean: rm -f myfact README.html main.o fact.o 11 / 22 / Version 1.0 Pattern rules A pattern rule %.o: %.c says how to generate any file .o from another file . c.
all: myfact README.html
myfact: main.o fact.o
gcc -Wall -Wextra -Werror -o $@ $^ %.o: %.c fact.h
gcc -Wall -Wextra -Werror -c -o $@ $< %.html: %.md pandoc -o $@ $< clean: rm -f myfact README.html main.o fact.o 12 / 22 / Version 1.0 Variables $ make gcc -Wall -Wextra -Werror -g -c -o main.o main.c gcc -Wall -Wextra -Werror -g -c -o fact.o fact.c gcc -Wall -Wextra -Werror -g -o myfact main.o fact.o pandoc -o README.html README.md CC := gcc CFLAGS := -Wall -Wextra -Werror CFLAGS += -g PANDOC := pandoc all: myfact README.html myfact: main.o fact.o $(CC) $(CFLAGS) -o $@ $^ %.o: %.c fact.h $(CC) $(CFLAGS) -c -o $@ $< %.html: %.md $(PANDOC) -o $@ $< clean: rm -f myfact README.html \ main.o fact.o 13 / 22 / Version 2.0 More variables $ make gcc -Wall -Wextra -Werror -g -c -o main.o main.c gcc -Wall -Wextra -Werror -g -c -o fact.o fact.c gcc -Wall -Wextra -Werror -g -o myfact main.o fact.o pandoc -o README.html README.md targets := myfact README.html objs := main.o fact.o CC := gcc CFLAGS := -Wall -Wextra -Werror CFLAGS += -g PANDOC := pandoc all: $(targets) myfact: $(objs) $(CC) $(CFLAGS) -o $@ $^ %.o: %.c fact.h $(CC) $(CFLAGS) -c -o $@ $< %.html: %.md $(PANDOC) -o $@ $< clean: rm -f $(targets) $(objs) 14 / 22 / Version 2.0 Nice output $ make CC main.o CC fact.o CC myfact MD README.html ... myfact: $(objs) @echo "CC $@" @$(CC) $(CFLAGS) -o $@ $^ %.o: %.c fact.h @echo "CC $@" @$(CC) $(CFLAGS) -c -o $@ $< %.html: %.md @echo "MD $@" @$(PANDOC) -o $@ $< clean: @echo "CLEAN" @rm -f $(targets) $(objs) $ make clean CLEAN In case of debug, how can we still see the commands that are executed? 15 / 22 / Version 3.0 Conditional variables $ make CC main.o CC fact.o CC myfact MD README.html ... ifneq ($(V),1) Q=@ endif myfact: $(objs) @echo "CC $@" $(Q)$(CC) $(CFLAGS) -o $@ $^ %.o: %.c fact.h @echo "CC $@" $(Q)$(CC) $(CFLAGS) -c -o $@ $< %.html: %.md @echo "MD $@" $(Q)$(PANDOC) -o $@ $< clean: @echo "CLEAN" $(Q)rm -f $(targets) $(objs) $ make V=1 CC main.o gcc -Wall -Wextra -Werror -g -c -o main.o main.c CC fact.o gcc -Wall -Wextra -Werror -g -c -o fact.o fact.c CC myfact gcc -Wall -Wextra -Werror -g -o myfact main.o fact.o MD README.html pandoc -o README.html README.md 16 / 22 / Version 3.0 Generic rules vs dependency tracking Non-generic rule Generic rule %.o: %.c fact.h @echo "CC $@" $(Q)$(CC) $(CFLAGS) -c -o $@ $< $ make CC main.o CC fact.o CC myfact MD README.html %.o: %.c @echo "CC $@" $(Q)$(CC) $(CFLAGS) -c -o $@ $M #include “fact.h”
int fact(int n) { if (n == 0)
return 1;
return n * fact(n – 1);
}
$ gcc -Wall -Wextra -Werror -MMD -c -o fact.o fact.c
$ cat fact.d
fact.o: fact.c fact.h
19 / 22
/

Version 3.0
Dependency tracking Makefile integration
targets := myfact README.html
objs := main.o fact.o

CFLAGS := -Wall -Wextra -Werror -MMD

all: $(targets)
# Dep tracking *must* be below the ‘all’ rule
deps := $(patsubst %.o,%.d,$(objs)) -include $(deps)

%.o: %.c
@echo “CC $@”
$(Q)$(CC) $(CFLAGS) -c -o $@ $< ... clean: @echo "clean" $(Q)rm -f $(targets) $(objs) $(deps) Makefile_v3.0 $(deps) will be computed from $(obj) into main.d fact.d Prefix - ignores inclusion errors 20 / 22 / Version 3.0 First run Dependency files don't exist but make won't complain GCC generates them $ ls *.d Following runs Dependency files are included by the Makefile They are used to compose the generic rule for object generation $ make make: Nothing to be done for 'all' $ touch fact.h $ make CC main.o CC fact.o CC myfact $ make CC main.o CC fact.o CC myfact MD README.html $ ls *.d main.d fact.d $ cat main.d main.o: main.c fact.h $ cat fact.d fact.o: fact.c fact.h 21 / 22 / Final Makefile targets := myfact README.html objs := main.o fact.o CC := gcc CFLAGS := -Wall -Wextra -Werror -MMD CFLAGS += -g PANDOC := pandoc ifneq ($(V),1) Q=@ endif all: $(targets) # Dep tracking *must* be below the 'all' rule deps := $(patsubst %.o,%.d,$(objs)) -include $(deps) myfact: $(objs) @echo "CC $@" $(Q)$(CC) $(CFLAGS) -o $@ $^ %.o: %.c @echo "CC $@" $(Q)$(CC) $(CFLAGS) -c -o $@ $< %.html: %.md @echo "MD $@" $(Q)$(PANDOC) -o $@ $< clean: @echo "clean" $(Q)rm -f $(targets) $(objs) $(deps) Makefile_v3.0 22 / 22 /