现在开发服务器端的程序写的文件越来越多,linux系统并没有windows那样的集成工具(IDE)开始写代码直接用 gcc就可以编译成功了,可是文件越来越多,编译起来是一件很痛苦是的事,是时候写Makefile让机器自动编译了。
Makefile的语言规则一般是这样的。
目标:依赖(依赖项可有可无)
命令
注意命令的输入必须要有tab键要不然是不行的。
第一个最简单的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
解释一下:
第一组代码:
main:main.o add.o sub.o
gcc -Wall -g main.o add.o sub.o -o main
main是要生成的目标,main.o add.o sub.o是依赖项。gcc -Wall -g main.o add.o sub.o -o main 是命令。完全是按照Makefile文件格式来的。
但是并没有main.o add.o sub.o 所以需要在下面找。
第二组代码:
main.o:main.c
gcc -Wall -g -c main.c -o main.o
main.o是目标,main.c 是依赖项。 gcc -Wall -g -c main.c -o main.o 是命令。语法符合Makefile的语法。
同理第三组,第四组代码都是这么来的。
再上升一个层次:
伪目标:
clean:
rm -f main main.o add.o sub.o
这一组命令是伪目标。
作用是可以清除不需要的中间文件。
输入 make clean 就可清除main main.o add.o sub.o这些文件。假如不想删除main文件,不要在命令中写就行了。
这就是执行伪目标的效果。
假如你修改了 main.c 文件只需要重新编译main.o文件即可不想都重新编译一次。只要输入命令 make main.o 即可;
但是这样做会遇到一个问题那就是,假如文件夹下有个clean的文件那么make clean就出错了,怎么办那?
只要在开头声明一下就可以了。
.PHONY:clean
加上这句话的意思就是clean为伪目标
再执行make clean就没什么问题了。
最终的代码就这这样的就行了。
.PHONY: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 -f main.o add.o sub.o
当然,这只是一个最简单的,不是很专业。下面介绍专业的Makefile的写法,首先介绍一下Makefile的自动化变量:
选项名 | 作用 |
$@ | 规则的目标文件名 |
$< | 规则的第一个依赖文件名 |
$^ | 规则的所有依赖文件列表 |
.PHONY:clean
OBJECTS=main.o add.o sub.o
main:$(OBJECTS)
gcc -Wall -g $^ -o $@
main.o:main.c
gcc -Wall -g -c $< -o $@
add.o:add.c add.h
gcc -Wall -g -c $< -o $@
sub.o:sub.c sub.h
gcc -Wall -g -c $< -o $@
clean:
rm -f $(OBJECTS)
解释一下:
其实这个Makefile和上面的Makefile没什么区别,只是一些文件用变量代替了。比如main.o add.o sub.o用OBJECTS 代替。以下有关main.o add.o sub.o的都可以用$(OBJECTS)代替。
$^是规则依赖的文件列表,$@是目标名,$<规则的第一个依赖文件。
假如文件夹下有两个Makefile文件 可是使用make -f Makefile1 来指定执行的Makefile文件
Makefile编译多个可执行文件:
模式规则
%.o:%.c
后缀规则
.c.o:
先来个最简单的Makefile
.PYONY:clean
BIN=01test 02test
all:$(BIN)
clean:
rm -f $(BIN)
解释一下,这个就是让编译器自己推导,让同名.c文件生成同名可执行文件
正常的Makefile
.PYONY:clean
BIN=01test 02test
all:$(BIN)
%.o:%.c
gcc -Wall -g -c $< -o $@
01test:01test.o
gcc -Wall -g $^ -o $@
02test:02test.o
gcc -Wall -g $^ -o $@
clean:
rm -f $(BIN) *.o
解释一下:
%.o:%.c
这个的意思是把.c文件生成同名的.o文件。也可以使用.c.o:代替;
当然也可以写的更加专业一点:
.PYONY:clean
CC=gcc
CFLAGS=-Wall -g
BIN=01test 02test
all:$(BIN)
%.o:%.c
$(CC) $(CFLAGS) -c $< -o $@
01test:01test.o
$(CC) $(CFLAGS) $^ -o $@
02test:02test.o
$(CC) $(CFLAGS) $^ -o $@
clean:
rm -f $(BIN) *.o
make常用内嵌函数
函数调用
$(function arguments)
$(wildcard PATTERN)
当前目录下匹配模式的文件
例如:src=$(patsubst %.c,%.o,$src)
$(patsubst PATTERN,REPLACEMENT,TEXT)//第一个参数是模式,第二个参数是替换的模式,第三个参数是文本
模式替换函数
例如:$(patsubst %.c,%.o,$src)
等价于$(src:.c=.o) //比较常用
shell函数
执行shell命令
例如:$(shell ls -d */) 意思是讲目录下所有的文件夹显示出来。
举个栗子:
cc = gcc
CFLAGS = -Wall -g
BIN = main
SUBDIR = $(shell ls -d */) //显示所有的文件
ROOTSRC = $(wildcard *.c)
ROOTOBJ = $(ROOTSRC:%.c=%.o)
SUBSRC = $(shell find $(SUBDIR) -name '*.c')
SUBOBJ = $(SUBSRC:%.c=%.o)
$(BIN):$(ROOTOBJ) $(SUBOBJ)
$(cc) $(CFLAGS) -o $(BIN) $(ROOTOBJ) $(SUBOBJ)
$(SUBOBJ).c.o:
$(cc) $(CFLAGS) -c $< -o $@
clean:
rm -f $(BIN) $(ROOTOBJ) $(SUBOBJ)
第一行代码:cc = gcc 定义变量gcc为变量cc 引用的时候需要使用$(cc)即可
第二行代码:CFLAGS = -Wall -g 定义变量 CFLAGS
第三行代码:BIN = main 定义变量main为变量BIN 这个是生成的目标文件。
第四行代码:SUBDIR = $(shell ls -d */) 这行代码的意思是把当前目录下的所有文件夹的名字赋值到SUBDIR中
第五行代码:ROOTSRC = $(wildcard *.c)这行代码的意思是匹配文件夹下的所有.c文件。
第六行代码:ROOTOBJ = $(ROOTSRC:%.c=%.o)这行代码的意思是把匹配到的.c文件换成.o 文件,是和第五行代码连用的
第七行代码:SUBSRC = $(shell find $(SUBDIR) -name '*.c')这行代码是寻找在当前文件夹下所有文件夹下的.c文件赋值给SUBSRCT
第八行代码:SUBOBJ = $(SUBSRC:%.c=%.o)这行代码的意思是把第七行找到的.c 文件转化成.o文件。
第十行代码:$(BIN):$(ROOTOBJ) $(SUBOBJ)
$(cc) $(CFLAGS) -o $(BIN) $(ROOTOBJ)
这一组代码的意思是编译上边的.o文件,编译成目标文件
第十三行代码:.c.o:
$(cc) $(CFLAGS) -c $< -o $@
这一组代码的意思是。把所有.c 文件编译成.o文件。
第十五行代码:
clean:
rm -f $(BIN) $(ROOTOBJ) $(SUBOBJ)
这一组代码的意思是伪目标,用来清除中间文件的。
Makefile编辑多个可执行文件
SUBDIRS = test1 test2
.PHONY:default all clean $(SUBDIRS)
default:all
all clean:
$(MAKE) $(SUBDIRS) TARGET=$(@)
$(SUBDIRS):
$(MAKE) -C $@ $(TARGET)
CC = gcc
BIN = test1
OBJS = ts1.o
.PHONY: all clean print
all:print $(BIN)
print:
@echo "-----------make all in $(PWD) ------------"
$(BIN):$(OBJS)
$(CC) $(OBJS) -o $@
%.o:%.c
$(CC) -c $<
clean:
@clean "----------make clean in $(PWD)------------"
rm -f $(BIN) $(OBJS)
CXX = g++
BIN = test2
OBJS = test2.o
CPPFLAGS = -Wall -g
.PHONY:all clean print
all:print $(BIN)
print:
@echo "-----------make all in $(PWD)-------------"
$(BIN):$(OBJS)
$(CXX) $(OBJS) -o $@
.o:%.cpp
(CXX) -c $<
clean:
@echo "-------------make clean in $(PWD)------------ "
rm -f $(BIN) $(OBJS)
解释一下假如有两个文件需要编译时需要写三个Makefile的一个是总的Makefile的另外两个是在文件下的,大体就是一个Makefile编译两个Makefile。