Makefile要点梳理

目录

目录说明

Makefile_1

Makefile_2

Makefile_3

Makefile_4

Makefile_5

Makefile_6

Makefile_7

Makefile_8

Makefile_9

Makefile_10

Makefile_11

Makefile_12

Makefile_13

其他


基于陈皓大佬的教程整理的思维框架:跟我一起写 Makefile(一)_《跟我一起写makefile》_haoel的博客-CSDN博客

目录说明


	Makefile_1:		编写基础代码
	Makefile_2:		声明一个变量(objects)包含所有目标文件
	Makefile_3:		由于make可以自动推导,所以脚本可以再次简化	#伪目标文件
	Makefile_4:		优化clean,清空目标文件的规则
	Makefile_5:		显式规则、隐晦规则、变量定义、文件指示和注释
	Makefile_6:		make的工作步骤
	Makefile_7:		makefile规则
	Makefile_8:		makefile命令
	Makefile_9:		变量的使用
	Makefile_10:	函数的使用	字符串函数	文件名操作函数	foreach循环函数	if函数
	Makefile_11:	make的运行
	Makefile_12:	隐含规则
	Makefile_13:	库函数的使用
	

Makefile_1

#编写基础代码
edit : main.o kbd.o command.o display.o insert.o serach.o files.o utils.o
	cc -o edit main.o kbd.o command.o display.o insert.o serach.o files.o utils.o 

main.o : main.c defs.h
	cc -c main.c
	
kbd.o : kbd.c defs.h command.h
	cc -c kbd.c
	
command.o : command.c defs.h command.h
	cc -c command.c
	
display.o : display.c defs.h buffer.h
	cc -c display.c
	
insert.o : insert.c defs.h buffer.h
	cc -c insert.c
	
search.o : search.c defs.h buffer.h
	cc -c search.c
	
files.o : files.c defs.h buffer.h command.h
	cc -c files.c
	
utils.o : utils.c defs.h
	cc -c utils.c

clean:
	rm edit main.o kbd.o command.o display.o insert.o serach.o files.o utils.o



Makefile_2

#声明一个变量 objects 包含所有目标文件
objects : main.o kbd.o command.o display.o insert.o serach.o files.o utils.o

edit : $(objects)
	cc -o edit $(objects)

main.o : main.c defs.h
	cc -c main.c
	
kbd.o : kbd.c defs.h command.h
	cc -c kbd.c
	
command.o : command.c defs.h command.h
	cc -c command.c
	
display.o : display.c defs.h buffer.h
	cc -c display.c
	
insert.o : insert.c defs.h buffer.h
	cc -c insert.c
	
search.o : search.c defs.h buffer.h
	cc -c search.c
	
files.o : files.c defs.h buffer.h command.h
	cc -c files.c
	
utils.o : utils.c defs.h
	cc -c utils.c

clean:
	rm edit $(objects)



Makefile_3

#由于 make 可以自动推导,所以脚本可以再次简化
objects : main.o kbd.o command.o display.o insert.o serach.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)



Makefile_4

#优化 clean ,清空目标文件的规则
objects : main.o kbd.o command.o display.o insert.o serach.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) #小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事



Makefile_5

# 显式规则、隐晦规则、变量定义、文件指示和注释。
objects : main.o kbd.o command.o display.o insert.o serach.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) # 小减号的意思就是,也许某些文件出现问题,但不要管,继续做后面的事




Makefile_6

# make的工作步骤

1、读入所有的Makefile;
2、读入被include的其它Makefile;
3、初始化文件中的变量;
4、推导隐晦规则,并分析所有规则;
5、为所有的目标文件创建依赖关系链;

6、根据依赖关系,决定哪些目标要重新生成;
7、执行生成命令。

1-5步为第一个阶段,6-7为第二个阶段。
第一个阶段中,如果定义的变量被使用了,那么,make会把其展开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。

Makefile_7

一、Makefile 规则

	规则包含两个部分,一个是依赖关系,一个是生成目标的方法。
	Makefile表达式中,规则的顺序是非常重要的,一般第一个目标会成为最终目标!

	
二、Makefile 语法

	targets : prerequisites
		command
		...

	或是这样:

	targets : prerequisites ; command
		command
		...

	targets 		:是文件名,以空格分开,可以使用通配符。
	prerequisites 	:是目标所依赖的文件(或依赖目标)。
	command 		:是命令行,如果其不与 “target:prerequisites” 在一行,那么,必须以[ Tab键 ]开头,
						如果和 prerequisites 在一行,那么可以用分号做为分隔。(见上)
	如果命令太长,你可以使用反斜框(‘/’)作为换行符。

	
三、在规则中使用通配符 wildcard

	*					//通配符
	?					//通配符
	[]					//通配符
	/					//转义符 将通配符转化为其原本表示的真实字符

	objects = *.o				//表示"*.o"
	objects := $(wildcard *.o)	//表示所有.o文件的合集

	
四、路径--文件搜索 VPATH 和 vpath

	VPATH = src:../headers //定义了两个目录 src 和 ../headers ,多个目录之间用“:”分开
	
	vpath有三种使用方法:
	
	1、vpath <pattern> <directories>
    为符合模式<pattern>的文件指定搜索目录<directories>。

    2、vpath <pattern>
    清除符合模式<pattern>的文件的搜索目录。

    3、vpath
    清除所有已被设置好了的文件搜索目录。
	
	vapth 使用方法中的 <pattern> 需要包含“%”字符。“%”的意思是匹配零或若干字符,例如 “%.h”表示所有以“.h”结尾的文件。	
	vpath %.h ../headers	//该语句表示,要求make在“../headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话)
	
	
五、伪目标	.PHONY

	伪目标的重要特性:总是被执行!!!
	伪目标不是目标文件,只是一个标签,调用 make “name” 执行name伪目标的依赖命令
	为了避免伪目标与目标文件重名,可以用 “.PHONY” 来显示地指明一个目标是“伪目标”,向 make 说明,不管是否有这个文件,这个目标就是“伪目标”。
	

六、多目标	自动化变量	$@

	$@ :这个变量表示着目前规则中所有的目标的集合
	
	bigoutput littleoutput : text.g
            generate text.g -$(subst output,,$@) > $@

    上述规则等价于:

    bigoutput : text.g
            generate text.g -big > bigoutput
    littleoutput : text.g
            generate text.g -little > littleoutput

	其中,-$(subst output,,$@) 中的 “$” 表示执行一个 Makefile 的函数,函数名为 subst ,后面的为参数。关于函数,将在后面讲述。这里的这个函数是截取字符串的意思,“$@” 表示目标的集合,就像一个数组,“$@” 依次取出目标,并执于命令。

	
七、静态模式

	目标:目标模式:目标依赖
		命令
	<targets ...>: <target-pattern>: <prereq-patterns ...>
		<commands>
		...
			
    targets 定义了一系列的目标文件,可以有通配符。是目标的一个集合。
    target-parrtern 是指明了 targets 的模式,也就是的目标集模式。
    prereq-parrterns 是目标的依赖模式,它对 target-parrtern 形成的模式再进行一次依赖目标的定义。
	
案例一:
	objects = foo.o bar.o
    all: $(objects)
    $(objects): %.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@

	等价于
	
	foo.o : foo.c
        $(CC) -c $(CFLAGS) foo.c -o foo.o
    bar.o : bar.c
        $(CC) -c $(CFLAGS) bar.c -o bar.o
	
案例二:
	
	files = foo.elc bar.o lose.o
    $(filter %.o,$(files)): %.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@
    $(filter %.elc,$(files)): %.elc: %.el
        emacs -f batch-byte-compile $<
	
	
	
八、自动生成依赖性 -M

	如果我们执行下面的命令:
	
		cc -M main.c

	其输出是:

		main.o : main.c defs.h
	
	比较复杂,后面再详细解释。


Makefile_8

#	makefile 会一按顺序一条一条的执行命令,每条命令的开头必须以 [Tab] 键开头,
#	除非,命令是紧跟在依赖规则后面的分号 “;” 后的。

一、显示命令
	用“@”字符在命令行前,那么,这个命令将不被 make 显示出来。


二、执行命令
	如果要让上一条命令的结果应用在下一条命令时,需要使用分号 “;” 分隔这两条命令。
	首先,make 会在 SHELL 所指定的路径中找寻命令解释器;
	如果找不到,其会在当前盘符中的当前目录中寻找;
	如果再找不到,其会在PATH环境变量中所定义的所有路径中寻找。

	
三、命令出错
	当命令运行完后,make 会检测每个命令的返回码,
	如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。
	如果一个规则中的某个命令出错了(命令退出码非零),那么 make 就会终止执行当前规则,这将有可能终止所有规则的执行。
	为了做到忽略命令的出错,我们可以在 Makefile 的命令行前加一个减号 “-” (在 Tab键 之后),标记为不管命令出不出错都认为是成功的。如:

	clean:
		-rm -f *.o

	“-i”或是“--ignore-errors”:参数,Makefile 中所有命令都会忽略错误。
	而如果一个规则是以 “.IGNORE” 作为目标的,那么这个规则中的所有命令将会忽略错误。

	“-k”或是“--keep-going”:如果某规则中的命令出错了,那么就终目该规则的执行,但继续执行其它规则。


四、嵌套执行make
	先进入 “subdir” 目录,然后执行make命令
	subsystem:
        cd subdir && $(MAKE)  # subdir--子目录名称
	
	如果你要传递变量到下级 Makefile 中,那么你可以使用这样的声明:
		export <variable ...>

	如果你不想让某些变量传递到下级 Makefile 中,那么你可以这样声明:
		unexport <variable ...>

	有两个变量,一个是 SHELL ,一个是 MAKEFLAGS ,这两个变量不管你是否 export ,其总是要传递到下层 Makefile 中,特别是 MAKEFILES 变量,其中包含了 make 的参数信息,如果我们执行 “总控Makefile” 时有 make 参数或是在上层 Makefile 中定义了这个变量,那么 MAKEFILES 变量将会是这些参数,并会传递到下层 Makefile 中,这是一个系统级的环境变量。

	
五、定义命令包
	define 开始, endef 结束
	
	define run-yacc
    yacc $(firstword $^)
    mv y.tab.c $@
    endef

	“run-yacc”是这个命令包的名字,其不要和Makefile中的变量重名。

	foo.c : foo.y
		$(run-yacc)

	“$^” 就是 “foo.y” , “$@” 就是 “foo.c” 
	



Makefile_9

# 变量的使用 
	变量大小写敏感!
	变量的命名字可以包含字符、数字,下划线(可以是数字开头),但不应该含有 “:” “#” “=” 或是 空格、回车。

一、变量的基础

	变量在声明时需要给予初值,而在使用时,需要给在变量名前加上 “$” 符号,但最好用小括号 “()” 或是大括号 “{}” 把变量给包括起来。如果你要使用真实的 “$” 字符,那么你需要用 “$$” 来表示。
	
	
二、变量中的变量 条件表达式
	:=					//前面的变量不能使用后面的变量,只能使用前面已定义好了的变量
	?=					//FOO ?= bar 如果FOO没有被定义过,FOO = “bar”,如果FOO先前被定义过,那么什么也不做
	

三、变量的高级用法
	1、变量值的替换
		foo := a.o b.o c.o
		bar := $(foo:.o=.c)
	
		foo := a.o b.o c.o
		bar := $(foo:%.o=%.c)
	
	2、把变量的值当成变量
		x = y
		y = z
		z = u
		a := $($($(x)))
		$(a) 的值是 “u” 
	
		x = variable1
		variable2 := Hello
		y = $(subst 1,2,$(x))
		z = y
		a := $($($(z)))
		$(a) 的值是 “Hello” 
		
		first_second = Hello
		a = first
		b = second
		all = $($a_$b)
		$(all) = Hello
		
		a_objects := a.o b.o c.o
		1_objects := 1.o 2.o 3.o
		sources := $($(a1)_objects:.o=.c)
		//如果 al= a 则 $(sources)= a.o b.o c.o
		//如果 al= 1 则 $(sources)= 1.o 2.o 3.o
		
		ifdef do_sort
		func := sort
		else
		func := strip
		endif
		bar := a d b g q c
		foo := $($(func) $(bar))
		//如果定义	 do_sort 则 foo := $(sort a d b g q c)
		//如果没定义 do_sort 则 foo := $(strip a d b g q c)

		dir = foo
		$(dir)_sources := $(wildcard $(dir)/*.c)
		define $(dir)_print
		lpr $($(dir)_sources)
		endef
		

四、追加变量值 +=
	variable := value
    variable += more

	等价于:

    variable := value
    variable := $(variable) more
	

五、override 指示符
	override <variable> = <value>
    override <variable> := <value>
	追加
	override <variable> += <more text>

	override define foo
    bar
    endef

	
六、多行变量
	define 				//关键字 设置变量值
	echo foo			//
    echo $(bar)			//	
	endif				//结束关键字
	

七、环境变量
	CFLAGS				//全局环境变量
	-e					//覆盖makefile中的系统环境变量
	exprot				//传递变量到下级  


八、目标变量
	局部变量语法
	<target ...> : <variable-assignment>
	<variable-assignment>可以是前面讲过的各种赋值表达式,如“=”、“:=”、“+=”或是“?=”
	
    <target ...> : overide <variable-assignment>
	针对于make命令行带入的变量,或是系统环境变量。
		
	
九、模式变量  Pattern-specific Variable
	make的“模式”一般是至少含有一个“%”的
	 %.o : CFLAGS = -O

	同样,模式变量的语法和“目标变量”一样:

    <pattern ...> : <variable-assignment>

    <pattern ...> : override <variable-assignment>

	override同样是针对于系统环境传入的变量,或是make命令行指定的变量。




十、条件判断
	是否被定义
		ifdef *	//判断 * 是否被定义
			...
		else
			...
		endif
	
	是否不被定义
		ifndef *	//判断 * 是否不被定义
			...
		else
			...
		endif	
		
	是否等于	
		ifeq (a,b)	//判断 a 是否等于 b 
			...
		else
			...
		endif
		
	是否不等于	
		ifneq (a,b)	//判断 a 是否不等于 b 
			...
		else
			...
		endif	




Makefile_10

# 函数

一、函数的调用语法

	$(<function> <arguments>)

	或是

    ${<function> <arguments>}

	<function> 就是函数名,make 支持的函数不多。
	<arguments> 是函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。
	函数的调用和变量的调用一样,函数调用以“$”开头,以圆括号或花括号把函数名和参数括起。


二、字符串处理函数

	$(subst <from>,<to>,<text>)	//字符串替换函数
		
		功能:
			把字串<text>中的<from>字符串替换成<to>。

		示例:
			$(subst ee,EE,feet on the street)       
			把“feet on the street”中的“ee”替换成“EE”,返回结果是“fEEt on the strEEt”

		
	$(patsubst <pattern>,<replacement>,<text>)	//模式字符串替换函数
	
		功能:
			查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。这里,<pattern>可以包括通配符“%”,表示任意长度的字串。如果<replacement>中也包含“%”,那么,<replacement>中的这个“%”将是<pattern>中的那个“%”所代表的字串。(可以用“/”来转义,以“/%”来表示真实含义的“%”字符)。
		
		示例:
			$(patsubst %.c,%.o,x.c.c bar.c)
			把字串“x.c.c bar.c”符合模式[%.c]的单词替换成[%.o],返回结果是“x.c.o bar.o”


	$(strip <string>)	//去空格函数

		功能:
			去掉<string>字串中开头和结尾的空字符。
			
		示例:
			$(strip a b c )
			把字串“a b c ”去到开头和结尾的空格,结果是“a b c”


	$(findstring <find>,<in>)	//查找字符串函数
	
		功能:
			在字串<in>中查找<find>字串。		
		
		示例:
			$(findstring a,a b c)
			$(findstring a,b c)
			第一个函数返回“a”字符串,第二个返回“”字符串(空字符串)
		
	
	$(filter <pattern...>,<text>)	//过滤函数
	
		功能:
			以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式。
		
		示例:
			sources := foo.c bar.c baz.s ugh.h
			foo: $(sources)
					cc $(filter %.c %.s,$(sources)) -o foo
			$(filter %.c %.s,$(sources))
			返回值:“foo.c bar.c baz.s”
			
			
	$(filter-out <pattern...>,<text>)	//反过滤函数
	
		功能:
			以<pattern>模式过滤<text>字符串中的单词,去除符合模式<pattern>的单词。可以有多个模式。
		
		示例:
			objects=main1.o foo.o main2.o bar.o
			mains=main1.o main2.o		   
			$(filter-out $(mains),$(objects)) 
			返回值:“foo.o bar.o”
		
		
	$(sort <list>)	//排序函数
	
		功能:
			给字符串<list>中的单词排序(升序)。
		
		示例:
			$(sort foo bar lose)	
			返回值:“bar foo lose” 
				

	$(word <n>,<text>)	//取单词函数
	
		功能:
			取字符串<text>中第<n>个单词。(从一开始)
		
		示例:
			$(word 2, foo bar baz)
			返回值是:“bar”
		
		
	$(wordlist <s>,<e>,<text>)	//取单词串函数
	
		功能:
			字符串<text>中取从<s>开始到<e>的单词串。<s>和<e>是一个数字。
		
		示例:
			$(wordlist 2, 3, foo bar baz)
			返回值:“bar baz”
				

	$(words <text>)	//单词个数统计函数
	
		功能:
			统计<text>中字符串中的单词个数。
		
		示例:
			$(words, foo bar baz)
			返回值:“3”
			
		备注:
			如果我们要取<text>中最后的一个单词,我们可以这样:$(word $(words <text>),<text>)。
				

	$(firstword <text>)	//获取首单词函数
	
		功能:
			取字符串<text>中的第一个单词。
		
		示例:
			$(firstword foo bar)
			返回值:“foo”
			
		备注:
			这个函数可以用word函数来实现:$(word 1,<text>)。
		

三、文件名操作函数
	
	$(dir <names...>)	//取目录函数
	
		功能:
			从文件名序列<names>中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。
		
		示例:
			$(dir src/foo.c hacks)
			返回值:“src/ ./”
		
		
	$(notdir <names...>)	//取文件函数
	
		功能:
			从文件名序列<names>中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)之后的部分。
		
		示例:
			$(notdir src/foo.c hacks)
			返回值:“foo.c hacks”
				

	$(suffix <names...>)	//取后缀函数
	
		功能:
			从文件名序列<names>中取出各个文件名的后缀。
		
		示例:
			$(suffix src/foo.c src-1.0/bar.c hacks)
			返回值:“.c .c”
		
		

	$(basename <names...>)	//取前缀函数
	
		功能:
			从文件名序列<names>中取出各个文件名的前缀部分。
		
		示例:
			$(basename src/foo.c src-1.0/bar.c hacks)
			返回值:“src/foo src-1.0/bar hacks”
		
		

	$(addsuffix <suffix>,<names...>)	//加后缀函数
	
		功能:
			把后缀<suffix>加到<names>中的每个单词后面。
		
		示例:
			$(addsuffix .c,foo bar)
			返回值:“foo.c bar.c”
		
		

	$(addprefix <prefix>,<names...>)	//加前缀函数
	
		功能:
			把前缀<prefix>加到<names>中的每个单词后面。
		
		示例:
			$(addprefix src/,foo bar)
			返回值:“src/foo src/bar”
		
		
	
	$(join <list1>,<list2>)	//连接函数
	
		功能:
			把<list2>中的单词对应地加到<list1>的单词后面。如果<list1>的单词个数要比<list2>的多,那么,<list1>中的多出来的单词将保持原样。如果<list2>的单词个数要比<list1>多,那么,<list2>多出来的单词将被复制到<list2>中。
		
		示例:
			$(join aaa bbb , 111 222 333)
			返回值:“aaa111 bbb222 333”
		
		
四、foreach 循环函数

	$(foreach <var>,<list>,<text>)
	
		参数:
			<var> 最好是一个变量名
			<list> 可以是一个表达式
			<text> 中一般会使用<var>这个参数来依次枚举<list>中的单词

		示例:
			names := a b c d
			files := $(foreach n,$(names),$(n).o)
			$(name)中的单词会被挨个取出,并存到变量“n”中,“$(n).o”每次根据“$(n)”计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,$(files)的值是“a.o b.o c.o d.o”。
		
		备注:
			foreach中的<var>参数是一个临时的局部变量,foreach函数执行完后,参数<var>的变量将不在作用,其作用域只在foreach函数当中。


五、if 函数

    $(if <condition>,<then-part>)

	或是 

    $(if <condition>,<then-part>,<else-part>)

	参数:
		<condition>参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,
		表达式为真:<then-part>会被计算,
		表达式为假:<else-part>会被计算。


六、call 函数	
	
	all函数是唯一一个可以用来创建新的参数化的函数。
	
	$(call <expression>,<parm1>,<parm2>,<parm3>...)
	
	参数:
		当make执行这个函数时,<expression>参数中的变量,如$(1),$(2),$(3)等,会被参数<parm1>,<parm2>,<parm3>依次取代。而<expression>的返回值就是call函数的返回值。
	
	示例:	
		reverse =  $(1) $(2)
		foo = $(call reverse,a,b)

		此时,foo的值就是“a b”。当然,参数的次序是可以自定义的,不一定是顺序的,如:

		reverse =  $(2) $(1)
		foo = $(call reverse,a,b)
		
		此时,foo的值就是“b a”。
	
	
七、origin 函数
	
	origin函数不像其它的函数,他并不操作变量的值,他只是告诉你你的这个变量是哪里来的?其语法是:

    $(origin <variable>)
	
	参数:
		<variable> 是变量的名字,不应该是引用,所以不要在<variable>中使用“$”字符。
	
	返回值:
		“undefined”
			如果 <variable> 从来没有定义过,origin函数返回这个值“undefined”。 

		“default”
			如果 <variable> 是一个默认的定义,比如“CC”这个变量,这种变量我们将在后面讲述。
                           
		“environment”      
			如果 <variable> 是一个环境变量,并且当Makefile被执行时,“-e”参数没有被打开。
                           
		“file”             
			如果 <variable> 这个变量被定义在Makefile中。
                           
		“command line”     
			如果 <variable> 这个变量是被命令行定义的。
                           
		“override”         
			如果 <variable> 是被override指示符重新定义的。
                           
		“automatic”        
			如果 <variable> 是一个命令运行中的自动化变量。关于自动化变量将在后面讲述。
	  
	
八、shell 函数	
	
	shell函数也不像其它的函数。顾名思义,它的参数应该就是操作系统Shell的命令。它和反引号“`”是相同的功能。
	shell函数把执行操作系统命令后的输出作为函数返回。
	
	
	示例:
		contents := $(shell cat foo)
		
		files := $(shell echo *.c)
		这个函数会新生成一个Shell程序来执行命令,所以要注意其运行性能,如果Makefile中有一些比较复杂的规则,并大量使用了这个函数,那么对于系统性能是有害的。特别是Makefile的隐晦的规则可能会让shell函数执行的次数比想像的多得多。
	
	
九、控制 make 的函数	
	make提供了一些函数来控制make的运行。通常,你需要检测一些运行Makefile时的运行时信息,并且根据这些信息来决定,让make继续执行,还是停止。
	
	$(error <text ...>)
	
	产生一个致命的错误,<text ...>是错误信息。
	
	示例一:

		ifdef ERROR_001

		$(error error is $(ERROR_001))

		endif
 
    示例二:

		ERR = $(error found an error!)

		.PHONY: err

		err: ; $(ERR)

		示例一会在变量ERROR_001定义了后执行时产生error调用,而示例二则在目录err被执行时才发生error调用。

		$(warning <text ...>)

		这个函数很像error函数,只是它并不会让make退出,只是输出一段警告信息,而make继续执行。
	
	

Makefile_11

# make 的运行

一、make 的退出码

	make命令执行后有三个退出码:

    0 —— 表示成功执行。
    1 —— 如果make运行时出现任何错误,其返回1。
    2 —— 如果你使用了make的“-q”选项,并且make使得一些目标不需要更新,那么返回2。


二、指定 Makefile
	
		-f / --file					//给make命令指定一个特殊名字的Makefile / make –f hchen.mk "hchen.mk"是新makefile的名字
	

三、指定目标
	
	“all”
        这个伪目标是所有目标的目标,其功能一般是编译所有的目标。
		
     “clean”
        这个伪目标功能是删除所有被make创建的文件。
		
     “install”
        这个伪目标功能是安装已编译好的程序,其实就是把目标执行文件拷贝到指定的目标中去。
		
     “print”
        这个伪目标的功能是例出改变过的源文件。
		
     “tar”
        这个伪目标功能是把源程序打包备份。也就是一个tar文件。
		
     “dist”
        这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。
		
     “TAGS”
        这个伪目标功能是更新所有的目标,以备完整地重编译使用。
		
     “check”和“test”
        这两个伪目标一般用来测试makefile的流程。
	

四、检查规则

	“-n”
    “--just-print”
    “--dry-run”
    “--recon”
    不执行参数,这些参数只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行,这些参数对于我们调试makefile很有用处。

    “-t”
    “--touch”
    这个参数的意思就是把目标文件的时间更新,但不更改目标文件。也就是说,make假装编译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。

    “-q”
    “--question”
    这个参数的行为是找目标的意思,也就是说,如果目标存在,那么其什么也不会输出,当然也不会执行编译,如果目标不存在,其会打印出一条出错信息。

    “-W <file>”
    “--what-if=<file>”
    “--assume-new=<file>”
    “--new-file=<file>”
    这个参数需要指定一个文件。一般是是源文件(或依赖文件),Make会根据规则推导来运行依赖于这个文件的命令,一般来说,可以和“-n”参数一同使用,来查看这个依赖文件所发生的规则命令。

	另外一个很有意思的用法是结合“-p”和“-v”来输出makefile被执行时的信息(这个将在后面讲述)。


五、make 参数

	“-b”
	“-m”
		这两个参数的作用是忽略和其它版本make的兼容性。

	“-B”
	“--always-make”
		认为所有的目标都需要更新(重编译)。

	“-C <dir>”
	“--directory=<dir>”
		指定读取makefile的目录。如果有多个“-C”参数,make的解释是后面的路径以前面的作为相对路径,并以最后的目录作为被指定目录。如:“make –C ~hchen/test –C prog”等价于“make –C ~hchen/test/prog”。

	“—debug[=<options>]”
		输出make的调试信息。它有几种不同的级别可供选择,如果没有参数,那就是输出最简单的调试信息。下面是<options>的取值:
			a —— 也就是all,输出所有的调试信息。(会非常的多)
			b —— 也就是basic,只输出简单的调试信息。即输出不需要重编译的目标。
			v —— 也就是verbose,在b选项的级别之上。输出的信息包括哪个makefile被解析,不需要被重编译的依赖文件(或是依赖目标)等。
			i —— 也就是implicit,输出所以的隐含规则。
			j —— 也就是jobs,输出执行规则中命令的详细信息,如命令的PID、返回码等。
			m —— 也就是makefile,输出make读取makefile,更新makefile,执行makefile的信息。

	“-d”
		相当于“--debug=a”。

	“-e”
	“--environment-overrides”
		指明环境变量的值覆盖makefile中定义的变量的值。

	“-f=<file>”
	“--file=<file>”
	“--makefile=<file>”
		指定需要执行的makefile。

	“-h”
	“--help”
		显示帮助信息。

	“-i”
	“--ignore-errors”
		在执行时忽略所有的错误。

	“-I <dir>”
	“--include-dir=<dir>”
		指定一个被包含makefile的搜索目标。可以使用多个“-I”参数来指定多个目录。

	“-j [<jobsnum>]”
	“--jobs[=<jobsnum>]”
		指同时运行命令的个数。如果没有这个参数,make运行命令时能运行多少就运行多少。如果有一个以上的“-j”参数,那么仅最后一个“-j”才是有效的。(注意这个参数在MS-DOS中是无用的)

	“-k”
	“--keep-going”
		出错也不停止运行。如果生成一个目标失败了,那么依赖于其上的目标就不会被执行了。

	“-l <load>”
	“--load-average[=<load]”
	“—max-load[=<load>]”
		指定make运行命令的负载。

	“-n”
	“--just-print”
	“--dry-run”
	“--recon”
		仅输出执行过程中的命令序列,但并不执行。

	“-o <file>”
	“--old-file=<file>”
	“--assume-old=<file>”
		不重新生成的指定的<file>,即使这个目标的依赖文件新于它。

	“-p”
	“--print-data-base”
		输出makefile中的所有数据,包括所有的规则和变量。这个参数会让一个简单的makefile都会输出一堆信息。如果你只是想输出信息而不想执行makefile,你可以使用“make -qp”命令。如果你想查看执行makefile前的预设变量和规则,你可以使用“make –p –f /dev/null”。这个参数输出的信息会包含着你的makefile文件的文件名和行号,所以,用这个参数来调试你的makefile会是很有用的,特别是当你的环境变量很复杂的时候。

	“-q”
	“--question”
		不运行命令,也不输出。仅仅是检查所指定的目标是否需要更新。如果是0则说明要更新,如果是2则说明有错误发生。

	“-r”
	“--no-builtin-rules”
		禁止make使用任何隐含规则。

	“-R”
	“--no-builtin-variabes”
		禁止make使用任何作用于变量上的隐含规则。

	“-s”
	“--silent”
	“--quiet”
		在命令运行时不输出命令的输出。

	“-S”
	“--no-keep-going”
	“--stop”
		取消“-k”选项的作用。因为有些时候,make的选项是从环境变量“MAKEFLAGS”中继承下来的。所以你可以在命令行中使用这个参数来让环境变量中的“-k”选项失效。

	“-t”
	“--touch”
		相当于UNIX的touch命令,只是把目标的修改日期变成最新的,也就是阻止生成目标的命令运行。

	“-v”
	“--version”
		输出make程序的版本、版权等关于make的信息。

	“-w”
	“--print-directory”
		输出运行makefile之前和之后的信息。这个参数对于跟踪嵌套式调用make时很有用。

	“--no-print-directory”
		禁止“-w”选项。

	“-W <file>”
	“--what-if=<file>”
	“--new-file=<file>”
	“--assume-file=<file>”	
		假定目标<file>需要更新,如果和“-n”选项使用,那么这个参数会输出该目标更新时的运行动作。如果没有“-n”那么就像运行UNIX的“touch”命令一样,使得<file>的修改时间为当前时间。

	“--warn-undefined-variables”
		只要make发现有未定义的变量,那么就输出警告信息。


Makefile_12

# 隐含规则
	
一、使用隐含规则
	1、编译C程序的隐含规则。
		“<n>.o”的目标的依赖目标会自动推导为“<n>.c”,并且其生成命令是“$(CC) –c $(CPPFLAGS) $(CFLAGS)”

	2、编译C++程序的隐含规则。
		“<n>.o”的目标的依赖目标会自动推导为“<n>.cc”或是“<n>.C”,并且其生成命令是“$(CXX) –c $(CPPFLAGS) $(CFLAGS)”。(建议使用“.cc”作为C++源文件的后缀,而不是“.C”)

	3、编译Pascal程序的隐含规则。
		“<n>.o”的目标的依赖目标会自动推导为“<n>.p”,并且其生成命令是“$(PC) –c  $(PFLAGS)”。

	4、编译Fortran/Ratfor程序的隐含规则。
		“<n>.o”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F”或“<n>.f”,并且其生成命令是:
			“.f”  “$(FC) –c  $(FFLAGS)”
			“.F”  “$(FC) –c  $(FFLAGS) $(CPPFLAGS)”
			“.f”  “$(FC) –c  $(FFLAGS) $(RFLAGS)”

	5、预处理Fortran/Ratfor程序的隐含规则。
		“<n>.f”的目标的依赖目标会自动推导为“<n>.r”或“<n>.F”。这个规则只是转换Ratfor或有预处理的Fortran程序到一个标准的Fortran程序。其使用的命令是:
			“.F”  “$(FC) –F $(CPPFLAGS) $(FFLAGS)”
			“.r”  “$(FC) –F $(FFLAGS) $(RFLAGS)”

	6、编译Modula-2程序的隐含规则。
		“<n>.sym”的目标的依赖目标会自动推导为“<n>.def”,并且其生成命令是:“$(M2C) $(M2FLAGS) $(DEFFLAGS)”。“<n.o>” 的目标的依赖目标会自动推导为“<n>.mod”,并且其生成命令是:“$(M2C) $(M2FLAGS) $(MODFLAGS)”。

	7、汇编和汇编预处理的隐含规则。
		“<n>.o” 的目标的依赖目标会自动推导为“<n>.s”,默认使用编译品“as”,并且其生成命令是:“$(AS) $(ASFLAGS)”。“<n>.s” 的目标的依赖目标会自动推导为“<n>.S”,默认使用C预编译器“cpp”,并且其生成命令是:“$(AS) $(ASFLAGS)”。

	8、链接Object文件的隐含规则。
		“<n>”目标依赖于“<n>.o”,通过运行C的编译器来运行链接程序生成(一般是“ld”),其生成命令是:“$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)”。这个规则对于只有一个源文件的工程有效,同时也对多个Object文件(由不同的源文件生成)的也有效。例如如下规则:

			x : y.o z.o

		并且“x.c”、“y.c”和“z.c”都存在时,隐含规则将执行如下命令:

			cc -c x.c -o x.o
			cc -c y.c -o y.o
			cc -c z.c -o z.o
			cc x.o y.o z.o -o x
			rm -f x.o
			rm -f y.o
			rm -f z.o
		如果没有一个源文件(如上例中的x.c)和你的目标名字(如上例中的x)相关联,那么,你最好写出自己的生成规则,不然,隐含规则会报错的。

	9、Yacc C程序时的隐含规则。
		“<n>.c”的依赖文件被自动推导为“n.y”(Yacc生成的文件),其生成命令是:“$(YACC) $(YFALGS)”。(“Yacc”是一个语法分析器,关于其细节请查看相关资料)

	10、Lex C程序时的隐含规则。
		“<n>.c”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。(关于“Lex”的细节请查看相关资料)

	11、Lex Ratfor程序时的隐含规则。
		“<n>.r”的依赖文件被自动推导为“n.l”(Lex生成的文件),其生成命令是:“$(LEX) $(LFALGS)”。

	12、从C程序、Yacc文件或Lex文件创建Lint库的隐含规则。
		“<n>.ln” (lint生成的文件)的依赖文件被自动推导为“n.c”,其生成命令是:“$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”。对于“<n>.y”和“<n>.l”也是同样的规则。

	
二、命令的变量
	AR
		函数库打包程序。默认命令是“ar”。
	AS
		汇编语言编译程序。默认命令是“as”。
	CC
		C语言编译程序。默认命令是“cc”。
	CXX
		C++语言编译程序。默认命令是“g++”。
	CO
		从 RCS文件中扩展文件程序。默认命令是“co”。
	CPP
		C程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”。
	FC
		Fortran 和 Ratfor 的编译器和预处理程序。默认命令是“f77”。
	GET
		从SCCS文件中扩展文件的程序。默认命令是“get”。
	LEX
		Lex方法分析器程序(针对于C或Ratfor)。默认命令是“lex”。
	PC
		Pascal语言编译程序。默认命令是“pc”。
	YACC
		Yacc文法分析器(针对于C程序)。默认命令是“yacc”。
	YACCR
		Yacc文法分析器(针对于Ratfor程序)。默认命令是“yacc –r”。
	MAKEINFO
		转换Texinfo源文件(.texi)到Info文件程序。默认命令是“makeinfo”。
	TEX
		从TeX源文件创建TeX DVI文件的程序。默认命令是“tex”。
	TEXI2DVI
		从Texinfo源文件创建军TeX DVI 文件的程序。默认命令是“texi2dvi”。
	WEAVE
		转换Web到TeX的程序。默认命令是“weave”。
	CWEAVE
		转换C Web 到 TeX的程序。默认命令是“cweave”。
	TANGLE
		转换Web到Pascal语言的程序。默认命令是“tangle”。
	CTANGLE
		转换C Web 到 C。默认命令是“ctangle”。
	RM
		删除文件命令。默认命令是“rm –f”。
		
	
三、关于命令参数的变量	

	ARFLAGS
		函数库打包程序AR命令的参数。默认值是“rv”。
	ASFLAGS
		汇编语言编译器参数。(当明显地调用“.s”或“.S”文件时)。
	CFLAGS
		C语言编译器参数。
	CXXFLAGS
		C++语言编译器参数。
	COFLAGS
		RCS命令参数。
	CPPFLAGS
		C预处理器参数。( C 和 Fortran 编译器也会用到)。
	FFLAGS
		Fortran语言编译器参数。
	GFLAGS
		SCCS “get”程序参数。
	LDFLAGS
		链接器参数。(如:“ld”)
	LFLAGS
		Lex文法分析器参数。
	PFLAGS
		Pascal语言编译器参数。
	RFLAGS
		Ratfor 程序的Fortran 编译器参数。
	YFLAGS
		Yacc文法分析器参数。

四、隐含规则链

五、定义模式规则
	1、模式规则介绍
	
	2、模式规则示例
	
	3、自动化变量

	4、模式的匹配
	
	5、重载内建隐含规则

六、老式风格的“后缀规则”

七、隐含规则搜索算法

Makefile_13

# 库函数的使用

一、函数库文件的成员


二、函数库成员的隐含规则
	当make搜索一个目标的隐含规则时,一个特殊的特性是,如果这个目标是"a(m)"形式的,其会把目标变成"(m)"。

	如果我们的成员是"%.o"的模式定义,并且如果我们使用"make foo.a(bar.o)"的形式调用Makefile时,隐含规则会去找"bar.o"的规则,如果没有定义bar.o的规则,那么内建隐含规则生效,make会去找bar.c文件来生成bar.o,如果找得到的话,make执行的命令大致如下:

    cc -c bar.c -o bar.o
    ar r foo.a bar.o
    rm -f bar.o

	还有一个变量要注意的是"$%",这是专属函数库文件的自动化变量。
	

三、函数库文件的后缀规则

	你可以使用"后缀规则"和"隐含规则"来生成函数库打包文件,如:

		.c.a:
				$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
				$(AR) r $@ $*.o
				$(RM) $*.o

	其等效于:

		(%.o) : %.c
				$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
				$(AR) r $@ $*.o
				$(RM) $*.o

				
四、注意事项

其他


命令说明
	#make -f 文件名 	//指定编译文件
	
	MAKEFILES			//环境变量
	-					//编译过程中,无论加了-的语句出现什么错误,都不要报错,继续执行
	*					//系统通配符	不知道目标的名字,系统该目录下中所有后缀为.c的文件都是
	%					//系统通配符	应用在当前的Makefile文件中
	?					//系统通配符
	[]					//系统通配符
	/					//转义符 将通配符转化为其原本表示的真实字符
	@					//用在命令行前,这个命令将不被make显示出来


自动化变量说明
	$@					//目标的名字
	$^					//构造所需文件列表所有所有文件的名字
	$<					//构造所需文件列表的第一个文件的名字
	$?					//构造所需文件列表中更新过的文件
		test1.o:test1.c
		gcc -o $@ $<
		test1.o:test1.c head.c
		gcc -o $@ $^
		
	
操作符
	:=					//前面的变量不能使用后面的变量,只能使用前面已定义好了的变量
	?=					//FOO ?= bar 如果FOO没有被定义过,FOO = “bar”,如果FOO先前被定义过,那么什么也不做
		
		
参数说明
	-i / --ignore-errors		//忽略错误
	-k / --keep-going			//命令出错时,终止该规则的执行,但继续执行其它规则。
	-e 							//覆盖makefile中的系统环境变量
	export <variable>			//传递变量到下级  
	-w / --print-directory		//在make的过程中输出一些信息,显示目前的工作目录
	-C							//指定 make 下层 makefile , -w自动生效
	-S / --slient				//-w自动失效
	-f / --file					//给make命令指定一个特殊名字的Makefile / make –f hchen.mk "hchen.mk"是新makefile的名字
		
		
系统变量
	MAKELEVEL					//记录当前Makefile的调用层数。
	CFLAGS						//可以控制编译时的编译器参数
				
		
条件判断
	是否被定义
		ifdef *	//判断 * 是否被定义
			...
		else
			...
		endif
	
	是否不被定义
		ifndef *	//判断 * 是否不被定义
			...
		else
			...
		endif	
		
	是否等于	
		ifeq (a,b)	//判断 a 是否等于 b 
			...
		else
			...
		endif
		
	是否不等于	
		ifneq (a,b)	//判断 a 是否不等于 b 
			...
		else
			...
		endif	
		
		
函数
	$(subst 要被替换的字符串,用来替换的字符串,被处理的字符串):
		用“用来替换的字符串”替换“被处理的字符串”中的“要被替换的字符串”
		$(subst .c,.o,test1.c test2.c) //把 test1.c test2.c 中的 .c 替换为 .o 
	
	
	$(wildcard 寻找的文件)
		在系统中寻找文件
		$(wildcard *.c)
		就等于找到系统中所有后缀为.c的文件,返回成以空格隔开的一整行字符
		
		
环境变量	
	
	MAKECMDGOALS			//这个变量中会存放你所指定的终极目标的列表
		
		

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值