Makefile语法简介

原文链接http://blog.163.com/zcym925@126/blog/static/534279222007101061757452/

Makefile 语法简介

Makefile 语法简介


 

有稍稍在 Linux 下碰过程序设计的开发者应该会知道,make 是用来将程序代码、函式库、头文件及其它资源文件 build 成最终成果(即:最终的应用程序)的超强力辅助工具。

当然了,并不是非得动用到 make 才能 build 程序,或许有什么程序设计魔人喜欢什么都自己手动进行;但利用 make 及其参考档(输入档案)Makefile 将会让整个编译工作轻松许多。若您曾经打包过 Debian Package,那么应该会发现 debuan/rule 这个档案的语法和 Makefile 几乎是一模一样,所以学习 Makefile 的语法对于 Debian Package Maintainer 而言也是一门必要的功课。

 

Makefile 语法:

以下为 Makefile 的基本语法,

批注:

以 # 开头的即为批注。

变量宣告:(有人称之为宏)

语法:

MACRO = value

注意到,在 = 前后必须加上空白,而变量名称为大小写相异。利用 MACRO = 来取消该变数。

在惯例上,Makefile 内部使用的变量名称使用小写;而使用者很可能从命令行自行另外指定数值的变量,像是 CFLAGS,则是使用大写

在 Makefile 中,可利用 $(MACRO) 或 ${MACRO} 来存取已定义的变量。例:

tragets = foo
$(targets): common.h
    gcc -o $(targets) foo.c

效果等同:

foo: common.h
    gcc -o foo foo.c

:= 语法

注意到,make 会将整个 Makefile 展开后,再决定变数的值。也就是说,变量的值将会是整个 Mackfile 中最后被指定的值。例:

x = foo
y = $(x) bar
x = xyz
# y 的值为 xyz bar

在上例中,y 的值将会是 xyz bar,而不是 foo bar。

您可以利用 := 来避开这个问题。:= 表示变量的值决定于它在 Makefile 中的位置,而不是整个 Makefile 展开后最终的值。

x := foo
y := $(x) bar
x := xyz
# y 的值为 foo bar

在上例中,y 的值将会是 foo bar,而不是 xyz bar 了。

?= 语法:

?= 是一个简化的语法:若变量未定义,则替它指定新的值。否则,采用原有的值。例:

FOO ?= bar

若 FOO 未定义,则 FOO = bar;若 FOO 已定义,则 FOO 的值维持不变。

+= 语法:

例:

CFLAGS = -Wall -g
CFLAGS += -O2

此时 CFLAGS 的值就变成 -Wall -g -O2 了。

define 语法:

使用 define 语法的唯一优点是它可以让变量直接使用『断行』。例:

define foo
uname -a
echo $$SHELL
endef

all:
        $(foo)

上例可以视同于:

foo = uname -a; echo $$SHELL

all:
        $(foo)

注意到在上例中使用了 $$,让 '$' 能传到 Shell 中。

在 target 里另外指定变量的值

可以在 target 里另外指定变量的值。例:

foo = abc

all: foo = xyz
all:
        echo $(foo)
        # 此时,foo 的值为 xyz

以下的语法提供了和上例相同的功能:

all: override foo = xyz

all: export foo = xyz

make 也可以存取环境变量。例:

all:
    @echo $(CFLAGS)

在上例中,虽然在 Makefile 里虽然没有指定 CFLAGS 的值,但 make 会试图以环境变量来代出 CFLAGS 的值。

可搭配 wildcard 指令在变量里展开 * ? [...] 等通配符。例:

objects=$(wildcard *.o)

规则:(Rule)

指示 make 如何进行编译。

主要语法:

target: dependencies
<Tab>Commands

target: dependencies; Commands
<Tab>Commands

Rule 指示了 make 如何建立 target;及何时要重新建立 target。

target:所要建立的档案
dependencies:相依项目。make 会据此决定是否要重新编译 target。
Commands:建立 target 的指令。

在 Makefile 里并没有限定 Rule 的先后顺序。但默认上,make 会参考 all 这个目标项目,并依据它的 dependencies 来决定要建立哪些项目。若没有 all 项目,则会采用 Makefile 里的第一个项目。

target:(目标项目)

这个项目所要建立的档案,必须以 : 结尾。例:

foo.o: common.h
    gcc -c foo.c

其中,foo.o 是这个项目要建立的档案;common.h 是相依性的项目/档案;而 gcc -c foo.c 则为要产生这个项目所要执行的指令。

make 在编译时,若发现 target 比较新,也就是 dependencies 都比 target 旧,那么将不会重新建立 target,如此可以避免不必要的编译动作。

若该项目并非档案,则为 fake 项目。如此一来将不会建立 target 档案。但为了避免 make 有时会无去判断 target 是否为档案或 fake 项目,建议利用 .PHONY 来指定该项目为 fake 项目。例:

.PHONY: clean
clean:
    rm *.o

在上例中,若不使用 .PHONY 来指定 clean 为 fake 项目的话,若目录中同时存在了一个名为 clean 的档案,则 clean 这个项目将被视为要建立 clean 这个档案,但 clean 这个项目却又没有任何的 dependencies,也因此,clean 项目将永远被视为 up-to-date,永远不会被执行

因为利用了 .PHONY 来指定 clean 为 fake 项目,所以 make 不会去检查目录中是否存在了一个名为 clean 的档案。如此也可以提升 make 的执行效率。

其它类以 .PHONY 的语法请参考:

GNU `make': 4.9 Special Built-in Target Names

另外,如果某个非 fake 项目里的 dependencies 包含了 fake 项目的话,因为 make 一定会执行 fake 项目,这样一来,这个非 fake 项目一定也会被执行。这可能不是理想的做法。

dependencies:(相依性项目,以空白间隔)

dependencies 是指定在建立 target 之前,必须先检查的项目。可以不指定。例:

foo.o: common.h
    gcc -c foo.c

上例中是指:检查 common.h。如果它的建立日期比 foo.o 新,就执行 gcc -c foo.c 来重新产生 foo.o。也就是说,可以依需求建立 dependencies,即使它和 target 一点关系也没有。

相依性项目可以是 Makefile 中其它的 target。也因此,在建立该 target 之前,它会先检查在 dependencies 里所指定的所有 target。

Commands:(即为要执行的 Shell 指令)

必须以 <Tab> 开头。使用 Shell Script 语法。在 Makefile 里,只要以 <Tab> 开头都将会被视为 Shell Script 执行。

每条法则必须写在同一行。每条 Command 会启动一个新的 Shell,预设为 /bin/sh。若执行完某条 Command 但传回了错误值,make 就会中断执行。

因为每条 Command 会启动一个新的 Shell,所以有时执行的指令必须写在同一行,像是使用 if 来进行条件判断,此时可以用 ; 来分隔指令。例:

all:
    if [ -f foo ];
        then rm foo;
    fi

而以下是错误示范:

all:
    cd subdir; $(MAKE)

这时因为 make 只会检查最后一个指令的传回值,所以在以上指令中,即使 subdir 不存在,但 make 并不会因而中断执行,并会继续执行 $(MAKE) 指令,而产生了不可预期的结果。

为了避免这个问题,可以利用 && 来检查其中某个指令是否成功执行,再决定是否执行下个指令。例:

all:
    cd subdir && $(MAKE)

特别字符:

@:不要显示执行的指令。

-:表示即使该行指令出错,也不会中断执行。

例:

.PHONY: clean
clean:
    @echo "Clean..."
    -rm *.o

因为 make 会一行一行将正在执行的 Commands 显示在屏幕上,但您可以利用 @ 来暂时关闭这个功能。

而 make 只要遇到任何错误就会中断执行。但像是在进行 clean 时,也许根本没有任何档案可以 clean,因而 rm 会传回错误值,因而导致 make 中断执行。我们可以利用 - 来关闭错误中断功能,让 make 不会因而中断。

隐性法则:

在上例中的:

foo.o: common.h
    gcc -c foo.c

由于产生 foo.o 的指令就是 gcc -c foo.c,因此在 Makefile 里可以将其简化为:

foo.o: common.h

此时 make 会依据 target 的扩展名来猜测该如何编译 target。如此可以让 Makefile 更为简洁。

您可以利用【空白指令】来避免 make 依据隐性法则而进行编译。例:

foo.o: common.h
<Tab>

内部变数:

$?:

代表已被更新的 dependencies 的值。
也就是 dependencies 中,比 tragets 还新的值。

$@:

代表 tragets 的值。

$<:

代表第一个 dependencies 的值。

$* :

代表 tragets 所指定的档案,但不包含扩展名。


例:

print: foo1.c foo2.c foo3.c
    lpr -p $?
    touch print

这样会将 foo1.c foo2.c foo3.c 中已有更新的内容印至打印机。

内部函数:

您可以在 Makefile 使用 make 所支持的一些内部函数。详情请参考:

GNU `make': 8 Functions for Transforming Text

条件判断:

可以在 Makefile 中使用以下的条件判断语法。但由于它们不是 rule,所以不可以 <Tab> 开头;但其后要执行的指令则必须以 <Tab> 开头,make 才会视其为 Shell 指令。

ifeq:(检查 value1, value2 是否相等)

ifeq (value1, value2)
    ...
else
    ...
endif

ifneq:(提供和 ifeq 相反的功能)

ifneq (value1, value2)
    ...
else
    ...
endif

ifdef:(检查 variable 变量是否为空的)

ifdef variable
    ...
else
    ...
endif


ifndef:(提供和 ifdef 相反的功能)

ifdef variable
    ...
else
    ....
endif

引入档案:

将外部档案引入 Makefile 中。可以视为直接在此将该档案全数插入 Makefile 中。

例:

include foo.in

将 foo.in 的内容全数引入 Makefile 里。

可以同时引入多个档案、使用变量 $(MACRO) 或是使用通配符(* ? 或 [...])。例:

include foo.in common*.in $(MAKEINCS)

子目录:

如果该项目有多个目录,且每一个目录中都有 Makefile,则利用以下指令来进入子目录并进行编译:

cd dir;$(MAKE)

例:

SUBDIRS = dir1 dir2 dir3

all:
        for i in $(SUBDIRS); do
                (cd $$i; make);
        done

clean:
        for i in $(SUBDIRS); do
                (cd $$i; make clean);
        done

install:
        for i in $(SUBDIRS); do
                (cd $$i; make install);
        done

make 参数:

可以用 make 的参数来盖过 Makefile 里,用变数所指定的参数。例:

make CFLAGS="-g -O2"

您可以使用 override 来避免在 Makefile 里的变量的值被 make 的参数所取代。例:

override CFLAGS = -Wall -g

可以在 make 后指定要重新建立的 target。例:

make clean

以上会执行 Makefile 中的 clean 区段。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值