Linux/Uinx Makefile介绍以及使用方法和代码演示

Linux/Uinx Makefile介绍以及使用方法和代码演示

在写完我们的C语言程序之后,我们通常可以使用shell脚本来编译和链接C语言程序的源文件,但是这种方式有一个缺点:当我们更改了几个源文件的名称之后,shell脚本就会编译失败,为了解决这种问题,我们可以使用Makefile,自动有选择的执行编译链接。

makefile 格式


一个make文件由一系列目标项、依赖项、规则组成。

下面是makefile的格式:

目标项依赖项列表
target:file1 file2 … fileN
规则
<tab>command1
<tab>command2
<tab>other command

目标项通常是需要创建或者更新的文件,但他也可能是make程序要引用的指令标签

依赖项是目标项的提前执行项,也就是说如果一个依赖项比目标项新(也就是说,依赖项的修改时间在目标项之后),或者目标项不存在,那么make就会执行对应的命令来生成或者更新目标项。这种机制可以确保当你修改了一部分源代码后,只有依赖于这部分源代码的目标会被重新编译,而其他不受影响的目标则不会被重新编译。这样可以大大提高编译的效率。

依赖项列表执行示例

假定有三个文件:type.h mysum.c t.c

我们通过以下的方式使用makefile去编译链接这些文件:

myt: type.h t.c mysum.c				#依赖项列表,目标项名称为myt,依赖项为type.h, t.c, mysum.cgcc -o myt t.c mysum.c 		#链接规则

生成的可执行文件名(在例子中是myt)通常与目标项名称匹配。这样可以使得make通过目标项时间戳与依赖项时间戳之间的比较,来决定稍后是否再次构建目标项。

使用mk1作为makefile文件,并且使用make指令去运行该文件:

make -f mk1

make将会构建目标文件(.o文件),并且将命令执行显示为:

gcc -o myt t.c mysum.c

如果此时再次运行make,将会看到信息:

 make: 'myt' is up to date

这将会意味着make程序不会执行,因为根据makefile文件中目标项和依赖项的时间戳的比较,目标项myt是时间戳中最后一个更改的,因此说明myt已经是根据其他依赖项编译成功的了,无需重新编译。

如果我们从依赖项列表中删除一些文件名称,即使这些文件有更改,make也不会执行rule命令。

原因仍然是时间戳的关系,因为依赖项中的文件少了=当这些文件的时间戳不能被检测

makefile中的宏

在makefile中,宏定义的符号——$被替换为他们的值。

CC = gcc										#将CC替换为GCCCFLAGS = -Wall									#将CFLAGS替换为-WallOBJS = t.o mysum.o								#将OBJS替换为t.o mysum.oINCLUDE = -Ipath								#将将INCLUDE替换为-Ipath,这里其实是告诉编译器在path路径下查找头文件的路径myt: type.h $(OBJS)								#创建了一个目标文件type.h,依赖项包括t.o mysum.o$(CC) $(CFLAGS) -o t $(OBJS) $(INCLUDE)		#gcc -Wall -o t t.o mysum.o path

执行之后出现下面的效果:

gcc -Wall   -c -o solve.o solve.c
gcc -Wall   -c -o myt.o myt.c
gcc -Wall -o t solve.o myt.o -Ipath

对于依赖项列表中的每个.o文件,make首先会将相应的.c文件编译成.o文件(这里你可能会有疑问,这个问题放到本段最后去讨论),但是!这只会适用于.c文件,因为所有的.c文件都依赖于.h文件,所以必须在依赖项列表中显式地包含type.h(当然,其他.h文件也是这样的),不过也有其他解决办法,如下所示

t.o: t.c type.hgcc -c t.c
mysum.o: mysum.c type.hgcc -c mysum.c

如果我们将上述目标项添加到makefile文件中,.c文件或者type.h中的任何更改都将触发make重新编译.c文件。

但是这种方法在工程量巨大的时候会非常繁琐,因此还有其他办法。

你可能会有的疑问

在上面的文章的示例中,我们写了这样的代码:

CC = gcc										#将CC替换为GCCCFLAGS = -Wall									#将CFLAGS替换为-WallOBJS = t.o mysum.o								#将OBJS替换为t.o mysum.oINCLUDE = -Ipath								#将将INCLUDE替换为-Ipath,这里其实是告诉编译器在path路径下查找头文件的路径myt: type.h $(OBJS)								#创建了一个目标文件type.h,依赖项包括t.o mysum.o$(CC) $(CFLAGS) -o t $(OBJS) $(INCLUDE)		#gcc -Wall -o t t.o mysum.o path

但是你可能会有一个问题:我的目录中没有.o文件,我也没有去写对应的规则,为什么它会知道自己去编译同名的.c文件呢?

这是因为Makefile中的规则和隐含规则。在Makefile中,指定了myt依赖于mysum.ot.o。当你运行make myt时,make会检查mysum.ot.o是否存在,如果不存在,它会查找能够生成这些文件的规则。

在我们的Makefile中并没有明确的规则来生成solve.omyt.o,但是make有一套默认的隐含规则。其中一个隐含规则是,如果一个.o文件不存在,make会查找同名的.c文件,然后使用C编译器(在你的Makefile中定义为gcc)来编译这个.c文件,生成对应的.o文件。

所以,即使你的Makefile中并没有明确指定如何从.c文件生成.o文件,make也能通过它的隐含规则自动完成这个过程。

按名称编译目标

什么是按名称编译目标呢?这里的名称并不是指的.c文件或者.o文件的名称,而是指的目标项的名称,以如下程序举例:

假设有下面三个文件:

solve.c:

#include <stdio.h>int solve(int a, int b) {return (a - b) * b;
}

myso.h:

#ifndef _MYSO_H
#define _MYSO_H#include <stdio.h>int solve(int a, int b);#endif

myt:包含main函数的文件

#include <stdio.h>
#include "myso.h"int main() {int a, b;scanf("%d %d", &a, &b);printf("%d", solve(a, b));return 0;
}

那么关于上面三个文件的makefile如下所示:

#makefile
CC = gcc
CFLAGS = -Wall
OBJSC = solve.o myt.o
INCLUDE = -Ipathall: myt installmyt: myso.h $(OBJSC)$(CC) $(CFLAGS) -o myt $(OBJSC) $(INCLUDE)solve.o: solve.c myso.hgcc -c solve.cmyt.o: myt.c myso.hgcc -c myt.cinstall: mytecho install myt to /usr/local/binsudo mv myt /usr/local/bin/run: installecho run executable image mytmyt || /bin/trueclean:rm -f *.o 2> /dev/nullsudo rm -f ./usr/local/bin/myt

接下来对上面的程序中的目标项做出分析:

  • all:这个目标项包括两个依赖项mytinstall,当运行这个目标项时
    (指令为make all makefile),会直接执行mytinstall两个目标项
  • myt:正常编译链接指令,生成的文件名称为myt,这里不再解释
  • solve.omyt.o:可有可无,对myt中的依赖项的补充,其实可以由make程序自带的隐式规则自动执行
  • install:将生成的myt文件转移至/usr/local/bin/目录,这个目录中通常放置着用户安装的可执行文件,在系统变量PATH中,该目录地址被包含,因此放在这个目录中的可执行文件可以在terminal中直接执行
  • run:只有install执行之后才会执行,执行myt指令,如果执行成功则结束,如果执行失败就执行/bin/true确保命令的返回值为0,这样可以防止myt运行失败导致整个makefile运行失败
  • clean:删除掉所有.o文件还有可执行文件,包括系统目录中(/usr/local/bin/)对应名称的可执行文件

执行上面的程序时有以下几种方式:

  • make all -f makefile
  • make myt -f makefile
  • make install -f makefile
  • make run -f makefile
  • make clean -f makefile

其功能详细读者已经能够心领神会,这里不再多做解释

makefile 变量 和 后缀规则

makefile支持变量,在makefile中,%是一个与sh中的*类似的通配符变量。

makefile还包含自动变量,这些变量在匹配规则后由make设置,自动变量规定了对目标和依赖项列表中元素的访问(也就是说可以通过自动变量在规则中来表示依赖项名称),下面是makefile的一些自动变量

  • $@:当前目标名
  • $<:第一个依赖项名
  • $^:所有依赖项名
  • $*:不包含扩展名的当前依赖项名
  • $?:比当前目标更新的依赖项列表

make也支持后缀规则,后缀规则并非目标,而是make程序的指令,我们通过一个例子来了解make变量后缀规则

DEPS = type.h
%.o: %.c $(DEPS)$(CC) -c -o $@

在上面的程序中,%.o代表所有.o文件$@表示设置为当前目标名称,这样可以避免为单个.o文件定义单独的目标。

接下来再举一个后缀规则的例子:

CC = gcc			#编译器
CFLAGS = -I.		#表示搜索路径包含当前目录
OBJS = myt.o solve.o
AS = as				#汇编器
DEPS = type.h.s.o:$(AS) -o $< -o $@	#.s->.o的规则
.c.o:$(CC) -c $< -o $@	#.c->.o的规则
%.o: %.c $(DEPS)$(CC) -c -o $@ $<
myt: $(OBJS)$(CC) $(CFLAGS)	-o $@ $^

其中需要解释的有以下几点:

  • AS = as :设置变量AS的值为as,表示使用as作为汇编器
  • DEPS = type.h设置变量DEPS的值为type.h,表示依赖的头文件

然后定义了几个规则:

  • .s.o:这是一个后缀规则,表示如何从.s文件生成.o文件。规则的内容是$(AS) -o $< -o $@,表示使用汇编器as将源文件(<)编译为目标文件(@)。
  • .c.o:这是另一个后缀规则,表示如何从.c文件生成.o文件。规则的内容是$(CC) -c $< -o $@,表示使用C编译器gcc将源文件(<)编译为目标文件(@)。
  • %.o: %.c $(DEPS):这是一个模式规则,表示如何从.c文件和依赖的头文件生成.o文件。规则的内容是$(CC) -c -o $@ $<,表示使用C编译器gcc将源文件(<)编译为目标文件(@)。

这些规则指定:对于每一个.o文件,如果他们的时间戳不同,即.s或者.o文件已经修改,则应当立即创建一个与之对应的.s或者.o文件。

最后定义了一个目标:

  • myt: $(OBJS):这是一个目标,表示如何从目标文件生成可执行文件myt。规则的内容是$(CC) $(CFLAGS) -o $@ $^,表示使用C编译器gcc和编译选项-I.将所有的目标文件链接为可执行文件myt(@)。

嵌套makefile

大型的C语言变成项目通常由数十到数百个源文件组成。为了便于维护,源文件通常被放到不同级别的目录中,每个目录都有自己的makefile。make很容易进入子目录以通过命令执行该目录中的本地makefile:

(cd DIR; $(MAKE)) OR cd DIR && $(MAKE)

关于这个指令,具有如下值得在意的解释:

  • (cd DIR; $(MAKE)):这个命令会在一个子shell中执行。首先,它会切换到DIR目录,然后在该目录中执行Makefile文件。执行完毕后,它会返回到原来的目录。这个命令的优点是不会改变当前shell的工作目录,但是如果cd DIR命令失败,它仍然会尝试执行$(MAKE)命令。

  • cd DIR && $(MAKE):这个命令会在当前的shell中执行。它首先会切换到DIR目录,如果成功,然后在该目录中执行Makefile文件。如果cd DIR命令失败,它不会执行$(MAKE)命令。这个命令的优点是如果cd DIR命令失败,它不会尝试执行$(MAKE)命令,从而避免可能的错误。但是,它会改变当前shell的工作目录

废话不多说,上例子:
假设你有一个项目,它的目录结构如下:

/myprojectMakefile/srcMakefilemain.c/libMakefilemylib.c

在这个项目中,src目录和lib目录都有自己的Makefile文件。你可以在顶级目录的Makefile文件中使用make命令来调用子目录中的Makefile文件,如下所示:

# /myproject/Makefile
all:make -C srcmake -C lib

在这个Makefile文件中,make -C src命令会切换到src目录,并调用该目录中的Makefile文件。同样,make -C lib命令会切换到lib目录,并调用该目录中的Makefile文件。

当然还有其他的两种写法:

# /myproject/Makefile
all:(cd src; $(MAKE))(cd lib; $(MAKE))

或者

# /myproject/Makefile
all:cd src && $(MAKE)cd lib && $(MAKE)

如果子文件中的makefile文件是其他名称怎么办呢?可以这样写:

  1. make -C DIR -f MyMakefile
  2. (cd DIR; $(MAKE) -f MyMakefile)
  3. cd DIR && $(MAKE) -f MyMakefile

这三种方法都可以用来在子目录中执行具有特定名称的Makefile文件。

然后,在src目录和lib目录中的Makefile文件中,你可以定义如何编译该目录中的源文件,例如:

# /myproject/src/Makefile
CC = gcc
CFLAGS = -I../lib
OBJS = main.o%.o: %.c$(CC) $(CFLAGS) -c $< -o $@main: $(OBJS)$(CC) $(CFLAGS) -o $@ $^
# /myproject/lib/Makefile
CC = gcc
CFLAGS = -I.
OBJS = mylib.o%.o: %.c$(CC) $(CFLAGS) -c $< -o $@mylib: $(OBJS)$(CC) $(CFLAGS) -o $@ $^

在这两个Makefile文件中,我们定义了如何从.c文件生成.o文件,以及如何从.o文件生成可执行文件或库文件。

在顶级目录中运行make命令时,make工具会自动编译和链接所有的源文件,无论它们在哪个子目录中。


以上就是本文章的所有内容啦~~~创作不易,希望多多点赞、收藏、关注!!!!
欢迎评论区提问!!有交流才会有进步!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/633356.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【vue】ant-col多列栅格式的表单排列方式布局异常:

文章目录 一、效果&#xff1a;二、解决&#xff1a;三、问题&#xff1a; 一、效果&#xff1a; 二、解决&#xff1a; 在row中添加布局类型&#xff1a;type“flex” 三、问题&#xff1a; 后期正式环境还是存在该问题 >>>.ant-form-item {max-height: 32px; }多…

python期末:数据文件

文件和数据练习题 一、文件的打开、操作和关闭 1.以下关于文件的描述&#xff0c;错误的是&#xff08;&#xff09;。 A 二进制文件和文本文件的操作步骤都是“打开-操作-关闭”&#xff1b; B open() 打开文件之后&#xff0c;文件的内容并没有在内存中&#xff1b; C o…

搭建web网站

要求 1.基于域名[www.openlab.com](http://www.openlab.com)可以访问网站内容为 welcome to openlab!!! 2.给该公司创建三个子界面分别显示学生信息&#xff0c;教学资料和缴费网站&#xff0c;基于[www.openlab.com/student](http://www.openlab.com/student) 网站访问学生信…

【Linux 命令】tree 对目录进行树形展示

目录 1、tree 命令功能展示 2、tree 命令安装 3、tree 命令语法及其参数功能 4、终止 tree 展开树命令 1、tree 命令功能展示 在 Linux 中&#xff0c;我们使用 ll 命令对目录的展示并不太方便我们查看&#xff0c;不太清晰明了&#xff0c;所以我们可以使用 tree 命令以…

分类预测 | Matlab实现KPCA-EBWO-SVM分类预测,基于核主成分分析和改进的白鲸优化算法优化支持向量机分类预测

分类预测 | Matlab实现KPCA-EBWO-SVM分类预测&#xff0c;基于核主成分分析和改进的白鲸优化算法优化支持向量机分类预测 目录 分类预测 | Matlab实现KPCA-EBWO-SVM分类预测&#xff0c;基于核主成分分析和改进的白鲸优化算法优化支持向量机分类预测分类效果基本描述程序设计参…

GO基础进阶篇 (十四)、Http编程

Web基础概念 web应用程序 web程序可以提供浏览器访问的程序。Web应用程序通常采用客户端-服务器模型。客户端是用户使用的Web浏览器或其他Web客户端&#xff0c;而服务器是存储和处理数据的远程计算机。 我们能访问到的任何一个页面或资源&#xff0c;都存在于世界的某一个角落…

ARM的一些基础知识

1.低功耗接口 P-CHANNEL和Q-CHANNEL AMBA低功耗接口&#xff08;一&#xff09;Q_Channel_q-channel p-channel-CSDN博客 AMBA低功耗接口&#xff08;二&#xff09;P_Channel_p channel-CSDN博客 2.WFI和WFE指令 ARM WFI和WFE指令 ARM hint instruction-WFI(Wait For In…

TDengine 创始人陶建辉在汽车 CIOCDO 论坛发表演讲,助力车企数字化转型

当前&#xff0c;汽车行业的数字化转型如火如荼。借助数字技术的充分利用&#xff0c;越来越多的车企进一步提升了成本优化、应用敏捷性、高度弹性和效率。这一转型使得业务应用的开发和管理模式发生了颠覆性的创新&#xff0c;赋予了汽车软件快速响应变化和动态调度资源的能力…

Windows打印堆栈

//打印堆栈 #include <stdarg.h> #include <windows.h> //#include <dbghelp.h> #include <stdio.h> #if _MSC_VER #define snprintf _snprintf #endif #define STACK_INFO_LEN 1024 static int vsnprintf_s(char* buf, size_t len, const char…

仓储的未来:为叉车配备智能设备

近年来&#xff0c;数字化和自动化极大地重塑了仓储行业。叉车是仓库的主力&#xff0c;正在配备智能设备以简化操作。 点击下载Dynamsoft最新版https://www.evget.com/product/3691/download 智能叉车的序列化艺术 序列化是为每个商品或托盘分配唯一标识符&#xff08;通常采…

文件夹里的文件消失了?3个方法轻松找回文件!

“我在电脑上建了个文件夹&#xff0c;用来保存比较重要的文件和数据&#xff0c;但是不知道为什么&#xff0c;我文件夹里的文件莫名其妙就消失了&#xff0c;有什么方法可以找回消失的文件吗&#xff1f;” 为了更好的给文件进行分类&#xff0c;很多用户会选择将文件放置到不…

【并发编程】synchornized原理

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;并发编程 ⛺️稳重求进&#xff0c;晒太阳 目录 Monitor概念 Java对象头 普通对象 数组对象 Monitor(锁) Monitor结构如下&#xff1a; 注意&#xff1a; 原理之synchornized 轻量…

跟杰哥一起学Flutter (一、开发初体验)

引言 学习Flutter的动机 Flutter出了好些年头了&#xff0c;最早可以追溯到 2015.6 的Google I/O大会&#xff0c;会上首次公开介绍了Flutter&#xff0c;不过直到 2017.5 才正式发布首个 Alpha版本&#xff0c;于 2018.12 发布 1.0版本。而后Flutter为了尽快推出产品&#xf…

Git 基本命令与操作流

记录 Git 中的基本命令和创建仓库、提交文件、删除文件等方面的操作 Git 基本命令 git status&#xff1a;查看状态 nothing to commit, working directory clean&#xff1a;所有已跟踪文件在上次提交后都未被更改过&#xff0c;或者说当前目录下没有出现任何处于未跟踪状态…

百度搜索Push个性化:新的突破

作者 | 通用搜索产品研发组 导读 本文简单介绍了百度搜索Push个性化的发展过程&#xff0c;揭示了面临的困境和挑战&#xff1a;如何筛选优质物料、如何对用户精准推荐等。我们实施了一系列策略方法进行突破&#xff0c;提出核心的解决思路和切实可行的落地方案。提升了搜索DAU…

postman案例

一、表单接口 基本正向 有效反向 无效反向 JSON接口 基本正向 有效反向 无效反向 文件上传接口 token 获取token值 一&#xff1a; 二&#xff1a; Bearer 获取的token的值&#xff0c;至于鉴权方式要根据swagger接口文档要求

uniapp踩坑之项目:canvas第一次保存是空白图片

在ctx.draw()回调生成图片&#xff0c;参考canvasToTempFilePath接口文档 // data imgFilePath: null,// 缓存二维码图片canvas路径//js // 首先在draw&#xff08;&#xff09;里进行本地存储 ...... ctx.draw(false, () >{uni.canvasToTempFilePath({ // 把画布转化成临时…

Zookeeper简介

系列文章目录 Zookeeper安装教程 目录 一、Zookeeper简介 二、Zookeeper的数据结构 三、CPA理论 四、BASE 理论 五、ZooKeeper的特性 前言 这是我的学习笔记&#xff0c;以便后面翻阅。 一、Zookeeper简介 ZooKeeper是一个分布式的、开放源码的分布式应用程序协调服务&a…

轻量化/高效扩散模型文献综述

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…

适合进阶学习的 机器学习 开源项目(可快速下载)

目录 开源项目合集[>> 开源的机器学习平台&#xff1a;mlflow/mlflow](https://gitcode.com/mlflow/mlflow)[>> 机器学习路线图&#xff1a;mrdbourke/machine-learning-roadmap](https://gitcode.com/mrdbourke/machine-learning-roadmap)[>> 机器学习理论和…