当前位置: 首页 > news >正文

Linux:Makefile

编译器gcc

使用方式:gcc [ 选项 ] 要编译的⽂件 [ 选项 ] [ ⽬标⽂件 ]

编译分为以下几个步骤:

1.预处理(进⾏宏替换)

预处理功能主要包括宏定义,⽂件包含,条件编译,去注释等。

预处理指令是以#号开头的代码⾏。

实例:

gcc –E hello.c –o hello.i

选项“-E”,该选项的作⽤是让gcc在预处理结束后停⽌编译过程。

选项“-o”是指⽬标⽂件,“.i”⽂件为已经过预处理的C原始程序。

2.编译(⽣成汇编)

在这个阶段中,gcc⾸先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的⼯作, 在检查⽆误后,gcc把代码翻译成汇编语⾔。

⽤⼾可以使⽤“-S”选项来进⾏查看,该选项只进⾏编译⽽不进⾏汇编,⽣成汇编代码。

实例:

gcc –S hello.i –o hello.s

3.汇编(⽣成机器可识别代码)

汇编阶段是把编译阶段⽣成的“.s”⽂件转成⽬标⽂件

读者在此可使⽤选项“-c”就可看到汇编代码已转化为“.o”的⼆进制⽬标代码了

实例:

gcc –c hello.s –o hello.o

4.连接(⽣成可执⾏⽂件或库⽂件)

在成功编译之后,就进⼊了链接阶段。

实例:

gcc hello.o –o hello

动态链接和静态链接

在实际开发中,不可能将所有代码放在⼀个源⽂件中,所以会出现多个源⽂件,⽽且多个源⽂件之间不是独⽴的,⽽会存在多种依赖关系,如⼀个源⽂件可能要调⽤另⼀个源⽂件中定义的函数, 但是每个源⽂件都是独⽴编译的,即每个*.c⽂件会形成⼀个*.o⽂件,为了实现这种依赖关系,则需要将这些源⽂件产⽣的⽬标⽂件进⾏链接,从⽽形成⼀个可以执⾏的程序。这个链接的过程就是静态链接。

静态链接的缺点很明显:

浪费空间:因为每个可执⾏程序中对所有需要的⽬标⽂件都要有⼀份副本,所以如果多个程序对 同⼀个⽬标⽂件都有依赖,如多个程序中都调⽤了printf()函数,则这多个程序中都含有 printf.o,所以同⼀个⽬标⽂件都在内存存在多个副本;

更新⽐较困难:因为每当库函数的代码修改了,这个时候就需要重新进⾏编译链接形成可执⾏程 序。

但是静态链接的优点就是,在可执⾏程序中已经具备了所有执⾏程序所需要的任何东西,在执⾏的时候运⾏速度快。

而动态链接则是把程序按照模块拆分成各个相对独⽴的部分,在程序运⾏时才将它们链接在⼀起形成⼀个完整的程序,⽽不是像静态链接⼀样把所有程序模块都链接成⼀个单独的可执⾏⽂件。

静态库和动态库

静态库是指编译链接时,把库⽂件的代码全部加⼊到可执⾏⽂件中,因此⽣成的⽂件⽐较⼤,但在运⾏时也就不再需要库⽂件了。

其后缀名⼀般为“.a”

动态库与之相反,在编译链接时并没有把库⽂件的代码加⼊到可执⾏⽂件中,⽽是在程序执⾏时由运⾏时链接⽂件加载库,这样可以节省系统的开销。

动态库⼀般后缀名为“.so”。

gcc在编译时默认使⽤动态库。完成了链接之后,gcc就可以⽣成可执⾏⽂件,

如: 

gcc hello.o –o hello

gcc默认⽣成的⼆进制程序,是动态链接的。

make/Makefile

对于一个内容复杂、源文件众多的工程而言,仅使用gcc指令对其文件进行逐个编译会显得过于繁琐低效,因此linux提供了一个对工程进行自动编译的指令:make

make实际执行的是一个名为makefile的文件内的指令(该文件由用户自己创建)

因此,我们要想编译一个工程,不妨写一个makefile文件,这样每次编译时只需调用make指令即可

写makefile的思路:

形成目标文件的依赖关系(即由什么文件生成)+依赖方法(即如何生成)

以一个简单的makefile文件为例:

code:code.c
gcc -o code code.c
.PHONY:clean
clean:
rm -f code

依赖关系:形成code⽂件依赖于code.c

依赖⽅法:gcc -o myproc myproc.c 

项⽬清理 :删除所有的目标文件

make是如何⼯作的

1. make会在当前⽬录下寻找名为“Makefile”或“makefile”的⽂件。

2. 如果找到,它会找⽂件中的第⼀个⽬标⽂件(target),在上⾯的例⼦中,他会找到code⽂件,并把这个⽂件作为最终的⽬标⽂件。

3. 如果 code ⽂件不存在,或是依赖文件code.c与目标文件code相比更新(即文件修改时间更晚),那么,他就会执⾏后⾯所定义的命令来⽣成 myproc 这个⽂件。

4. 如果 code 所依赖的 code.c ⽂件不存在,那么 make 会在当前⽂件中找⽬标文件为 code.c的依赖文件,如果找到则根据上述规则形成code.c⽂件(这里是一个递归调用,即不断寻找依赖文件,当找到最后一个依赖文件时,开始依次执行依赖方法)。

5. 在找寻的过程中,如果出现错误,⽐如最后被依赖的⽂件找不到,那么make就会直接退出,并 报错,⽽对于所定义的命令的错误,或是编译不成功,make不做处理。

clean

clean也是一个目标文件,但它不依赖于任何文件,也不与其他的目标文件直接或间接关联,这导致它后⾯所定义的命令将不会被⾃动执⾏,不过,我们仍然可以使用指令“make clean”来显式执⾏,以此来清除所有的⽬标⽂件,以便重编译。

而.PHONY则是将clean修饰为伪目标,效果是:让make忽略源⽂件和可执⾏⽬标⽂件的M时间对⽐(直白的讲就是clean可以总是被执行)

什么是总是被执行

当我们查看一个文件的信息注意到:

Access :⽂件最后⼀次被访问的时间。

Modify: 文件内容最后一次变更的时间

Change :文件属性最后一次变更的时间

当目标文件的修改时间晚于依赖文件时,此时编译器会认为目标文件已由依赖文件形成,不需再执行,而总是被执行就是无论目标文件的修改实际是否晚于依赖文件,命令都会被执行
 

Makefile的扩展语法

BIN=code          
CC=gcc    
#SRC=$(shell ls *.c)  #采⽤shell命令⾏⽅式,获取当前所有.c⽂件名
SRC=$(wildcard *.c)   #或者使⽤wildcard 函数,获取当前所有.c⽂件名
OBJ=$(SRC:.c=.o)      #将SRC的所有同名.c 替换成为.o 形成⽬标⽂件列表
LFLAGS=-o             #链接选项      
FLAGS=-c              #编译选项
RM=rm -f              #引⼊命令 $(BIN):$(OBJ)        @$(CC) $(LFLAGS) $@ $^          # $@:代表⽬标⽂件名。$^: 代表依赖⽂件列表@echo "linking ... $^ to $@"    
%.o:%.c                             # %.c: 展开当前⽬录下所有的.c。%.o: 同时展开同名.o@$(CC) $(FLAGS) $<             # %<: 对展开的依赖.c⽂件,⼀个⼀个的交给gcc。
@echo "compling ... $< to $@"       # @:不回显命令        
.PHONY:clean    
clean:$(RM) $(OBJ) $(BIN)                # $(RM): 替换,⽤变量内容替换它.PHONY:test    
test:    @echo $(SRC)     @echo $(OBJ)

相关文章:

  • 数字电子技术基础(四十七)——使用Mutlisim软件来模拟74LS85芯片
  • STM32基础教程——DMA+ADC多通道
  • 【后端】【python】利用反射器----动态设置装饰器
  • 智能语音处理+1.1下载需要的库(100%实现)
  • 【Lerobot】加载本地数据LeRobotDataset数据、读取并解析parquet
  • 【c语言】深入理解指针1
  • 排序(java)
  • 任务的状态
  • 投资理财_从0到1:如何用1000元开启你的二级市场投资之旅?
  • 实战5:Python使用循环神经网络生成诗歌
  • 解决virtualbox7.1无法启动3d加速的问题
  • 大数据人工智能
  • 算法的时间复杂度
  • L37.【LeetCode题解】三数之和(双指针思想)
  • Java练习——day2(集合嵌套)
  • Nginx:轻量级高性能的Web服务器与反向代理服务器
  • 开源推荐#6:可爱的临时邮箱服务
  • 模型提示词
  • Ubuntu源码制作openssh 9.9p2 deb二进制包修复安全漏洞 —— 筑梦之路
  • 基于.NET后端实现图片搜索图片库 核心是计算上传图片与库中图片的特征向量相似度并排序展示结果
  • 我要自学网网站/东莞今天新增加的情况
  • 山西省建设厅政务中心网站/如何开展网络营销活动
  • 犀牛云做网站/东方网络律师团队
  • 网站建设 项目经验/有效果的网站排名
  • 网站建设与网页设计专业的/贺州seo
  • wordpress 注册 验证码/班级优化大师官方免费下载