LINUX系统编程_makefile

不定期补充、修正、更新;欢迎大家讨论和指正
本文也就图一乐,覆盖的层面有限,想深入了解的可以看大佬的博客
陈皓大佬原文
整合版

概览

学习makefile前先了解Linux下C语言怎么从源码生成可执行文件。

在这里插入图片描述

gcc参数

-E		//只预处理 不做编译、汇编和链接
-S		//只编译、不做汇编和链接(当然已经囊括了预处理那一步)
-c		//编译和汇编 但不作链接
-o		//将处理的结果保存成一个文件  不然预处理编译的结果会直接输出在屏幕上
-I		//如果头文件与源文件不在一个目录下,要用-I选项指定头文件的目录路径
-g		//添加调试语句,编译时不添加此选项就不能使用gdb调试工具调试。
-Wall 	//显示所有警告信息  

静态库

1.gcc -c 源文件 -o 目标文件
2.ar rcs lib库名.a  目标文件
3.gcc 要编译的文件(源码) 库文件路径(已转换成二进制)  -o  可执行文件

动态库

1.gcc -c 源文件 -o 目标文件 -fPIC(生成与位置无关代码)
2.gcc -shared -o lib库名.so  目标文件
3.gcc 要编译的文件 -o 可执行文件  -l 库名 -L 库路径  

实例

1.源码
vim hello.c

在这里插入图片描述

2.预处理preprocess
展开宏、头文件,替换条件编译,删除注释、空行、空白
gcc -E hello.c   -o  hello.i

在这里插入图片描述

3.编译compile
检查语法规范
gcc -S hello.i  -o hello.s

在这里插入图片描述

4.汇编assemble
将汇编指令翻译成机器指令
gcc -c hello.s -o hello.o

在这里插入图片描述

5.链接link
数据段合并(占坑)
地址回填(当还未做链接时,程序在内存中的位置还不确定,但是程序内的函数与main函数的关系是确定的,
链接后,程序的在的内存地址将赋予main,其他函数的地址也就确定了)
gcc hello.o  -o hello

在这里插入图片描述

平时大多使用gcc -c 源码 -o 目标文件 然后再链接函数库或者其他程序完成一个程序。
但如果一个工程中有一堆需要编译的文件和函数库,作为作者还好,但作为使用者得阅读源码,了解各文件的联系再一个个编译链接,即使有文档可以阅读也会裂开。
相信我们使用源码包的时候并没有自己来编译,而是使用./configure-->make-->make install的方式来安装源码包软件。
所以makefile的功能就像Shell脚本一样,提供“自动化编译”,所有的规则由作者写好,使用者只用简单得执行命令就可以批量编译链接。

makefile关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
————————————————
原文链接:https://blog.csdn.net/haoel/article/details/2886

规则

语法
目标(target) : 生成目标所需要的依赖文件(prerequisites)
	(必须是一个tab缩进)命令command
如
	hello.o : hello.c
		gcc -c hello.c -o hello.o
//*********************************************************************************************
基本原则
1.若想生成文件,检查规则中的依赖条件是否存在,若不存在,则寻找是否有规则用来生成该依赖文件。
如
	hello :hello.o
		gcc hello.o -o hello
生成hello文件依赖于hello.o 但hello.o也是经过hello.c编译出来的,所以查看是否存在以hello.o为目标的规则.
2.检查规则目标是否需要更新,必须先检查它的依赖,只要依赖有任意一个被更新,则目标更新。
//*********************************************************************************************

三个重要变量
$@----指代目标target文件
$^-----指代所有依赖文件
$<-----指代第一个依赖文件,如果用于模式规则中,它可将依赖所有文件列表中依次将依赖文件取出
后续的实验会看到他们的作用
//*********************************************************************************************
makefile函数
src = $(wildcard  *.c)		//找到和makefile一个目录下所有后缀为.c的文件赋值给src
obj = $(patsubst %.c,%o,$(src))	//把src变量里所有后缀为.c的文件替换成.o

实例

正常编译

这里新增了三个简单的用于运算的源文件add.c sub.c div.c,并在hello.c里调用
gcc hello.c add.c sub.c div.c -o a.out

在这里插入图片描述
在这里插入图片描述

makefile_v1

写第一版makefile,并make执行(注意删除之前生成的a.out)

在这里插入图片描述
在这里插入图片描述

makefile_v2

这时有一个问题,若add.c的文件源码修改,我们希望的是只需重新编译修改过的文件即可
但是v1一步到位的方式不并不能实现这样的效果,需要删除生成好的a.out,重新一起编译,显然在源文件数量多的情况下是浪费资源的。
容易想到的方法是,各个源文件先生成中间文件比如add.o,在对.o文件链接,
如果add.c被修改,仅需对add.o重新编译,其他源文件的.o文件不需要改动
写入规则

在这里插入图片描述
在这里插入图片描述

这时我们修改add.c的源码测试效果
注意执行make命令后的差别

在这里插入图片描述

可以发现只编译了add.c和重新链接(看似链接也要和编译一样,都得全部再弄一遍,甚至比v1多了编译add.c的步骤,
实际上,在生成可执行文件中,编译消耗的资源远大于链接消耗的资源,所以这“多此一举”的做法比一步到位的方法更有效率。)
那么makefile是怎么判断哪些目标文件需要更新,哪些是不需要的呢?
其实简单地通过目标文件和依赖文件的修改日期即可自动判断目标文件是否需要更新
目标文件是在依赖文件编译之后的生成文件,修改时间必然比依赖文件晚
但是修改依赖文件源码后,依赖文件的修改时间会比目标文件修改时间新,系统就根据其来判断是否需要更新。

还得注意的是,系统只会执行makefile文件第一条规则,规则内依赖文件也存在规则,才会搜寻依赖文件的规则,以此类推
所以当
a.out:hello.o add.o div.o sub.o
    gcc hello.o add.o div.o sub.o -o a.out
规则在
hello.o:hello.c
    	gcc -c hello.c -o hello.o
之后的话,系统发现hello.c是存在的依赖文件,所以直接执行命令,a.out的规则就不会执行,
即makefile第一条规则是最终所需要生成的文件,也可以在首行添加ALL:a.out说明最终生成的文件后,规则的次序可以随意

在这里插入图片描述

makefile_v3

使用makefile函数和三个自动变量模式化
$@----指代目标target文件
$^-----指代所有依赖文件
$<-----指代第一个依赖文件,如果用于模式规则中,它可将依赖所有文件列表中依次将依赖文件取出
当规则只有一个依赖文件$^和$<都可以使用,但还是建议使用$<来模式化
src = $(wildcard  *.c)		//找到和makefile一个目录下所有后缀为.c的文件赋值给src
obj = $(patsubst %.c,%o,$(src))	//把src变量里所有后缀为.c的文件替换成.o

在这里插入图片描述

makefile_v4

模式化的好处是方便拓展
比如现在想添加乘法模块,在之前的版本需要追加规则并在a.out的规则添加依赖文件,这种方法在“自动化编译”的思想中是很笨拙的。
v3版本虽然规范化,但仍需自行添加mul规则,所以需要更加的模式化。
可以发现除了a.out的规则外,其他规则的格式和命令是一模一样的,显然可以在里面做文章。

添加clean规则 用于删除.o文件 为了后续方便操作,我还写上了a.out(注意是$(obj)别是$(src)否则删除的是源码文件)
只需执行make clean即可删除.o文件和a.out 添加-n选项是只查看clean的命令 不执行。
如果当前目录下有clean文件,make clean 就会出错了,为了避免这种问题用PHONY为clean创建一个伪目标

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值