实验二Makefile实验.docx
- 文档编号:29521780
- 上传时间:2023-07-24
- 格式:DOCX
- 页数:14
- 大小:97.21KB
实验二Makefile实验.docx
《实验二Makefile实验.docx》由会员分享,可在线阅读,更多相关《实验二Makefile实验.docx(14页珍藏版)》请在冰豆网上搜索。
实验二Makefile实验
实验二Makefile实验
【实验目的】
1、了解makefile的概念和构成。
2、会使用GNUmake编译一个或者多个文件。
3、掌握Makefile文件的编写。
【实验条件】
1、VMware虚拟机。
2、RedhatLinux平台。
【实验内容与步骤】
1、使用命令编译程序
在这里通过vi编译器来创建两个文件hello.c和makefile。
[hello.c]
*testmakefile
*Emdoor
*
#include"stdio.h"
main()
{
printf("wellcomemdoor!
\n");
}
[makefile]
#testformakefile
CC=gcc
CFLAGS=
all:
hello
hello:
hello.o
$(CC)$(CFLAGS)hello.o-ohello
hello.o:
hello.c
$(CC)$(CFLAGS)-chello.c-ohello.o
clean:
rm-rfhello*.o
在将上述Makefile文件与源文件hello.c保存到同一目录之后,就可以在命令行中输入“make”命令来编译整个项目了。
make在执行过程中,首先会查找到Makefile文件第一条规则中的目标,即上述文件中的all。
根据设定好的规则,该目标需要依赖于hello。
由于all并不是一个已经存在的文件,所以每次在make被调用的时候,显然都需要先检查hello。
继续往下不难发现,hello目标是依赖于hello.o。
当make处理到目标hello.o时,会先查看其对应的依赖对象,这个以来对象是hello.c,此时就会对hello.c进行编译,得到目标文件hello.o,然后是目标文件hello.o被连接,得到可执行文件hello。
先后执行如下命令:
#make
#ls
#./hello
可以看到输出结果:
在Makefile中,并不是所有的目标都对应于磁盘上的文件。
有的目标存在只是为了形成一条规则,从而完成特定的工作,并不生成新的目标文件,这样的目标称为伪目标。
它并不是真正意义上的目标文件,只是一个标签,为了满足Makefile的语法规则而存在的。
在已经给出的Makefile文件中,最后一个目标clean就是伪目标。
它规定了make应该执行的命令。
当make处理到目标clean时,会先查看其对应的依赖对象。
由于clean没有任何依赖对象,所以make会认为该目标是最新的而不会执行任何操作。
为了编译这个目标体,必须手工执行如下命令:
#makeclean
可以看到目标文件hello.o和可执行文件hello被清除。
2、使用预定义变量编译程序
在这里同样是使用以上的hello.c,但是makefile中使用了预定义变量。
[makefile]
#testformakefile
CC=gcc
CFLAGS=
OBJS=hello.o
all:
hello
hello:
hello.o
$(CC)$(CFLAGS)$^-o$@
hello.o:
hello.c
$(CC)$(CFLAGS)-c$<-o$@
clean:
rm-rfhello*.o
命令中的“$<”,“$^”和“$@”则是预定义变量,“$<”表示当前的依赖目标文件(也就是“hello.c”),“$@”表示当前目标文件,“$^”表示所有的依赖文件。
此时make的执行过程与2.1一致,关于预定义变量可参看1.5小节。
先后执行如下命令:
#make
#ls
#./hello
可以看到输出结果:
3、使用预定义变量对多个.c文件编译
创建文件hello1.c,hello2.c,hello.h和makefile。
[hello1.c]
*
*/
#include"stdio.h"
#include"hello2.h"
main()
{
printf("wellcomemdoor!
-hello1\n");
test2();
}
[hello2.c]
/*
*testmakefile
*Emdoor
*/
#include"stdio.h"
test2()
{
printf("wellcomemdoor!
-hello2\n");
}
[hello2.h]
voidtest2(void);
[makefile]
#testformakefile
CC=gcc
CFLAGS=
OBJS=hello1.ohello2.o
all:
hello
hello:
hello1.ohello2.o
$(CC)$(CFLAGS)$^-o$@
hello1.o:
hello1.c
$(CC)$(CFLAGS)-c$<-o$@
hello2.o:
hello2.c
$(CC)$(CFLAGS)-c$<-o$@
clean:
rmhello*.o
命令中的“$<”,“$^”和“$@”则是预定义变量,“$<”表示当前的依赖目标文件(也就是“hello.c”),“$@”表示当前目标文件,“$^”表示所有的依赖文件。
先后执行如下命令:
#ls
#make
#ls
#./hello
#makeclean
可以看到输出结果:
4、使用wildcard对多个.c文件编译
GNUMake里有一个叫'wildcard'的函数,它有一个参数,功能是展开成一列所有符合由其参数描述的文件名,文件间以空格间隔。
语法如下:
$(wildcardPATTERN...)
这个在makefile任何地方出现的字符串,会被匹配任何一个文件名格式的以空格隔开的现有文件列表替换。
如果没有任何文件匹配一个模式,这个模式从’wildcard’的输出中忽略,注意,这和上述的通配符的处理是不一样的。
‘wildcard’函数的一个功能是找出目录中所有的’.c’文件:
$(wildcard*.c)
SOURCES=$(wildcard*.c)
这行会产生一个所有以'.c'结尾的文件的列表,然后存入变量SOURCES里。
当然你不需要一定要把结果存入一个变量。
hello1.c,hello2.c,hello.h这几个文件和3.3一样,makefile如下:
[makefile]
#testformakefile
CC=gcc
CFLAGS=-Wall-O2/*-wall:
生成所有级别的警告信息,-O2:
比-01进行更多的代码优化*/
CFILES=$(wildcard*.c)
OBJS=$(CFILES:
%.c=%.o)
all:
hello
hello:
$(OBJS)
$(CC)$(CFLAGS)-ohello$(OBJS)
.c.o:
/*将.c文件转换为.o文件的后缀规则*/
$(CC)-c$<
clean:
rm-rfhello*.o
先后执行如下命令:
#ls
#make
#ls
#./hello
可以看到输出结果:
【实验原理】
1、编译与链接
一般来说,无论是C、C++、还是pascal,首先要把源文件编译成中间代码文件,在Windows下也就是.obj文件,Linux下是.o文件,即ObjectFile,这个动作叫做编译(compile)。
然后再把大量的ObjectFile合成执行文件,这个动作叫做链接(link)。
编译时,编译器需要的是语法的正确,以及函数与变量的声明的正确。
对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。
一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。
链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。
链接器并不管函数所在的源文件,只管函数的中间目标文件(ObjectFile),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(LibraryFile),也就是.lib文件,在UNIX下,是ArchiveFile,也就是.a文件。
总结一下:
源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。
在编译时,编译器只检测程序语法,和函数、变量是否被声明。
如果函数未被声明,编译器会给出一个警告,但可以生成ObjectFile。
而在链接程序时,链接器会在所有的ObjectFile中找寻函数的实现。
如果找不到,那到就会报链接错误码(LinkerError)。
意思就是说,链接器未能找到函数的实现。
你需要指定函数的ObjectFile。
2、GNUmake
在大型的开发项目中,通常有几十到上百个的源文件,如果每次均手工键入gcc命令进行编译的话,则会非常不方便。
因此,人们通常利用make工具来自动完成编译工作。
这些工作包括:
如果仅修改了某几个源文件,则只重新编译这几个源文件;如果某个头文件被修改了,则重新编译所有包含该头文件的源文件。
利用这种自动编译可大大简化开发工作,避免不必要的重新编译。
实际上,make工具通过一个称为makefile的文件来完成并自动维护编译工作。
makefile需要按照某种语法进行编写,其中说明了如何编译各个源文件并连接生成可执行文件,并定义了源文件之间的依赖关系。
当修改了其中某个源文件时,如果其他源文件依赖于该文件,则也要重新编译所有依赖该文件的源文件。
当Makefile来告诉make命令如何编译和链接文件时,它的规则是:
如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。
3、makefile的基本结构
makefile中一般包含了如下内容:
目标——需要由make工具创建的项目,通常是目标文件和可执行文件。
通常使用“target”一词来表示要创建的项目
依赖——要创建的项目依赖于哪些文件
命令——创建每个项目时需要运行的命令
target:
dependency...
command...
在这里,目标(target)部分指定的是指令(command)执行的结果文件。
此结果文件是目标文件(objectfile)或执行文件。
指令(command)部分定义的指令在依赖(dependency)部分定义的文件内容有所变更或者找不到目标部分的相关文件时被执行。
指令(command)部分每行的缩进必须要使用Tab而不能使用多个空格,否则编译时会有错误产生。
来开始目标部分不仅仅是结果文件,也可以是makeclean,makeall等lable。
例如,假设现在有一个C源文件test.c,该源文件包含有自定义的头文件test.h,则目标文件test.o明确依赖于两个源文件:
test.c和test.h。
另外,你可能只希望利用gcc命令来生成test.o目标文件。
这时,就可以利用如下的makefile来定义test.o的创建规则:
#Thismakefilejustisaexample.
#Thefollowinglinesindicatehowtest.odepends
#test.Candtest.h,andhowtocreatetest.o
test.o:
test.ctest.h
gcc-ctest.c
从上面的例子注意到,第一个字符为#的行为注释行。
第一个非注释行指定test.o为目标,并且依赖于test.c和test.h文件。
随后的行指定了如何从目标所依赖的文件建立目标。
当test.c或test.h文件在编译之后又被修改,则make工具可自动重新编译test.o,如果在前后两次编译之间,test.c和test.h均没有被修改,而且test.o还存在的话,就没有必要重新编译。
这种依赖关系在多源文件的程序编译中尤其重要。
通过这种依赖关系的定义,make工具可避免许多不必要的编译工作。
当然,利用Shell脚本也可以达到自动编译的效果,但是,Shell脚本将全部编译任何源文件,包括哪些不必要重新编译的源文件,而make工具则可根据目标上一次编译的时间和目标所依赖的源文件的更新时间而自动判断应当编译哪个源文件。
一个makefile文件中可定义多个目标,利用maketarget命令可指定要编译的目标,如果不指定目标,则使用第一个目标。
通常,makefile中定义有clean目标,可用来清除编译过程中的中间文件,例如:
clean:
rm-f*.o
运行makeclean时,将执行rm-f*.o命令,最终删除所有编译过程中产生的所有中间文件。
4、makefile变量
GNU的make工具除提供有建立目标的基本功能之外,还有许多便于表达依赖性关系以及建立目标的命令的特色。
其中之一就是变量或宏的定义能力。
如果你要以相同的编译选项同时编译十几个C源文件,而为每个目标的编译指定冗长的编译选项的话,将是非常乏味的。
但利用简单的变量定义,可避免这种乏味的工作:
#Definemacrosfornameofcompiler
CC=gcc
#DefineamacrofortheCCflags
CCFLAGS=-g
//
gcc指令的选项设置
#Aruleforbuildingaobjectfile
test.o:
test.ctest.h
$(CC)-c$(CCFLAGS)test.c
在上面的例子中,CC和CCFLAGS就是make的变量。
GNUmake通常称之为变量,而linux的make工具称之为宏,实际是同一个东西。
在makefile中引用变量的值时,只需变量名之前添加$符号,如上面的$(CC)和$(CCFLAGS)。
5、GNUmake的主要预定义变量
GNUmake有许多预定义的变量,这些变量具有特殊的含义,可在规则中使用。
以下给出了一些主要的预定义变量,除这些变量外,GNUmake还将所有的环境变量作为自己的预定义变量。
预定义变量
变量说明
$*
不包含扩展名的目标文件名称。
$+
所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
$<
第一个依赖文件的名称。
$?
所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
$@
目标的完整名称。
$^
所有的依赖文件,以空格分开,不包含重复的依赖文件。
$%
如果目标是归档成员,则该变量表示目标的归档成员名称。
例如,如果目标名称为mytarget.so(image.o),则$@为mytarget.so,而$%为image.o。
AR
归档维护程序的名称,默认值为ar。
ARFLAGS
归档维护程序的选项。
AS
汇编程序的名称,默认值为as。
ASFLAGS
汇编程序的选项。
CC
C编译器的名称,默认值为cc。
CCFLAGS
C编译器的选项。
CPP
C预编译器的名称,默认值为$(CC)-E。
CPPFLAGS
C预编译的选项。
CXX
C++编译器的名称,默认值为g++。
CXXFLAGS
C++编译器的选项。
FC
FORTRAN编译器的名称,默认值为f77
FFLAGS
FORTRAN编译器的选项
表2.1GNUmake的主要预定义变量
6、隐含规则
GNUmake包含有一些内置的或隐含的规则,这些规则定义了如何从不同的依赖文件建立特定类型的目标。
GNUmake支持两种类型的隐含规则:
*后缀规则(SuffixRule)。
后缀规则是定义隐含规则的老风格方法。
后缀规则定义了将一个具有某个后缀的文件(例如,.c文件)转换为具有另外一种后缀的文件(例如,.o文件)的方法。
每个后缀规则以两个成对出现的后缀名定义,例如,将.c文件转换为.o文件的后缀规则可定义为:
.c.o:
$(CC)$(CCFLAGS)$(CPPFLAGS)-c-o$@$<
*模式规则(patternrules)。
这种规则更加通用,因为可以利用模式规则定义更加复杂的依赖性规则。
模式规则看起来非常类似于正则规则,但在目标名称的前面多了一个%号,同时可用来定义目标和依赖文件之间的关系,例如下面的模式规则定义了如何将任意一个X.c文件转换为X.o文件:
%.c:
%.o
$(CC)$(CCFLAGS)$(CPPFLAGS)-c-o$@$<
7、运行make
直接可在make命令的后面键入目标名可建立指定的目标,如果直接运行make,则建立第一个目标。
还可以用make-fmymakefile这样的命令指定make使用特定的makefile,而不是默认的GNUmakefile、makefile或Makefile。
但GNUmake命令还有一些其他选项,表2.2给出了这些选项。
命令行选项
变量说明
-CDIR
在读取makefile之前改变到指定的目录DIR。
-fFILE
以指定的FILE文件作为makefile。
-h
显示所有的make选项。
-i
忽略所有的命令执行错误。
-IDIR
当包含其他makefile文件时,可利用该选项指定搜索目录。
-n
只打印要执行的命令,但不执行这些命令。
-p
显示make变量数据库和隐含规则。
-s
在执行命令时不显示命令。
-w
在处理makefile之前和之后,显示工作目录。
-WFILE
假定文件FILE已经被修改。
表2.2GNUmake命令的常用命令行选项
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验二 Makefile实验 实验 Makefile