Makefile自动生成依赖,自动化编译(3)

seq.d : seq.c
@set -e; \
gcc -MM $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
-include seq.d

生成规则中的执行命令解释

第一个命令@set -e@关键字告诉make不输出该行命令;set -e的作用是,当后面的命令的返回值非0时,立即退出。

那么为什么要把几个命令写在"同一行"(是对于make来说,因为\的作用就是连接行),并用分号隔开每个命令?因为在Makefile这样做才能使上一个命令作用于下一个命令。这里是想要set -e作用于后面的命令。

第二个命令gcc -MM $< > $@.$$$$, 作用是根据源文件生成依赖关系,并保存到临时文件中。内建变量$<的值为第一个依赖文件(那seq.c)$$$$为字符串"$$",由于makefile中所有的$字符都是特殊字符(即使在单引号之中!),要得到普通字符$,需要用$$来转义; $$shell的特殊变量,它的值为当前进程号;使用进程号为后缀的名称创建临时文件,是shell编程常用做法,这样可保证文件唯一性。这个临时文件的名字为seq.d.1223 $@代表目标文件 seq.d gcc –MM seq.c > seq.d.1223

注意这一步的输出是:

a2de3.o : a2de3.cpp a2de3.h incrementalLearner.h learner.h \

instanceStream.h instance.h capabilities.h xxxyDist3.h xxyDist.h \

xyDist.h smoothing.h utils.h mtrand.h FILEtype.h crosstab.h \

correlationMeasures.h xxxyDist.h globals.h

既不包含子目录路径,也不包含 a2de3.d所以需要加工

第三个命令作用是将目标文件加入依赖关系的目录列表中,并保存到目标文件。唯一要注意的是内建变量$*$*的值为第一个依赖文件去掉后缀的名称(这里即是seq)

sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@;

sed linux文本处理工具

这条sed命令的结构是s/match/replace/g。有时为了清晰,可以把每个/写成逗号,即这里的格式s,match,replace,g
该命令表示把源串内的match都替换成replaces指示match可以是正则表达式。
g
表示把每行内所有match都替换,如果去掉g,则只有每行的第1match被替换(实际上不需要g,因为一个.d文件中,只会在开头有一个main.o:)

所以要寻找的目标是:\($*\)\.o[ :]*

替换成: \1.o $@ :

 使用到了4个正则表示式的知识点。

$*第一个依赖文件去掉后缀的名称假设为main

     1.  \(main\)为创建一个字符标签,给后边的replacement-pattern使用。如\1.o,展开后就是main.o \显然是为了转义

    2.  \. 在正则表达式中'.'作用是匹配一个字符。所以需要使用转义元字符'\'来转义。

3. [ :] 匹配一组字符里的任意字符。这个[] 里面放的是一个空格和一个冒号

4 *匹配0个或多个前一字符

所以正则式[ :]*,表示若干个空格或冒号,(其实一个.d里只会有一个冒号,如果这里写成[ ]*:,即匹配若干个空格后跟一个冒号,也是可以的)

去掉转义后就是寻找 main.o: 或者 main.o :这样的字符串替换成

\1.o也是为了转义,不然就真的替换成了1.o了,这里是字符标签main

所以替换成 main.o main.d :

最后的效果就是

把临时文件main.d.temp的内容main.o : main.c command.h改为main.o main.d : main.c command.h,并存入main.d文件的功能。

这个命令为子文件版的打下了基础

第四个命令是将该临时文件删除。

如果把内建变量都替换成其值后,实际内容是这样子:

全选复制放进笔记

seq.d : seq.c

@set -e; \

gcc -MM seq.c > seq.d.$$$$; \

sed 's,\(seq\)\.o[ :]*,\1.o seq.d : ,g' < seq.d.$$$$ > seq.d; \

rm -f seq.d.$$$$

-include seq.d

最后,再把Makefile的模式匹配应用上,就完成自动生成头文件依赖功能了:

  $'\t' command not find

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

这是因为在规则后面有换行符,把换行符删去即可

第五版 子文件版

但是src文件夹下面文件太多了,最好能有许多的文件夹,一方面区分自己写的和其它文件,另一方面把aode优化算法,放在一个文件夹下,便于管理,也能达到自动编译的效果

根据要把 naomiaode.o的路径写全的提示,其实只要在sed文本替换上做文章就好了。

文件夹结构:

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

CC = g++

CFLAGS = -O3 -DNDEBUG

SOURCES = $(wildcard *.cpp )

OBJS := $(patsubst %.cpp, %.o,$(SOURCES))

SUB_DIR1 = naomi

SUB_SOURCES1 = $(wildcard $(SUB_DIR1)/*.cpp)

SUB_OBJS1 = $(patsubst %.cpp, %.o, $(SUB_SOURCES1))

#一个子目录

SUB_DIR2 = test

SUB_SOURCES2 = $(wildcard $(SUB_DIR2)/*.cpp)

SUB_OBJS2 = $(patsubst %.cpp, %.o, $(SUB_SOURCES2))

#一个子目录

petal: $(SUB_OBJS1) $(SUB_OBJS2) $(OBJS)

@echo "源文件:" $(SOURCES)

@echo "在子目录下的源文件: " $(SUB_SOURCES1) $(SUB_SOURCES2)

@echo "目标文件:" $(OBJS)

@echo "子目录下的目标文件: " $(SUB_OBJS1) $(SUB_OBJS2)

$(CC) -o $@ $(SUB_OBJS1) $(SUB_OBJS2) $(OBJS) $(CFLAGS)

%.d: %.cpp

@echo "create depend" $< $@ $(subst naomi/,,$*)

@set -e; \

gcc -MM $< > $@.$$$$; \

if [ $(findstring $(SUB_DIR1)/,$<)a = $(SUB_DIR1)/a ]; then sed 's,\($(subst $(SUB_DIR1)/,,$*)\)\.o[ :]*,$(SUB_DIR1)\1.o $@ : ,g' < $@.$$$$ > $@; rm -f $@.$$$$; \

elif [ $(findstring $(SUB_DIR2)/,$<)a = $(SUB_DIR2)/a ]; then sed 's,\($(subst $(SUB_DIR2)/,,$*)\)\.o[ :]*,$(SUB_DIR2)/\1.o $@ : ,g' < $@.$$$$ > $@; rm -f $@.$$$$;\

else sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; rm -f $@.$$$$;\

fi

-include $(OBJS:.o=.d) ${SUB_OBJS1:.o=.d} ${SUB_OBJS2:.o=.d}

.PHONY:clean

clean:   

@echo "开始清理项目..."

@echo "正在删除所有的.d文件"

rm -f $(OBJS:.o=.d) $(SUB_OBJS1:.o=.d) $(SUB_OBJS2:.o=.d)

@echo "正在删除所有的.o文件"

rm -rf $(OBJS) ${SUB_OBJS1} ${SUB_OBJS2}

@echo "正在删除petal"

rm -f petal.exe

@echo "清理结束"

值得一提的就是

$(subst naomi/,,$*)

$(subst FROM,TO,TEXT)

函数名称:字符串替换函数

函数功能:把字符串TEXT中的FROM字符串替换为TO

返回值:替换后的新字符串

$(subst ee,EE,feet on the stree) //替换"feet on the street"中的ee为EE。结果得到字符串"fEEt on the strEEt"

当处理naomi文件夹下的文件时,如果仍旧采用第四版,那么

naomiaode.o: naomi/naomiaode.cpp naomi/naomiaode.h \

naomi/../incrementalLearner.h naomi/../learner.h \

naomi/../instanceStream.h naomi/../instance.h naomi/../capabilities.h \

naomi/../xxxxyDist.h naomi/../xxxyDist.h naomi/../xxyDist.h \

naomi/../xyDist.h naomi/../smoothing.h naomi/../utils.h \

naomi/../mtrand.h naomi/../FILEtype.h naomi/../crosstab.h

而没有 naomiaode.d

因为$* 是第一个依赖文件去掉后缀名,第一个依赖文件是naomi/naomiaode.cpp

去掉后缀名后是naomi/naomiaode,而gcc –mm的输出是 naomiaode.o,根本找不到

naomi/naomiaode,所以我们要把naomi/naomiaode里的naomi/去掉,使用替换函数把naomi/替换成空的,就能找到要替换的naomiaode.o了

我们的目标是:

naomi/naomiaode.o naomi/naomiaode.d : naomi/naomiaode.cpp naomi/naomiaode.h \

naomi/../incrementalLearner.h naomi/../learner.h \

naomi/../instanceStream.h naomi/../instance.h naomi/../capabilities.h \

naomi/../xxxxyDist.h naomi/../xxxyDist.h naomi/../xxyDist.h \

naomi/../xyDist.h naomi/../smoothing.h naomi/../utils.h \

naomi/../mtrand.h naomi/../FILEtype.h naomi/../crosstab.h

所以要给1.o加上文件夹名,而目标文件$@是自带路径的,无需处理。

但是你还要判断处理的文件究竟属于哪个文件夹好把naomi/或者test/去掉,所以想到了条件语句if,makefile有自己的条件语句,但是

ifeq ( $(findstring ${SUB_DIR1}/,$<) , $(SUB_DIR1) );

永远都不等于,为什么?因为条件判断里不可以使用自动变量,$<永远不会被展开!

所以不能使用ifeq只能使用 shell condition

all:

    if [ "$(BUILD)" = "debug" ]; then  echo "build debug"; else echo "build release"; fi

    echo "done"

尽量放在一行里,不要用 == if和[] 之间有空格,a = b之间有空格 a=b是错误的

if [ $(findstring $(SUB_OBJS1)/,$<) = $(SUB_OBJS1)/ ];

$(findstring FIND,IN)

        函数名称:查找字符串函数

        函数功能:在字符串IN中查找FIND字符串

        返回值:如果在IN中找到FIND子字符串,则返回FIND,否则返回空

        函数说明:收索是严格的文本匹配

                  $(findstring a,a b c)    返回 a

                  $(findstring a,b c)      返回空字符

查找在第一个依赖文件里有没有naomi/如果有,就返回naomi/=naomi/

但是 

shell脚本报错:"[: =: unary operator expected"

在匹配字符串相等时,我用了类似这样的语句:

if [ $STATUS == "OK" ]; then     

echo "OK"

fi

在运行时出现了 [: =: unary operator expected 的错误,

究其原因,是因为如果变量STATUS值为空,那么就成了 [ = "OK"] ,显然 [ 和 "OK" 不相等并且缺少了 [ 符号,所以报了这样的错误。当然不总是出错,如果变量STATUS值不为空,程序就正常了,所以这样的错误还是很隐蔽的。

用下面的方法也能避免这种错 误:if [ "$STATUS"x == "OK"x ]; then    echo

"OK"fi。当然,x也可以是其他字符。

所以

if [ $(findstring $(SUB_OBJS1)/,$<)a = $(SUB_OBJS1)/a ];

另外rm -f $@.$$$$;拿到if外面去都失败了,总是出错。

效果就是即使是 修改naomi/naomiaode.h 也可以重新编译

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

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/1bd469904afe66bc5805f6dc554a61ec.html