利用make工具可以自动完成编译工作。这些工作包括:如果仅仅修改了某几个文件,则只重新编译这几个源文件;如果某个头文件被修改了,则重新编译所有包含该头文件的源文件。这样做的好处就是不必要每次去编译所有文件,可大大的简化工作和节约时间。
make工具通过一个称为Makefile的文件来完成并自动编译工作。Makefile文件描述了整个工程的编译、连接等规则。
Makefile基本规则
Target .. : Dependencies...
command
..
目标(Target)程序产生的文件,如可执行文件和目标文件;目标文件也可以是要执行的动作,如:clean,也称为伪文件
依赖(Dependencies)是用来产生目标的输入文件列表,一个目标文件通常依赖多个文件
命令(command)是make执行的动作;但是需要注意的是:每个命令行的起始字符必须始TAB字符!
如果Dependencies中有一个或多个文件更新的话,command就要执行,这是Makefile最核心的内容
看一个简单的例子
main.c add.c sub.c add.h sub.h
有上面四个文件,下面编写一个最简单的makefile文件
// makefile
main:main.o add.o sub.o
gcc -Wall -g main.o add.o sub.o -o main
main.o:main.c
gcc -Wall -g -c main.c -o main.o
add.o:add.c add.h
gcc -Wall -g -c add.c -o add.o
sub.o:sub.c sub.h
gcc -Wall -g -c sub.c -o sub.o
执行
make
gcc -Wall -g -c main.c -o main.o
gcc -Wall -g -c add.c -o add.o
gcc -Wall -g -c sub.c -o sub.o
gcc -Wall -g main.o add.o sub.o -o main
就这么的简单;如果我们更新add.h文件,再执行make
gcc -Wall -g -c add.c -o add.o
gcc -Wall -g main.o add.o sub.o -o main
在makefile文件的最后加上
clean:
rm -r main main.o add.o sub.o
它只是用来删除生成的文件,又称之为伪目标,执行make clean
如果我们只是要编译mai.o,我们只需要 make main.o即可
再修改上面的代码:
.PHONY:clean #伪目标一般需要在首行加入.PHONY:clean来定义,如果有个clean就不能执行了
main:main.o add.o sub.o
gcc -Wall -g main.o add.o sub.o -o main
main.o:main.c
gcc -Wall -g -c main.c -o main.o
add.o:add.c add.h
gcc -Wall -g -c add.c -o add.o
sub.o:sub.c sub.h
gcc -Wall -g -c sub.c -o sub.o
clean:
rm -r main main.o add.o sub.o
从上面的代码得知,我们最好还是需要有伪目标的时候定义;
在上面的代码中,你一定可以看出有非常多的重复的代码;在makefile中是可以定义变量
选项名 | 作用 |
$@ | 规则的目标文件 |
$< | 规则的第一个依赖文件名 |
$^ | 规则的所有依赖文件列表 |
.PHONY:clean #伪目标一般需要在首行加入.PHONY:clean来定义,如果有个clean就不能执行了
OBJECTS=main.o add.o sub.o
main:$(OBJECTS)
gcc -Wall -g $^ -o $@ // gcc -Wall -g main.o add.o sub.o -o main
main.o:main.c
gcc -Wall -g -c $< -o $@ // gcc -Wall -g -c main.c -o main.o
add.o:add.c add.h
gcc -Wall -g -c $< -o $@ // gcc -Wall -g -c add.c -o add.o
sub.o:sub.c sub.h
gcc -Wall -g -c $< -o $@ // gcc -Wall -g -c sub.c -o sub.o
clean:
@echo "delete ...."
rm -r main $(OBJECTS) // rm -r main main.o add.o sub.o
再次运行make是可以执行的。(一般,makefile命名为Makefile, 可以指定文件为makefile; make -f fileName
如果你不想打印,就在前面加@
Makefile编译多个可执行文件
模式规则:%.o; %.c 后缀规则.c .o
有两个文件,test1.c 和test2.c
test1.c
int main(void)
{
return 0;
}
test2.c
int main(void)
{
return 0;
}
它们都有main函数,看Makefile文件
.PHONY:clean all
BIN=testt test2 //BIN变量依赖test1.c和test2.c的文件
all:$(BIN) // all是伪目标;它需要生成test1和test2文件,编译器会自动的推导去生成文件名相同的执行文件
// 如果不需要推导,需要加上完整的代码
test1:test1.o
gcc -Wall -g -c $^ -o $@
test2:test2.o
gcc -Wall -g -c $^ -o $@
clean:
rf -f $(BIN)
它生成了 test1 和test2可执行文件,显示生成.o文件所有的
.PHONY:clean all
BIN=test1 test2
all:$(BIN)
#将所有的.c文件生成.o文件
%.o:%.c // .c.o:一样的效果
gcc -Wall -g -c $< -o $@
test1:test1.o
gcc -Wall -g $^ -o $@
test2:test2.o
gcc -Wall -g $^ -o $@
clean:
rm -f *.o $(BIN)
更加专业,修改上面的代码
.PHONY:clean all
CC=gc
CFLAGS=-Wall -g
BIN=test1 test2
all:$(BIN)
#将所有的.c文件生成.o文件
#%.o:%.c
.c.o:
$(CC) $(CFLAGS) -c $< -o $@
test1:test1.o
$(CC) $(CFLAGS) $^ -o $@
test2:test2.o
$(CC) $(CFLAGS) $^ -o $@
clean:
rm -f *.o $(BIN)
如果我们需要再加入test3.c,里面也包含main文件,但是它需要一个pub.c和pub.h文件,看下面代码
.PHONY:clean all
CC=gcc
CFLAGS=-Wall -g
BIN=test1 test2 test3
all:$(BIN)
#将所有的.c文件生成.o文件
#%.o:%.c
.c.o:
$(CC) $(CFLAGS) -c $< -o $@
test1:test1.o
$(CC) $(CFLAGS) $^ -o $@
test2:test2.o
$(CC) $(CFLAGS) $^ -o $@
test3:test3.o pub.o
$(CC) $(CFLAGS) $^ -o $@
clean:
rm -f *.o $(BIN)
Makefile的函数调用
$(function arguments)
下面是常见的Makefile函数
$(wildcard PATTERN) 当前目录下匹配模式的文件;如src = $(wildcard *.c)
$(patsubst PATTERN, REPLACENT, TEXT) 模式替换函数,例如($patsubst %.c, %.o, $src)<==>$(src:.c=.o)
shell函数,执行shell命令 $(shell ls -d */)
看下面的列子,下面是包含2级目录的
.PHONY:clean
CC = gcc
CFLAGS = -Wall -g
BIN = main #要生成的执行文件
SUBDIR = $(shell ls -d */) #将当前文件下的所有文件都列出来,匹配/线
ROOTSRC = $(wildcard *.c) #当前下所有.c文件
ROOTOBJ = $(ROOTSRC:%.c=%.o) #将所有.c替换为o
SUBSRC = $(shell find $(SUBDIR) -name '*.c') #列出所有.c文件
SUBOBJ = $(SUBSRC:%.c=%.o) #所有.c文件替换为.o文件
$(BIN):$(ROOTOBJ) $(SUBOBJ)
$(CC) $(CFLAGS) -o $(BIN) $(ROOTOBJ) $(SUBOBJ)
.c.o:
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(BIN) $(ROOTOBJ) $(SUBOBJ)
我们如果仔细查看mysql或者php的源码,你可以发现,它其实是一次行 编译多个可执行文件,我们看个实验
一个目录结构
Makefile1 ---文件
--test1 ---目录
----main.c---主文件
----Makefile2---主文件
--test2 ---目录
----main.c---主文件
----Makefile3---主文件
SUBDIRS = test1 test2 (Makefile1)
.PHONY:default all clean $(SUBDIRS)
default:all
all clean:
$(MAKE) $(SUBDIRS) TARGET=$@
$(SUBDIRS):
$(MAKE) -C $@ $(TARGET)
CC = gcc (Makefile2)
BIN = main
OBJ = main.o
.PHONY: all clean print
all:print $(BIN)
print:
@echo "------ make all in $(PWD) ----"
$(BIN):$(OBJ)
$(CC) $(OBJ) -o $@
%.o:%.c
$(CC) -c $<
clean:
@echo "----- make clean in $(PWD) ----"
rm -f $(BIN) $(OBJ)
CC = gcc (Makefile3)
BIN = main
OBJ = main.o
.PHONY: all clean print
all:print $(BIN)
print:
@echo "------ make all in $(PWD) ----"
$(BIN):$(OBJ)
$(CC) $(OBJ) -o $@
%.o:%.c
$(CC) -c $<
clean:
@echo "----- make clean in $(PWD) ----"
rm -f $(BIN) $(OBJ)