Makefile 笔记

首先要把源文件编译成中间代码文件,在Windows下也就是  .obj  文件,UNIX下是  .o 文件,即  Object File,这个动作叫做编译(compile)。然后再把大量的Object File 合成执行文件,这个动作叫作链接(link)。

   Makefile的规则

目标 : 依赖的条件 (注意冒号两边有空格)

命令  (注意前面用tab键开头)


正如前面所说的,如果一个工程有3个头文件,和8个C文件,我们为了完成前面所述的那三个规则,我们的Makefile应该是下面的这个样子的。

edit : main.o kbd.o command.o display.o insert.o search.o files.o utils.o
cc -o edit      main.o kbd.o command.o display.o insert.o search.o files.o utils.o
//-o 指定输出文件名

main.o : main.c defs.h
cc -c main.c                  //-c 只编译不链接,产生.o文件,不产生执行文件

kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c

clean :
rm edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o

我们可以把这个内容保存在文件为“Makefile”或“makefile”的文件中,然后在该目录下直接输入命令“make”就可以生成执行文件edit。如果要删除执行文件和所有的中间目标文件,那么,只要简单地执行一下“make clean”就可以了。 或者自己随便起名字,比如Makefile111,  不过要用make -f Makefile111或者make --f Makefile111

 

使用变量:

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o 
 
edit : $(objects) 
cc -o edit $(objects) 

main.o : main.c defs.h 
cc -c main.c 
kbd.o : kbd.c defs.h command.h 
。。。。。
。。。。。
utils.o : utils.c defs.h 
cc -c utils.c 

.PHONY : clean 
clean : 
-rm edit $(objects) 

GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个[.o]文件后都写上类似的命令,也就是说上面的
cc -c main.c 
。。
cc -c utils.c 
都可以省去。

每个Makefile 中都应该写一个清空目标文件(.o 和执行文件)的规则,
.PHONY意思表示clean是一个“伪目标”。我们并不生成“clean”这个文件。“伪目标”并不是一个文件,只是一个标签。
“.PHONY”来显示地指明一个目标是“伪目标”
“clean从来都是放在文件的最后”。


伪目标同样可以作为“默认目标”。一个示例就是,如果你的Makefile 需要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,并且,所有的目标文件都写在一个Makefile 中,那么你可以使用“伪目标”这个特性。
Makefile 中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标,其依赖于其它2个目标。伪目标的特性是,总是被执行的。
所以,其它2个目标的规则总是会被执行。
all : prog1 prog2
.PHONY : all 
 
prog1 : prog1.o utils.o 
cc -o prog1 prog1.o utils.o 
 
prog2 : prog2.o 
cc -o prog2 prog2.o 

而在rm命令前面加了一个小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。在Makefile 的命令行前加一个减号“-”(在Tab键之后),标记为不管命令出不出错都认为是成功的。

在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的
源文件分类,并存放在不同的目录中。所以,当make需要去找寻文
件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个
路径告诉make,让make在自动去找。 
vpath是关键字

vpath  
为符合模式的文件指定搜索目录。

vapth使用方法中的需要包含“%”字符。“%”的意思是匹配零
或若干字符。

vpath %.h ../headers 
该语句表示,要求make在“../headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话) 

多目标 
刚刚我们用伪目标的方法实现了一种多目标的生成方法。

当多个目标的生成规则的执行命令是同一个,这可能会可我们带来麻烦,不过好在我们的可以使用一个自动化变量“$@”,这个变量表示着目前规则中所有的目标的集合。
静态模式可以更加容易地定义多目标的规则
: :  
 
... 

看一个例子

objects = foo.o bar.o 
all: $(objects)                 //这里all可以是伪目标
$(objects): %.o: %.c 
$(CC) -c    $< -o $@

上面的例子中,指明了我们的目标all依赖于$object,
对于  $(objects): %.o: %.c 
“%.o”(就是上面的
target-pattern
)表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量$object集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foo   bar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”。而命
令中的“$<”和“$@”则是自动化变量,“$<”表示所有的依赖目标集(也
就是“foo.c bar.c”),“$@”表示目标集(也就是“foo.o bar.o”)。
于是,上面的规则展开后等价于下面的规则: 

foo.o : foo.c 
$(CC) -c   foo.c -o foo.o 
bar.o : bar.c 
$(CC) -c   bar.c -o bar.o


自动生成依赖性 
 
在Makefile 中,我们的依赖关系可能会需要包含一系列的头文件,比
如,如果我们的main.c 中有一句“#include "defs.h"”,那么我们的依赖
关系应该是: 
main.o : main.c defs.h
但是,如果是一个比较大型的工程,你必需清楚哪些C文件包含了哪些头文件,并且,你在加入或删除头文件时,也需要小心地修改Makefile。
大多数C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。例如,如果我们执行下面的命令: 
cc -M main.c
就行了
如果你使用GNU的C/C++编译器,你得用“-MM”参数,不然,“-M”
参数会把一些标准库的头文件也包含进来。



书写规则:
make 会把其要执行的命令行在命令执行前输出到屏幕上。当
我们用“@”字符在命令行前,那么,这个命令将不被make显示出来。
命令: @echo 正在编译XXX模块...... 
显示: 正在编译XXX模块...... 
命令: echo 正在编译XXX模块...... 
显示: echo 正在编译XXX模块...... 


如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。比如你的第一条命令是cd命令,你希望第二条命令得在cd之后的基础上运行,那么你就不能把这两条命令写在两行上,而应该把这两条命
令写在一行上,用分号分隔。比如这样才能打印当前路径

exec: 
cd /home/hchen; pwd 
pwd会打印出“/home/hchen”。


make中的另一种用变量来
定义变量的方法。这种方法使用的是“:=”操作符(其实就相当于perl里面的连字符),如: 
 
x := foo 
y := $(x) bar 
x := later 
 
其等价于: 
 
y := foo bar 
x := later 

值得一提的是,这种方法,前面的变量不能使用后面的变量,只能使
用前面已定义好了的变量。


还有一个比较有用的操作符是“?=”,先看示例: 
 
FOO ?= bar 
 
其含义是,如果FOO没有被定义过,那么变量FOO的值就是“bar”,
如果FOO先前被定义过,那么这条语将什么也不做。
 

还有一个比较有用的操作符是“?=”,先看示例: 
 
FOO ?= bar 
 
其含义是,如果FOO没有被定义过,那么变量FOO的值就是“bar”,
如果FOO先前被定义过,那么这条语将什么也不做,其等价于: 
 
变量值的替换
我们可以替换变量中的共有的部分,其格式是“$(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”。 

Makefile 文件里面

用 :=,表示变量赋值的时候立刻展开。

用 =,表示变量被用的时候才展开。

B :=$(A)时,它只会到这句语句之前去找A的值,因A没有定义所以什么都没有输出。
B = $(A)时,虽然该语句之前A没有定义,但是在其后定义了,用后面定义的。

下面是例子:

animal=frog
var="$(animal) dog cat"
animal=hello
test:
        @echo $(var)

#输出结果是:
#hello dog cat


animal=frog
var:="$(animal) dog cat"
animal=hello
test:
        @echo $(var)

#输出结果是:
#frog dog cat


第二种高级用法是——“把变量的值再当成变量”。先看一个例子: 
 
x = y 
y = z 
a := $($(x)) 
 
在这个例子中,$(x)的值是“y”,所以$($(x))就是$(y),于是$(a)的值就
是“z”。(注意,是“x=y”,而不是“x=$(y)”)

我们可以使用“+=”操作符给变量追加值,如:
 
objects = main.o foo.o bar.o utils.o 
objects += another.o 

于是,我们的$(objects)值变成:“main.o foo.o bar.o utils.o another.o”


还有一种设置变量值的方法是使用define关键字。使用define关键字
设置变量的值可以有换行,这有利于定义一系列的命令
因为命令需要以[Tab]
键开头,所以如果你用define定义的命令变量中没有以[Tab]键开头,
那么make就不会把其认为是命令。
下面的这个示例展示了define的用法: 
 
define two-lines 
echo foo 
echo $(bar) 
endef 

使用条件判断 
下面的例子,判断$(CC)变量是否“gcc”,如果是的话,则使用GNU
函数编译目标。 
 
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 

三个关键字:ifeq、else 和endif。

类似的还有 “ifneq”“ifdef”“ifndef”
注意这些不是命令。不 能以[Tab]键做为开始(不然就被认为是命令),但可以有空格。
你最好不要把自动化变量(如“$@”等)放入条件表达式中,因为自动化变量是在运行时才有的。


函数调用,很像变量的使用,也是以“$”来标识的,其语法如下:
$( ) 
或是
${ } 

示例:
$(subst ee,EE, feet on thestreet),
把“feet  on  the  street”中的“ee”替换成“EE”,返回结果是“fEEt  on  the  strEEt”。

名称:去空格函数— strip。
功能:去掉字串中开头和结尾的空字符。
返回:返回被去掉空格的字符串值。
示例:
$(strip a b c  
把字串“a b c   ”去到开头和结尾的空格,结果是“a b c”

过滤函数— filter。
$(filter , ) 
示例:
sources  :=  foo.c bar .c baz.s ugh. h 
foo: $(sources) 
cc $(filter %.c %.s,$(sources)) -o  foo 

$(filter %.c %.s,$(sources))返回的值是“foo.c bar .c baz.s”

$(dir )
名称:取目录函数— dir。
功能:从文件名序列中取出目录部分。目录部分是指最后一
个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。
返回:返回文件名序列的目录部分。
示例:  $(dir src/foo.c hacks)返回值是“src/./”
  (hacks就是当前目录下的所以返回./)

$(notdir )
功能:从文件名序列中取出非目录部分。非目录部分是指最
后一个反斜杠(“/”)之后的部分。


names  := a b c d 
files := $(foreach  n,$(names),$(n).o) 
上面的例子中,$(name)中的单词会被挨个取出,并存到变量“n”中,
“$(n).o”每次根据“$(n)”计算出一个值, 这些值以空格分隔, 最后作为
foreach 函数的返回,所以,$(files)的值是“a.o b.o c.o d.o”。

一般的,我们会使用命令


gcc  -o calc main.c getch.c getop.c stack.c -I.

 

来编译。-I.是指gcc在当前目录(.)下寻找include文件。


CC = gcc
CFLAGS = -I.     //gcc的标志参数,比如-g -I 等等
DEPS = calc.h
OBJ = main.o getch.o getop.o stack.o
  
%.o : %.c $(DEPS)
    $(CC) -c -o $@ $< $(CFLAGS)
  
calc: $(OBJ)
    $(CC) -o $@ $^ $(CFLAGS)

 


%表示匹配0个或多个字符。例如 %.h”表示所有以“.h”结尾的文件。

首先宏定义DEPS,声明.c文件所依赖的.h文件。然后我们定义一条规则,为所有的.c文件生成一个.o文件。规则描述:.o文件依赖于.c文件和DEPS中声明的.h文件,为了产生.o文件,make需要使用CC中声明的编译器来编译.c文件。

-c 意味着产生object文件,-o $@ 意思是编译生成的文件用上面的%.o来命名,$< 指依赖关系中的第一项(%.c)CFLAGS的定义和之前一样。最后为了简化,我们使用特殊的宏定义 $@ 和 $^ ,分别表示冒号(:)的左右两边。为了让make中所有的规则更具通用性,在Version 4中,我们把所有的include文件作为DEPS的一部分,所有的object文件作为OBJ的一部分:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值