makefile实例讲解.docx
- 文档编号:23867230
- 上传时间:2023-05-21
- 格式:DOCX
- 页数:24
- 大小:21.93KB
makefile实例讲解.docx
《makefile实例讲解.docx》由会员分享,可在线阅读,更多相关《makefile实例讲解.docx(24页珍藏版)》请在冰豆网上搜索。
makefile实例讲解
Makefile学习笔记:
为什么要学习makefile:
工作经验让我认识到会不会写makefile从一个侧面说明了一个人是否有完成大型工程的能力,makefile关系到整个工程的编译规则,一个工程的文件不计其数,其按类型,功能,模块分别放在不同的目录下,makefile定义了一些规则来指定,哪些文件需要先编译,哪些文件需要重新编译,甚至进行更复杂的功能操作,因为makefile就像一个shell脚本一样,其中也可以执行操作系统命令。
而make只是一个命令工具,是一个解释makefile中的指令的命令工具,一般来说IDE即集成开发环境都有这个命令。
Makefile的环境:
我是在linux下进行的实验linux系列下,我用的是ubuntu,当然你可以用redhat红旗后其他,我想都没有什么问题的。
在做实验的时候我会做一些linux写的c/c++例子来演示,以加深理解。
关于程序的编译和链接:
一般来说,c或者是c++,首先把源文件(*.c或*.cpp)编译成为中间代码文件,这个中间代码文件在windows下是*.obj文件在linux或unix是*.o文件即objectfile目标文件这个动作就叫做编译,即把源文件编译成目标文件的过程就叫做编译(compile)。
这以后,再把大量的*.obj或*.o目标文件合成一个可以执行的文件,这个工程就叫做链接link。
编译时,主要是检查程序的语法是否正确,函数,变量是否都有声明。
至于链接呢,主要是链接函数,和全局变量。
一.Makefile的规则
Target:
prerequisites
Command
。
。
。
。
。
。
。
。
。
Target就是一个目标文件,可以使obj或是可执行文件还可以是一个标签label,关于标签label会在下面的文目标中讲解。
所以我们现在只关注obj和可执行文件即可,其实大部分还都是obj文件,或许可执行文件就只有一个。
Prerequisites是先决条件的意识,其实在这里只是依赖的意思,prerequisites在这里是生成target的所需要的文件或目标。
Command就是make需要执行的命令的,任意的shell的命令,如果prerequisites中有一个以上的文件比target文件要新的话吗,command所定义的命令的就会被执行。
这就是makefile的规则,也是makefile中最核心的东西。
一个例子:
这个makefile有六个源文件和六个头文件分别是:
func1.cfunc2.cfunc3.cfunc4.cfunc5.cmain.c
head1.hhead2.hhead3.hhead4.hhead5.hhead.h
上面的c源文件分别会用到其下的头文件各个文件的内容分别是:
func1.c文件
#include"head.h"
#include"head1.h"
voidf1()
{
structstudent1stu;
stu.id=10101;
strcpy(stu.name,"ygt1");
stu.sex='m';
printf("id=%d\tname=%s\tsex=%c\n",stu.id,stu.name,stu.sex);
}
func2.c文件
#include"head.h"
#include"head2.h"
voidf2()
{
structstudent2stu;
stu.id=10102;
strcpy(stu.name,"ygt2");
stu.sex='m';
printf("id=%d\tname=%s\tsex=%c\n",stu.id,stu.name,stu.sex);
}
func3.c文件
#include"head.h"
#include"head3.h"
voidf3()
{
structstudent3stu;
stu.id=10103;
strcpy(stu.name,"ygt3");
stu.sex='m';
printf("id=%d\tname=%s\tsex=%c\n",stu.id,stu.name,stu.sex);
}
func4.c文件
#include"head.h"
#include"head4.h"
voidf4()
{
structstudent4stu;
stu.id=10104;
strcpy(stu.name,"ygt4");
stu.sex='m';
printf("id=%d\tname=%s\tsex=%c\n",stu.id,stu.name,stu.sex);
}
func5.c文件
#include"head.h"
#include"head5.h"
voidf5()
{
structstudent5stu;
stu.id=10105;
strcpy(stu.name,"ygt5");
stu.sex='m';
printf("id=%d\tname=%s\tsex=%c\n",stu.id,stu.name,stu.sex);
}
main.c文件
#include"head.h"
externvoidf1();
externvoidf2();
externvoidf3();
externvoidf4();
externvoidf5();
intmain()
{
f1();
f2();
f3();
f4();
f5();
printf("theend\n");
return0;
}
以上是这个工程的的所有源文件及其代码
head1.h头文件
structstudent1
{
intid;
charname[20];
charsex;
};
head2.h头文件
structstudent2
{
intid;
charname[20];
charsex;
};
head3.h头文件
structstudent3
{
intid;
charname[20];
charsex;
};
head1.h头文件
structstudent3
{
intid;
charname[20];
charsex;
};
head4.h头文件
structstudent4
{
intid;
charname[20];
charsex;
};
head51.h头文件
structstudent5
{
intid;
charname[20];
charsex;
};
head.h头文件
#include
#include
#include
以上是头文件的内容
以上文件都准备好后就要开始写makefile文件了
Makefile文件的可以这么写:
exefile:
main.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o
gcc-oexefilemain.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o
main.o:
main.chead.h
gcc-cmain.c
func1.o:
func1.chead.hhead1.h
gcc-cfunc1.c
func2.o:
func2.chead.hhead2.h
gcc-cfunc2.c
func3.o:
func3.chead.hhead3.h
gcc-cfunc3.c
func4.o:
func4.chead.hhead4.h
gcc-cfunc4.c
func5.o:
func5.chead.hhead5.h
gcc-cfunc5.c
clean:
rm-f*.oexefile
在这个makefile中蓝色的就是目标文件或可执行文件目标文件时那些*.o文件,可执行文件就是exefile文件,它是最终可以执行的文件。
依赖文件就是那些*.c和*.h文件。
每一个*.o文件都有一组依赖文件,*.o文件就是靠这些依赖文件生成。
而生成的这些*.o文件又都是exefile可执行文件的依赖文件,他们生成exefile可执行文件。
依赖关系其实就是说明了目标文件是由哪些文件生成的,换言之,就是目标文件是由哪些文件更新的。
定义好依赖关系下一行就是make执行的命令,这个命令定义了操作系统如何生成这些目标文件的。
命令行开始一定要以tab键开头。
其执行过程就是make会比较target文件和prerequisites文件的修改日期,如果prerequisites文件的日期比target文件的日期要新,或者target不存在的话,make就会执行后续定义的命令。
讲解:
exefile:
main.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o
gcc-oexefilemain.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o
exefile依赖main.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o这些*.o文件,也即是说exefile就是开这些文件生成的。
一开始这些*.o文件是不存在的,那么make就会往下执行语句,而暂时先不执行gcc-omainmain.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o这句命令。
main.o:
main.chead.h
gcc-cmain.c
main.o依赖main.chead.h这两个文件执行其命令生成main.o目标文件,指着往下执行。
。
。
func1.o:
func1.chead.hhead1.h
gcc-cfunc1.c
同main.o的执行。
。
。
func2.o:
func2.chead.hhead2.h
gcc-cfunc2.c
同main.o的执行。
。
。
func3.o:
func3.chead.hhead3.h
gcc-cfunc3.c
同main.o的执行。
。
。
func4.o:
func4.chead.hhead4.h
gcc-cfunc4.c
同main.o的执行。
。
。
func5.o:
func5.chead.hhead5.h
gcc-cfunc5.c
同main.o的执行。
。
。
当这些*.o文件都别生成了后make就会执行第一个依赖和第一个依赖之后的命令
exefile:
main.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o
gcc-oexefilemain.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o
最终生成exefile之可行文件。
clean:
rm-f*.oexefile
clean后面没有依赖文件,make是不会执行其后的命令的,只能makeclean显视的执行。
这句就是伪命令,就是做一些清理,把生成的目标文件*.o文件和exefile删掉。
Make后执行结果:
[yanggentao@wkpmfile]$makeclean
rm-f*.oexefile
[yanggentao@wkpmfile]$make
gcc-cmain.c
gcc-cfunc1.c
gcc-cfunc2.c
gcc-cfunc3.c
gcc-cfunc4.c
gcc-cfunc5.c
gcc-omainmain.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o
[yanggentao@wkpmfile]$
Makeclean后执行结果:
[yanggentao@wkpmfile]$makeclean
rm-f*.oexefile
[yanggentao@wkpmfile]$
根据makefile的依赖规则我们还可以这样写,至于为什么这样写,我们先且不说。
exefile:
main.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o
gcc-oexefilemain.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o
main.o:
main.c
gcc-cmain.c
func1.o:
func1.c
gcc-cfunc1.c
func2.o:
func2.c
gcc-cfunc2.c
func3.o:
func3.c
gcc-cfunc3.c
func4.o:
func4.c
gcc-cfunc4.c
func5.o:
func5.c
gcc-cfunc5.c
clean:
rm-f*.oexefile
这样写是把头文件都给去掉了,这样也对的,makefile的隐式规则会自动找这些在文件里包含的头文件的。
其实Makefile中的命令就像是shell里一样可以使用变量
二.Makefile中使用变量
Makefile中的变量就像是c语言的中宏一样
怎样定义变量呢?
我们在makefile最上面定义一个变量
OBJS=main.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o
引用变量$(OBJS)这就等价于main.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o就像宏一样的会被替换掉。
所以我们的makefile可以这样写了:
OBJS=main.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o
exefile:
$(OBJS)
gcc-oexefile$(OBJS)
main.o:
main.c
gcc-cmain.c
func1.o:
func1.c
gcc-cfunc1.c
func2.o:
func2.c
gcc-cfunc2.c
func3.o:
func3.c
gcc-cfunc3.c
func4.o:
func4.c
gcc-cfunc4.c
func5.o:
func5.c
gcc-cfunc5.c
clean:
rm-f$(OBJS)exefile
这样写很方便,如果你想在这个工程里面加一个文件的话就不会很麻烦。
三.让make自动推导
Gnu的make功能很强大他可以自动推导文件及文件的依赖关系后面的命令所以我们没有必要去为*.O文件都写出其命令。
只要make看到一个*.o文件,它就会自动的吧*.c文件加到依赖关系中,如果make找到一个func2.o那么func2.c就会使func2.o的依赖文件。
并且gcc–cfunc2.c也会被推导出来。
所以我们的makefile就会简单多了:
我们还可以这样写:
OBJS=main.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o
exefile:
$(OBJS)
gcc-oexefile$(OBJS)
clean:
rm-f$(OBJS)exefile
有时候我们会这样写:
OBJS=main.ofunc1.ofunc2.ofunc3.ofunc4.ofunc5.o
exefile:
$(OBJS)
gcc-oexefile$(OBJS)
.PHONY:
clean
clean:
rm-f$(OBJS)exefile
.PHONY:
clean是声明一下clean是一个伪命令。
到这里makefile的基本东西已经讲完了。
还有很多细节,下面来看一下。
四.Makefile的5个内容
1.显示规则
2.隐晦规则
3.变量的定义
4.文件指示
5.注释
1.显示规则:
就是显示的在命令行中写出目标文件的依赖关系
2.隐晦规则:
就是利用make的自动推导的功能
3.变量的定义:
就变量的宏替换
4.文件指示:
其中包括三部分的内容,一个是在一个makefile中引用另一个makefile,就像c语言中的include一样;另一个是根据某些情况指定makefile中的有效部分,就像c语言的预编译#ifdef一样;还有一个就是定义一个多行的命令。
5.注释:
只有行注释用#号字符注释如果你的makefile中用到了#你可以用“\#“转义
Makefile的文件名默认会找这三个文件GNUmakefile,makefile和Makefile
当然你也可以任意起名字比如linuxmakefilemymakefile等但是如果要用的话,那么你就要指定它这样用make–flinuxmakefile或make–fmymakefile
引用其他的makefile
Makefile中也有include命令,这个命令就像cc++里的#include关键字一样包含
Include的语法是
Include
伪目标:
先前的一个例子:
Clean:
Rm–f*.oexefile
Clean就是一个伪目标因为呢,clean并不是一个文件只是一个标签,所以make无法生成它的依赖关系和决定是否要执行它的命令,所以呢我们只能通过显示的指明这个目标才能让其生效。
当然为目标的取名不能喝文件名同名,不然就失去了为目标的意义了。
所以我们可以用.PHONY来显示的指明一个伪目标向make说明不管是否有这个文件,这个目标就是伪目标。
.PHONY:
clean
Clean:
Rm–f*.o
伪目标没有依赖关系但是我们可以为他指定依赖文件。
例子:
这个例子可以生成三个可执行文件
先来看:
在linux下创建toucha.cb.cc.cmain1.cmain2.cmain3.ca.hb.hc.hmain.h执行此命令就会创建好所需要的文件,每个文件内容如下:
a.c文件
#include"main.h"
#include"a.h"
voidfa()
{
structstudentastud;
stud.id=10101;
strcpy(stud.name,"a.c");
stud.sex='f';
printf("studenta-->id=%d\tname=%s\tsex=%c\n",stud.id,stud.name,stud.sex);
}
b.c文件
#include"main.h"
#include"b.h"
voidfb()
{
structstudentbstud;
stud.id=10101;
strcpy(stud.name,"b.c");
stud.sex='f';
printf("studentb-->id=%d\tname=%s\tsex=%c\n",stud.id,stud.name,stud.sex);
}
c.c文件
#include"main.h"
#include"c.h"
voidfc()
{
structstudentcstud;
stud.id=10101;
strcpy(stud.name,"c.c");
stud.sex='f';
printf("studentc-->id=%d\tname=%s\tsex=%c\n",stud.id,stud.name,stud.sex);
}
main1.c文件
#include"main.h"
externvoidfa();
externvoidfb();
externvoidfc();
intmain()
{
fa();
//fb();
//fc();
printf("inthemain1\n");
return0;
}
main2.c文件
#include"main.h"
externvoidfa();
externvoidfb();
externvoidfc();
intmain()
{
fa();
fb();
fc();
printf("inthemain2\n");
return0;
}
main3.c文件
#include"main.h"
externvoidfa();
externvoidfb();
externvoidfc();
intmain()
{
fa();
fb();
fc();
printf("inthemain3\n");
return0;
}
main.h文件
#include
#include
#include
a.h文件
structstudenta
{
intid;
charname[20];
charsex;
};
b.h文件
structstudentb
{
intid;
charname[20];
charsex;
};
c.h文件
structstudentc
{
intid;
charname[20];
charsex;
};
我们的makefile先这样写:
main1:
main1.oa.ob.oc.o
gcc-omain1main1.oa.ob.oc.o
main2:
main2.oa.ob.oc.o
gcc-omain2main2.oa.ob.oc.o
main3:
main3.oa.ob.oc.o
gcc-omain3main3.oa.ob.oc.o
看看执行结果是什么why?
执行结果只执行了第一个生成了main1why;
正确的写法:
all:
main1main2main3
.PHONY:
all
main1:
main1
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- makefile 实例 讲解