Chinaunix首页 | 论坛 | 博客
  • 博客访问: 337836
  • 博文数量: 92
  • 博客积分: 2500
  • 博客等级: 少校
  • 技术积分: 960
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-21 19:38
文章分类

全部博文(92)

文章存档

2010年(71)

2009年(21)

我的朋友

分类: 嵌入式

2010-03-30 10:44:29

Makefile技术文档(1)

在此,我想多说关于程序编译的一些规范和方法,一般来说,无论是CC++、还是pas,首先要把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。 编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫库文件Library File),也就是 .lib 文件,UNIX下,是Archive File,也就是 .a 文件。

总结一下,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error,在VC下,这种错误一般是:Link 2001错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的Object File.

 

Makefile 要做的事情:

1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。

2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程。

3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。

 

Makefile 规则:

target ... : prerequisites ...

command

target也就是一个目标文件,可以是Object File,也可以是执行文件。

prerequisites就是,要生成那个target所需要的文件或是目标。

prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。

在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个Tab作为开头。

 

文件搜寻

VPATH = src:../headers

上面的的定义指定两个目录,“src”“../headers”make会按照这个顺序进行搜索。目录由冒号分隔。(当然,当前目录永远是最高优先搜索的地方)

vpath %.c foo:bar

vpath % blish

而上面的语句则表示“.c”结尾的文件,先在“foo”目录,然后是“bar”目录,最后才是“blish”目录。

 

PHONY“伪目标

.PHONY: cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff

rm program

cleanobj :

rm *.o

cleandiff :

rm *.diff

“make clean”将清除所有要被清除的文件。“cleanobj”“cleandiff”这两个伪目标有点像子程序的意思。我们可以输入“make cleanall”“make cleanobj”“make cleandiff”命令来达到清除不同种类文件的目的。

 

多目标

bigoutput littleoutput : text.g

generate text.g -$(subst output,,$@) > $@

上述规则等价于:

bigoutput : text.g

generate text.g -big > bigoutput

littleoutput : text.g

generate text.g -little > littleoutput

其中,-$(subst output,,$@)中的“$”表示执行一个Makefile的函数,函数名为subst,后面的为参数。关于函数,将在后面讲述。这里的这个函数是截取字符串的意思,“$@”表示目标的集合,就像一个数组,“$@”依次取出目标,并执于命令。

 

静态模式

: :

....

objects = foo.o bar.o

all: $(objects)

$(objects): %.o: %.c

$(CC) -c $(CFLAGS) $< -o $@

 

 

而命令中的“$<”“$@”则是自动化变量,“$<”表示所有的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也就是“foo.o bar.o”

 

foo.o : foo.c

$(CC) -c $(CFLAGS) foo.c -o foo.o

bar.o : bar.c

$(CC) -c $(CFLAGS) bar.c -o bar.o

 

自动生成依赖性

cc -M main.c

其输出是:

main.o : main.c defs.h

于是由编译器自动生成的依赖关系,这样一来,你就不必再手动书写若干文件的依赖关系,而由编译器自动生成了。需要提醒一句的是,如果你使用GNUC/C++编译器,你得用-MM参数,不然,-M参数会把一些标准库的头文件也包含进来。

gcc -M main.c的输出是:

main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h \

/usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \

/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \

/usr/include/bits/types.h /usr/include/bits/pthreadtypes.h \

/usr/include/bits/sched.h /usr/include/libio.h \

/usr/include/_G_config.h /usr/include/wchar.h \

/usr/include/bits/wchar.h /usr/include/gconv.h \

/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \

/usr/include/bits/stdio_lim.h

gcc -MM main.c的输出则是:

main.o: main.c defs.h

那么,编译器的这个功能如何与我们的Makefile

 

嵌套执行make

我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。那么我们总控的Makefile可以这样书写:

subsystem:

cd subdir && $(MAKE)

其等价于:

subsystem:

$(MAKE) -C subdir

 

 

使用条件判断

判断$(CC)变量是否“gcc”,如果是的话,则使用GNU函数编译目标。

foo: $(objects)

ifeq ($(CC),gcc)

$(CC) -o foo $(objects) $(libs_for_gcc)

else

$(CC) -o foo $(objects) $(normal_libs)

Endif

 

字符串处理函数

1subst

$(subst ,,)

名称:字符串替换函数——subst

功能:把字串中的字符串替换成

返回:函数返回被替换过后的字符串。

示例:

$(subst ee,EE,feet on the street)

feet on the street中的ee替换成EE,返回结果是fEEt on the strEEt

 

2patsubst

$(patsubst ,,)

名称:模式字符串替换函数——patsubst

功能:查找中的单词(单词以空格Tab回车”“换行分隔)是否符合模式,如果匹配的话,则以替换。这里,可以包括通配符%,表示任意长度的字串。如果中也包含%,那么,中的这个%将是中的那个%所代表的字串。(可以用\来转义,以\%来表示真实含义的%字符)

返回:函数返回被替换过后的字符串。

示例:

$(patsubst %.c,%.o,x.c.c bar.c)

把字串x.c.c bar.c符合模式[%.c]的单词替换成[%.o],返回结果是x.c.o bar.o

 

备注:

这和我们前面变量章节说过的相关知识有点相似。

如:

$(var:=)

相当于

$(patsubst ,,$(var))

$(var: =) 则相当于

$(patsubst %,%,$(var))

例如有:objects = foo.o bar.o baz.o

那么,$(objects:.o=.c)$(patsubst %.o,%.c,$(objects))是一样的。

 

3strip

$(strip )

名称:去空格函数——strip

功能:去掉字串中开头和结尾的空字符。

返回:返回被去掉空格的字符串值。

示例:

$(strip a b c )

把字串a b c 去到开头和结尾的空格,结果是a b c

 

4findstring

$(findstring ,)

名称:查找字符串函数——findstring

功能:在字串中查找字串。

返回:如果找到,那么返回,否则返回空字符串。

示例:

$(findstring a,a b c)

$(findstring a,b c)

第一个函数返回a字符串,第二个返回“”字符串(空字符串)

 

5filter

$(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

 

6filter-out

$(filter-out ,)

名称:反过滤函数——filter-out

功能:以模式过滤字符串中的单词,去除符合模式的单词。可以有多个模式。

返回:返回不符合模式的字串。

示例:

objects=main1.o foo.o main2.o bar.o

mains=main1.o main2.o

$(filter-out $(mains),$(objects)) 返回值是foo.o bar.o

 

7sort

$(sort )

名称:排序函数——sort

功能:给字符串中的单词排序(升序)。

返回:返回排序后的字符串。

示例:$(sort foo bar lose)返回bar foo lose

备注:sort函数会去掉中相同的单词。

8word

$(word ,)

名称:取单词函数——word

功能:取字符串中第个单词。(从一开始)

返回:返回字符串中第个单词。如果中的单词数要大,那么返

回空字符串。

示例:$(word 2, foo bar baz)返回值是bar

9wordlist

$(wordlist ,,)

名称:取单词串函数——wordlist

功能:从字符串中取从开始到的单词串。是一个数字。

返回:返回字符串中从的单词字串。如果中的单词数要大,那么返回空字符串。如果大于的单词数,那么返回从开始,到结束的单词串。

示例: $(wordlist 2, 3, foo bar baz)返回值是bar baz

10words

$(words )

名称:单词个数统计函数——words

功能:统计中字符串中的单词个数。

返回:返回中的单词数。

示例:$(words, foo bar baz)返回值是3

备注:如果我们要取中最后的一个单词,我们可以这样:$(word $(words

xt>),)

 

综合一下:

override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))

如果我们的$(VPATH)值是src:../headers,那么$(patsubst %,-I%,$(subst :, ,$(VPATH)))将返回-Isrc -I../headers,这正是ccgcc搜索头文件路径的参数。

 

文件名操作函数

1dir

$(dir )

名称:取目录函数——dir

功能:从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(/)之前的部分。如果没有反斜杠,那么返回./

返回:返回文件名序列的目录部分。

示例: $(dir src/foo.c hacks)返回值是src/ ./

2notdir

$(notdir )

名称:取文件函数——notdir

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

返回:返回文件名序列的非目录部分。

示例: $(notdir src/foo.c hacks)返回值是foo.c hacks

 

3suffix

$(suffix )

名称:取后缀函数——suffix

功能:从文件名序列中取出各个文件名的后缀。

返回:返回文件名序列的后缀序列,如果文件没有后缀,则返回空字串。

示例:$(suffix src/foo.c src-1.0/bar.c hacks)返回值是.c .c

 

4basename

$(basename )

名称:取前缀函数——basename

功能:从文件名序列中取出各个文件名的前缀部分。

返回:返回文件名序列的前缀序列,如果文件没有前缀,则返回空字串。

示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是src/foo src-1.0/b

ar hacks

 

5addsuffix

$(addsuffix ,)

名称:加后缀函数——addsuffix

功能:把后缀加到中的每个单词后面。

返回:返回加过后缀的文件名序列。

示例:$(addsuffix .c,foo bar)返回值是foo.c bar.c

 

6addprefix

$(addprefix ,)

名称:加前缀函数——addprefix

功能:把前缀加到中的每个单词后面。

返回:返回加过前缀的文件名序列。

示例:$(addprefix src/,foo bar)返回值是src/foo src/bar

 

7join

$(join ,)

名称:连接函数——join

功能:把中的单词对应地加到的单词后面。如果的单词个数要比的多,那么,中的多出来的单词将保持原样。如果的单词个数要比多,那么,多出来的单词将被复制到中。

返回:返回连接过后的字符串。

示例:$(join aaa bbb , 111 222 333)返回值是aaa111 bbb222 333

 

 

阅读(838) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~