推荐学习资料:
跟我一起学makefile“”,相当全面。
一、基本思想
Make的思想是为每一块模块都设置一个时间标记,然后根据时间标记和依赖关系来决定哪一些文件需要更新。
一旦依赖模块的状态改变了,make就会根据时间标记的新旧执行预先定义的一组命令来生成新的目标。
二、依赖关系
就图1而言,我们可以说可执行程序main依赖于main.o、f1.o和ff1.o。
与此同时,main.o依赖于main.c和def1.h;f1.o依赖于f1.c、def1.h和def2.h;而ff1.o则依赖于ff1.c、def2.h和def3. h。
在makefile中,我们可以用目标名称,加冒号,后跟空格键或tab键,再加上由空格键或tab键分隔的一组用于生产目标模块的文件来描述模块之间的依赖关系。
对于上例来说,可以作以下描述:
main: main.o f1.o f2.o
main.o: main.c def1.h
f1.o: f1.c def1.h def2.h
f2.o: f2.c def2.h def3.h
不难发现,上面的各个源文件跟各模块之间的关系具有一个明显的层次结构,如果def2.h发生了变化,
那么就需要更新f1.o和f2.o,而f1.o和f2.o发生了变化的话,那么main也需要随之重新构建。
默认时,make程序只更新makefile中的第一个目标,如果希望更新多个目标文件的话,可以使用一个特殊的目标all,
假如我们想在一个makefile中更新main和hello这两个程序文件的话,可以加入下列语句达到这个目的:
all: main hello
三、makefile中的规则
格式:目标:[依赖模块][;命令]
习惯上写成2行为:
目标:[依赖模块]
命令
例如:
test: $(OBJS)
gcc -o test -rdynamic $(OBJS) $(LIBS)
注意:
对于makefile而言,空格字符和tab字符是不同的。所有规则所在的行必须以tab键开头,而不是空格键。
此外,如果在makefile文件中的行尾加上空格键的话,也会导致make命令运行失败。
四、Makefile文件举例
根据图1的规则,写一个简单的makefile如下:
main: main.o f1.o f2.o
gcc -o main main.o f1.o f2.o
main.o: main.c def1.h
gcc -c main.c
f1.o: f1.c def1.h def2.h
gcc -c f1.c
f2.o: f2.c def2.h def3.h
gcc -c f2.c
四、makefile中的常用宏
Make的宏分为两类,一类是用户自己定义的宏,一类是系统内部定义的宏。
用户定义的宏必须在makefile或命令行中明确定义,系统定义的宏不由用户定义
在makefile中定义宏的基本语法是:
宏标识符=值列表
当一个宏定义之后,我们就可以通过$(宏标识符)或者${宏标识符}来访问这个标识符所代表的值了。
举例:
all: main
# 使用的编译器
CC=gcc
#包含文件所在目录
INCLUDE=.
# 在开发过程中使用的选项
CFLAGS=-g -Wall –ansi
# 在发行时使用的选项
# CFLAGS = -O -Wall –ansi
main: main.o f1.o f2.o
$(CC) -o main main.o f1.o f2.o
main.o: main.c def1.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
f1.o: f1.c def1.h def2.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c f1.c
f2.o: f2.c def2.h def3.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c f2.c
现在介绍make的内部宏。常用的内部宏有:
$? :比目标的修改时间更晚的那些依赖模块表。
$@ :当前目标的全路径名。可用于用户定义的目标名的相关行中。
$< :比给定的目标文件时间标记更新的依赖文件名。
$* :去掉后缀的当前目标名。例如,若当前目标是pro.o,则$*表示pro。
五、构建多个目录
all: main
# 使用的编译器
CC = gcc
# 安装位置
INSTDIR = /usr/local/bin
# include文件所在位置
INCLUDE = .
# 开发过程中所用的选项
CFLAGS = -g -Wall –ansi
# 发行时用的选项
# CFLAGS = -O -Wall –ansi
main: main.o f1.o f2.o
$(CC) -o main main.o f1.o f2.o
main.o: main.c def1.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c main.c
f1.o: f1.c def1.h def2.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c f1.c
f2.o: f2.c def2.h def3.h
$(CC) -I$(INCLUDE) $(CFLAGS) -c f2.c
clean:
-rm main.o f1.o f2.o
install: main
@if [ -d $(INSTDIR) ]; \
then \
cp main $(INSTDIR);\
chmod a+x $(INSTDIR)/main;\
chmod og-w $(INSTDIR)/main;\
echo "Installed in $(INSTDIR)";\
else \
echo "Sorry, $(INSTDIR) does not exist";\
fi
六、内部规则
实际上,除了我们显式给出的规则外,make还具有许多内部规则,这些规则是由预先规定的目标、依赖文件及其命令组成的相关行。
七、makefile自动推导功能
只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么 whatever.c,
就会是whatever.o的依赖文件。并且 cc -c whatever.c 也会被推导出来
上面的makefile可以再优化
objects = main.o f1.o f2.o
main: ${objects}
$(CC) -o main ${objects}
main.o: def1.h
f1.o: def1.h def2.h
f2.o: def2.h def3.h
.PHONY : clean
clean :
-rm main $(objects)
八、常用函数说明
wildcard: 扩展通配符 例如将* 进行匹配
notdir: 去除目录 类似于basename的用法
patsubst: 替换通配符
例子:
在test目录下a.cpp和b.cpp,sub目录下c.cpp和d.cpp
src=$(wildcard *.c ./sub/*.c)
dir=$(notdir $(src))
obj=$(patsubst %.c,%.o,$(dir) )
all:
@echo $(src)
@echo $(dir)
@echo $(obj)
@echo "end"
输出结果:
#wildcard把 指定目录 ./ 和 ./sub/ 下的所有后缀是c的文件全部展开
a.cpp b.cpp ./sub/c.cpp ./sub/d.cpp
#notdir把展开的文件去除掉路径信息
a.cpp b.cpp c.cpp d.cpp
#patsubst把$(dir)中的变量符合后缀是.c的全部替换成.o
a.o b.o c.o d.o