Makefile 7——自动生成依赖关系 三颗星

后面会介绍gcc获得源文件依赖的方法,gcc这个功能就是为make而存在的。我们采用gcc的-MM选项结合sed命令。使用sed进行替换的目的是为了在目标名前加上“objs/”前缀。gcc的-E选项,预处理。在生成依赖关系时,其实并不需要gcc编译源文件,只要预处理就可以获得依赖关系了。通过-E选项,可以避免生成依赖关系时gcc发出警告,以及提高依赖关系的生成效率。

现在,已经找到自动生成依赖关系的方法了,那么如何将其整合到我们complicated项目的Makefile中呢?自动生成的依赖信息不能直接出现在Makefile中,因为不能动态地改变Makefile中的内容,此时我们需要通过创建依赖关系文件的方式。假设依赖关系的文件以“.dep”结尾,因此我们新创建一个deps文件,用来存放依赖关系文件信息。

Makefile如下:

 1 .PHONY: all clean
 2 
 3 MKDIR = mkdir
 4 RM = rm
 5 RMFLAGS = -rf
 6 
 7 CC=gcc
 8 
 9 DIR_OBJS=objs
10 DIR_EXES=exes
11 DIR_DEPS=deps
12 
13 DIRS =$(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)
14 EXE=complicated
15 EXE:=$(addprefix $(DIR_EXES)/,$(EXE))
16 SRCS=$(wildcard *.c)
17 OBJS=$(SRCS:.c=.o)
18 OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS))
19 DEPS=$(SRCS:.c=.dep)
20 DEPS:=$(addprefix $(DIR_DEPS)/,$(DEPS))
21 
22 all:$(DIRS) $(DEPS) $(EXE)
23 $(DIRS):
24     $(MKDIR) $@
25 $(EXE):$(OBJS)
26     $(CC) -o $@ $^
27 $(DIR_OBJS)/%.o:%.c 
28     $(CC) -o $@ -c $^
29 $(DIR_DEPS)/%.dep:%.c
30     @echo "Creating $@ ..."
31     @set -e;\
32     $(RM) $(RMFLAGS) $@.tmp;\
33     $(CC) -E -MM $^ >$@.tmp;\
34     sed 's,\(.*\)\.o[:]*,objs/\1.o:,g' <$@.tmp >$@;\
35     $(RM) $(RMFLAGS) $@.tmp
36 clean:
37     $(RM) $(RMFLAGS) $(DIRS) 

(这个Makefile废了不少力气才想明白。。。)

和之前的complicated项目的Makefile相比:

1,增加了deps文件夹

2,删除了目标文件创建规则中的foo.h依赖,并将规则中的$<变回了$^

3,增加了了DEPS变量用于存放文件

4,为all目标增加了$(DEPS)

5,增加了一个用于创建依赖关系问价你的规则。在这个规则中,使用了gcc的-E和-MM选项来获取依赖关系。在生成最终的依赖关系文件之前,使用了一个由$@.tmp表示的临时文件,且在依赖文件生成以后将其删除。set -e的作用是告诉shell,在生成依赖关系文件的过程中如果出现任何错误就直接退出。shell异常退出的最终表现就是make会告诉我们出错了,从而停止后续的make工作。如果不设置这一行,当构建依赖出错时,make还会继续后面的工作并最终出错,这并不是我们希望看到的。读者可以测试故意在源文件或者头文件中植入错误并去掉set -e选项观察make的行为和加上set -e有上面不同。

这里还有几个知识点需要补充。

1.对于规则中的每一条命令,make都是在一个新的shell上运行它的。

2.如果希望多个命令在同一个shell中运行,可以用“;”将这些命令连起来。

3.当命令很长时,可以用“\”将一个命令书写成多行。

为了更好的理解第一点,我们做一个实验。现假设需要创建一个test目录,然后在这个test目录下再创建一个subtest子目录。编写Makefile如下:

 

1 .PHONY:all
2 all:
3     @mkdir test
4     @cd    test
5     @mkdir subtest

 

可以看到test和subtest是同级目录并非父子目录,然后用上面提到的知识点更改Makefile:

1 .PHONY:all
2 all:
3     @mkdir test;\
4     cd    test;\
5     mkdir subtest

 

这样就可以达到目的了。不过你可能会想,为什么这里后面的cd和最后一个mkdir不需要在前面加上@呢?那么我们加上试试呢?

 

如果使用了分号“ ;”,表示命令在同一个shell中运行,而且使用“ \”链接一条命令,既然是一条命令,自然不能够识别后面的@cd或者@mkdir,因为最开始的mkdir使用@,让终端不显示执行的指令,后面的cd和mkdir是在前面操作的情况下进行的 ,此时,直接使用命令即可。

还有一个需要注意的地方:

如同

EXE=complicated
EXE:=$(addprefix $(DIR_EXES)/,$(EXE))
这样的Makefile,为什么第二个赋值我们是用:=而不是直接=呢?这也是需要注意的小细节,这个在之前的随笔中已经说过,要是用=,会导致无限递归,为什么呢?因为EXE在复制号左边,而右边又有$(EXE)(EXE的引用),这样会无限调用,make报错。不信你可以试试。
最后,来到最难的一个东西:
sed 's,\(.*\)\.o[:]*,objs/\1.o:,g' <$@.tmp >$@;
这个语句才是最难的,也是最费力的。
首先要简单说明一下linux中sed的用法:
1.简介
sed是非交互式的编辑器。它不会修改文件,除非使用shell重定向来保存结果(这里这个复杂命令就用了重定向来更改)。默认情况下,所有的输出行都被打印到屏幕上。
sed编辑器逐行处理文件(或输入),并将结果发送到屏幕。具体过程如下:首先sed把当前正在处理的行保存在一个临时缓存区中(也称为模式空间),然后处理临时缓冲区中的行,完成后把该行发送到屏幕上。sed每处理完一行就将其从临时缓冲区删除,然后将下一行读入,进行处理和显示。处理完输入文件的最后一行后,sed便结束运行。sed把每一行都存在临时缓冲区中,对这个副本进行编辑,所以不会修改原文件。
2.定址
定址用于决定对哪些行进行编辑。地址的形式可以是数字、正则表达式、或二者的结合。如果没有指定地址,sed将处理输入文件的所有行

3.命令与选项

sed命令告诉sed如何处理由地址指定的各输入行,如果没有指定地址则处理所有的输入行。

此处sed引用此博客, 参考链接:http://www.cnblogs.com/edwardlost/archive/2010/09/17/1829145.html

3.1 sed命令

 命令 功能
 a\

 在当前行后添加一行或多行。多行时除最后一行外,每行末尾需用“\”续行

 c\ 用此符号后的新文本替换当前行中的文本。多行时除最后一行外,每行末尾需用"\"续行
 i\ 在当前行之前插入文本。多行时除最后一行外,每行末尾需用"\"续行
 d 删除行
 h 把模式空间里的内容复制到暂存缓冲区
 H 把模式空间里的内容追加到暂存缓冲区
 g 把暂存缓冲区里的内容复制到模式空间,覆盖原有的内容
 G 把暂存缓冲区的内容追加到模式空间里,追加在原有内容的后面
 l 列出非打印字符
 p 打印行
 n 读入下一输入行,并从下一条命令而不是第一条命令开始对其的处理
 q 结束或退出sed
 r 从文件中读取输入行
 ! 对所选行以外的所有行应用命令
 s 用一个字符串替换另一个
 g 在行内进行全局替换
  
 w 将所选的行写入文件
 x 交换暂存缓冲区与模式空间的内容
 y 将字符替换为另一字符(不能对正则表达式使用y命令)

 

3.2 sed选项

 选项 功能
 -e 进行多项编辑,即对输入行应用多条sed命令时使用
 -n 取消默认的输出
 -f 指定sed脚本的文件名
 
 
 
 
4.退出状态
sed不向grep一样,不管是否找到指定的模式,它的退出状态都是0。只有当命令存在语法错误时,sed的退出状态才不是0。
 
 
 
5.正则表达式元字符
 与grep一样,sed也支持特殊元字符,来进行模式查找、替换。不同的是,sed使用的正则表达式是括在斜杠线"/"之间的模式。
如果要把正则表达式分隔符"/"改为另一个字符,比如o,只要在这个字符前加一个反斜线,在字符后跟上正则表达式,再跟上这个字符即可。例如:sed -n '\o^Myop' datafile
 

 

 元字符 功能 示例
 ^ 行首定位符 /^my/  匹配所有以my开头的行
 $ 行尾定位符 /my$/  匹配所有以my结尾的行
 . 匹配除换行符以外的单个字符 /m..y/  匹配包含字母m,后跟两个任意字符,再跟字母y的行
 * 匹配零个或多个前导字符 /my*/  匹配包含字母m,后跟零个或多个y字母的行
 [] 匹配指定字符组内的任一字符 /[Mm]y/  匹配包含My或my的行
 [^] 匹配不在指定字符组内的任一字符 /[^Mm]y/  匹配包含y,但y之前的那个字符不是M或m的行
 \(..\) 保存已匹配的字符 1,20s/\(you\)self/\1r/  标记元字符之间的模式,并将其保存为标签1,之后可以使用\1来引用它。最多可以定义9个标签,从左边开始编号,最左边的是第一个。此例中,对第1到第20行进行处理,you被保存为标签1,如果发现youself,则替换为your。
 & 保存查找串以便在替换串中引用 s/my/**&**/  符号&代表查找串。my将被替换为**my**
 \< 词首定位符 /\<my/  匹配包含以my开头的单词的行
 \> 词尾定位符 /my\>/  匹配包含以my结尾的单词的行
 x\{m\} 连续m个x /9\{5\}/ 匹配包含连续5个9的行
 x\{m,\} 至少m个x /9\{5,\}/  匹配包含至少连续5个9的行
 x\{m,n\} 至少m个,但不超过n个x /9\{5,7\}/  匹配包含连续5到7个9的行
 
1.并不是只有 / 可作为模式分割符,很多符合如 , ; 都可以,尤其是模式中有 / 时使用其他分割符更方便,这里这个复杂例子使用逗号,做模式分隔符;
sed 's,\(.*\)\.o[:]*,objs/\1.o:,g' <$@.tmp >$@;
现在来分解这个复杂表达式,首先,sed s表示我们想用一个字符串替换另一个字符串,这也是我们使用sed的原因,它的s命令就
可以达到这个效果。
's,\(.*\)\.o[:]*,objs/\1.o:,g'第一次分解,此时需要知道,单引号是一对的,即s前面的'和g后面的'是一个整体单引号,
这也是sed命令的基础,至于单引号和双引号有什么区别,可百度谷歌或者必应。(但是我之前测试的单引号和双引号并不是我搜索所显示的那样,后面再试试吧)
继续分解,s,中s是替换字符串的意思,这个在上面的表格中可以查询到,逗号,表示模式分隔符,在这种有/出现的字符串中,我们选择了逗号,作为分隔符号。
所以下一次分解应该倒下一个逗号处,
\(.*\)\.o[:]*,
这里首先看 .* 它表示匹配任意字符,\( \)是一个整体,也是通过上面的表格得到的,然后转义字符\和.o在一起,把.的作用(匹配除换行符的单个字符)变成普通的.(就是一个字符.),那么这一句话就是
操作字符串所有有.o的且在.0后面(可以有空格)匹配:的零个或多个字符串。
objs/\1.o:,g
这里要解释的是\1.o 这里用了转义字符\加上1,这表示什么呢?尤其是这个1,表示的就是前面\( \)内的字符串,这是组
的概念,如何知道是第几组呢?前面的第一个\(\)的就是第一组,用转义字符\1表示,依次类推。g在sed中表示行内全局替换
这样,我们做一个假设例子来说明。
abc.o : 用这个代表
\(.*\)\.o[:]*
后objs/\1.o:,g之后呢,abc.o :变成了 objs/abc.o: 这里相当于给前面的通用匹配加上了objs/前缀,并且把:和.o之前的空格去掉了
最后这个<$@.tmp >$@;这不属于sed的内容了,属于linux和Makefile的东西,$@.tmp重定向输入给前面的sed替换操作,
$@代表目标在Makefile中,$@.tmp是前面的Makefile生成的,<重定向,看方向是输入,
就是把$@.tmp重定向输入给sed,经过sed替换之后,再输出重定向 > 到$@,这个是目标。
这样再回过头去看之前那个Makefile就可以看懂了。
 
 
 
 
 
 

转载于:https://www.cnblogs.com/yangguang-it/p/6818664.html

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

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

相关文章

集合添加元素python_Python 集合(Set)

Python 集合&#xff08;Set&#xff09; 在本文中&#xff0c;您将学习关于Python集的所有内容;如何创建它们、添加或删除其中的元素&#xff0c;以及在Python中对集合执行的所有操作。 Python中的集合是什么&#xff1f; 集合是项目的无序集合。每个元素都是唯一的&#xff0…

【分享】 codeReview 的重要性

研发都知道代码 Review 的重要性&#xff0c;在代码 Review 也越来越受大家重视&#xff0c;我参与了大量的代码 Review&#xff0c;明显地感受到有效的代码 Review 不但能提高代码的质量&#xff0c;更能促进团队沟通协作&#xff0c;建立更高的工程质量标准&#xff0c;无论对…

线程02

2019独角兽企业重金招聘Python工程师标准>>> 线程中有几个方法需要我们区分 1 sleep方法是表示线程执行到这的时候只是暂时处于“睡眠”状态&#xff0c;在这种状态下线程是不会释放CPU资源的&#xff0c;当到达休眠时间后&#xff0c;线程继续“起来”干活。当线程…

@postconstruct注解方法没有执行_把对象的创建交给spring来管理(注解IOC)

自动按照类型注入/** * 账户的业务层实现类 * * 曾经XML的配置&#xff1a; * <bean id"accountService" class"com.itheima.service.impl.AccountServiceImpl" * scope"" init-method"" destroy-method""> * <pro…

Kubernetes初步学习

今天分享如题&#xff1a; Kubernetes 本篇内容源于工作项目需要自学 但K8s确实现在十分的主流so推荐给大家 最近更新缓慢由于工作太忙惹&#xff0c;忙里偷闲整理愿分享能与君共勉&#x1f4aa; 大家新年快乐&#x1f389; &#x1f508;言归正题&#xff0c;相信很多朋友…

CABAC编码

H&#xff0e;264&#xff0f;AVC标准采用了很多新技术和新方法&#xff0c;大大提高了视频编码效率&#xff0c;其中CABAC便是H&#xff0e;264&#xff0f;AVC采用的新型熵编码方法之一。CABAC采用了高效的算术编码思想&#xff0c;同时充分考虑了视频流相关统计特性&#xf…

【教程分享】Jmeter入门教程

好&#xff01;回归学长每周的教程分享&#xff01; PART2 >今天又来分享Jmter 因为最近好像有相关工作内容 提前准备资修一下 分享仅供参考- JMeter的作用对软件做压力测试 1.能够对HTTP和FTP服务器进行压力和性能测试&#xff0c; 也可以对任何数据库进行同样的测试&…

快速傅里叶变换python_FFT快速傅里叶变换的python实现过程解析

FFT是DFT的高效算法&#xff0c;能够将时域信号转化到频域上&#xff0c;下面记录下一段用python实现的FFT代码。 # encodingutf-8 import numpy as np import pylab as pl # 导入和matplotlib同时安装的作图库pylab sampling_rate 8000 # 采样频率8000Hz fft_size 512 # 采样…

rabbitmq的安装全过程

2019独角兽企业重金招聘Python工程师标准>>> 1 首先下载安装依赖elang 添加yum支持 cd /usr/local/src/ mkdir rabbitmq cd rabbitmq wget http://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm rpm -Uvh erlang-solutions-1.0-1.noarch.rpm rp…

【框架学习分享】HttpRunner

今天文章分为两部分 :) PART1 HttpRunner内容分享/ PART2 关于后厂村儿 10 Minutes HttpRunner: PART 1 首先感谢作者开源&#x1f44d; 因为最近工作需要用到HttpRunner&#xff0c; 于是便趁着周末学了下这个测试框架&#xff0c;感觉还可以~ 所以分享一下今天的学习记…

python报名_2019年少儿Python创意编程比赛报名时间

2019年Python创意编程比赛时间及相关规定&#xff1a;参赛对象 Python创意编程比赛设初中组和高中组。 全国各地初中、高中(含中等职业学校)在校学生均以个人名义报名参加。 参赛步骤 Python创意编程比赛分初评、复评和终评三个阶段&#xff0c;初评和复评以线上形式开展&#…

【分享】后厂村鲜为人知的另一面

好&#xff01;回归学长每周的杂谈分享&#xff01; 有人说“后厂村”像一座孤岛&#xff0c; 这里远离喧嚣&#xff0c;没有生活气息。 而刚刚到厂的学长&#xff0c; 却想和你分享他鲜为人知的另一面。 内容整理源于网络看客原创侵删 说起后厂村&#xff0c;也许是老北京人…

windows 下安装rabbitmq

2019独角兽企业重金招聘Python工程师标准>>> 1、下载 下载地址&#xff1a;http://www.rabbitmq.com/download.html 2、Windows上安装 2.1 安装安装Erlang 下载erlang&#xff1a;http://www.erlang.org/download/otp_win64_17.3.exe 安装&#xff1a; erlang安装完…

【Kubernetes】k8s 的基本使用指令

今天分享如题&#xff1a; Kubernetes 最近更新缓慢由于工作太忙惹&#xff0c;忙里偷闲整理愿能与君共勉&#x1f4aa; K8S对我来说是个新的技术栈&#xff0c;程序员就是需要一直充电&#x1f50b; 加油&#xff0c;一起进步&#x1f4aa; 结构模型 k8s 是经典的一对多模…

c语言 malloc_C语言快速入门——动态内存分配

在前面一系列的字符串操作中&#xff0c;我们都是先定义一个固定大小的字符数组&#xff0c;然后根据所需&#xff0c;或拷贝、或连接、或格式化来为这个数组提供内容。固定大小的数组意味着在程序运行期间&#xff0c;数组所占用的内存是确定的(即划分了固定数量的内存)&#…

【经验分享】工程开发与Coding规范

今天分享分为两部分 :) PART01 工程开发代码规范分享/ PART02 关于某易云自动签到听歌分享- 5Mins DevCoding Rule: PART 1 了解真实工程开发&#x1f3d7;..... 本篇内容分享的宗旨: 学长工作经验之谈仅作分享&#x1f3f7; Ready,Go 代码管理——工具篇 •工程上首先需要…

过滤器与拦截器区别

过滤器与拦截器区别 参考&#xff1a;http://www.cnblogs.com/dreamroute/p/4198087.html?utm_sourcetuicool 过滤器 过滤器是一个程序&#xff0c;它先于与之相关的servlet或JSP页面运行在服务器上。它是随你的web应用启动而启动的&#xff0c;只初始化一次&#xff0c;以后就…

二进制-高效位运算

数独 数独是介绍位运算的好例子&#xff0c;运用位运算和不运用效率差别还是挺大的。我们先看数独需求: 1、当前数字所在行数字均含1-9&#xff0c;不重复 2、当前数字所在列数字均含1-9&#xff0c;不重复 3、当前数字所在宫&#xff08;即3x3的大格&#xff09;数字均含1-9&a…

pytorch resnet50_PyTorch终于能用上谷歌云TPU,推理性能提升4倍,我们该如何薅羊毛?...

晓查 发自 凹非寺量子位 报道 | 公众号 QbitAIFacebook在PyTorch开发者大会上正式推出了PyTorch 1.3&#xff0c;并宣布了对谷歌云TPU的全面支持&#xff0c;而且还可以在Colab中调用云TPU。之前机器学习开发者虽然也能在Colab中使用PyTorch&#xff0c;但是支持云TPU还是第一次…

Android Studio主题设置、颜色背景配置

2019独角兽企业重金招聘Python工程师标准>>> color-themes 效果展示 打开http://color-themes.com/有很多样式可供选择 1. Monokai Sublime Text 3(color theme) 2. Solarized Light (color theme) 3. Visual Studio 2015 Dark(color theme) 导入方式 下载主…