makefile作用:用于管理编译文件。通常编译linux项目时,有长达几千个文件,在某次更改其中一个文件的话,若要将整个工程全部编译,太繁琐耗时。引入makefile,它依据文件更新的时间,从而只编译改动过的文件。
1 makefile名词解释:
目标:指编译后生成文件。
依赖:生成这个目标,所依赖的文件。(注意在执行这条规则时,makefile首先会去遍历这些依赖,如不存在,则会从这makefile文件中寻找依赖,并执行依赖所在的规则)。
eg:
test:a1.o a2.o
gcc a1.o a2.o –o test
a1.o:a1.c
gcc –c a1.c
a2.o:a2.c
gcc –c a2.c
命令:如何生成标。(命令行首格 按tab键,否则报错)
2 makefile基础规则
2.1赋值
A = 1 #普通赋值
B:= 2 #立即赋值,不管B之前有没有被赋值,立即被赋值为2
B?=3 #询问赋值,若B在之前没有被赋值,则赋值。若有,则不赋值
C =$A #将变量A的值赋值给C “$”在makefile中特别常用,用于变量的赋值
2.2 特殊符号
$@: 目标
$^: 全部依赖
$<: 第一个依赖
%:通配符
3 编写一个所有文件均在同一目录下的makefie
TGT = a.out
SRC = test.c tst.c
OBJ := $(SRC:.c = .o)
CC = gcc
$(TGT):$(OBJ)
$(CC) –o $@ $^
%.o : %.c
$(CC) –o $@ –c $<
.PHONY:clean
clean:
rm –rf $(SRC) $(OBJ)
4上面那个makefile,头文件发生更改时,由于没有加入检测头文件更新的机制,在执行make的时候会忽略头文件的更改。下面完善检测头文件机制的makefile
%.d:%.c TGT=a.out
SRC=test.c tst.c
OBJ := $(SRC:.c = .o)
CROSS_COMPILER= #arm-linux-
CC=$(CROSS_COMPILER)gcc
CUR_DIR=$(shell pwd)
DIR=$(CUR_DIR)/head
CFLAGF=-I$(DIR)
$(TGT):$( OBJ )
$(CC) $^ -o $@ $(CFLAGF)
%.o:%.c
$(CC) $< -c -o $@
$(CC) $< -MM > $@
-include $(SRC:.c=.d)
clean:
rm -vf $(TGT) $( OBJ ) $(SRC:.c=.d)
.PHONY:clean
5 编写一个所有文件均在不同目录下的makefie
目录结构为:
book@www.100ask.org:~/makefile/04$ tree
.
├── head
│ └── head.h
├── head.h
├── main.c
├── makefile
├── test
│ ├── hello
│ │ ├── head.h
│ │ ├── hello.c
│ │ └── makefile
│ ├── makefile
│ └── test.c
└── tst
├── makefile
└── tst.c
5.1在当前目录(主目录)makefile文件为
export CC = gcc
export LD =ld
SRC=main.c
TGT =a.out
SUB_DIR =test tst
export SUB_TGT =build-in.o
TOP_DIR =$(shell pwd)
HEAD_DIR =$(TOP_DIR)/head
export CFLAGS=-I $(HEAD_DIR)
$(TGT):$(SUB_DIR) $(SRC:.c=.o)
$(CC) $(CFLAGS) $(SUB_DIR:=/$(SUB_TGT)) $(SRC:.c=.o)
$(SUB_DIR):
make -C $@
%.o:%.c
$(CC) $(CFLAGS) -c $<
%.d:%.c
$(CC) $(CFLAGS) -MM $< > $@
sinclude $(SRC:.c=.d)
clean:
rm -vf $(TGT)
rm -vf $(SRC:.c=.o) $(SRC:.c=.d)
for dir in $(SUB_DIR); do \
make -C $$dir clean; \
done
.PHONY:clean $(SUB_DIR)
解析:在通过makefile编译的过程中,SUB_DIR指定了要去遍历的子目录,通过make -C $@
语句,执行各个子目录下的makefile文件,并生成一个叫build-in.o的文件,通过export SUB_TGT =build-in.o 指定。等待各个子目录都被编译完了,然后在返回主目录,连同build-in.o和main.o生成TGT。
5.2在test目录下的makefile文件为:
SRC =test.c
SUB_DIR=hello
$(SUB_TGT):$(SUB_DIR) $(SRC:.c=.o)
$(LD) $(SRC:.c=.o) $(SUB_DIR:=/$(SUB_TGT)) -r -o $@
%.o:%.c
$(CC) $(CFLAGS) $< -c
%.d:%.c
$(CC) $(CFLAGS) -MM $< > $@
$(SUB_DIR):
make -C $@
sinclude $(SRC:.c=.d)
clean:
rm -rvf $(SUB_TGT)
rm -rvf $(SRC:.c=.d) $(SRC:.c=.o)
for dir in $(SUB_DIR); do \
make -C $$dir clean; \
done
.PHONY:clean $(SUB_DIR)
解析:通过主目录执行该子目录的makefile文件,该目录下有test.c文件和hello目录,大体执行与主目录相同。它先去hello目录执行该目录的makefile,并生成build-o,然后将build-o文件和该目录下的test.o一起打包生成build.o文件。
5.3 在hello目录下的makefile文件
SRC =hello.c
SUB_DIR=
$(SUB_TGT):$(SRC:.c=.o) $(SUB_DIR)
$(LD) $(SRC:.c=.o) $(SUB_DIR:=/$(SUB_TGT)) -r -o $@
%.o:%.c
$(CC) $(CFLAGS) $< -c
%.d:%.c
$(CC) $(CFLAGS) -MM $< > $@
$(SUB_DIR):
make -C $@
sinclude $(SRC:.c=.d)
clean:
rm -rvf $(SUB_TGT)
rm -rvf $(SRC:.c=.d) $(SRC:.c=.o)
for dir in $(SUB_DIR); do \
make -C $$dir clean; \
done
.PHONY:clean $(SUB_DIR)
解析:该makefile 与上面基本相同,由于该目录下没有其他目录,所以SUB_DIR=
为空。
5.4 在tst目录下的makefile
SRC =tst.c
SUB_DIR=
$(SUB_TGT):$(SRC:.c=.o) $(SUB_DIR)
$(LD) $(SRC:.c=.o) $(SUB_DIR:=/$(SUB_TGT)) -r -o $@
%.o:%.c
$(CC) $(CFLAGS) $< -c
echo $(CFLAGS)
%.d:%.c
$(CC) $(CFLAGS) -MM $< > $@
$(SUB_DIR):
make -C $@
sinclude $(SRC:.c=.d)
clean:
rm -rvf $(SUB_TGT)
rm -rvf $(SRC:.c=.d) $(SRC:.c=.o)
for dir in $(SUB_DIR); do \
make -C $$dir clean; \
done
.PHONY:clean $(SUB_DIR)
解析:同上
make之后的目录结构
book@www.100ask.org:~/makefile/04$ tree
.
├── a.out
├── head
│ └── head.h
├── head.h
├── main.c
├── main.d
├── main.o
├── makefile
├── test
│ ├── build-in.o
│ ├── hello
│ │ ├── build-in.o
│ │ ├── head.h
│ │ ├── hello.c
│ │ ├── hello.d
│ │ ├── hello.o
│ │ └── makefile
│ ├── makefile
│ ├── test.c
│ ├── test.d
│ └── test.o
└── tst
├── build-in.o
├── makefile
├── tst.c
├── tst.d
└── tst.o