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] ${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

所以会按序重新生成:

速度很快,但是这个版本还是有他自己的问题:

  1. 修改.h的时候,没有重新生成.depend会出错
  2. 修改一个.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

把规则写在第二条下面也是一样的效果

所以相当于写成了

  1. 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 也可以重新编译

时间: 2024-11-08 18:53:13

Makeflie自动生成依赖,自动化编译的相关文章

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

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

生成Makefile自动化编译文件

makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率.make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make.可见,makefile都成为了一种在工程方面的编译方法. 那么如何才能生成Makefile文件呢??好吧,让我们一起进入今天的正题吧! 1.首先生成一个目录:mkdi

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

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

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

Makefile中自动生成头文件依赖

为什么需要自动生成头文件依赖? 编译单个源文件时,需要获取文件中包含的头文件的信息,但是一般的Makefile不会在规则中明确写明文件依赖的头文件,所以单独修改头文件后,不会导致包含头文件的源文件重新编译.如果每次手动的添加头文件依赖,又会非常的繁琐,所以需要一种自动生成依赖的方法. 编译器中神奇的选项 使用$(CC)中的-M命令就可以完美的解决问题,因为-M选项可以将源文件依赖的所有头文件,自动解析出来. 例子:在当前路径下,编辑test.c和test.h文件,test.c如下所示,test.

Makefile自动生成头文件依赖

前言 Makefile自动生成头文件依赖是很常用的功能,本文的目的是想尽量详细说明其中的原理和过程. Makefile模板 首先给出一个本人在小项目中常用的Makefile模板,支持自动生成头文件依赖. CC = gcc CFLAGS = -Wall -O INCLUDEFLAGS = LDFLAGS = OBJS = seq.o TARGETS = test_seq .PHONY:all all : $(TARGETS) test_seq:test_seq.o $(OBJS) $(CC) -o