0. Makefile是一个指示make命令如何为我们工作的命令文件

1. make 不带命令,则以Makefile文件中定义的第一个目标为运行目标(默认目标)

2. @echo "Hello" 与 echo "Hello",区别是多了个@,这一符号告诉make,在运行时不要将这一行的命令显示出来

3. 生成目标的命令行前必须至少有一个TAB

4. 目标如果有先决条件,则先依次执行该先决条件

5. make构建目标有判断准则,这个准则是先决条件中相关的文件是否比目标更新(通过文件的时间戳)

6. 如果目标名跟当前目录下一文件名重名,比如clean,那么make clean会将clean当作文件来处理,认为当前目录找到了clean文件,如果再加上clean目标没有任何先决条件,它就会认为clean是最新的

参考:《驾驭Makefile》 李云著

Makefile
 --------
 simple: main.o foo.o
    gcc -o simple main.o foo.o
 main.o: main.c
    gcc -o main.o -c main.c
 foo.o: foo.c
    gcc -o foo.o -c foo.c
 clean: 


7. 针对6要采用假目标(phony target)来解决问题,关键字是.PHONY,它告诉make要把clean当作概念上的目标,不要当作文件来处理 

Makefile
 --------
 .PHONY: clean
 simple: main.o foo.o
    gcc -o simple main.o foo.o
 main.o: main.c
    gcc -o main.o -c main.c
 foo.o: foo.c
    gcc -o foo.o -c foo.c
 clean: 


8. 引入变量,方便以后维护和利用,变量有定义和引用,引用用$(变量名)或${变量名},可以存放多个值,用空格分开 

Makefile
 --------
 .PHONY: clean
 CC = gcc
 RM = rm
 EXE = simple
 OBJS = main.o foo.o
 $(EXE): $(OBJS)
    $(CC) -o $(EXE) $(OBJS)
 main.o: main.c
    $(CC) -o main.o -c main.c
 foo.o: foo.c
    $(CC) -o foo.o -c foo.c
 clean: 



9. 自动变量
 $@用于表示一个规则中的目标
 $^表示的是规则中的所有先决条件
 $<表示的是规则中的第一个先决条件 

Makefile
 --------
 .PHONY: clean
 CC = gcc
 RM = rm
 EXE = simple
 OBJS = main.o foo.o
 $(EXE): $(OBJS)
    $(CC) -o $@ $^
 main.o: main.c
    $(CC) -o $@ -c $^
 foo.o: foo.c
    $(CC) -o $@ -c $^
 clean: 



10.特殊变量
 MAKE表示的是make命令名是什么,当我们要在Makefile中调用另一个Makefile时需要用到,采用这种方式有利于写一个容易移植的Makefile
 MAKECMDGOALS表示的是当前用户所输入的make目标是什么 

Makefile
 --------
 .PHONY: all
 all:
    @echo "MAKE = $(MAKE)"
//screen content begin
 $make
 MAKE = make
 //screen content end

Makefile
 --------
 .PHONY: all clean
 all clean:
    @echo "\$$@ = $@"
    @echo "MAKECMDGOALS = $(MAKECMDGOALS)"
//screen content begin
 $make all
 $@ = all
 MAKECMDGOALS = all
 $make
 $@ = all
 MAKECMDGOALS = // 这里需要注意,虽然第一目标为缺省,但MAKECMDGOALS仍然是空
 //screen content end 


11. =符号和:=符号区别:=符号是递归扩展的,:=符号是简单扩展的即只赋值一次 

Makefile
 --------
 .PHONY: all
 x = foo
 y = $(x) b
 x = later
 xx := foo
 yy := $(xx) b
 xx := later
 all:
    @echo "y = $(y), xx = $(yy)"
// screen content begin
 $make
 y = later b, xx = foo b
 // screen content begin 

还有一种条件赋值操作符?=,其用法是当变量以前没有定义时,就定义它并且将左边的值赋值给它,如果已经定义了那么就不再改变其值 

Makefile
 --------
 .PHONY: all
 foo = x
 foo ?= y
 bar ?= y
 all:
    @echo "foo = $(foo), bar = $(bar)"
// screen content begin
 $make
 foo = x, bar = y
 // screen content end 

另外还有+=操作符,跟C中功能类似 


12. Makefile中的变量不仅可以在Makefile中赋值,还可以通过Shell和运行make命令时进行赋值 

// screen content begin
 $make bar=x
 foo =x, bar = x
 // screen content end
 // screen content begin
 $make
 foo = x, bar = y
 $export bar=x
 $make
 foo = x, bar = x
 // screen content end 

13. 在给变量赋值的同时完成后缀的替换,以此变得更简洁 

Makefile
 --------
 .PHONY: all
 foo = a.o b.o c.o
 bar := $(foo:.o=.c)
 all:
    @echo "bar = $(bar)"
// screen content begin
 $make
 bar = a.c b.c c.c
 // screen content end 


14. 如果要防止变量被Makefile之外的途径覆盖掉,那么需要通过override关键字 

Makefile
 --------
 .PHONY: all
 overrride foo = x
 all: 

// screen content begin
 
$make foo=haha
 foo = x
 // screen content end 


15. 通配符%提供的模式可大大简化Makefile,不用每个源文件都需要去构建一条规则 

Makefile
 --------
 .PHONY: clean
 CC = gcc
 RM = rm
 EXE = simple
 OBJS = main.o foo.o
 $(EXE): $(OBJS)
    $(CC) -o $@ $^
 %.o: %.c
    $(CC) -o $@ -c $^
 clean: 


16. 使用函数wildcard和patsubst,将会使15的Makefile变得更简洁,通过wildcard可以得到我们所需的文件,其形式是$(wildcard pattern),而patsubst是用来进行字符串替换
 的,其形式是$(patsubst pattern, replacement, text) 

Makefile
 --------
 .PHONY: clean
 CC = gcc
 RM = rm
 EXE = simple
 SRCS = $(wildcard *.c)
 OBJS = $(patsubst %.c,%.o,$(SRCS))
 $(EXE): $(OBJS)
    $(CC) -o $@ $^
 %.o: %.c
    $(CC) -o $@ -c $^
 clean: 

还有addprefix函数,是用来给字符串中的每个子串加上一个前缀,形式是$(addprefix prefix, names...) 

Makefile
 --------
 .PHONY: all
 without_dir = foo.c bar.c main.o
 with_dir := $(addprefix objs/, $(without_dir))
 all: 

// screen content begin
 
$make
 objs/foo.c objs/bar.c objs/main.o
 // screen content end 

还有filter函数,是用于从一个字符串中,根据模式得到满足模式的字符串,其形式是$(filter patten..., text) 

Makefile
 --------
 .PHONY: all
 sources = foo.c bar.c baz.s ugh.h
 sources := $(filter %.c %.s, $(sources))
 all: 
@echo $(sources)
// screen content begin
 
$make
 foo.c bar.c baz.s
 // screen content end 

还有filter-out函数,是用于从一个字符串中根据模式滤除一部分字符串,其形式是$(filter-out pattern..., text) 

Makefile
 --------
 .PHONY: all
 objects = main1.o foo.o main2.o bar.o
 result = $(filter-out main%.o, $(objects))
 all: 

// screen content begin
 
$make
 foo.o bar.o
 // screen content end  
还有strip函数,是用于去除变量中的多余的空格,其形式是$(strip string) 

Makefile
 --------
 .PHONY: all
 original = foo.c       bar.c
 stripped := $(strip $(original))
 all:
    @echo "original = $(original)" 

// screen content begin
 
$make
 original = foo.c        bar.c
 stripped = foo.c bar.c  
// screen content end