0voice-1.4.1-Makefile
简单 Makefile
darren:
@echo "hello darren"
all:
@echo "hello all"
test:
@echo "hello test"
clean:
@echo "hello clean"
- 下载 \(make\) 工具后,\(make \ darren\) 就会输出
hello darren
, \(make \ all\) 就会输出hello all
- 如果直接 \(make\) 呢?经过查询,\(make\) 会默认地执行 \(Makefile\) 中第一个非特殊目标
Makefile 三要素
简单来讲就是:依赖、目标和命令
例子 \(1\)
all (目标) : test (依赖)
@echo "hello all" (命令)
test (子目标) :
@echo "hello test"
- 有依赖的话,相当于一个递归执行的关系。
- \(Makefile\) 命令的前面必须有一个制表符 ( \(Tab\) )
例子 \(2\)
.PHONY: main clean
simple: main.o foo.o
@echo "simple: main.o foo.o"
gcc -o simple main.o foo.o
main.o: main.c
@echo "gcc -o main.o -c main.c"
gcc -o main.o -c main.c
foo.o: foo.c
@echo "gcc -o foo.o -c foo.c"
gcc -o foo.o -c foo.c
clean:
rm simple main.o foo.o
- 当
make simple
时,结果是
gcc -o main.o -c main.c
gcc -o main.o -c main.c
gcc -o foo.o -c foo.c
gcc -o foo.o -c foo.c
simple: main.o foo.o
gcc -o simple main.o foo.o
gcc -o main.o -c main.c
会多打印出一遍的原因是,作为 \(shell\) 指令输出。- \(Makefile\) 每次编译链接只会去管修改的部分,这样就大大节省编译时间。
伪对象 .PHONY
上一个例子,当把 .PHONY: main clean
注释掉的话,则没有 rm simple main.o foo.o
的效果的(源文件夹下有 \(clean\)) 文件。
原因:当一个目标没有被声明为 \(.PHONY\) 时,\(make\) 会将其视为一个实际的文件目标。
- \(make\) 会检查当前目录下是否存在一个名为 \(clean\) 的文件。
- 如果 \(clean\) 文件存在,并且它没有依赖(或者它的依赖都比它旧),\(make\) 就会认为 \(clean\) 目标已经是最新的("\(up \ to \ date\)"),因此不会执行 \(clean\) 规则下的命令。
变量
几个约定俗称的变量名 :
- \(CC\) 保存编译器名
- \(RM\) 用于指示删除文件的命令
- \(EXE\) 存放可执行文件
- \(OBJS\) 放置所有的目标文件名
总的来说就是,类比 \(C++\) 的宏定义,很多地方要用到,一旦要修改,就可以只修改最开始的定义。
其次来讲,像文件路径之类的,比较长,就可以用简短的变量名取代。
.PHONY: clean
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o foo2.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
foo2.o: foo2.c
$(CC) -o foo2.o -c foo2.c
clean:
$(RM) $(EXE) $(OBJS)
自动变量
.PHONY: all
all: first second third
@echo "\$$@ = $@"
@echo "$$^ = $^"
@echo "$$< = $<"
first:
@echo "1 first"
second:
@echo "2 second"
third:
@echo "3 third"
-
\(\$@\) 用于表示一个规则中的目标。当我们的一个规则中有多个目标时,$@ 所指的是其中任何造成命令被运行的目标。(理解一个规则有多个目标)
-
$^ 则表示的是规则中的所有依赖
-
$< 表示的是规则中的第一个依赖
1 first
2 second
3 third
$@=all
$^ = first second third
$< = first
自动变量-编译
.PHONY: clean
CC = gcc
RM = rm
EXE = simple
#SRCS = main.c foo.c
SRCS = $(wildcard *.c)
# 把.c换成对应的.o
#OBJS = foo.o foo2.o main.o
OBJS = $(patsubst %.c,%.o,$(SRCS))
$(EXE): $(OBJS)
$(CC) -o $@ $^
%.o: %.c
$(CC) -o $@ -c $^
clean:
$(RM) $(EXE) $(OBJS)
src: # 测试make src显示相应的xx.c
@echo $(SRCS)
objs:# 测试make objs显示相应的xx.o
@echo $(OBJS)
$(wildcard pattern)
是 \(make\) 的一个函数。它会查找当前目录下所有匹配 \(pattern\) 的文件,并返回一个文件列表,比如这里的 \(pattern\) 就是*.c
。- 意义:不用手动维护文件列表(当要删除和添加时),
make
自动更新。 $(patsubst pattern,replacement,text)
用于模式替换。比如上述的例子,将捕获的 \(SRCS\) 文件列表里面的.c
结尾的文件换成.o
结尾的文件(注意这里不是*.c
,*
是shell
的通配符,%
是字符串通配符,也就是说前者应用的场景是文件系统)。-c
指的是只编译,不链接。%.o: %.c
: 当需要生成main.o
时:gcc -o main.o -c main.c
; 当需要生成foo.o
时:gcc -o foo.o -c foo.c
。
依赖第三方库
.PHONY: clean
CC = $(CROSS)gcc
CXX = $(CROSS)g++
DEBUG = -g -O2
CFLAGS = $(DEBUG) -Wall -c
RM = rm -rf
SRCS = $(wildcard ./*.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))
VERSION = 1.0.0
TARGET = simple.$(VERSION)
HEADER_PATH = -I ./include
LIB_PATH = -L ./lib
$(warning LIB_PATH) # 这一行是用于调试,会打印LIB_PATH的值
LIBS = -lpthread
$(TARGET): $(OBJS)
$(CXX) $^ -o $@ $(LIB_PATH) $(LIBS)
%.o: %.c
$(CXX) $(CFLAGS) $< -o $@ $(HEADER_PATH)
clean:
$(RM) $(TARGET) *.o
%.o: %.c
是一一对应关系,$<
和$^
的效果是一样的。simple.$(VERSION)
,强调版本,好习惯。- 编译阶段
-I
找头文件,链接阶段-L
找依赖的库,不写出来去系统默认路径上找。