1. Makefile 的自动推导
Makefile 对于文件中的 .o 文件,会自动加入 .c 文件作为依赖,并且会自动推导出 cc -c whatever.c 生成 whatever.o 这样的代码
这样可以简化 Makefile 的书写,.o 文件可以自动加入 .c 文件作为依赖,但是依赖的头文件需要预先加入依赖列表
# 注意连接符 \ 后面不能再有字符
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
.PHONY : clean
clean :
rm edit $(objects)
也可以将具有相同的头文件的目标进行合并,形成多目标的格式:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
.PHONY : clean
clean :
rm edit $(objects)
Makefile 支持通配符,比如:
~ 表示用户的主目录,~/test 用户主目录的 test 目录,~users/test 用户 users 目录下的 test 目录
*.o 表示当前目录下所有的 .o 文件,适用于命令,不适用于变量中,如果文件名中有字符和通配符同名,可以使用转义字符的方式实现
.PHONY clean
clean
rm -rf *.o
Makefile 支持多目标的实现比如:
bigoutput littleouptut : text.g
generate text.g -$(subst output,, $@) > $@
等同于使用下面的命令:
bigoutput: text.g
generate text.g -big > bigoutput
littleoutput:text.g
generate text.g -little > littleoutput
Makefile 的一些常用命令
make -n 只会显示命令,但是不会真正的执行
make -s 全面禁止命令的显示
make -w 显示makefile 进入或者离开某个目录
makefile 头文件的搜索路径:
1. 当前的执行目录下
2. make -I 目录 makefile 会在这个参数所包含的目录下去寻找
2. Makefile 的分支语句
第一种形式:ifeq else endif
ifeq ($(CC),gcc)
else
endif
举例: 注意有neq的用法
ifeq 'arg1' 'arg2'
ifeq “arg1” “arg2”
ifeq 'arg1' “arg2”
ifeq “arg1” 'arg2'
ifneq 'arg1' 'arg2'
ifneq “arg1” “arg2”
ifneq 'arg1' “arg2”
ifneq “arg1” 'arg2'
第二种形式: ifdef else endif ifndef else endif
ifdef foo 同样支持 ifndef
option1
else
option2
endif
第三者形式:ifeq else ifeq else ifeq endif
ifeq (arg1,arg2)
......
else ifeq
......
else ifeq
......
else
......
endif
3.Makefile 中的函数
函数的基本形式为:
$(函数名 参数1,参数2,参数3)
- 函数名和参数之间以空格分隔
- 参数之间用逗号分隔
- 参数可以使用变量
Makefile 中的常用函数如下:
3.1 字符串处理函数
$(subst <from>, <to>,<text>)
• 名称:字符串替换函数 subst
• 功能:把字串<text> 中的<from> 字符串替换成<to>
• 返回:函数返回被替换过后的字符串
• 示例:
$(subst ee,EE,feet on the street)
$(patsubst <pattern>,<replacement>,<text>)
• 名称:模式字符串替换函数——patsubst。
• 功能:查找<text> 中的单词(单词以空格 TAB 或者 回车 换行分隔)是否符合模式< pattern>,如果匹配的话,则以<replacement> 替换。这里,<pattern> 可
以包括通配符 %,表示任意长度的字串。如果<replacement> 中也包含 %,那么,
<replacement> 中的这个% 将是<pattern> 中的那个% 所代表的字串。(可以用
“\”来转义,以“\%”来表示真实含义的“%”字符)
• 返回:函数返回被替换过后的字符串
$(strip <string>)
- 名称:去空格函数——strip
- 功能:去掉<string> 字串中开头和结尾的空字符
- 返回:返回被去掉空格的字符串值
示例代码:
CURRENTDIT = $(shell pwd)
OBJECTS := $(wildcard $(CURRENTDIT)/*.cpp )
# "wildcard \/cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/*.cpp
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/a.cpp
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/b.cpp
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/c.cpp"
#$(warning "wildcard \$(CURRENTDIT)/*.cpp = $(OBJECTS)")
#$(error "wildcard \$(CURRENTDIT)/*.cpp = $(OBJECTS)")
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/a.o
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/b.o
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/c.o"
OBJECTS_O := $(subst .cpp,.o, $(OBJECTS))
$(warning "subst OBJECTS = $(OBJECTS_O)")
PATSUBST := $(patsubst %.cpp, %.o, $(OBJECTS))
$(warning "PATSUBST = $(PATSUBST)")
# 注意中间不能有空格
SIMPLE := $(OBJECTS:%.cpp=%.o)
$(warning "SIMPLE = $(strip $(SIMPLE))")
$(warning origin func is $(origin OBJECT))
$(warning origin func is $(origin PATH))
3.2 文件名操作函数
$(dir <names...>)
名称:取目录函数——dir
功能:从文件名序列<names> 中取出目录部分。目录部分是指最后一个反斜杠(“/”)之
前的部分。如果没有反斜杠,那么返回“./”。
返回:返回文件名序列<names> 的目录部分
$(suffix <names...>)
名称:取后缀函数——suffix。
功能:从文件名序列<names> 中取出各个文件名的后缀。
返回:返回文件名序列<names> 的后缀序列,如果文件没有后缀,则返回空字串
示例:$(suffix src/foo.c src-1.0/bar.c hacks) 返回值是 .c .c
$(basename <names...>)
名称:取前缀函数——basename
功能:从文件名序列<names> 中取出各个文件名的前缀部分
返回:返回文件名序列<names> 的前缀序列,如果文件没有前缀,则返回空字串
示例:$(basename src/foo.c src-1.0/bar.c hacks) 返回值是 src/foo src-1.0/
bar hacks
$(addsuffix <suffix>,<names...>)
名称:加后缀函数——addsuffix
功能:把后缀<suffix> 加到<names> 中的每个单词后面
返回:返回加过后缀的文件名序列
示例:$(addsuffix .c , foo bar) 返回值是 foo.c bar.c
$(addprefix <prefix>,<names...>)
名称:加前缀函数——addprefix
功能:把前缀<prefix> 加到<names> 中的每个单词后面
返回:返回加过前缀的文件名序列
示例:$(addprefix src/, foo bar) 返回值是 src/foo src/bar
$(join <list1>,<list2>)
名称:连接函数——join
功能:把<list2> 中的单词对应地加到<list1> 的单词后面如果<list1> 的单词
个数要比<list2> 的多,那么,<list1> 中的多出来的单词将保持原样如果<list2>
的单词个数要比<list1> 多,那么,<list2> 多出来的单词将被复制到<list2> 中
返回:返回连接过后的字符串
示例:$(join aaa bbb , 111 222 333) 返回值是 aaa111 bbb222 333
DIR := $(dir $(OBJECTS))
NOTDIR := $(notdir $(OBJECTS))
SUFFIX := $(suffix $(OBJECTS))
BASENAME := $(basename $(OBJECTS))
ADDSUFFIX := $(addsuffix _f, $(OBJECTS))
ADDPREFIX := $(addprefix _FUNC_, $(OBJECTS))
JOIN := $(join hello, world)
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/
$(warning "dir DIR = $(DIR)")
# a.cpp
# b.cpp
# c.cpp
$(warning "notdir NOTDIR = $(NOTDIR)")
# .cpp
# .cpp
# .cpp
$(warning "suffix SUFFIX = $(SUFFIX)")
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/a
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/b
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/c"
$(warning "prefix BASENAME = $(BASENAME)")
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/a.cpp_f
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/b.cpp_f
# /cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/c.cpp_f"
$(warning "suffix ADDSUFFIX = $(ADDSUFFIX)")
# _FUNC_/cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/a.cpp
# _FUNC_/cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/b.cpp
# _FUNC_/cygdrive/d/Ubuntu/source_code/cygwin/makefile_test/c.cpp"
$(warning "prefix ADDPREFIX = $(ADDPREFIX)")
# join = helloworld
$(warning "join = $(JOIN)")
3.3 origin 函数
origin 函数用于标识变量的来源
$(origin <variable>;)
注意,<variable>; 是变量的名字,不应该是引用 所以你最好不要在<variable>; 中使
用 $ 字符 Origin 函数会以其返回值来告诉你这个变量的出生情况 ,下面是origin
函数的返回值:
undefined 如果<variable>; 从来没有定义过,origin 函数返回这个值 undefined
default 如果<variable>; 是一个默认的定义,比如 CC 这个变量,这种变量我们将在
后面讲述
environment 如果<variable>; 是一个环境变量,并且当Makefile 被执行时, -e 参
数没有被打开
file 如果<variable>; 这个变量被定义在Makefile 中
command line 如果<variable>; 这个变量是被命令行定义的
override 如果<variable>; 是被override 指示符重新定义的
automatic 如果<variable>; 是一个命令运行中的自动化变量
3.4 Shell 函数
shell 函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell 的命令。它
和反引号有相同的功能,这就是说,shell 函数把执行操作系统命令后的输出作为函数返
回。于是,我们可以用操作系统命令以及字符串处理命令awk,sed 等等命令来生成一个变量,
如:
contents := $(shell cat foo)
files := $(shell echo *.c)
注意,这个函数会新生成一个Shell 程序来执行命令,所以你要注意其运行性能,如果你
的Makefile 中有一些比较复杂的规则,并大量使用了这个函数,那么对于你的系统性能是有
害的, 特别是Makefile 的隐晦的规则可能会让你的shell 函数执行的次数比你想像的多得
多
3.5 控制Make 的函数
$(error <text ...>;)
产生一个致命的错误,<text ...>; 是错误信息。注意,error 函数不会在一被使用就会
产生错误信息,所以如果你把其定义在某个变量中,并在后续的脚本中使用这个变量,那么也
是可以的
$(warning <text ...>;)
这个函数很像 error 函数,只是它并不会让 make 退出,只是输出一段警告信息,而 make
继续执行