在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] ${SOURCE} $(CFLAGS)
.PHONY:clean
clean:
rm -f petal.exe
简简单单,petal依赖所有的cpp,如果cpp有所修改那么就会全部重新编译,生成新的petal
十分耗时,大致需要
CFLAGS 就是传给编译器的编译参数,定义成了一个变量
第二版
雏形版
:
CC = g++
CFLAGS = -O3 -DNDEBUG Debug版会使用参数-g;Release版使用-O3 –DNDEBUG
SOURCE =AdaBoost.cpp aodeselect.cpp sample.cpp变量的用法
default: petal 目标是生成default,要生成default,就要生成 petal ,规则没写,就是生成petal
echo "default"
depend: .depend 没什么用
echo "depend"
.depend: $(SOURCE) 依赖的是所有的.cpp 只要一个cpp文件有所修改 就要执行如下命令
echo ".depend"
rm -f ./.depend 删除当前目录下的.depend
$(CC) $(CFLAGS) -MM $^ >> ./.depend; -MM 自动找寻源文件中包含的非标准库头文件,并生成一个依赖关系 > 是定向输出到文件,如果文件不存在,就创建文件;如果文件存在,就将其清空; >> 这个是将输出内容追加到目标文件中。如果文件不存在,就创建文件;如果文件存在,则将新的内容追加到那个文件的末尾,该文件中的原有内容不受影响
$^--所有的依赖文件
.depend里的示例内容如下:
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
其实就是找出source所有的cpp文件依赖的头文件,并放到.depend里
include .depend 要把.depend加进来
petal: ${SOURCE}
$(CC) -o [email protected] ${SOURCE} $(CFLAGS) g++ -o 目标可执行文件 源文件 参数
petal64: ${SOURCE} 没有写要生成它,毫无作用
$(CC) -o [email protected] ${SOURCE} $(CFLAGS) –DSIXTYFOURBITCOUNTS
输出:
.depend文档:
流程:
首先我遇到了include 就要先生成.depend文档,生成完文档后,把文档中的.o与.c的依赖关系代替include指令,包括到makefile里面来。所以要生成.depend对象,如果源代码.cpp有所修改的话,就要删除原来的.depend文档,把依赖关系重新写入文档中。
Include阶段结束
然后,最终目标是default,要生成default,就要生成依赖petal,要生成petal,看一看发现.cpp有所修改,重新全部编译,删去.o中间文件(直接 – a.cpp不会出现.o中间文件)
生成了petal,生成完petal后,就返回去生成deafault,所谓的生成default不是说我一定要真的-o deafault才好,我要有default.exe,不是这样的,执行规则,就是生成对象,可惜default的规则,就是一条echo.那么打印完字符串,default也就生成了
这一版本因为有要输出所有头文件依赖,耗时惊人 2m57s
而且白把依赖关系include进来了,可惜petal依赖的还是cpp文件,生成依赖关系毫无作用
第三版 自动化编译 依赖.o 依赖于目标代码:
我们把petal不依赖于cpp,不然每次都要全部重新编译,不如依赖.o ,哪个cpp改了,就生成那个的.o,其它cpp的.o都不用动,编译迅速
在旧版本的make中,使用编译器此项功能通常的做法是:在Makefile中书写一个伪目标"depend"的规则来定义自动产生依赖关系文件的命令。输入"make depend"将生成一个称为"depend"的文件,其中包含了所有源文件的依赖规则描述。Makefile使用"include"指示符包含这个文件。
这就是第二版的做法的初衷,depend对象也就是这么来的,这样petal依赖.o 再把.o依赖那些cpp和头文件都包含进来,哪个cpp改了,从而要改哪个.o对象,也就明白了,那么别的.o都不用重新编译,连接起来是非常快的,而且可以利用隐含规则,不必写如何通过.cpp和.h生成.o,十分爽
CC = g++
CFLAGS = -O3 -DNDEBUG
SOURCE = naomi/naomiaode.cpp naomi/naominbaode.cpp sampler.cpp trainTest.cpp ALGLIB_ap.cpp
OBJ= naomi/naomiaode.o naomi/naominbaode.o sampler.o trainTest.o ALGLIB_ap.o
default:petal 要注意的是要把目标放在第一个 一旦include进来之后,include进来的第一条目标naomi/naomiaode.o: naomi/naomiaode.cpp naomi/naomiaode.h \就是总目标了,所以就不以petal作为目标了
echo "default"
depend: .depend 可以使用make depend手动产生.depend依赖文件
echo "depend"
.depend: $(SOURCE)
echo ".depend"
rm -f ./.depend
$(CC) $(CFLAGS) -MM $^ >> ./.depend;
include .depend
petal: ${OBJ}
$(CC) -o [email protected] ${OBJ} $(CFLAGS)
.PHONY:clean
clean:
rm -f ${OBJ} petal.exe
现在makefile文件其实是这样的:
.depend: $(SOURCE)
echo ".depend"
rm -f ./.depend
$(CC) $(CFLAGS) -MM $^ >> ./.depend;
Petal: naomi/naomiaode.o naomi/naominbaode.o sampler.o trainTest.o ALGLIB_ap.o
$(CC) -o [email protected] ${OBJ} $(CFLAGS)
现在petal依赖于.o,而.o 依赖于cpp 与 h,当修改一个cpp的时候,会重新生成.depend文件,包括进.o最新的依赖,然后检查petal是否需要重新生成,petal依赖的naomi/naomiaode.o需要重新生成吗?由于cpp的修改时间比.o的早,要重新生成.o petal依赖的sampler.o需要重新生成吗?一个个检查下去
比如我修改了
以下四个文件,而petal的依赖关系为:
petal:AdaBoost.o aodeselect.o sample.o vfan.o
所以会按序重新生成:
速度很快,但是这个版本还是有他自己的问题:
- 修改.h的时候,没有重新生成.depend会出错
- 修改一个.cpp,没有必要重新产生所有cpp的依赖,依赖关系其实也像目标文件一样,修改 了cpp或h的文件重新产生它的依赖关系,没有修改的依赖关系不动。所以才有了.d文件版
当修改一个cpp的时候
,比如naomi/naomiaode.cpp
就要重新写.depend文件,耗时较大
可以很明显地看到,只重新编译了naomiaode.cpp,耗时只有40s!!!! 从3分钟降到40s秒,划时代的进步,真正发挥了make的作用。
当修改一个非子目录下的.h的时候
因为修改头文件,可以把新的头文件包含在文件中,需要重新生成.o的依赖关系,但是因为.depend只依赖了cpp所以不会因为头文件的修改而修改.depend文件,实际上这样是错误的。
因为 aode.o: aode.h 所以它就会根据.o的依赖关系重新生成.o 速度十分快
对于naomi文件夹下的
.depend里的对象是
naominbaode.o: naomi/naominbaode.cpp naomi/naominbaode.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
并不存在的一个目标,正确的路径是naomi/ naominbaode.o
首先按照道理来说 naominbaode.o根本不存在,那么每次编译的时候就会重新产生naomiaode.o,但是事实并非如此:
比如我删除了aode.o就会重新生成.o,因为petal的依赖的.o不存在
但是我修改了aode.cpp之后,并没有重新生成naominbaode.o,仅仅重新生成了aode.o
注:不可以去掉OBJ = naomi/naomiaode.o naomi/naominbaode.o 里的naomi
然后修改
改naominbaode.cpp会自动地重新生成.o 和.depend
但是依旧能正确地产生
g++ -c -o naomi/naominbaode.o naomi/naominbaode.cpp
但是如果改的是naominbaode.h就 不同了,
并没有重新生成g++ -c -o naomi/naominbaode.o naomi/naominbaode.cpp
而只修改了learnerRegistry.cpp
如果真的能找到naominbaode.o的话,它依赖了naomi/naominbaode.h,那么头文件被修改,.o应该重新生成,但是没有,如果真的找不到naominbaode.o的话,为什么修改了naomi/naominbaode.cpp会重新生成 naomi/naominbaode.o ,而且并不是每次都会重新生成naomi/naominbaode.o的,这样不一致的表现,为子目录版带来了巨大的困难,
说明这是路径问题,检查naominbaode.o是否需要重新产生的时候,根本没有找到它在哪里,它不在根目录下。
只要改成naomi/ naominbaode.o 修改naomi/naominbaode.h时就会连同naominbaode.o一起更新
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 \
在makefile下执行命令
pwd;
cd ./naomi; \
pwd; \
pwd;
pwd;
Pwd就是显示当前目录
那么为什么要把几个命令写在"同一行"(是对于make来说,因为\的作用就是连接行),并用分号隔开每个命令?因为在Makefile这样做才能使上一个命令作用于下一个命令。
\为运行环境续命,第一个pwd把它的路径传给了第二个pwd,第二个pwd没有加\,所以最后一个pwd又反弹回了原来的路径。
一个目标多条
如果有多条命令,就会忽略老的一条
而如果是这样的话
如果我修改了a.cpp b.h或者 a.h就会重新生成a.o
gcc –c a.pp生成a.o
把规则写在第二条下面也是一样的效果
所以相当于写成了
- o: a.cpp b,h a.h
gcc –c a.cpp
有重复的效果也是一样的
其中a.cpp是重复的,但是没有关系
这为下面.d文件版分开写的规则奠定了基础
第四版:无子文件版
参考:
http://blog.chinaunix.net/uid-20316928-id-3395996.html
https://segmentfault.com/a/1190000000349917
CC = g++
CFLAGS = -O3 -DNDEBUG
SOURCES = $(wildcard *.cpp)
OBJS := $(patsubst %.cpp, %.o,$(SOURCES))
petal:$(OBJS)
@echo "源文件:" $(SOURCES)
@echo "目标文件:" $(OBJS)
$(CC) -o [email protected] $(OBJS) $(CFLAGS)
%.d :%.cpp
@echo "create depend";
@set -e; \
gcc -MM $< > [email protected]$$$$; \
sed ‘s,\($*\)\.o[ :]*,\1.o [email protected] : ,g‘ < [email protected]$$$$ > [email protected]; \
rm -f [email protected]$$$$
-include $(OBJS:.o=.d)
.PHONY:clean
clean:
@echo "开始清理项目..."
@echo "正在删除所有的.d文件"
rm -f $(OBJS:.o=.d)
@echo "正在删除所有的.o文件"
rm -rf $(OBJS)
@echo "正在删除petal"
rm -f petal.exe
@echo "清理结束"
前面两行很简单就是定义编译变量和编译选项。
SOURCES = $(wildcard *.c) 这句话意思是定义一个变量SOURCES,它的值包含当前目录下所有.c文件。 在我的例子里我把这个值打印出来了就是dList.c memory.c test.c debug.c
$(wildcard PATTEN) 是Makefile内建的一个函数:
函数名称:获取匹配模式文件名函数—wildcard
函数功能:列出当前目录下所有符合模式"PATTERN"格式的文件名。
返回值:空格分割的、存在当前目录下的所有符合模式"PATTERN"的文件名。
函数说明:"PATTERN"使用shell可识别的通配符,包括"?"(单字符)、"*"(多字符)等
OBJS := $(patsubst %.c, %.o,$(SOURCES)) 这一行是定义了一个变量OBJS,它的值是将变量SOURCES里的内容以空格分开,将所有.c文件替换成.o. 在我的例子里打印出来就是dList.o memory.o test.o debug.o。
$(patsubst PATTEN, REPLACEMENT, TEXT)也是内建函数
函数名称:模式替换函数—patsubst。
函数功能:搜索"TEXT"中以空格分开的单词,将否符合模式"TATTERN"替换为"REPLACEMENT"
sinclude $(SOURCES:.c=.d) 这一行是非常关键的,它在当前Makefile里去include另外的Makefile. 这里"另外"的Makefile是将SOURCES变量里所有.c替换成.d。 在我的例子里就是dList.d memory.d test.d debug.d. 意思就是执行到这里
的时候先去依次执行dList.d memory.d test.d debug.d. 这里的.d文件就是包含了每个.c文件自动生成的对头文件的依赖关系。这个依赖关系将由下面的%d:%c来完成。
%d: %c
此规则的含义是:所有的.d文件依赖于同名的.c文件。
第一行;使用c编译器自自动生成依赖文件($<)的头文件的依赖关系,并输出成为一个临时文件,"$$$$"表示当前进程号。如果$(CC)为GNU的c编译工具,产生的依赖关系的规则中,依赖头文件包括了所有的使用的系统头文件和用户定义的头文件。如果需要生成的依赖描述文件不包含系统头文件,可使用"-MM"代替"-M"。
第二行;使用sed处理第二行已产生的那个临时文件并生成此规则的目标文件。经过这一行后test.d里内容如下:test.o: test.c aaron.h dList.h debug.h 其他.d里以此类推。
第三行;删除临时文件。
.d文件的内容如下:
a2de3.o a2de3.d: 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
在.d文件里,不仅阐明了如何生成.o文件,而且阐明了如何生成它自己,结合
%d: %c规则,在第一次时sinclude $(SOURCES:.c=.d) 在例子里其实.d文件开始并不存在,所以当Makefile在include这些.d文件时首先看.d存在不,不存在就要去寻找.d的依赖文件和规则。这里就找到了%d: %c从而创建出真正的.d文件。
而后来头文件或者cpp发生修改了,.makefile会将所读取的每个makefile(.d)作为一个目标,如重新生成a.d 寻找更新a.d的规则。如果需要重新产生a.d就重新产生,重新引入,所以会重新产生.d文件。这样的规则是符合逻辑的,因为不论是头文件还是cpp的修改,都有可能导致目标文件的依赖关系发生变化,必须重新生成依赖文件,解决了上一版的问题。
到这里基本的意义弄明白了,但是让我不解的是%d: %c这个依赖的规则怎么能被执行到的?按照我的理解Makefile在执行时首先检查终极目标main是否存在,如果不存在则建立(根据main的依赖规则),如果存在在需要查看
main的依赖文件是否存在并且是最新的,这我的例子里就是要看test.o dList.o memory.o debug.o是否存在且最新。这样追下去是否没有%d: %c什么事啊, .d文件也应该不存在或者说是空的。尽管我们include了.d文件,但是没有依赖规则去执行它啊。后来仔细阅读了
Makefile文件的重建才明白了。
Makefile如果由其它文件重建(这里我的Makefile include了所有.d文件,.d也可以看成是一个Makefile),Makefile在读入所有其他makefile文件(.d)之后,先把这些.d包括进来,然后将所读取的每个makefile(.d)作为一个目标,如重新生成a.d 寻找更新a.d的规则。如果需要重新产生a.d就重新产生,重新引入 同样
如果此目标不存在则根据依赖规则重新创建。其实这里的关键点就是对于
include了理解,它是把include的文件首先当成一个目标,然后要去寻找其依赖文件和规则的,而不是我事先想象的简单的把其他文件的内容包含过来。
到此,问题解决,基本达到预期。
seq.d : seq.c
@set -e;
gcc -MM $< > [email protected]$$$$;
sed ‘s,\($*\)\.o[ :]*,\1.o [email protected] : ,g‘ < [email protected]$$$$ > [email protected];
rm -f [email protected]$$$$
-include seq.d
生成规则中的执行命令解释
第一个命令@set -e。@关键字告诉make不输出该行命令;set -e的作用是,当后面的命令的返回值非0时,立即退出。
那么为什么要把几个命令写在"同一行"(是对于make来说,因为\的作用就是连接行),并用分号隔开每个命令?因为在Makefile这样做才能使上一个命令作用于下一个命令。这里是想要set -e作用于后面的命令。
第二个命令gcc -MM $< > [email protected]$$$$, 作用是根据源文件生成依赖关系,并保存到临时文件中。内建变量$<的值为第一个依赖文件(那seq.c),$$$$为字符串"$$",由于makefile中所有的$字符都是特殊字符(即使在单引号之中!),要得到普通字符$,需要用$$来转义; 而$$是shell的特殊变量,它的值为当前进程号;使用进程号为后缀的名称创建临时文件,是shell编程常用做法,这样可保证文件唯一性。这个临时文件的名字为seq.d.1223 [email protected]代表目标文件 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 [email protected] : ,g‘ < [email protected]$$$$ > [email protected];
sed是 linux文本处理工具
这条sed命令的结构是s/match/replace/g。有时为了清晰,可以把每个/写成逗号,即这里的格式s,match,replace,g。
该命令表示把源串内的match都替换成replace,s指示match可以是正则表达式。
g表示把每行内所有match都替换,如果去掉g,则只有每行的第1处match被替换(实际上不需要g,因为一个.d文件中,只会在开头有一个main.o:)。
所以要寻找的目标是:\($*\)\.o[ :]*
替换成: \1.o [email protected] :
使用到了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
这是因为在规则后面有换行符,把换行符删去即可
第五版 子文件版
但是src文件夹下面文件太多了,最好能有许多的文件夹,一方面区分自己写的和其它文件,另一方面把aode优化算法,放在一个文件夹下,便于管理,也能达到自动编译的效果
根据要把 naomiaode.o的路径写全的提示,其实只要在sed文本替换上做文章就好了。
文件夹结构:
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 [email protected] $(SUB_OBJS1) $(SUB_OBJS2) $(OBJS) $(CFLAGS)
%.d: %.cpp
@echo "create depend" $< [email protected] $(subst naomi/,,$*)
@set -e;
gcc -MM $< > [email protected]$$$$;
if [ $(findstring $(SUB_DIR1)/,$<)a = $(SUB_DIR1)/a ]; then sed ‘s,\($(subst $(SUB_DIR1)/,,$*)\)\.o[ :]*,$(SUB_DIR1)\1.o [email protected] : ,g‘ < [email protected]$$$$ > [email protected]; rm -f [email protected]$$$$;
elif [ $(findstring $(SUB_DIR2)/,$<)a = $(SUB_DIR2)/a ]; then sed ‘s,\($(subst $(SUB_DIR2)/,,$*)\)\.o[ :]*,$(SUB_DIR2)/\1.o [email protected] : ,g‘ < [email protected]$$$$ > [email protected]; rm -f [email protected]$$$$;
else sed ‘s,\($*\)\.o[ :]*,\1.o [email protected] : ,g‘ < [email protected]$$$$ > [email protected]; rm -f [email protected]$$$$;
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加上文件夹名,而目标文件[email protected]是自带路径的,无需处理。
但是你还要判断处理的文件究竟属于哪个文件夹好把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 [email protected]$$$$;拿到if外面去都失败了,总是出错。
效果就是即使是 修改naomi/naomiaode.h 也可以重新编译