2个小时学会写makefile

本文只是介绍如何编写一个基本的Makefile,并不是一个非常完整的参考手册,其中很多类容都没有涉及到
1.基本
一个简单的Makefile,为了实际说明,我举我写的链表例子
man.c, list.c, list.h其中main.c和list.c中包含了list.h
main:main.o list.o
    gcc -o main main.o list.o
main.o:main.c list.h
    gcc -c -o main.o main.c
list.o:list.c list.h
    gcc -c -o list.o list.c

clean:
    rm main main.o list.o

Makefile总是由
target ... : prerequisites
    command1
    command2
    ...
这样的规则组成,命令前面必须以TAB键开头
目标可以是最终产生的可执行文件,也可以是中间的目标文件,比如.o文件,还可以是一个动作
比如clean,依赖不是必须的,比如clean就没有依赖,命令可以是linux下任意的命令

从上面Makefile文件可以知道,最终的目标文件为main

2.使用变量
Makefile中的变量相当于c语言中的宏,是一系列字符串,一次定义,多处使用,使用变量只需
要$(变量名)就可以了,比如这里定义一个变量OBJECTS = main.o list.o
简化后的makefile为
OBJECTS = main.o list.o

main:$(OBJECTS)
    gcc -o main $(OBJECTS)
main.o:main.c list.h
    gcc -c -o main.o main.c
list.o:list.c list.h
    gcc -c -o list.o list.c

clean:
    rm main $(OBJECTS)

3.变量定义的方式
以上只是介绍了一种变量的定义方式,在Makefile中定义变量有多种方式,除了上面的=号外,
还有+=, ?=
(1)变量定义方式1“=”
递归展开,这是默认的赋值方式
SUBARCH=arm
ARCH=$(SUBARCH)
all:
    @echo$(ARCH)
输出arm

ARCH=$(SUBARCH)
SUBARCH=arm
all:
    @echo$(ARCH)
也输出arm,也就是说我们可以把变量值推迟到后面定义,一样可以得到正确结果,但是这种也
有坏处,就是有可能写出无穷递归定义,例如:
A = $(B)
B = $(A)

(2)变量定义方式2“:=”
遇到变量直接展开,若引用变量不存在,就展开为空串
SUBARCH=arm
ARCH=$(SUBARCH)
all:
    @echo$(ARCH)
输出arm

ARCH=$(SUBARCH)
SUBARCH=arm
all:
    @echo$(ARCH)
输出为空
(3)变量定义方式3“?=”
如果前面没有定义该变量,就给它赋上默认值,这种赋值方式相当于“=”
ARCH=arm
ARCH?=i386
all:
    @echo$(ARCH)
输出arm
ARCH?=i386
all:
    @echo$(ARCH)
则输出i386

(4)给变脸追加值“+=”
“+=”并不会改变变量的定义方式,即:
如果变量是用”=“定义的,+=仍然保持“=”的特性
如果变脸是用“:=”定义的,+=仍然保持“:=”的特性

4.特殊变量
$@规则中的目标文件
$^规则中的所有依赖文件
$<规则中的第一个依赖文件
$?规则中所有比目标新的依赖条件

5.隐含规则中的变量
make隐含规则数据库中定义了很多变量,有些变量没有定义,有些变量定义了缺省值
我们写Makefile时可以重新定义这些变量,也可以在缺省值基础上追加
常用变量有
AR
静态库打包命令的名字,缺省值为ar
ARFLAGS
静态库打包命令选项,缺省值是rv
AS
汇编器的名字,缺省值为as
ASFLAGS
汇编器的选项,没有定义
CC
c编译器的名字,缺省值是cc
CFLAGS
c编译器选项,没有定义
CXX
c++编译器名字,缺省值是g++
CXXFLAGS
c++编译选项,没有定义
CPP
c预处理名字,缺省值是$(CC) -E
LD
链接器名字,缺省值是ld
LDFLAGS
链接选项,没有定义
TARGET_ARCH
和目标平台相关的命令选项
OUTPUT_OPTION
输出命令选项,缺省值为-o $@
我们写makefile,可以按照一些约定成熟的惯例,比如使用上面的变量
还比如,我们可以使用约定成熟的名字来作为目标命名:
all,用作缺省的目标
install,用于执行编译后的安装工作,比如把可执行文件拷贝到不同目录
clean,用于删除编译过程中生成二进制文件
distclean,不仅删除编译生成后的二进制文件,还删除其它生成的文件,总之,应该
清除所有的文件,只留下源文件。

6.隐含规则
如果一个目标在Makefile中所规则中都没有命令,make会尝试在内建的隐含规则数据
中查找适用的规则,其中和我们这个例子有关的隐含规则有:
OUTPUT_OPTION = -o $@
CC = cc
COMPOLE.C = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
%.o:%.c
    $(COMPILE.C) $(OUTPUT_OPTION) $<
main.o:main.c list.h
于是make根据main.o这个目标查找到一条cc -c -o main.o main.c这样的命令,
cc是一个符号链接,通常指向gcc
根据隐含规则,简化后的Makefile为
OBJECTS = main.o list.o

main:$(OBJECTS)
    gcc -o main $(OBJECTS)
main.o:list.h
list.o:list.h

clean:
    rm main $(OBJECTS)
虽然这样达到了简化的目的,但是我们规则中的依赖没有写全的原因,当我们修改了main.c时,使用make命令
并不能重新编译main.c,也就不能生成最新的可执行文件main,所以在写规则时,要将依赖写全。
OBJECTS = main.o list.o

main:$(OBJECTS)
    gcc -o main $(OBJECTS)
main.o:main.c list.h
list.o:list.c list.h

clean:

    rm main $(OBJECTS)


7.以条件为中心
上面的Makefile都是以目标为中心,去写每条规则,当然还可以以条件为中心
改写上一个Makefile后如下
OBJECTS = main.o list.o

main:$(OBJECTS)
    gcc -o main $(OBJECTS)
$(OBJECTS):list.h

clean:
    rm main $(OBJECTS)

8.其它
(1)使用“#”单行注释,使用“\”表示换行
(2)如果命令前面加“@”,表示不显示命令本身,只显示它的结果,比如@rm -f
(3)通常make执行的命令如果出错(该命令的返回状态非0)就立刻终止,不再执行
后续命令,但是如果命令前面加上了“-”,表示即使这条命令出错,make也要
继续执行后续命令,通常rm命令和mkdir命令前面要加“-”,因为rm要删除的
的文件可能不存在,mkdir要创建的目录可能已存在。
clean:
    rm main $(OBJECTS)
    echo "end"
如果main等文件不存在,则不会打印end
clean:
    -rm main $(OBJECTS)
    echo "end"
即使main等文件不存在,也还是要打印end
(4)如果当前目录下有个clean文件,clean目标又不依赖于任何条件,make就认为
它不需要更新了,所以make clean是错的。而我们希望把clean当作一个特殊的名字
来使用,可以添加一条特殊规则,把clean声明为一个伪目标
.PHONY:clean








posted @ 2011-11-28 09:48  移动应用开发  阅读(252)  评论(0编辑  收藏  举报