浅析Makefile

来源:互联网 发布:程序员 转行 编辑:程序博客网 时间:2024/05/22 08:05

Makefile极大地方便了linux内核的编译。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,Makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。

 

概述

Makefile有五大部分:

       Makefile : 顶层Makefile

       .config : 内核配置文件

       arch/*/Makefile : 主要Makefile文件

       各个子目录下的Makefile : 大概有300个该文件

       Rules.make : 子文件目录Makefile的规则

      

       顶层Makefile读取内核配置程序的(.comfig)配置文件。

       顶层Makefile主要负责两大主要部分的编译:vmlinux(the resident kernel image,常驻内核映像)和模块(一些模块文件)。通过顶层Makefile递归进入到子目录,形成一个内核代码树。访问子目录需要通过内核的配置。在顶层Makefile有一句:

include arch/$(ARCH)/Makefile

该语句是提供CPU的体系结构给顶层Makefile

       在顶层Makefile还有一句这样:

ARCH := arm

是指明CPU的体系。

       每一个子目录下都有Makefile,作用是完成上一层目录给出的命令。子目录的Makefile使用.congif文件中的信息来构建不同类别的文件列表,并且它在文件结尾都有

include $(TOPDIR)/Rules.make

       Rules.make定义使得子目录中的规则一致。(注:Rules.make位于主目录下)

Makefile语言

       Linux内核的Makefile是为了能在GUN Make下运行而设计出的。GUN Make支持基本的链表处理功能。内核的Makefile使用一种以”if”声明的新颖链表编译以及处理。如:

ifeq (.config,$(wildcard .config))

include .config

       GUN Makefile有两种赋值控制符号:” := ”  “ = ”

“ :=  ”--- 直接展开控制符右侧的赋值,并保存实际值到左侧。

“ = ” --- 是普通的公式定义,保存右侧值到左侧。

 

具体的区别,一下摘自:

http://topic.csdn.net/u/20081012/12/b587ef47-ae30-4b9b-b30a-f9ac78b9ff4a.html

       在定义变量的值时,我们可以使用其它变量来构造变量的值,在Makefile中有两种方式来在用变量定义变量的值。 

先看第一种方式,也就是简单的使用“=”号,在“=”左侧是变量,右侧是变量的值,右侧变量的值可以定义在文件的任何一处,也就是说,右侧中的变量不一定非要是已定义好的值,其也可以使用后面定义的值。如: 

foo = $(bar) 
bar = $(ugh) 
ugh = Huh? 

all: 
echo $(foo) 

我们执行“make all”将会打出变量$(foo)的值是“Huh?” $(foo)的值是$(bar)$(bar)的值是$(ugh)$(ugh)的值是“Huh?”)可见,变量是可以使用后面的变量来定义的。 

这个功能有好的地方,也有不好的地方,好的地方是,我们可以把变量的真实值推到后面来定义,如: 

CFLAGS = $(include_dirs) -O 
include_dirs = -Ifoo -Ibar 

“CFLAGS”在命令中被展开时,会是“-Ifoo -Ibar -O”。但这种形式也有不好的地方,那就是递归定义,如: 

CFLAGS = $(CFLAGS) -O 

或: 

A = $(B) 
B = $(A) 

这会让make陷入无限的变量展开过程中去,当然,我们的make是有能力检测这样的定义,并会报错。还有就是如果在变量中使用函数,那么,这种方式会让我们的make运行时非常慢,更糟糕的是,他会使用得两个make的函数“wildcard”“shell”发生不可预知的错误。因为你不会知道这两个函数会被调用多少次。 

为了避免上面的这种方法,我们可以使用make中的另一种用变量来定义变量的方法。这种方法使用的是“:=”操作符,如: 

x := foo 
y := $(x) bar 
x := later 

其等价于: 

y := foo bar 
x := later 

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

y := $(x) bar 
x := foo 

那么,y的值是“bar”,而不是“foo bar” 

上面都是一些比较简单的变量使用了,让我们来看一个复杂的例子,其中包括了make的函数、条件表达式和一个系统变量“MAKELEVEL”的使用: 

ifeq (0,${MAKELEVEL}) 
cur-dir := $(shell pwd) 
whoami := $(shell whoami) 
host-type := $(shell arch) 
MAKE := ${MAKE} host-type=${host-type} whoami=${whoami} 
endif 

关于条件表达式和函数,我们在后面再说,对于系统变量“MAKELEVEL”,其意思是,如果我们的make有一个嵌套执行的动作(参见前面的嵌套使用make”),那么,这个变量会记录了我们的当前Makefile的调用层数。 

下面再介绍两个定义变量时我们需要知道的,请先看一个例子,如果我们要定义一个变量,其值是一个空格,那么我们可以这样来: 

nullstring := 
space := $(nullstring) # end of the line 

nullstring 
是一个Empty变量,其中什么也没有,而我们的space的值是一个空格。因为在操作符的右边是很难描述一个空格的,这里采用的技术很管用,先用一个 Empty变量来标明变量的值开始了,而后面采用“#”注释符来表示变量定义的终止,这样,我们可以定义出其值是一个空格的变量。请注意这里关于“#”的使用,注释符“#”的这种特性值得我们注意,如果我们这样定义一个变量: 

dir := /foo/bar # directory to put the frobs in 

dir
这个变量的值是“/foo/bar”,后面还跟了4个空格,如果我们这样使用这样变量来指定别的目录——“$(dir)/file”那么就完蛋了。 

还有一个比较有用的操作符是“?=”,先看示例: 

FOO ?= bar 

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

ifeq ($(origin FOO), undefined) 
FOO = bar 
endif


0 0
原创粉丝点击