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

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

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

Makefile如下:

.PHONY: all clean

MKDIR = mkdir
RM = rm
RMFLAGS = -rf

CC=gcc

DIR_OBJS=objs
DIR_EXES=exes
DIR_DEPS=deps

DIRS =$(DIR_OBJS) $(DIR_EXES) $(DIR_DEPS)
EXE=complicated
EXE:=$(addprefix $(DIR_EXES)/,$(EXE))
SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o)
OBJS:=$(addprefix $(DIR_OBJS)/,$(OBJS))
DEPS=$(SRCS:.c=.dep)
DEPS:=$(addprefix $(DIR_DEPS)/,$(DEPS))

all:$(DIRS) $(DEPS) $(EXE)
$(DIRS):
    $(MKDIR) [email protected]
$(EXE):$(OBJS)
    $(CC) -o [email protected] $^
$(DIR_OBJS)/%.o:%.c
    $(CC) -o [email protected] -c $^
$(DIR_DEPS)/%.dep:%.c
    @echo "Creating [email protected] ..."
    @set -e;    $(RM) $(RMFLAGS) [email protected];    $(CC) -E -MM $^ >[email protected];    sed ‘s,\(.*\)\.o[:]*,objs/\1.o:,g‘ <[email protected] >[email protected];    $(RM) $(RMFLAGS) [email protected]
clean:
    $(RM) $(RMFLAGS) $(DIRS)

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

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

1,增加了deps文件夹

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

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

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

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

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

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

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

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

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

 .PHONY:all
 all:
     @mkdir test
     @cd    test
     @mkdir subtest

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

 .PHONY:all
 all:
     @mkdir test;     cd    test;     mkdir subtest

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

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

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

如同

EXE=complicated

EXE:=(addprefix(addprefix(DIR_EXES)/,$(EXE))

这样的Makefile,为什么第二个赋值我们是用:=而不是直接=呢?这也是需要注意的小细节,这个在之前的随笔中已经说过,要是用=,会导致无限递归,为什么呢?因为EXE在复制号左边,而右边又有$(EXE)(EXE的引用),这样会无限调用,make报错。不信你可以试试。

最后,来到最难的一个东西:

sed ‘s,\(.*\)\.o[:]*,objs/\1.o:,g‘ <[email protected] >[email protected];

这个语句才是最难的,也是最费力的。

首先要简单说明一下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/youyouself/\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‘ <[email protected] >[email protected];现在来分解这个复杂表达式,首先,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之前的空格去掉了
最后这个<[email protected] >[email protected];这不属于sed的内容了,属于linux和Makefile的东西,[email protected]重定向输入给前面的sed替换操作,[email protected]代表目标在Makefile中,[email protected]是前面的Makefile生成的,<重定向,看方向是输入,就是把[email protected]重定向输入给sed,经过sed替换之后,再输出重定向 > 到[email protected],这个是目标。这样再回过头去看之前那个Makefile就可以看懂了。

原文地址:https://www.cnblogs.com/Liu-Jing/p/8290118.html

时间: 2024-07-28 22:16:58

Makefile学习7————自动生成依赖关系 三颗星的相关文章

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

后面会介绍gcc获得源文件依赖的方法,gcc这个功能就是为make而存在的.我们采用gcc的-MM选项结合sed命令.使用sed进行替换的目的是为了在目标名前加上"objs/"前缀.gcc的-E选项,预处理.在生成依赖关系时,其实并不需要gcc编译源文件,只要预处理就可以获得依赖关系了.通过-E选项,可以避免生成依赖关系时gcc发出警告,以及提高依赖关系的生成效率. 现在,已经找到自动生成依赖关系的方法了,那么如何将其整合到我们complicated项目的Makefile中呢?自动生成

自动生成依赖关系(十)

我们在之前的 makefile 学习中,其目标文件(.o)只依赖于源文件(.c).那么如果在源文件中还包含有头文件,此时编译器如何编译源文件和头文件呢?我们来看看编译行为带来的缺陷:1.预处理器将头文件中的代码直接插入源文件:2.编译器只通过预处理后的源文件产生目标文件:3.规则中以源文件为依赖,命令就可能无法执行. 我们来看看下面的 makefile 有没有问题 makefile 源码 OBJS := func.o main.o hello.out : $(OBJS)     @gcc -o 

make--变量与函数的综合示例 自动生成依赖关系

一.变量与函数的示例 示例的要求1.自动生成target文件夹存放可执行文件2.自动生成objs文件夹存放编译生成的目标文件3.支持调试版本的编译选项4.考虑代码的扩展性完成该示例所需的1.$(wildcardpattern)获取当前工作目录中满足pattern的文件或目录列表2.$(addprefix,_name)给名字列表name的每一个名字增加前缀_prefix关键技巧1.自动获取当前目录下的源文件列表(函数调用) SRC : = $(wildcard *.c) 2.根据源文件列表生成目标

makefile自动生成依赖关系 可自动检测头文件变化

DEBUG=1 CC = gcc CXX=g++ ifeq ($(DEBUG), 1) OPTS=-O0 -g -DDEBUG endif CFLAGS = -fPIC   -I$(COMPILE_DIR) -I$(vesdkdev)  -I$(es_common) -I$(vesdk) -I$(vesdk)/boost-153/include -I$(vesdkproject) CXXFLAGS=$(CFLAGS) Target = libServerCfg.so VPATH = src OB

Makeflie自动生成依赖,自动化编译

在netbeans里开发,有一个重要文件makefile,是用来编译所有的文件. 项目的目录结构如下,扁平的目录结构,如何实现自动化编译,写makefile呢? 第一版 基础版: CC = g++ CFLAGS = -O3 -DNDEBUG SOURCE =AdaBoost.cpp aodeselect.cpp sample.cpp vfan.cpp kdbext2.cpp tan_gen.cpp petal: ${SOURCE} $(CC) -o [email protected] ${SOU

Makefile中头文件在依赖关系中作用

摘于:http://bbs.csdn.net/topics/120024677 (1)在makefile的依赖关系中用不用体现.h头文件?(2)如果在依赖关系中要体现.h头文件,应该体现到什么层次?==============================(1)在makefile的依赖关系中用不用体现.h头文件?============================== 下面是我的一些认识: 头文件中定义的是接口(函数接口,文件外全局变量和宏定义),它的作用是向调用文件封装函数的实现过程.在

make自动生成依赖文件的两种形式

最近编译源文件发现当修改头文件后,make并不会自动把包含此头文件的所有源文件重新编译,而每次都是需要把对应的中间文件清除才能重新编译,非常的麻烦.因此需要make自动对源文件所依赖的头文件进行管理,即make自动生成依赖文件.鉴于本人的刚开始写的博客,很多方面经验不足,比如如何介绍我所用到的知识等,现在只是对我在过程中遇到的问题进行记录,相关的知识可以查看gnu make中文文档,上网等等. 遇到的问题记录如下:1.make在生成依赖文件后并不正确:原因是生成的依赖文件中的目标文件(.d与.o

makefile 自动处理头文件的依赖关系 (zz)

现在我们的Makefile写成这样: all: main main: main.o stack.o maze.ogcc $^ -o [email protected] main.o: main.h stack.h maze.hstack.o: stack.h main.hmaze.o: maze.h main.h clean:-rm main *.o .PHONY: clean按照惯例,用all做缺省目标.现在还有一点比较麻烦,在写main.o.stack.o和maze.o这三个目标的规则时要查

Spring学习总结(5)-Spring依赖关系

参考资料:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-dependencies 1. Spring依赖关系注入基本方式 1.1 构造方法 官方文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-constr