Linux gcc和make学习

GCC

gcc全程是(GNU compiler collection CNU编译器套件),是由GNU开发的编程语言编辑器,它包含有gcc、g++,但是不仅限于这两种编译器;gcc不仅可以编译c/c++,还可以编译其他的语言包括java等,gcc可以实现跨不同的硬件平台编译,也就是交叉编译,比如在a平台可以编译b平台上的程序,常用支持linux、windows等。

gcc的安装

# 安装必须有管理员程序
# ubuntu
$ sudo apt update # 更新本地下载列表,获取最新的下载地址
$ sudo apt install gcc g++

# centos
$ sudo yum update
$ sudo yum install gcc g++
# 查看gcc版本
$ gcc -v
$ gcc --version

$ g++ -v

gcc的工作流程

gcc编译器对程序的编译主要分为4个阶段:预编译(预处理)、编译和优化、汇编、链接,gcc的编译器可以将这4个步骤合并成一个,下面分别介绍这四个步骤做了什么事情

  • 预编译(预处理):在这个阶段主要做了三件事情:展开头文件、宏替换、去掉注释行,这一阶段由gcc的预处理器完成,最终得到的还是源文件,文本格式。
  • 编译:这个阶段需要调用gcc编译器对文件进行编译,最终得到一个汇编文件
  • 汇编:这个阶段需要调用gcc的汇编器对文件进行汇编,最终得到一个二进制文件
  • 链接:这个阶段需要调用gcc的链接器对程序需要调用的库进行链接,得到一个可以执行的文件

分步完成:比如有一个为file.c的源文件(麻烦版)

预处理 gcc -E file.c -o file.i
编译 gcc -S file.i -o file.s
汇编 gcc -c file.s -o file.o
链接 gcc file.o -o file
file.c
file.i
file.s
file.o
file可执行文件

一步到位:使用gcc file.c -o aa可以直接生成编译链接后的file的可执行文件aa,直接使用./aa就可以执行。

GCC用到的参数

  • -E:对文件进行预处理,不进行编译,生成的还是源文件
  • -S:对文件进行编译,生成一个汇编文件
  • -c:对文件进行汇编,生成一个二进制文件
  • -o:用来指定生成的文件的名称
  • -I:指定include包含文件的搜索目录

makefile

  • 一般来说,使用gcc的命令行进行程序的编译在单个文件下是非常方便的,但是在实际的应用中,一个工程总不会是由单个文件构成的,往往是由多个文件构成的,当工程的文件逐渐增多,甚至非常庞大,使用GCC进行编译就会变得力不从心,这个时候可以使用make构造工具来完成这个艰巨的任务。
  • make是一个解释makefile中指令的命令工具。make工具在构造项目的时候需要加载一个makefile的文件,makefile关系到整个工程的编译规则,当一个工程中的源文件不计其数,按照类型、功能、模块划分在若干目录中时,这些文件之间存在着各种的依赖关系,makefile可以定义一系列的规则来指定哪一些文件需要先编译,哪一些文件需要后编译,哪一些文件需要重新编译等。
  • mekefile类似于一个shell脚本,所以也可以执行操作系统的一些命令。
  • 使用makefile可以自动化编译,极大的提高了软件开发的效率
  • makefile文件有两种命名的方式makefileMakefile,构建的时候在哪一个目录下执行make命令,哪一个目录的makefile文件就会被加载,因此一个项目中可以有多个makefile文件,分别存放在不同的项目目录中。
# 比如当前目录下有以下文件:add.c,clean,dic.c,head.h,main.c,makefile,sub.c
# 如果是使用gcc来对文件进行编译可以执行一下命令,生成app可执行命令
$ gcc *.c -o app
# 如果使用make命令可以直接
$ make
# 如果编译完成,可以直接使用以下命令删掉所有的编译文件
$ make clean

makefile的规则

# 每条规则的语法格式为:
target1,target2,... : depend1.depend2,...
	command
	......
	......

每一条规则都有三个部分组成:目标(target)、依赖(depend)、命令(command)

  • 命令(command):当前这条规则的动作,一般情况下这个动作都是一个shell命令;动作可以是一个也可以是多个,每个命令前都必须有一个Tab缩进并且独占一行

  • 依赖(depend):规则所必须的依赖条件,如果规则不需要任何依赖,那么依赖可以为空;当前的规则可以是其规则的某一个目标,这就形成了规则之间的嵌套;依赖可以有多个

  • 目标(target):规则中的目标,目标与命令是对应的;规则中可以有多个命令,多条命令可以生成多个目标,所以目标可以有多个;通过规则中的命令值执行一个动作,不生成任何文件,这样的目标叫做伪目标

    举一个不太恰当的比喻,目标、依赖、命令的关系就好像是用材料做蛋糕的关系:目标就是我们要做的蛋糕,依赖就是做蛋糕原材料,命令就是用原材料做蛋糕的具体步骤,一个语法正确的规则,往往是一条shell命令对依赖关系的处理得到目标的过程。

# 规则中的嵌套关系
# 规则1
app:a.o b.o c.o
	gcc a.o b.o c.o -o app
# 规则2
a.o:a.c
	gcc -c a.c -o a.o
# 规则3
b.o:b.c
	gcc -c b.c -o b.o
# 规则4
c.0:c.c
	gcc -c c.c -o c.o

工作原理

  • 在调用make命令编译程序的时候,make会首先找到makefile文件的第1个规则,分析并执行相关的动作。但是需要注意的是,很多时候要执行的动作(命令)中的以来关系是不存在的,如果使用的以来关系不存在这个动作也不会被执行。
  • 遇到以上的情况,首先会将需要的依赖关系生成出来,以此类推,直到makefile的第一条规则的所有的依赖关系都被生成,第一条命令就可以基于这些依赖生成对应的目标,make任务也就结束了。
  • 换句话说,makefile的后续的规则,都是为了第一条命令来服务的。
  • 如果想要执行单条规则,可以使用make 目标名来执行单条规则。

情况1:依赖存在,但是目标不存在

  • 直接根据依赖生成目标

情况2:目标和依赖都不存在,目标时间戳>依赖时间戳,属于正常情况,不执行
情况3:依赖的时间戳>目标的时间戳,make重新生成目标

自动生成

make有自动推导能力,不会完全依赖与makefile。换句话说,make有一些隐藏的默认规则,这些规则就算不编写在makefile,make也会自动执行对应的规则,生产目标文件。

有的时候,我们可以不使用默认的规则生成目标,比如在编译一个多文件的程序中,如果只使用一条规则对多文件进行编译,当我们修改其中的一个文件时,通过make构建工具还是会把整个的文件全部编译一次,如果文件的数量很多,就会非常消耗时间;但是如果我们把默认规则都列举出来,就可以进行分步编译,当只修改其中一两个文件是,不需要对全部的依赖文件都编译一次,可以大大的提高编译的效率。

如果文件有修改,只编译修改的东西,可以加快编译的速度,只编译对应修改的以来关系的链条

makefile的变量

makefile的变量分为三种:自定义变量、预定义变量和自动变量

自定义变量

用户自己定义的变量叫做用户自定义变量,makefile的变量是没有类型的,直接创建变量的名字然后给其赋值即可

# 正确的定义自定义变量
变量名=变量值

在给makefile的变量赋值之后,可以使用$(变量的名字)来将变量的值取出来

# 用户自定义变量
# 定义一个变量
obj=a.o b.o c.o
# 规则1
app:$(obj)
	gcc $(obj) -o app
预定义变量

makefile中有一些已经定义的变量,用户可以直接使用这些变量,不用进行定义,在某一些条件下,makefile会使用这些预定义的值进行编译,这些预定义变量的名字一般都是大写,经常采用的预定义变量如下表所示:

在这里插入图片描述

  • CC:默认值为 cc ,是gcc的意思,一般用来编译c程序
  • CXX:默认值为 g++,一般用来编译c++程序
  • CFLAGS:c语言编译器的编译选项,没有默认值,要用户自己赋值
# 预定义变量
# 定义变量
target=app
obj=a.o b.o c.o
CFLAGS=-O3 # 代码优化(有四种优化级别分别是0-3)
# 规则1
$(target):$(obj)
	$(CC) $(obj) -o $(target) $(CFLAGS)
自动变量

自动变量用来代表这些规则中的目标文件和依赖关系,并且它们只能在规则的命令中使用

在这里插入图片描述

  • $@:表示目标文件的名称。包括扩展名
  • $^:在依赖项中,所有不重复的依赖文件,这些文件直接以空格分开
  • $<:在依赖项中的第一个依赖文件的名称
# 自动变量
# 定义变量
target=app
obj=a.o b.o c.o

# 规则1
$(target):$(obj)
	gcc $^ -o $@

模式匹配

通过一个公式来代表若干满足条件的规则,用于精简makefile

以下代码中可以使用通配符%来匹配名字

# 规则中的嵌套关系
# 规则1
app:a.o b.o c.o
	gcc a.o b.o c.o -o app
# 规则2
a.o:a.c
	gcc -c a.c -o a.o
# 规则3
b.o:b.c
	gcc -c b.c -o b.o
# 规则4
c.0:c.c
	gcc -c c.c -o c.o
# 规则中的嵌套关系
# 规则1
app:a.o b.o c.o
	gcc a.o b.o c.o -o app
%.o:%.c
	gcc -c $< -o $@

函数

makefile中有很多函数并且所有的函数都具有返回值,makefile中的函数写法如下:$(函数名 参数1,参数2,参数3,...)

wildcard函数

这个函数的主要作用是获取指定目录下指定类型的文件名,其返回值是以空格分割的,指定目录下所有符合条件的文件名列表

# 函数原型
$(wildcard PATTERN...)
	参数:指定某个目录,搜索这个路径下指定类型的文件,可以指定多个目录,每一个路径之间用空格隔开

# 返回值:得到若干文件的文件列表,文件名之间使用空格间隔
# 搜索三个不同目录下的.c源格式文件
src=$(wildcard *.c ./sub/*.c /home/robin/b/*.c)
patsubst函数

这个函数的功能是按照孩子定的模式替换指定的文件名的后缀

# 有三个参数,参数之间用逗号隔开
$(patsubst pattern,replacement.text)

# pattern:模式字符串。需要指出要被替换的文件名的后缀是什么,文件名和路径不需要关心,使用 % 即可,如 %.c
# replacement:模式字符串,致命pattern中的后缀最终要被替换为什么;还是使用 % 表示路径和名字,指定新的后缀名即可,比如 %.o
# text:该参数中存储要被替换的原始数据
# 返回值:返回被替换过后的字符串
# 把变量src中所有文件名的后缀从.c替换为.o
src=a.c b.c c.c
obj=$(patsubst %.c,%.o,$(src))

使用函数搜索当前的目录下的c文件,并且修改名字

# 规则中的嵌套关系
# 规则1
app:a.o b.o c.o
	gcc a.o b.o c.o -o app
%.o:%.c
	gcc -c $< -o $@
target=app
src=$(whilecard *.c)# 搜索当前目录下所有的.c文件
obj=$(patsubst %.c,%.o,$(src))
$(target):$(obj)
	gcc $^ -o $@

%.o:%.c
	gcc -c $<

伪声明

target=app
src=$(whilecard *.c)# 搜索当前目录下所有的.c文件
obj=$(patsubst %.c,%.o,$(src))
$(target):$(obj)
	gcc $^ -o $@

%.o:%.c
	gcc -c $<

# 这是一条伪声明,没有依赖,也不生成文件(伪目标)
clean:
	rm $(obj) $(target)

对伪目标的声明,以避免make对文件的时间戳进行检测,需要使用.PHONY关键字,声明方式为.PHONY:伪文件名称

# 最终版
target=app
src=$(whilecard *.c)# 搜索当前目录下所有的.c文件
obj=$(patsubst %.c,%.o,$(src))
$(target):$(obj)
	gcc $^ -o $@

%.o:%.c
	gcc -c $<

# 这是一条伪声明,没有依赖,也不生成文件(伪目标)
.PHONY:clean
clean:
	-rm $(obj) $(target) # 加-表示即使第一条命令执行失败,后面的命令也要继续执行
	...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值