MakeFile 一些总结

下面我们要介绍的函数主要是处理文件名的。每个函数的参数字符串都会被当做一个或是一系列的文件名来对待。
$(dir ) 名称:取目录函数——dir。功能:从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。返回:返回文件名序列的目录部分。示例: $(dir src/foo.c hacks)返回值是“src/ ./”。
$(notdir ) 名称:取文件函数——notdir。功能:从文件名序列中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)之后的部分。返回:返回文件名序列的非目录部分。示例: $(notdir src/foo.c hacks)返回值是“foo.c hacks”。
$(suffix ) 名称:取后缀函数——suffix。功能:从文件名序列中取出各个文件名的后缀。返回:返回文件名序列的后缀序列,如果文件没有后缀,则返回空字串。示例:$(suffix src/foo.c src-1.0/bar.c hacks)返回值是“.c .c”。
$(basename )名称:取前缀函数——basename。功能:从文件名序列中取出各个文件名的前缀部分。返回:返回文件名序列的前缀序列,如果文件没有前缀,则返回空字串。示例:$(basename src/foo.c src-1.0/bar.c hacks)返回值是“src/foo src-1.0/bar hacks”。
$(addsuffix , ) 名称:加后缀函数——addsuffix。功能:把后缀加到中的每个单词后面。返回:返回加过后缀的文件名序列。示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”。
$(addprefix , ) 名称:加前缀函数——addprefix。功能:把前缀加到中的每个单词后面。返回:返回加过前缀的文件名序列。示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”。
$(join , )名称:连接函数——join。功能:把中的单词对应地加到的单词后面。如果的单词个数要比的多,那么,中的多出来的单词将保持原样。如果的单词个数要比多,那么,多出来的单词将被复制到中。返回:返回连接过后的字符串。示例:$(join aaa bbb , 111 222 333)返回值是“aaa111 bbb222 333”。

Makefile之wildcard

1、wildcard : 扩展通配符
2、notdir : 去除路径
3、patsubst :替换通配符

例子:
建立一个测试目录,在测试目录下建立一个名为sub的子目录
$ mkdir test
$ cd test
$ mkdir sub

在test下,建立a.c和b.c2个文件,在sub目录下,建立sa.c和sb.c2 个文件

建立一个简单的Makefile
src=$(wildcard *.c ./sub/*.c)
dir=$(notdir $(src))
obj=$(patsubst %.c,%.o,$(dir) )

all:
@echo $(src)
@echo $(dir)
@echo $(obj)
@echo "end"

执行结果分析:
第一行输出:
a.c b.c ./sub/sa.c ./sub/sb.c

wildcard把 指定目录 ./ 和 ./sub/ 下的所有后缀是c的文件全部展开。

第二行输出:
a.c b.c sa.c sb.c
notdir把展开的文件去除掉路径信息

第三行输出:
a.o b.o sa.o sb.o

在$(patsubst %.c,%.o,$(dir) )中,patsubst把$(dir)中的变量符合后缀是.c的全部替换成.o,
任何输出。
或者可以使用
obj=$(dir:%.c=%.o)
效果也是一样的。

这里用到makefile里的替换引用规则,即用您指定的变量替换另一个变量。
它的标准格式是
$(var:a=b) 或 ${var:a=b}
它的含义是把变量var中的每一个值结尾用b替换掉a

今天在研究makefile时在网上看到一篇文章,介绍了使用函数wildcard得到指定目录下所有的C语言源程序文件名的方法,这下好了,不用手工一个一个指定需要编译的.c文件了,方法如下:

SRC = $(wildcard *.c)

等于指定编译当前目录下所有.c文件,如果还有子目录,比如子目录为inc,则再增加一个wildcard函数,象这样:

SRC = $(wildcard *.c) $(wildcard inc/*.c)

也可以指定汇编源程序:
ASRC = $(wildcard *.S)

这样一来,makefile模板可修改的基本就是AVR名称和时钟频率了,其它的一般不用动了。


在手写Makefile时,用上这三个函数非常方便,减少了很多人力劳动哈

用法介绍:

wildcard:  扩展通配符 例如将* 进行匹配

notdir:  去除目录 类似于basename的用法

patsubst: 替换通配符

说起来比较费劲,直接看例子的话,比较好理解

例子介绍:

在test目录下a.cpp和b.cpp,sub目录下c.cpp和d.cpp

建立一个简单的Makefile文件:

点击隐藏MAKE CODE
src=$(wildcard *.c ./sub/*.c)
dir=$(notdir $(src))
obj=$(patsubst %.c,%.o,$(dir) )
或者 OBJS=$(src: .c=.o) # 后缀替换
all:
 @echo $(src)
 @echo $(dir)
 @echo $(obj)
 @echo "end"

输出结果为:

#wildcard把 指定目录 ./ 和 ./sub/ 下的所有后缀是c的文件全部展开
a.cpp b.cpp ./sub/c.cpp ./sub/d.cpp
#notdir把展开的文件去除掉路径信息
a.cpp b.cpp c.cpp d.cpp
#patsubst把$(dir)中的变量符合后缀是.c的全部替换成.o
a.o b.o c.o d.o

3.8 自动生成依赖性

在Makefile中,我们的依赖关系可能会需要包含一系列的头文件,比如,如果我们的main.c中有一句“#include "defs.h"”,那么我们的依赖关系应该是:

   main.o : main.c defs.h

但是,如果是一个比较大型的工程,你必需清楚哪些C文件包含了哪些头文件,并且,你在加入或删除头文件时,也需要小心地修改Makefile,这是一个很没有维护性的工作。为了避免这种繁重而又容易出错的事情,我们可以使用C/C++编译的一个功能。大多数的C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。例如,如果我们执行下面的命令:

   cc -M main.c

其输出是:

   main.o : main.c defs.h

于是由编译器自动生成的依赖关系,这样一来,你就不必再手动书写若干文件的依赖关系,而由编译器自动生成了。需要提醒一句的是,如果你使用GNU的C/C++编译器,你得用“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。

gcc-M main.c的输出是:

   main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h \

        /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \

        /usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \

        /usr/include/bits/types.h /usr/include/bits/pthreadtypes.h \

        /usr/include/bits/sched.h /usr/include/libio.h \

        /usr/include/_G_config.h /usr/include/wchar.h \

        /usr/include/bits/wchar.h /usr/include/gconv.h \

        /usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \

        /usr/include/bits/stdio_lim.h

 

gcc-MM main.c的输出则是:

   main.o: main.c defs.h

那么,编译器的这个功能如何与我们的Makefile联系在一起呢。因为这样一来,我们的Makefile也要根据这些源文件重新生成,让Makefile自已依赖于源文件?这个功能并不现实,不过我们可以有其它手段来迂回地实现这一功能。GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个“name.d”的Makefile文件,[.d]文件中就存放对应[.c]文件的依赖关系。

于是,我们可以写出[.c]文件和[.d]文件的依赖关系,并让make自动更新或自成[.d]文件,并把其包含在我们的主Makefile中,这样,我们就可以自动化地生成每个文件的依赖关系了。

这里,我们给出了一个模式规则来产生[.d]文件:

   %.d: %.c

           @set -e; rm -f $@; \

            $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \

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

            rm -f $@.$$$$

这个规则的意思是,所有的[.d]文件依赖于[.c]文件,“rm-f $@”的意思是删除所有的目标,也就是[.d]文件,第二行的意思是,为每个依赖文件“$<”,也就是[.c]文件生成依赖文件,“$@”表示模式“%.d”文件,如果有一个C文件是name.c,那么“%”就是“name”,“$$$$”意为一个随机编号,第二行生成的文件有可能是“name.d.12345”,第三行使用sed命令做了一个替换,关于sed命令的用法请参看相关的使用文档。第四行就是删除临时文件。

总而言之,这个模式要做的事就是在编译器生成的依赖关系中加入[.d]文件的依赖,即把依赖关系:

   main.o : main.c defs.h

转成:

   main.o main.d : main.c defs.h

于是,我们的[.d]文件也会自动更新了,并会自动生成了,当然,你还可以在这个[.d]文件中加入的不只是依赖关系,包括生成的命令也可一并加入,让每个[.d]文件都包含一个完赖的规则。一旦我们完成这个工作,接下来,我们就要把这些自动生成的规则放进我们的主Makefile中。我们可以使用Makefile的“include”命令,来引入别的Makefile文件(前面讲过),例如:

   sources = foo.c bar.c

 

   include $(sources:.c=.d)

example:

ifeq ($(MAKECMDGOALS), $(libtarget))  ##此目标的时候才包含依赖 -include $(objects:.o=.d) endif

上述语句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一个替换,把变量$(sources)所有[.c]的字串都替换成[.d],关于这个“替换”的内容,在后面我会有更为详细的讲述。当然,你得注意次序,因为include是按次来载入文件,最先载入的[.d]文件中的目标会成为默认目标

你可能感兴趣的:(MakeFile 一些总结)