Makefile笔记2

文件目录结构图

TEST2
│ README.txt


├─make1
│ defs.h
│ main.c
│ main.d
│ main.out
│ makefile
│ sort.c
│ sort.d
│ sort.h
│ utils.c
│ utils.d
│ utils.h

├─make2
│ defs.h
│ main.c
│ main.d
│ main.out
│ makefile
│ sort.c
│ sort.d
│ sort.h
│ utils.c
│ utils.d
│ utils.h

├─make3
│ │ all
│ │ command.c
│ │ command.d
│ │ command.h
│ │ main.cpp
│ │ main.d
│ │ Makefile
│ │ pub.h
│ │ 万能makefile写法详解.txt
│ │
│ └─ebook
│ GNU make v3.80中文手册v1.5.pdf
│ 跟我一起写Makefile.pdf

├─make4
│ │ main.out
│ │ makefile
│ │
│ ├─dFile
│ │ main.d
│ │ sort.d
│ │ utils.d
│ │
│ ├─obj
│ ├─project
│ │ defs.h
│ │ main.cpp
│ │
│ ├─sort
│ │ sort.cpp
│ │ sort.h
│ │
│ └─utils
│ utils.c
│ utils.h

└─mixCompileTst
├─assemblyInC
│ main.c
│ main.h
│ makefile
│ start.S

├─cIncpp
│ animal.c
│ animal.h
│ main.cpp
│ main.h
│ makefile

└─cppInc
animal.cpp
animal.h
main.c
main.h
makefile

README.txt:

文件夹说明,知识点导航:
make1:变量的用法,wildcard函数用法,变量后缀名改变方法,通配符用法,.d文件基础用法
make2:.d文件进阶用法
make3:.d文件详细教程,以及一个能自动编译,强关联性,支持C/C++混编的makefile模板
make4:我自己写的一个综合makefile示例,吸收了make3中.d文件的精华,并且支持源码分类存放编译,中间文件输出到指定位置,
clean的时候删除整个目录树中所有的特定文件,同样支持C/C++混合编译。

.d文件知识点总结:
1.gcc -MM xxx.c > xxx.d 可以用gcc查看.c文件生成.o文件依赖的所有头文件以及源文件,并输出到xxx.d文件中,这个.d文件可以
作为一种新的依赖被include进makefile中,这样做的好处是不用每次c文件的依赖关系改变了以后(比如说新include了一个头文件,
这个头文件的函数定义在另一个c文件中),都要到makefile中去改写新的依赖关系,这样不仅麻烦,而且随着工程量的提升,makefile
会变得难以维护。makefile语法保证每次源文件更新,.d文件也同样被更新,.d文件被include进makeflile保证,每次makefile要编译
.o文件的时候都会通过.d文件自动查询依赖关系。
2.gcc -M xxx.c 这个和-MM类似,不同的是-M会把真正意义上所有依赖的库文件都显示出来,比如<stdio.h>
3.include $(.d文件)的这个步骤必须放到主目标之后来写,不然.d文件中的依赖关系会被当作是主目标来制作
4.makefile在开始执行的时候会首先去展开include 的文件,发现没有该文件以后再根据makefile中声明的相应规则去生成.d文件,然后再是
主目标的执行。并且第一遍执行make的时候因为没有一个.d文件,所以什么都没有include进来,第二次make的时候才会有,因为这个的原因,之前
出现过第一次make报错第二次make通过的情况,更细节的原因忘记了,去重新看make3中详解的时候可以留意一下。
5.以上所有的.d文件基础用法在make1中体现。
6.d文件的基础用法虽然可以做到简单的自动查找依赖关系,但是在make1中,main.d的依赖为main.o: main.c defs.h sort.h utils.h
这个时候如果在任意一个头文件中添加一个#include "1.h",make的时候main.o的依赖中有更新的版本,main.o会重新被编译,编译后如果
再更改新加入进来的1.h,因为.d文件中main.o没有依赖他,所以main.o不会被更新,这个时候就需要用到.d文件的进阶用法。
7..d文件进阶用法:把原来的依赖关系改为main.o main.d: main.c defs.h sort.h utils.h,这样在任何一个.h文件改了以后,.d文件
也会得到更新把新的依赖关系包含进来(比如上例中的1.h)。
8.makefile进阶.d文件写法详解:
(五)
解决方法是行内字符串替换,对main.o,取出其中的子串main,加上.d后缀得到main.d,再插入到main.o后面。能实现这种替换功能的命令是sed。
实现的时候,先用gcc -MM命令生成临时文件main.d.temp,再用sed命令从该临时文件中读出内容(用<重定向输入)。做替换后,再用>输出到最终文件main.d。
命令可以这么写:
	g++ -MM main.c > main.d.temp
	sed 's,\(main\)\.o[ :]*,\1.o main.d : ,g' < main.d.temp > main.d
其中:
	sed 's,\(main\)\.o[ :]*,\1.o main.d : ,g',是sed命令。
	< main.d.temp,指示sed命令从临时文件main.d.temp读取输入,作为命令的来源字符串。
	> main.d,把行内替换结果输出到最终文件main.d。

(六)
这条sed命令的结构是s/match/replace/g。有时为了清晰,可以把每个/写成逗号,即这里的格式s,match,replace,g。
该命令表示把源串内的match都替换成replace,s指示match可以是正则表达式。
g表示把每行内所有match都替换,如果去掉g,则只有每行的第1处match被替换(实际上不需要g,因为一个.d文件中,只会在开头有一个main.o:)。
这里match是正则式\(main\)\.o[ :]*,它分成3段:
第1段是\(main\),在sed命令里把main用\(和\)括起来,使接下来的replace中可以用\1引用main。
第2段是\.o,表示匹配main.o,(这里\不知何意,去掉也是可以的)。
第3段是正则式[ :]*,表示若干个空格或冒号,(其实一个.d里只会有一个冒号,如果这里写成[ ]*:,即匹配若干个空格后跟一个冒号,也是可以的)。

总体来说match用来匹配'main.o :'这样的串。
这里的replace是\1.o main.d :,其中\1会被替换为前面第1个\(和\)括起的内容,即main,这样replace值为main.o main.d :
这样该sed命令就实现了把main.o :替换为main.o main.d :的目的。

这两行实现了把临时文件main.d.temp的内容main.o : main.c command.h改为main.o main.d : main.c command.h,并存入main.d文件的功能。

(七)
进一步修改,采用自动化变量。使得当前目录下有多个.c文件时,make会依次对每个.c文件执行这段规则,生成对应的.d:
	gcc -MM  $< > $@.temp;
	sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.temp > $@;
    
(九)
进一步修改,得到目前大家普遍使用的版本:
	set -e; rm -f $@; \
	$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
	sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
	rm -f $@.$$$$
第一行,set -e表示,如果某个命令的返回参数非0,那么整个程序立刻退出。
rm -f用来删除上一次make时生成的.d文件,因为现在要重新生成这个.d,老的可以删除了(不删也可以)。
第二行:前面临时文件是用固定的.d.temp作为后缀,为了防止重名覆盖掉有用的文件,这里把temp换成一个随机数,该数可用$$得到,$$的值是当前进程号。
由于$是makefile特殊符号,一个$要用$$来转义,所以2个$要写成$$$$(你可以在makefile里用echo $$$$来显示进程号的值)。
第三行:sed命令的输入也改成该临时文件.$$。
每个shell命令的进程号通常是不同的,为了每次调用$$时得到的进程号相同,必须把这4行放在一条命令中,这里用分号把它们连接成一条命令(在书写时为了易读,用\拆成了多行),这样每次.$$便是同一个文件了。
你可以在makefile里用下面命令来比较:
	echo $$$$
	echo $$$$; echo $$$$
第四行:当make完后,每个临时文件.d.$$,已经不需要了,删除之。
但每个.d文件要在下一次make时被include进来,要保留。

make1中的makefile

CC=gcc

sources:=$(wildcard *.c)
objects:=$(sources:.c=.o)
dependence=$(sources:.c=.d)

main.out:$(objects)
	$(CC) $^ -o main.out

%.o:%.c
	$(CC) -c $< -o $@

%.d:%.c
	$(CC) -MM $< > $@

include $(dependence)

echo:
	@echo sources=$(sources)
	@echo objects=$(objects)
	@echo dependence=$(dependence)

clean: 
	rm -rf *.d *.out *.o

make2中的makefile

CC=gcc

sources:=$(wildcard *.c)
objects:=$(sources:.c=.o)
dependence=$(sources:.c=.d)

main.out:$(objects)
	$(CC) $^ -o main.out

%.o:%.c
	$(CC) -c $< -o $@

%.d:%.c
	set -e; rm -f $@; \
	$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
	sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
	rm -f $@.$$$$

include $(dependence)

.PHONY:clean echo
echo:
	@echo sources=$(sources)
	@echo objects=$(objects)
	@echo dependence=$(dependence)

clean: 
	rm -rf *.d *.out *.o

make3中的makefile

#一个实用的makefile,能自动编译当前目录下所有.c/.cpp源文件,支持二者混合编译
#并且当某个.c/.cpp、.h或依赖的源文件被修改后,仅重编涉及到的源文件,未涉及的不编译
#详解文档:http://blog.csdn.net/huyansoft/article/details/8924624
#author:胡彦 2013-5-21

#----------------------------------------------------------
#编译工具用g++,以同时支持CC++程序,以及二者的混合编译
CC=g++

#使用$(winldcard *.c)来获取工作目录下的所有.c文件的列表
#sources:=main.cpp command.c

#变量sources得到当前目录下待编译的.c/.cpp文件的列表,两次调用winldcard、结果连在一起即可
sources:=$(wildcard *.c) $(wildcard *.cpp)

#变量objects得到待生成的.o文件的列表,把sources中每个文件的扩展名换成.o即可。这里两次调用patsubst函数,第1次把sources中所有.cpp换成.o,第2次把第1次结果里所有.c换成.o
objects:=$(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(sources)))

#变量dependence得到待生成的.d文件的列表,把objects中每个扩展名.o换成.d即可。也可写成$(patsubst %.o,%.d,$(objects))
dependence:=$(objects:.o=.d)

#----------------------------------------------------------
#当$(objects)列表里所有文件都生成后,便可调用这里的 $(CC) $^ -o $@ 命令生成最终目标all了
#把all定义成第1个规则,使得可以把make all命令简写成make
all: $(objects)
	$(CC) $(CPPFLAGS) $^ -o $@
	@./$@	#编译后立即执行

#这段使用make的模式规则,指示如何由.c文件生成.o,即对每个.c文件,调用gcc -c XX.c -o XX.o命令生成对应的.o文件
#如果不写这段也可以,因为make的隐含规则可以起到同样的效果
%.o: %.c
	$(CC) $(CPPFLAGS) -c $< -o $@

#同上,指示如何由.cpp生成.o,可省略
%.o: %.cpp
	$(CC) $(CPPFLAGS) -c $< -o $@

#----------------------------------------------------------
include $(dependence)	#注意该句要放在终极目标all的规则之后,否则.d文件里的规则会被误当作终极规则了

#因为这4行命令要多次凋用,定义成命令包以简化书写
define gen_dep
set -e; rm -f $@; \
$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
endef

#指示如何由.c生成其依赖规则文件.d
#这段使用make的模式规则,指示对每个.c文件,如何生成其依赖规则文件.d,调用上面的命令包即可
%.d: %.c
	$(gen_dep)

#同上,指示对每个.cpp,如何生成其依赖规则文件.d
%.d: %.cpp
	$(gen_dep)

#----------------------------------------------------------
#清除所有临时文件(所有.o和.d)。之所以把clean定义成伪目标,是因为这个目标并不对应实际的文件
.PHONY: clean
clean:	#.$$已在每次使用后立即删除。-f参数表示被删文件不存在时不报错
	rm -f all $(objects) $(dependence)

echo:	#调试时显示一些变量的值
	@echo sources=$(sources)
	@echo objects=$(objects)
	@echo dependence=$(dependence)
	@echo CPPFLAGS=$(CPPFLAGS)

#提醒:当混合编译.c/.cpp时,为了能够在C++程序里调用C函数,必须把每一个要调用的C函数,其声明都包括在extern "C"{}块里面,这样C++链接时才能成功链接它们。

万能makefile写法详解.txt

作者:胡彦 2013-5-21
本文档可能有更新,更新版本请留意http://blog.csdn.net/huyansoft/article/details/8924624

一
目的:编写一个实用的makefile,能自动编译当前目录下所有.c/.cpp源文件,支持二者混合编译。并且当某个.c/.cpp、.h或依赖的源文件被修改后,仅重编涉及到的源文件,未涉及的不编译。

二
要达到这个目的,用到的技术有:
1-使用wildcard函数来获得当前目录下所有.c/.cpp文件的列表。
2-make的多目标规则。
3-make的模式规则。
4-用gcc -MM命令得到一个.c/.cpp文件include了哪些文件。
5-用sed命令对gcc -MM命令的结果作修改。
6-用include命令包含依赖描述文件.d。

三 准备知识
(一)多目标
对makefile里下面2行,可看出多目标特征,执行make bigoutput或make littleoutput可看到结果:
bigoutput littleoutput: defs.h pub.h
	@echo $@ $(subst output,OUTPUT,$@) $^	# $@指这个规则里所有目标的集合,$^指这个规则里所有依赖的集合。该行是把目标(bigoutput或littleoutput)里所有子串output替换成大写的OUTPUT

(二)隐含规则
对makefile里下面4行,可看出make的隐含规则,执行foo可看到结果:
第3、4行表示由.c得到.o,第1、2行表示由.o得到可执行文件。
如果把第3、4行注释的话,效果一样。
即不写.o来自.c的规则,它会自动执行gcc -c -o foo.o foo.c这条命令,由.c编译出.o(其中-c表示只编译不链接),然后自动执行gcc -o foo foo.o链接为可执行文件。
foo:foo.o
	gcc -o foo foo.o; ./foo
foo.o:foo.c					#注释该行看效果
	gcc -c foo.c -o foo.o	#注释该行看效果

(三)定义模式规则
下面定义了一个模式规则,即如何由.c文件生成.d文件的规则。
foobar: foo.d bar.d
	@echo complete generate foo.d and bar.d
%.d: %.c		#make会对当前目录下每个.c文件,依次做一次里面的命令,从而由每个.c文件生成对应.d文件。
	@echo from $< to $@
	g++ -MM $< > $@
假定当前目录下有2个.c文件:foo.c和bar.c(文件内容随意)。
验证方法有2种,都可:
1-运行make foo.d(或make bar.d),表示想要生成foo.d这个目标。
根据规则%.d: %.c,这时%匹配foo,这样%.c等于foo.c,即foo.d这个目标依赖于foo.c。
此时会自动执行该规则里的命令gcc -MM foo.c > foo.d,来生成foo.d这个目标。
2-运行make foobar,因为foobar依赖于foo.d和bar.d这2个文件,即会一次性生成这2个文件。

四
下面详述如何自动生成依赖性,从而实现本例的makefile。

(一)
本例使用了makefile的模式规则,目的是对当前目录下每个.c文件,生成其对应的.d文件,例如由main.c生成的.d文件内容为:
	main.o : main.c command.h
这里指示了main.o目标依赖于哪几个源文件,我们只要把这一行的内容,通过make的include指令包含到makefile文件里,即可在其任意一个依赖文件被修改后,重新编译目标main.o。
下面详解如何生成这个.d文件。

(二)
gcc/g++编译器有一个-MM选项,可以对某个.c/.cpp文件,分析其依赖的源文件,例如假定main.c的内容为:
#include <stdio.h>//标准头文件(以<>方式包含的),被-MM选项忽略,被-M选项收集
#include "stdlib.h"//标准头文件(以""方式包含的),被-MM选项忽略,被-M选项收集
#include "command.h"
int main()
{
	printf("##### Hello Makefile #####\n");
	return 0;
}
则执行gcc -MM main.c后,屏幕输出:
main.o: main.c command.h
执行gcc -M main.c后,屏幕输出:
main.o: main.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-linux-gnu/4.4.3/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-linux-gnu/4.4.3/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
/usr/include/stdlib.h /usr/include/sys/types.h /usr/include/time.h \
/usr/include/endian.h /usr/include/bits/endian.h \
/usr/include/bits/byteswap.h /usr/include/sys/select.h \
/usr/include/bits/select.h /usr/include/bits/sigset.h \
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h command.h

(三)
可见,只要把这些行挪到makefile里,就能自动定义main.c的依赖是哪些文件了,做法是把命令的输出重定向到.d文件里:gcc -MM main.c > main.d,再把这个.d文件include到makefile里。
如何include当前目录每个.c生成的.d文件:
sources:=$(wildcard *.c)	#使用$(wildcard *.cpp)来获取工作目录下的所有.c文件的列表。
dependence=$(sources:.c=.d)	#这里,dependence是所有.d文件的列表.即把串sources串里的.c换成.d。
include $(dependence)	#include后面可以跟若干个文件名,用空格分开,支持通配符,例如include  foo.make  *.mk。这里是把所有.d文件一次性全部include进来。注意该句要放在终极目标all的规则之后,否则.d文件里的规则会被误当作终极规则了。

(四)
现在main.c command.h这几个文件,任何一个改了都会重编main.o。但是这里还有一个问题,如果修改了command.h,在command.h中加入#include "pub.h",这时:
1-再make,由于command.h改了,这时会重编main.o,并且会使用新加的pub.h,看起来是正常的。
2-这时打开main.d查看,发现main.d中未加入pub.h,因为根据模式规则%.d: %.c中的定义,只有依赖的.c文件变了,才会重新生成.d,而刚才改的是command.h,不会重新生成main.d、及在main.d中加入对pub.h的依赖关系,这会导致问题。
3-修改新加的pub.h的内容,再make,果然问题出现了,make报告up to date,没有像期望那样重编译main.o。
现在问题在于,main.d里的某个.h文件改了,没有重新生成main.d。进一步说,main.d里给出的每个依赖文件,任何一个改了,都要重新生成这个main.d。
所以main.d也要作为一个目标来生成,它的依赖应该是main.d里的每个依赖文件,也就是说make里要有这样的定义:
main.d: main.c command.h
这时我们发现,main.d与main.o的依赖是完全相同的,可以利用make的多目标规则,把main.d与main.o这两个目标的定义合并为一句:
main.o main.d: main.c command.h
现在,main.o: main.c command.h这一句我们已经有了,如何进一步得到main.o main.d: main.c command.h呢?

(五)
解决方法是行内字符串替换,对main.o,取出其中的子串main,加上.d后缀得到main.d,再插入到main.o后面。能实现这种替换功能的命令是sed。
实现的时候,先用gcc -MM命令生成临时文件main.d.temp,再用sed命令从该临时文件中读出内容(用<重定向输入)。做替换后,再用>输出到最终文件main.d。
命令可以这么写:
	g++ -MM main.c > main.d.temp
	sed 's,\(main\)\.o[ :]*,\1.o main.d : ,g' < main.d.temp > main.d
其中:
	sed 's,\(main\)\.o[ :]*,\1.o main.d : ,g',是sed命令。
	< main.d.temp,指示sed命令从临时文件main.d.temp读取输入,作为命令的来源字符串。
	> main.d,把行内替换结果输出到最终文件main.d。

(六)
这条sed命令的结构是s/match/replace/g。有时为了清晰,可以把每个/写成逗号,即这里的格式s,match,replace,g。
该命令表示把源串内的match都替换成replace,s指示match可以是正则表达式。
g表示把每行内所有match都替换,如果去掉g,则只有每行的第1处match被替换(实际上不需要g,因为一个.d文件中,只会在开头有一个main.o:)。
这里match是正则式\(main\)\.o[ :]*,它分成3段:
第1段是\(main\),在sed命令里把main用\(和\)括起来,使接下来的replace中可以用\1引用main。
第2段是\.o,表示匹配main.o,(这里\不知何意,去掉也是可以的)。
第3段是正则式[ :]*,表示若干个空格或冒号,(其实一个.d里只会有一个冒号,如果这里写成[ ]*:,即匹配若干个空格后跟一个冒号,也是可以的)。

总体来说match用来匹配'main.o :'这样的串。
这里的replace是\1.o main.d :,其中\1会被替换为前面第1个\(和\)括起的内容,即main,这样replace值为main.o main.d :
这样该sed命令就实现了把main.o :替换为main.o main.d :的目的。

这两行实现了把临时文件main.d.temp的内容main.o : main.c command.h改为main.o main.d : main.c command.h,并存入main.d文件的功能。

(七)
进一步修改,采用自动化变量。使得当前目录下有多个.c文件时,make会依次对每个.c文件执行这段规则,生成对应的.d:
	gcc -MM  $< > $@.temp;
	sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.temp > $@;

(八)
现在来看上面2行的执行流程:

第一次make,假定这时从来没有make过,所有.d文件不存在,这时键入make:
1-include所有.d文件的命令无效果。
2-首次编译所有.c文件。每个.c文件中若#include了其它头文件,会由编译器自动读取。由于这次是完整编译,不存在什么依赖文件改了不会重编的问题。
3-对每个.c文件,会根据依赖规则%.d: %.c,生成其对应的.d文件,例如main.c生成的main.d文件为:
	main.o main.d: main.c command.h

第二次make,假定改了command.h、在command.h中加入#include "pub.h",这时再make:
1-include所有.d文件,例如include了main.d后,得到依赖规则:
	main.o main.d: main.c command.h
注意所有include命令是首先执行的,make会先把所有include进来,再生成依赖规则关系。
2-此时,根据依赖规则,由于command.h的文件戳改了,要重新生成main.o和main.d文件。
3-先调用gcc -c main.c -o main.o生成main.o,
再调用gcc -MM main.c > main.d重新生成main.d。
此时main.d的依赖文件里增加了pub.h:
	main.o main.d: main.c command.h pub.h
4-对其它依赖文件没改的.c(由其.d文件得到),不会重新编译.o和生成其.d。
5-最后会执行gcc $(objects) -o main生成最终可执行文件。

第三次make,假定改了pub.h,再make。由于第二遍中,已把pub.h加入了main.d的依赖,此时会重编main.c,重新生成main.o和main.d。
这样便实现了当前目录下任一源文件改了,自动编译涉及它的.c。

(九)
进一步修改,得到目前大家普遍使用的版本:
	set -e; rm -f $@; \
	$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
	sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
	rm -f $@.$$$$
第一行,set -e表示,如果某个命令的返回参数非0,那么整个程序立刻退出。
rm -f用来删除上一次make时生成的.d文件,因为现在要重新生成这个.d,老的可以删除了(不删也可以)。
第二行:前面临时文件是用固定的.d.temp作为后缀,为了防止重名覆盖掉有用的文件,这里把temp换成一个随机数,该数可用$$得到,$$的值是当前进程号。
由于$是makefile特殊符号,一个$要用$$来转义,所以2个$要写成$$$$(你可以在makefile里用echo $$$$来显示进程号的值)。
第三行:sed命令的输入也改成该临时文件.$$。
每个shell命令的进程号通常是不同的,为了每次调用$$时得到的进程号相同,必须把这4行放在一条命令中,这里用分号把它们连接成一条命令(在书写时为了易读,用\拆成了多行),这样每次.$$便是同一个文件了。
你可以在makefile里用下面命令来比较:
	echo $$$$
	echo $$$$; echo $$$$
第四行:当make完后,每个临时文件.d.$$,已经不需要了,删除之。
但每个.d文件要在下一次make时被include进来,要保留。

(十)
综合前面的分析,得到我们的makefile文件:

#使用$(wildcard *.c)来获取工作目录下的所有.c文件的列表
sources:=$(wildcard *.c)
objects:=$(sources:.c=.o)
#这里,dependence是所有.d文件的列表.即把串sources串里的.c换成.d
dependence:=$(sources:.c=.d)

#所用的编译工具
CC=gcc

#当$(objects)列表里所有文件都生成后,便可调用这里的 $(CC) $^ -o $@ 命令生成最终目标all了
#把all定义成第1个规则,使得可以把make all命令简写成make
all: $(objects)
	$(CC) $^ -o $@

#这段是make的模式规则,指示如何由.c文件生成.o,即对每个.c文件,调用gcc -c XX.c -o XX.o命令生成对应的.o文件。
#如果不写这段也可以,因为make的隐含规则可以起到同样的效果
%.o: %.c
	$(CC) -c $< -o $@

include $(dependence)	#注意该句要放在终极目标all的规则之后,否则.d文件里的规则会被误当作终极规则了
%.d: %.c
	set -e; rm -f $@; \
	$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
	sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
	rm -f $@.$$$$

.PHONY: clean	#之所以把clean定义成伪目标,是因为这个目标并不对应实际的文件
clean:
	rm -f all $(objects) $(dependence)	#清除所有临时文件:所有.o和.d。.$$已在每次使用后立即删除。-f参数表示被删文件不存在时不报错

(十一)
上面这个makefile已经能正常工作了(编译C程序),但如果要用它编译C++,变量CC值要改成g++,每个.c都要改成.cpp,有点繁琐。
现在我们继续完善它,使其同时支持C和C++,并支持二者的混合编译。

#一个实用的makefile,能自动编译当前目录下所有.c/.cpp源文件,支持二者混合编译
#并且当某个.c/.cpp、.h或依赖的源文件被修改后,仅重编涉及到的源文件,未涉及的不编译
#详解文档:http://blog.csdn.net/huyansoft/article/details/8924624
#author:胡彦 2013-5-21

#----------------------------------------------------------
#编译工具用g++,以同时支持C和C++程序,以及二者的混合编译
CC=g++

#使用$(winldcard *.c)来获取工作目录下的所有.c文件的列表
#sources:=main.cpp command.c

#变量sources得到当前目录下待编译的.c/.cpp文件的列表,两次调用winldcard、结果连在一起即可
sources:=$(wildcard *.c) $(wildcard *.cpp)

#变量objects得到待生成的.o文件的列表,把sources中每个文件的扩展名换成.o即可。这里两次调用patsubst函数,第1次把sources中所有.cpp换成.o,第2次把第1次结果里所有.c换成.o
objects:=$(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(sources)))

#变量dependence得到待生成的.d文件的列表,把objects中每个扩展名.o换成.d即可。也可写成$(patsubst %.o,%.d,$(objects))
dependence:=$(objects:.o=.d)

#----------------------------------------------------------
#当$(objects)列表里所有文件都生成后,便可调用这里的 $(CC) $^ -o $@ 命令生成最终目标all了
#把all定义成第1个规则,使得可以把make all命令简写成make
all: $(objects)
	$(CC) $(CPPFLAGS) $^ -o $@
	@./$@	#编译后立即执行

#这段使用make的模式规则,指示如何由.c文件生成.o,即对每个.c文件,调用gcc -c XX.c -o XX.o命令生成对应的.o文件
#如果不写这段也可以,因为make的隐含规则可以起到同样的效果
%.o: %.c
	$(CC) $(CPPFLAGS) -c $< -o $@

#同上,指示如何由.cpp生成.o,可省略
%.o: %.cpp
	$(CC) $(CPPFLAGS) -c $< -o $@

#----------------------------------------------------------
include $(dependence)	#注意该句要放在终极目标all的规则之后,否则.d文件里的规则会被误当作终极规则了

#因为这4行命令要多次凋用,定义成命令包以简化书写
define gen_dep
set -e; rm -f $@; \
$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
endef

#指示如何由.c生成其依赖规则文件.d
#这段使用make的模式规则,指示对每个.c文件,如何生成其依赖规则文件.d,调用上面的命令包即可
%.d: %.c
	$(gen_dep)

#同上,指示对每个.cpp,如何生成其依赖规则文件.d
%.d: %.cpp
	$(gen_dep)

#----------------------------------------------------------
#清除所有临时文件(所有.o和.d)。之所以把clean定义成伪目标,是因为这个目标并不对应实际的文件
.PHONY: clean
clean:	#.$$已在每次使用后立即删除。-f参数表示被删文件不存在时不报错
	rm -f all $(objects) $(dependence)

echo:	#调试时显示一些变量的值
	@echo sources=$(sources)
	@echo objects=$(objects)
	@echo dependence=$(dependence)
	@echo CPPFLAGS=$(CPPFLAGS)

#提醒:当混合编译.c/.cpp时,为了能够在C++程序里调用C函数,必须把每一个要调用的C函数,其声明都包括在extern "C"{}块里面,这样C++链接时才能成功链接它们。

五
makefile学习体会:
刚学过C语言的读者,可能会觉得makefile有点难,因为makefile不像C语言那样,一招一式都那么清晰明了。
在makefile里到处是“潜规则”,都是一些隐晦的东西,要弄明白只有搞清楚这些“潜规则”。
基本的规则无非是“一个依赖改了,去更新哪些目标”。
正因为隐晦动作较多,写成一个makefile才不需要那么多篇幅,毕竟项目代码才是主体。只要知道makefile的框架,往它的套路里填就行了。

较好的学习资料是《跟我一起写Makefile.pdf》这篇文档(下载包里已经附带了),比较详细,适合初学者。
我们学习的目的是,能够编写一个像本文这样的makefile,以满足简单项目的基本需求,这要求理解前面makefile几个关键点:
1-多目标
2-隐含规则
3-定义模式规则
4-自动生成依赖性
可惜的是,这篇文档虽然比较全面,却没有以一个完整的例子为引导,对几处要点没有突出指明,尤其是“定义模式规则”在最后不显眼的位置(第十一部分第五点),导致看了“自动生成依赖性”一节后还比较模糊。
所以,看了《跟我一起写Makefile.pdf》后,再结合本文针对性的讲解,会有更实际的收获。
另一个学习资料是《GNU make v3.80中文手册v1.5.pdf》,这个手册更详细,但较枯燥,不适合完整学习,通常是遇到问题再去查阅。

其它文章和代码请留意我的blog: http://blog.csdn.net/huyansoft

[END]

make4中的makefile

#个人认为最完美的makefile(到写下这个makefile为止),功能:自动编译当前目录下所有.c/.cpp源文件,支持二者混合编译
#并且当某个.c/.cpp、.h或依赖的源文件被修改后,仅重编涉及到的源文件,未涉及的不编译。另外,编译产生的说明依赖关系的.d
#文件和中间文件.o文件会输出到指定目标路径,源文件和头文件按功能存放,删除特定路径下的中间文件。

#CC变量声明编译方式,可更改
CC=gcc
CPP=g++

#头文件路径声明,后续如果有新的头文件路径,在这里添加
INCDIRS		:= 	project \
				sort 	\
				utils 

#源文件路径声明,后续如果有新的源文件路径,在这里添加
SRCDIRS		:= 	project	\
				sort 	\
				utils 

#匹配每个INCDIRS字符,加 -I 参数返回,字符间以空格分开
INCLUDE			:= $(patsubst %, -I %, $(INCDIRS))

#筛选SRCDIRS路径下的所有.c文件,带路径返回
#筛选SRCDIRS路径下的所有.cpp文件,带路径返回
CFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))
CPPFILES		:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.cpp))

#去路径
CFILENDIR		:= $(notdir  $(CFILES))
CPPFILENDIR		:= $(notdir  $(CPPFILES))

#把CFILENDIR中的所有.c文件转换成.o结尾的字符串,再加上obj/的路径名返回,每个字符串间以空格隔开
COBJS			:= $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
CPPOBJS			:= $(patsubst %, obj/%, $(CPPFILENDIR:.cpp=.o))
OBJS			:= $(COBJS) $(CPPOBJS)

#VPATH:告诉makefile当前目录如果找不到依赖文件去哪里找(不是告诉gcc!!! gcc 或者g++仍然要加变量声明)
VPATH			:= $(SRCDIRS)


#dependence等于所有dFile文件夹路径下的.d文件
temp = $(patsubst %, dFile/%, $(CFILENDIR)) 
temp += $(patsubst %, dFile/%, $(CPPFILENDIR))
temp2 := $(temp:.c=.d)
dependence := $(temp2:.cpp=.d)
	
#整理当前工作目录下所有的.d .o文件,供make clean使用
#SUBDIR,SUBDIRS作为中间变量传参给SOURCE,SOURCE作为目标文件目录应用于最后clean的参数
#$(shell <命令>)这是makefile中常见的shell命令调用方法
#shell命令中的管道运算符“|”,command 1 | command 2,他的功能是把第一个命令command 1执行的结果作为command2的输入传给command 2
#ls -R:归列出遇到的子目录。(包括文件)
#subst:字符替换函数,格式为:$(subst from,to,text),函数功能:把字串“ text”中的“ from”字符替换为“ to”
#foreach:$(SUBDIRS)中的单词会被挨个取出,并存到变量“dir”中,“$(wildcard $(dir)*.o)每次计算出一个值,这些值以空格分隔,
#最后作为foreach函数的返回,所以,$(SOURCE)的值是“.//main.o ./utils/utils.o ./sort/sort.o"。(后记:后面加了一个$(dir)*.d),同理)
#注意,foreach中的<var>;参数是一个临时的局部变量,foreach函数执行完后,参数<var>;的变量将不在作用,其作用域只在foreach函数当中。
#wildcard:$(wildcard ./*.o)取出所有当前目录下所有的以.o文件结尾的字符串,当前目录也可以不加./,$(wildcard *.c ./sort/*.c)
#返回结果为:main.c ./sort/sort.c
SUBDIR = $(shell ls ./ -R | grep /)
SUBDIRS = $(subst :,/,$(SUBDIR))
OSOURCE = $(foreach dir, $(SUBDIRS),$(wildcard $(dir)*.o))
DSOURCE = $(foreach dir, $(SUBDIRS),$(wildcard $(dir)*.d))

#最终要生成的可执行文件
main.out:$(OBJS)
	$(CPP) $^ -o main.out

obj/%.o:%.c
	$(CC) $(INCLUDE) -c $< -o $@

obj/%.o:%.cpp
	$(CPP) $(INCLUDE) -c $< -o $@

#因为这4行命令要多次凋用,定义成命令包以简化书写
define gen_dep
set -e; rm -f $@; \
$(CPP) $(INCLUDE) -MM $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
endef

#指示如何由.c生成其依赖规则文件.d
#这段使用make的模式规则,指示对每个.c文件,如何生成其依赖规则文件.d,调用上面的命令包即可
dFile/%.d: %.c
	$(gen_dep)

#同上,指示对每个.cpp,如何生成其依赖规则文件.d
dFile/%.d: %.cpp
	$(gen_dep)

include $(dependence)

#伪目标集
.PHONY:echo cleanall cleanobj cleandfile
#打印参数值(make echo)
echo:
	@echo CFILES=$(CFILES)
	@echo OBJS=$(OBJS)
	@echo dependence=$(dependence)
	@echo SOURCE=$(SOURCE)

#清除全部编译产生文件(make cleanall)
cleanall: cleanobj cleandfile
	rm -rf *.out

#仅清除中间文件(make cleanobj)
cleanobj: 
	rm -rf $(OSOURCE)

#仅清除.d文件(make cleandfile)
cleandfile: 
	rm -rf $(DSOURCE)

mixCompileTst文件夹

assemblyInc中的makefile

TARGET:=prj
CROSS_COMPILE 	?= arm-linux-gnueabihf-

#命令行输入流程
#arm-linux-gnueabihf-gcc -g -c start.S -o start.o
#arm-linux-gnueabihf-gcc -g -c main.c -o main.o
#arm-linux-gnueabihf-ld -Ttext 0X87800000 start.o main.o -o start.elf
#arm-linux-gnueabihf-objcopy -O binary -S start.elf start.bin
#arm-linux-gnueabihf-objdump -D -m arm start.elf > start.dis

CC 				:= $(CROSS_COMPILE)gcc
LD				:= $(CROSS_COMPILE)ld		#链接起始地址用
OBJCOPY 		:= $(CROSS_COMPILE)objcopy	#生成bin文件用
OBJDUMP 		:= $(CROSS_COMPILE)objdump	#反汇编


$(TARGET).bin:$(TARGET).elf
	$(OBJCOPY) -O binary -S $< $@
	$(OBJDUMP) -D -m arm $< > $(TARGET).dis
	

#0X87800000 ARM A7一块板子的起始链接地址
#-Ttext:指定链接地址
$(TARGET).elf:start.o main.o     
	$(LD) -Ttext 0X87800000 start.o main.o -o $(TARGET).elf

#-g 产生调试信息
%.o:%.S 
	$(CC) -g -c $< -o $@

%.o:%.c
	$(CC) -g -c $< -o $@
	

.PHONY:clean exec
clean:
	rm -rf *.o *.bin *.dis *.elf

exec:
	@cd ~/mydir; ls	#命令行前加@符号可以让make不输出命令语句本身,仅输出执行结果
	cd ~/mydir; pwd	#如果想要让命令在上一条命令的基础上执行,那么两句命令要写在一起,用分号隔开
	@pwd

cIncpp中的makefile

#随手小记:
#1.c引入到c++文件,c文件中所有的变量,函数,要加“extern”关键字,然后在c++文件中引入c头文件的部分需要加“extern "C" ”关键字,
#最重要的是,c文件必须用gcc编译成.o文件,cpp文件用g++编译成.o文件。链接的时候c库文件放在前面(animal.o)
#2.	c引入到c++文件,结构体,枚举,联合等变量类型的声明不用加extern关键字
#3.%:通配符,表示匹配到的一段字符串,在一段话中出现的两个通配符相等。例如:%.o:%.c -> xx.o:xx.c,在本例中只有一个:animal.o:animal.c
#4.$@表示所有目标文件,就是命令上面第一行冒号左边的所有文件
#5.$<表示第一个依赖文件,命令上面第一行冒号右边的第一个文件
#6.$^表示所有依赖文件,命令上面第一行冒号右边的所有文件
CC=gcc
PP=g++

CFILES:=$(wildcard *.c)
CPPFILES:=$(wildcard *.cpp)

COBJS:=$(CFILES:.c=.o)
CPPOBJS:=$(CPPFILES:.cpp=.o)

OBJS:=$(COBJS) $(CPPOBJS) 


main.out:$(OBJS)
	$(PP) $^ -o $@

%.o:%.c
	$(CC) -c $< -o $@

%.o:%.cpp
	$(PP) -c $< -o $@

.PHONY:echo clean
echo:
	@echo CFILES=$(CFILES)
	@echo CPPFILES=$(CPPFILES)
	@echo COBJS=$(COBJS)
	@echo CPPOBJS=$(CPPOBJS)
	@echo OBJS=$(OBJS)

clean:
	rm -rf *.o *.out

cppInc中的makefile

#啥也别说了,抄就完了
#-c:只编译,不链接。-o:按指定名称输出
CC=g++

main.out:animal.o main.c 
	$(CC) $^ -o $@

%.o:%.cpp 
	$(CC) -c $<  -o $@

.PHONY:clean
clean:
	rm -rf *.o *.out

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GNU MAKE的详细中文手册,目录如下: 目 录 第一章:概述 1.1 概述 1.2 准备知识 第二章:GNU make 介绍 2.1 Makefile简介 2.2 Makefile规则介绍 2.3 简单的示例 2.4 make如何工作 2.5 指定变量 2.6 自动推导规则 2.7 另类风格的makefile 2.8 清除工作目录过程文件 第三章:Makefile 总述 3.1 Makefile的内容 3.2 makefile文件的命名 3.3 包含其它makefile文件 3.4 变量 MAKEFILES 3.5 变量 MAKEFILE_LIST 3.6 其他特殊变量 3.7 makefile文件的重建 3.8 重载另外一个makefile 3.9 make如何解析makefile文件 3.9.1 变量取值 3.9.2 条件语句 3.9.3 规则的定义 3.10 总结 第四章:Makefile的规则 4.1 一个例子 4.2 规则语法 4.3 依赖的类型 4.4 文件名使用通配符 4.4.1 统配符使用举例 4.4.2 通配符存在的缺陷 4.4.3 函数wildcard 4.5 目录搜寻 4.5.1 一般搜索(变量VPATH) 4.5.2 选择性搜索(关键字vpath) 4.5.3 目录搜索的机制 4.5.4 命令行和搜索目录 4.5.5 隐含规则和搜索目录 4.5.6 库文件和搜索目录 4.6 Makefile伪目标 4.7 强制目标(没有命令或依赖的规则) 4.8 空目标文件 4.9 Makefile的特殊目标 4.10 多目标 4.11 多规则目标 4.12 静态模式 4.12.1 静态模式规则的语法 4.12.2 静态模式和隐含规则 4.13 双冒号规则 4.14 自动产生依赖 第五章:规则的命令 5.1 命令回显 5.2 命令的执行 5.3 并发执行命令 5.4 命令执行的错误 5.5 中断make的执行 5.6 make的递归执行 5.6.1 变量MAKE 5.6.2 变量和递归 5.6.3 命令行选项和递归 5.6.4 -w选项 5.7 定义命令包 5.8 空命令 第六章:Makefile中的变量 6.1 变量的引用 6.2 两种变量定义(赋值 ) 6.2.1 递归展开式变量 6.2.2 直接展开式变量 6.2.3 如何定义一个空格 6.2.4 “?=”操作符 6.3 变量的高级用法 6.3.1 变量的替换引用 6.3.2 变量的套嵌引用 6.4 变量取值 6.5 如何设置变量 6.6 追加变量值 6.7 override 指示符 6.8 多行定义 6.9 系统环境变量 6.10 目标指定变量 6.11 模式指定变量 第七章:Makefile的条件执行 7.1 一个例子 7.2 条件判断的基本语法 7.2.1 关键字“ifeq” 7.2.2 关键字“ifneq” 7.2.3 关键字“ifdef” 7.2.4 关键字“ifndef” 7.3 标记测试的条件语句 第八章:make的内嵌函数 8.1 函数的调用语法 8.2 文本处理函数 8.2.1 $(subst FROM,TO,TEXT) 8.2.2 $(patsubst PATTERN,REPLACEMENT,TEXT) 8.2.3 $(strip STRINT) 8.2.4 $(findstring FIND,IN) 8.2.5 $(filter PATTERN…,TEXT) 8.2.6 $(filter-out PATTERN...,TEXT) 8.2.7 $(sort LIST) 8.2.8 $(word N,TEXT) 8.2.9 $(wordlist S,E,TEXT) 8.2.10 $(words TEXT) 8.2.11 $(firstword NAMES…) 8.3 文件名处理函数 8.3.1 $(dir NAMES…) 8.3.2 $(notdir NAMES…) 8.3.3 $(suffix NAMES…) 8.3.4 $(basename NAMES…) 8.3.5 $(addsuffix SUFFIX,NAMES…) 8.3.6 $(addprefix PREFIX,NAMES…) 8.3.7 $(join LIST1,LIST2) 8.3.8 $(wildcard PATTERN) 8.4 foreach 函数 8.5 if 函数 8.6 call函数 8.7 value函数 8.8 eval函数 8.9 origin函数 8.10 shell函数 8.11 make的控制函数 8.11.1 $(error TEXT…) 8.11.2 $(warning TEXT…) 第九章:执行make 9.1 指定makefile文件 9.2 指定终极目标 9.3 替代命令的执行 9.4 防止特定文件重建 9.5 替换变量定义 9.6 使用make进行编译测试 9.7 make的命令行选项 第十章:make的隐含规则 10.1 隐含规则的使用 10.2 make的隐含规则一览 10.3 隐含变量 10.3.1 代表命令的变量 10.3.2 命令参数的变量 10.4 make隐含规则链 10.5 模式规则 10.5.1 模式规则介绍 10.5.2 模式规则示例 10.5.3 自动化变量 10.5.4 模式的匹配 10.5.5 万用规则 10.5.6 重建内嵌隐含规则 10.6 缺省规则 10.7 后缀规则 10.8 隐含规则搜索算法 第十一章:使用make更新静态库文件 11.1 库成员作为目标 11.2 静态库的更新 11.2.1 更新静态库的符号索引表 11.3 make静态库的注意事项 11.4 静态库的后缀规则 第十二章 : GNU make的特点 12.1 源自System v的特点 12.2 源自其他版本的特点 12.3 GNU make自身的特点 第十三章 和其它版本的兼容 第十四章 Makefile的约定 14.1 基本的约定 14.2 规则命令行的约定 14.3 代表命令变量 14.4 安装目录变量 14.5 Makefile的标准目标名 14.6 安装命令分类 第十五章 make的常见错误信息   附录:关键字索引 1. GNU make可识别的指示 符 2. GNU make函数 3. GNU make的自动化变量 4. GNU make环境变量 后序
【推荐】GNU make中文手册,详细介绍了makefile的用法和规则等。目录如下:<br>第一章:概述<br>1.1 概述<br>1.2 准备知识<br>第二章:GNU make 介绍<br>2.1 Makefile简介<br>2.2 Makefile规则介绍<br>2.3 简单的示例<br>2.4 make如何工作<br>2.5 指定变量<br>2.6 自动推导规则<br>2.7 另类风格的makefile<br>2.8 清除工作目录过程文件<br>第三章:Makefile 总述<br>3.1 Makefile的内容<br>3.2 makefile文件的命名<br>3.3 包含其它makefile文件<br>3.4 变量 MAKEFILES<br>3.5 变量 MAKEFILE_LIST<br>3.6 其他特殊变量<br>3.7 makefile文件的重建<br>3.8 重载另外一个makefile<br>3.9 make如何解析makefile文件<br>3.9.1 变量取值<br>3.9.2 条件语句<br>3.9.3 规则的定义<br>3.10 总结<br>第四章:Makefile的规则<br>4.1 一个例子<br>4.2 规则语法<br>4.3 依赖的类型<br>4.4 文件名使用通配符<br>4.4.1 统配符使用举例<br>4.4.2 通配符存在的缺陷<br>4.4.3 函数wildcard<br>4.5 目录搜寻<br>4.5.1 一般搜索(变量VPATH)<br>4.5.2 选择性搜索(关键字vpath)<br>4.5.3 目录搜索的机制<br>4.5.4 命令行和搜索目录<br>4.5.5 隐含规则和搜索目录<br>4.5.6 库文件和搜索目录<br>4.6 Makefile伪目标<br>4.7 强制目标(没有命令或依赖的规则)<br>4.8 空目标文件<br>4.9 Makefile的特殊目标<br>4.10 多目标<br>4.11 多规则目标<br>4.12 静态模式<br>4.12.1 静态模式规则的语法<br>4.12.2 静态模式和隐含规则<br>4.13 双冒号规则<br>4.14 自动产生依赖<br>第五章:规则的命令<br>5.1 命令回显<br>5.2 命令的执行<br>5.3 并发执行命令<br>5.4 命令执行的错误<br>5.5 中断make的执行<br>5.6 make的递归执行<br>5.6.1 变量MAKE<br>5.6.2 变量和递归<br>5.6.3 命令行选项和递归<br>5.6.4 -w选项<br>5.7 定义命令包<br>5.8 空命令<br>第六章:Makefile中的变量<br>6.1 变量的引用<br>6.2 两种变量定义(赋值 )<br>6.2.1 递归展开式变量<br>6.2.2 直接展开式变量<br>6.2.3 如何定义一个空格<br>6.2.4 “?=”操作符<br>6.3 变量的高级用法<br>6.3.1 变量的替换引用<br>6.3.2 变量的套嵌引用<br>6.4 变量取值<br>6.5 如何设置变量<br>6.6 追加变量值<br>6.7 override 指示符<br>6.8 多行定义<br>6.9 系统环境变量<br>6.10 目标指定变量<br>6.11 模式指定变量<br>第七章:Makefile的条件执行<br>7.1 一个例子<br>7.2 条件判断的基本语法<br>7.2.1 关键字“ifeq”<br>7.2.2 关键字“ifneq”<br>7.2.3 关键字“ifdef” <br>7.2.4 关键字“ifndef”<br>7.3 标记测试的条件语句<br>第八章:make的内嵌函数<br>8.1 函数的调用语法<br>8.2 文本处理函数<br>8.2.1 $(subst FROM,TO,TEXT)<br>8.2.2 $(patsubst PATTERN,REPLACEMENT,TEXT)<br>8.2.3 $(strip STRINT)<br>8.2.4 $(findstring FIND,IN)<br>8.2.5 $(filter PATTERN…,TEXT)<br>8.2.6 $(filter-out PATTERN...,TEXT)<br>8.2.7 $(sort LIST)<br>8.2.8 $(word N,TEXT)<br>8.2.9 $(wordlist S,E,TEXT)<br>8.2.10 $(words TEXT)<br>8.2.11 $(firstword NAMES…)<br>8.3 文件名处理函数<br>8.3.1 $(dir NAMES…)<br>8.3.2 $(notdir NAMES…)<br>8.3.3 $(suffix NAMES…)<br>8.3.4 $(basename NAMES…)<br>8.3.5 $(addsuffix SUFFIX,NAMES…)<br>8.3.6 $(addprefix PREFIX,NAMES…)<br>8.3.7 $(join LIST1,LIST2)<br>8.3.8 $(wildcard PATTERN)<br>8.4 foreach 函数<br>8.5 if 函数<br>8.6 call函数<br>8.7 value函数<br>8.8 eval函数<br>8.9 origin函数<br>8.10 shell函数<br>8.11 make的控制函数<br>8.11.1 $(error TEXT…)<br>8.11.2 $(warning TEXT…)<br>第九章:执行make<br>9.1 指定makefile文件<br>9.2 指定终极目标<br>9.3 替代命令的执行<br>9.4 防止特定文件重建<br>9.5 替换变量定义<br>9.6 使用make进行编译测试<br>9.7 make的命令行选项<br>第十章:make的隐含规则<br>10.1 隐含规则的使用<br>10.2 make的隐含规则一览<br>10.3 隐含变量<br>10.3.1 代表命令的变量<br>10.3.2 命令参数的变量<br>10.4 make隐含规则链<br>10.5 模式规则<br>10.5.1 模式规则介绍<br>10.5.2 模式规则示例<br>10.5.3 自动化变量<br>10.5.4 模式的匹配<br>10.5.5 万用规则<br>10.5.6 重建内嵌隐含规则<br>10.6 缺省规则<br>10.7 后缀规则<br>10.8 隐含规则搜索算法<br>第十一章:使用make更新静态库文件<br>11.1 库成员作为目标<br>11.2 静态库的更新<br>11.2.1 更新静态库的符号索引表<br>11.3 make静态库的注意事项<br>11.4 静态库的后缀规则<br>第十二章 : GNU make的特点<br>12.1 源自System v的特点<br>12.2 源自其他版本的特点<br>12.3 GNU make自身的特点<br>第十三章 和其它版本的兼容<br>第十四章 Makefile的约定<br>14.1 基本的约定<br>14.2 规则命令行的约定<br>14.3 代表命令变量<br>14.4 安装目录变量<br>14.5 Makefile的标准目标名<br>14.6 安装命令分类<br>第十五章 make的常见错误信息<br> <br>附录:关键字索引<br>1. GNU make可识别的指示 符<br>2. GNU make函数<br>3. GNU make的自动化变量<br>4. GNU make环境变量<br>后序<br>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值