Makefile学习(一)

参看文档:《跟我一起写 Makefile》

Makefile的基本规则:

target ... : prerequisites ... 
    command 
...
    ...
target 也就是一个目标文件,可以是 Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。 prerequisites 就是,要生成那个 target 所需要的文件或是目标。 command 也就是 make 需要执行的命令。(任意的 Shell 命令)
 

举一个例子:

*表示通配符
main: main.o input.o calcu.o
    gcc -o main main.o input.o calcu.o
main.o: main.c
    gcc -c main.c
input.o: input.c
    gcc -c input.c
calcu.o: calcu.c
    gcc -c calcu.c

clean:
    rm *.o
    rm main
make编译如下:
0
make clean如下:
0
 
下面说明一个一个规则,将上面的Makefile代码进行简化:

1、Makefile中使用变量:

objects = main.o input.o calcu.o

main: $(objects)
    gcc -o main $(objects)
main.o: main.c
    gcc -c main.c
input.o: input.c
    gcc -c input.c
calcu.o: calcu.c
    gcc -c calcu.c

clean:
    rm $(objects)
    rm main

2、让Makefile自动推导:

objects = main.o input.o calcu.o

main: $(objects)

main.o: main.c

input.o: input.c

calcu.o: calcu.c


clean:
    rm $(objects)
    rm main

3、清空目标文件的规则:

.PHONY表示一个“伪目标”,也是一种Makefile编写规范
objects = main.o input.o calcu.o

main: $(objects)

main.o: main.c

input.o: input.c

calcu.o: calcu.c


.PHONY : clean   
clean:
    rm $(objects)
    rm main
 

Makefile总述:

1、显式规则:
显式规则说明了,如何生成一个或多的的目标文件。
 
2、隐晦规则:
由于我们的 make 有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写 Makefile,这是由 make 所支持的。
 
3、变量的定义:
在 Makefile 中我们要定义一系列的变量,变量一般都是字符串,这个有点你 C 语言中的宏,当 Makefile 被执行时,其中的变量都会被扩展到相应的引用位置上。
 
4、文件指示:
其包括了三个部分,一个是在一个 Makefile 中引用另一个 Makefile,就像 C 语言中的include 一样;另一个是指根据某些情况指定 Makefile 中的有效部分,就像 C 语言中的预编译#if 一样;还有就是定义一个多行的命令。
 
5、注释:
Makefile 中只有行注释,和 UNIX 的 Shell 脚本一样,其注释是用“#”字符,这个就像 C/C++中的“//”一样。如果你要在你的 Makefile 中使用“#”字符,可以用反斜框进行转义,如:“\#”。
 
值得一提的是,在 Makefile 中的命令,必须要以[Tab]键开始。
 

引入其他的Makefile:

在 Makefile 使用 include 关键字可以把别的 Makefile 包含进来,这很像 C 语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include 的语法是:
include filename 可以是当前操作系统 Shell 的文件模式(可以保含路径和通配符) 在 include前面可以有一些空字符,但是绝不能是[Tab]键开始。include 和可以用一个或多个空格隔开。举个例子,
有这样几个 Makefile:a.mk、b.mk、c.mk,还有一个文件叫foo.make,以及一个变量$(bar),其包含了 e.mk 和 f.mk,那么,下面的语句:
include foo.make *.mk $(bar)
等价于:
include foo.make a.mk b.mk c.mk e.mk f.mk
 
make 命令开始时,会把找寻 include 所指出的其它 Makefile,并把其内容安置在当前的位。就好像 C/C++的#include 指令一样。如果文件都没有指定绝对路径或是相对路径的话,make 会在当前目录下首先寻找,如果当前目录下没有找到,那么,make 还会在下面的几个目录下找:
1、如果 make 执行时,有“-I”或“--include-dir”参数,那么 make 就会在这个参数 所指定的目录下去寻找。
2、如果目录/include(一般是:/usr/local/bin 或/usr/include)存在的话,make 也会去找。如果有文件没有找到的话,make 会生成一条警告信息,但不会马上出现致命错误。它会继续载入其它的文件,一旦完成 makefile 的读取,make 会再重试这些没有找到,或是不能读取的文件,如果还是不行,make 才会出现一条致命信息。如果你想让 make不理那些无法读取的文件,而继续执行,你可以在 include 前加一个减号“-”。 如: -include 其表示,无论 include 过程中出现什么错误,都不要报错继续执行。和其它版本 make兼 容的相关命令是 sinclude,其作用和这一个是一样的。
 

文件搜索:

在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当 make 需要去找寻文件的依赖关系时,你可以在文件前加上路
径,但最好的方法是把一个路径告诉 make,让 make 在自动去找。
Makefile 文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make 只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make
就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。
 
VPATH = src:../headers
上面的的定义指定两个目录,“src”和“../headers”,make 会按照这个顺序进行搜索。目录由“冒号”分隔。(当然,当前目录永远是最高优先搜索的地方)
 
例如:
vpath %.h ../headers
该语句表示,要求 make 在“../headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)
 
 

使用变量:

1、变量:$

2、可使用变量中的变量:

先看第一种方式,也就是简单的使用“=”号,在“=”左侧是变量,右侧是变量的值,右侧变量的值可以定义在文件的任何一处,也就是说,右侧中的变量不一定非要是已定义好的值,
其也可以使用后面定义的值。如:
foo = $(bar)
bar = $(ugh)
ugh = Huh?
 
all:
    echo $(foo)
 
我们执行“make all”将会打出变量$(foo)的值是“Huh?”( $(foo)的值是$(bar),$(bar)的值是$(ugh),$(ugh)的值是“Huh?”)可见,变量是可以使用后面的变量来定义的。
为了避免上面的这种方法,我们可以使用 make 中的另一种用变量来定义变量的方法。
这种方法使用的是“:=”操作符,如:
x := foo
y := $(x) bar
x := later
其等价于:
y := foo bar
x := later
 
值得一提的是,这种方法,前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。如果是这样:
y := $(x) bar
x := foo
那么,y 的值是“bar”,而不是“foo bar”。
 

3、?=

FOO ?= bar
其含义是,如果 FOO 没有被定义过,那么变量 FOO 的值就是“bar”,如果 FOO 先前被定义过,那么这条语将什么也不做
 

4、变量值的替换

我们可以替换变量中的共有的部分,其格式是“$(var:a=b)”或是“${var:a=b}”,其意思是,把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符”。
看一个示例吧:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
这个示例中,我们先定义了一个“$(foo)”变量,而第二行的意思是把“$(foo)”中所有以“.o”字串“结尾”全部替换成“.c”,所以我们的“$(bar)”的值就是“a.c b.c c.c”。
另外一种变量替换的技术是以“静态模式”(参见前面章节)定义的,如:
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
这依赖于被替换字串中的有相同的模式,模式中必须包含一个“%”字符,这个例子同样让$(bar)变量的值为“a.c b.c c.c”。
 

5、合成使用

再来看看结合第一种技术的例子:
a_objects := a.o b.o c.o
1_objects := 1.o 2.o 3.o

sources := $($(a1)_objects:.o=.c) 
这个例子中,如果$(a1)的值是“a”的话,那么,$(sources)的值就是“a.c b.c c.c”;如果$(a1)的值是“1”,那么$(sources)的值是“1.c 2.c 3.c”。
 

6、追加变量值

可以使用“+=”操作符给变量追加值,如:
objects = main.o foo.o bar.o utils.o
objects += another.o
于是,$(objects)值变成:“main.o foo.o bar.o utils.o another.o”(another.o被追加进去了)
 

7、override指示符

如果有变量是通常 make的命令行参数设置的,那么 Makefile中对这个变量的赋值会被忽略。如果你想在 Makefile 中设置这类参数的值,那么,你可以使用“override”指示符。其语法是:
override =
override :=
override +=
 

8、环境变量

make 运行时的系统环境变量可以在 make 开始运行时被载入到 Makefile 文件中
 

判断语句

libs_for_gcc = -lgnu
normal_libs =
 
foo: $(objects)
ifeq ($(CC),gcc)
  $(CC) -o foo $(objects) $(libs_for_gcc)
else
  $(CC) -o foo $(objects) $(normal_libs)
endif
可见,在上面示例的这个规则中,目标“foo”可以根据变量“$(CC)”值来选取不同的函数库来编译程序。
 
从上面的示例中看到三个关键字:ifeq、else 和 endif。ifeq 的意思表示条件语句的开始,并指定一个条件表达式,表达式包含两个参数,以逗号分隔,表达式以圆括号括起。else 表示条件表达式为假的情况。endif 表示一个条件语句的结束,任何一个条件表达式都应该以 endif 结束。
ifeq:if equal
posted @ 2024-03-17 22:26  lethe1203  阅读(6)  评论(0编辑  收藏  举报