使用 Makefile 进行项目管理和构建
使用 Makefile 进行项目管理和构建
来源 https://zhuanlan.zhihu.com/p/661285050
什么是Makefile?
Makefile 可以简单的认为是一个工程文件的编译规则,描述了整个工程的编译和链接等规则。其中包含了那些文件需要编译,那些文件不需要编译,那些文件需要先编译,那些文件需要后编译,那些文件需要重建等等。编译整个工程需要涉及到的,在 Makefile 中都可以进行描述。换句话说,Makefile 可以使得我们的项目工程的编译变得自动化,不需要每次都手动输入一堆源文件和参数。 以 Linux 下的C语言开发为例来具体说明一下,多文件编译生成一个文件,编译的命令如下所示: gcc -o outfile name1.c name2.c ... outfile
要生成的可执行程序的名字,nameN.c 是源文件的名字。这是我们在 Linux 下使用 gcc 编译器编译 C 文件的例子。如果我们遇到的源文件的数量不是很多的话,可以选择这样的编译方式。如果源文件非常的多的话,就会遇到下面的这些问题。
1) 编译的时候需要链接库的的问题。拿C语言来说,编译的时候 gcc 只会默认链接一些基本的C语言标准库,很多源文件依赖的标准库都需要我们手动链接。 下面列举了一些需要我们手动链接的标准库: name1.c 用到了数学计算库 math 中的函数,我们得手动添加参数 -Im; name4.c 用到了小型数据库 SQLite 中的函数,我们得手动添加参数 -lsqlite3; name5.c 使用到了线程,我们需要去手动添加参数 -lpthread。 因为有很多的文件,还要去链接很多的第三方库。所以在编译的时候命令会很长,并且在编译的时候我们可能会涉及到文件链接的顺序问题,所以手动编译会很麻烦。 如果我们学会使用 Makefile 就不一样了,它会彻底简化编译的操作。把要链接的库文件放在 Makefile 中,制定相应的规则和对应的链接顺序。这样只需要执行 make 命令,工程就会自动编译。每次想要编译工程的时候就执行 make ,省略掉手动编译中的参数选项和命令,非常的方便。
2) 编译大的工程会花费很长的时间。 如果我们去做项目开发,免不了要去修改工程项目的源文件,每次修改后都要去重新编译。一个大的工程项目可不止有几个的源文件,里面的源文件个数可能有成百上千个。例如一个内核,或者是一个软件的源码包。这些都是我们做开发经常会遇到的。要完成这样的文件的编译,我们消耗的时间可不是一点点。如果文件特别大的话我们可能要花上半天的时间。 对于这样的问题我们 Makefile 可以解决吗?当然是可以的,Makefile 支持多线程并发操作,会极大的缩短我们的编译时间,并且当我们修改了源文件之后,编译整个工程的时候,make 命令只会编译我们修改过的文件,没有修改的文件不用重新编译,也极大的解决了我们耗费时间的问题。 这其实是我们遇到的比较常见的问题,当然可能遇到的问题还会有很多,比如:工程文件中的源文件的类型很多,编译的话需要选择的编译器;文件可能会分布在不同的目录中,使用时需要调价路径。这些问题都可以通过 Makefile 解决。并且文件中的 Makefile 只需要完成一次,一般我们只要不增加或者是删除工程中的文件,Makefile 基本上不用去修改,编译时只用一个 make 命令。为我们提供了极大的便利,很大程度上提高编译的效率。
Makefile结构说明
Makefile里主要包含了五个东西:变量定义、显式规则、隐晦规则、文件指示和注释。
1、变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
2、显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。 刚才写的疑似shell脚本的Makefile全部都是显示规则。
3、隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。
4、文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样。
5、注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符。
Makefile的优点
- 管理代码的编译,决定该编译什么文件,编译顺序,以及是否需要重新编译;
- 节省编译时间。如果文件有更改,只需重新编译此文件即可,无需重新编译整个工程;
- 一劳永逸。Makefile通常只需编写一次,后期就不用过多更改。
Makefile的工作流程
命名:我们推荐使用 Makefile(一般在工程中都这么写,大写的会比较的规范)。如果文件不存在,make 就会给我们报错,提示: make:***
没有明确目标并且找不到 makefile。
Makefile 的具体工作流程可以通过例子来看一下:创建一个包含有多个源文件和 Makefile 的目录文件,源文件之间相互关联。在 Makefile 中添加下面的代码:
main:main.o test1.o test2.o
gcc main.o test1.o test2.o -o main
main.o:main.c test.h
gcc -c main.c -o main.o
test1.o:test1.c test.h
gcc -c test1.c -o test1.o
test2.o:test2.c test.h
gcc -c test2.c -o test2.o
- 在我们编译项目文件的时候,默认情况下,make 执行的是 Makefile 中的第一规则(Makefile 中出现的第一个依赖关系),此规则的第一目标称之为“最终目标”或者是“终极目标”。 在 shell 命令行执行的 make 命令,就可以得到可执行文件 main 和中间文件 main.o、test1.o 和 test2.o,main 就是我们要生成的最终文件。通过 Makefile 我们可以发现,目标 main"在 Makefile 中是第一个目标,因此它就是 make 的终极目标,当修改过任何 C 文件后,执行 make 将会重建终极目标 main。 它的具体工作顺序是:当在 shell 提示符下输入 make 命令以后。 make 读取当前目录下的 Makefile 文件,并将 Makefile 文件中的第一个目标作为其执行的“终极目标”,开始处理第一个规则(终极目标所在的规则)。在我们的例子中,第一个规则就是目标 "main" 所在的规则。规则描述了 "main" 的依赖关系,并定义了链接 ".o" 文件生成目标 "main" 的命令;make 在执行这个规则所定义的命令之前,首先处理目标 "main" 的所有的依赖文件(例子中的那些 ".o" 文件)的更新规则(以这些 ".o" 文件为目标的规则)。 对这些 ".o" 文件为目标的规则处理有下列三种情况: 目标 ".o" 文件不存在,使用其描述规则创建它; 目标 ".o" 文件存在,目标 ".o" 文件所依赖的 ".c" 源文件 ".h" 文件中的任何一个比目标 ".o" 文件“更新”(在上一次 make 之后被修改)。则根据规则重新编译生成它; 目标 ".o" 文件存在,目标 ".o" 文件比它的任何一个依赖文件(".c" 源文件、".h" 文件)“更新”(它的依赖文件在上一次 make 之后没有被修改),则什么也不做。 通过上面的更新规则我们可以了解到中间文件的作用,也就是编译时生成的 ".o" 文件。作用是检查某个源文件是不是进行过修改,最终目标文件是不是需要重建。我们执行 make 命令时,只有修改过的源文件或者是不存在的目标文件会进行重建,而那些没有改变的文件不用重新编译,这样在很大程度上节省时间,提高编程效率。小的工程项目可能体会不到,项目工程文件越大,效果才越明显。 当然 make 命令能否顺利的执行,还在于我们是否制定了正确的的依赖规则,当前目录下是不是存在需要的依赖文件,只要任意一点不满足,我们在执行 make 的时候就会出错。所以完成一个正确的 Makefile 不是一件简单的事情。
Makefile基本语法
target(目标文件) ...: prerequisites(依赖的文件) ... command(命令) ... ...
指令:屏蔽指令
定义变量(变量大写)
变量名=值1 值2 ...
使用变量 $(变量名)
Makefile中CFLAGS,LDFLAGS,LIBS的说明
- CFLAGS:C编译器选项,而CPPFLAG/CXXFLAGS表示C++编译器的选项. 目的:输出文件名称,可调试,编译告警,指定头文件目录.
- LDFLAGS:链接器从哪里寻找库文件,围绕着编译时使用的库文件,添加库文件的路径
- LIBS:告诉链接器要链接哪些库文件,如LIBS = -lpthread,-lm(链接线程库和数学库)
有关具体参数可查看gcc编译选项
Makefile中的缩进
makefile中有两种不同的语言,shell语法(recipe)和makefile语法(non-recipe),为了区分这两种语言所以使用tab。以tab开头的是shell(recipe)。
- 在写makefile语法,非recipe的时候,缩进应该使用空格。
- 在写shell语法,recipe时,缩进使用TAB。因为实际上我们写的是希望shell执行的语句,所以使用的是shell syntax。而make识别recipe的方式就是。
Makefile 静态模式 %.o:%.c
静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活。
语法:
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
...
如果我们的定义成“%.o”,意思是我们的集合中都是以“.o”结尾的,而如果我们的定义成“%.c”, 意思是对所形成的目标集进行二次定义,其计算方法是,取模式中的“%”(也就是去掉了[.o]这个结尾), 并为其加上[.c]这个结尾,形成的新集合。所以,我们的“目标模式”或是“依赖模式”中都应该有“%”这个字符。
Makefile 赋值语句 =,:=,?=,+= 符号的含义
=
最简单的赋值.用=赋值的变量,在被解析时他的值取决于最后一次赋值时的值,所以你看变量引用的值时不能只往前面看,还要往后面看。:=
最简单的赋值.用:=来赋值的,则是就地直接解析 ,只用往前看即可。?=
?=如果变量前面并没有赋值过则执行这条赋值,如果前面已经赋值过了则本行被忽略 。(实验可以看出:所谓的没有赋值过其实就是这个变量没有被定义过)+=
+=用来给一个已经赋值的变量接续赋值,意思就是把这次的值加到原来的值的后面 ,有点类似于strcat。(在shell makefile等文件中,可以认为所有变量都是字符串,+=就相当于给字符串stcat接续内容)(注意一个细节,+=续接的内容和原来的内容之间会自动加一个空格隔开)
Makefile 符号@ - $ $$含义
@
(用于静默执行)
#示例
DIR_OBJ=./obj
CMD_MKOBJDIR=if [ -d ${DIR_OBJ} ]; then exit 0; else
mkdir ${DIR_OBJ}; fi
mkobjdir:
@${CMD_MKOBJDIR}
#命令行执行如下: make mkobjdir
#此时不会显示在命令行不会显示出if [ -d ${DIR_OBJ} ]; then exit 0; else mkdir ${DIR_OBJ}; fi,
#但如果规则行的TAB后没有以@开头,则会显示
-
这个符串通常用在“规则”行中,表示不显示命令本身,而只显示它的结果-rm dir; -mkdir aaadir;
$
美元符号$,主要扩展打开makefile中定义的变量$$
$$ 符号主要扩展打开makefile中定义的shell
Makefile ifeq、ifneq、ifdef和ifndef(条件判断)
关键字 | 功能 |
---|---|
ifeq | 判断参数是否相等,相等为 true,不相等为 false。 |
ifneq | 判断参数是否不相等,不相等为 true,相等为 false。 |
ifdef | 判断是否有值,有值为 true,没有值为 false。 |
ifndef | 判断是否有值,没有值为 true,有值为 false。 |
ifeq 表示如果比较相等,语法如下:
ifeq(<参数 1>, <参数 2>)
ifneq 表示如果不相等,语法如下:
ifneq(<参数 1>, <参数 2>)
ifdef 表示如果定义了变量,语法如下:
ifdef <变量名>
ifndef 表示如果没有定义变量,语法如下:
ifndef <变量
Makefile 通配符
$* #不包含扩展名的目标文件名称。
$+ #所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
$< #第一个依赖文件的名称。
$? #所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
$@ #目标的完整名称。
$^ #所有的依赖文件,以空格分开,不包含重复的依赖文件。
$% #如果目标是归档成员,则该变量表示目标的归档成员名称。
Makefile 内嵌变量 $(CURDIR) $0 $1 $2 $#
$(CURDIR) # CURDIR是make的内嵌变量, 为当前目录
SRCTREE := $(CURDIR) *$(CURDIR)为当前目录,相当于SRCTREE=./
MKCONFIG := $(SRCTREE)/mkconfig *相当于MKCONFIG=./mkconfig
$0 # Shell本身的文件名
$1 #添加到Shell的第一个参数
$2 #添加到Shell的第二个参数
$# #添加到Shell的总参数个数
Makefile中的常见自动变量$@, $^, $< , $?, $%, $+, $*
Makefile用一些特殊的符号来替代符合某种条件的文件集,这就形成了自动变量。 自动变量的含义:预定义的特殊意义的符号。就类似于C语言编译器中预制的那些宏__FILE__一样。
$@
#表示目标文件,表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
$^
#表示所有的依赖文件
#所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
$<
#表示第一个依赖文件,依赖目标中的第一个目标名字。
#如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$?
#表示比目标还要新的依赖文件列表/集合,以空格分隔。
$%
#仅当目标是函数库文件中,表示规则中的目标成员名。
#例如,如果一个目标是“foo.a(bar.o)”,那么,“$%”就是“bar.o”,“$@”就是“foo.a”。
#如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。
$+
#这个变量很像“$^”,也是所有依赖目标的集合。只是它不去除重复的依赖目标。
$*
#这个变量表示目标模式中“%”及其之前的部分。
#如果目标是“dir/a.foo.b”,并且目标的模式是“a.%.b”,那么,“$*”的值就是“dir/a.foo”。
#这个变量对于构造有关联的文件名是比较有较
#。如果目标中没有模式的定义,那么“$*”也就不能被推导出,但是,
#如果目标文件的后缀是make所识别的,那么“$*”就是除了后缀的那一部分。
#例如:如果目标是“foo.c”,因为“.c”是make所能识别的后缀名,所以,“$*”的值就是“foo”。
#这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用“$*”,除非是在隐含规则或是静态模式中。
#如果目标中的后缀是make所不能识别的,那么“$*”就是空值。
Makefile常用标识(CROSS_COMPILE和ARCH)
- CROSS_COMPILE
即交叉编译器的前缀(prefix),也就是选择将代码编译成目标cpu的指令的工具,如指定make CROSS_COMPILE=arm-none-linux-gnueabi-就是使用arm-none-linux-gnueabi-gcc, arm-none-linux-gnueabi-ld等工具将代码编译成arm的可执行指令。
如果不指定CROSS_COMPILE参数,make时将认为prefix为空,即使用gcc来编译。
这里cross_compile的设置,是假定所用的交叉工具链的gcc程序名称为arm-linux-gcc。
如果实际使用的gcc名称是some-thing-else-gcc,则这里填some-thing-else-即可。总之,要省去名称中最后的gcc那3个字母。
- ARCH
即architecture,就是选择编译哪一种cpu architecture,也就是编译arch/目录下的哪一个子目录。
如指定make ARCH=arm就是编译arch/arm下的代码。如果不指定,make将使用本机(用什么机器编译就是什么)的cpu作为缺省ARCH.
注意:arch/arm下不但有arm体系架构特有的代码,还有arm特有的kconfig,也就是配置选项,所以在make menuconfig,make xxxx_defconfig的时候也必须指定ARCH=arm。
Makefile唯一的循环控制结构foreach 命令
foreach
是 GNU make 的一个功能强大的函数,它允许你在 Makefile 中进行列表的迭代操作。
foreach
函数的基本语法如下:makefile $(foreach var, list, text)
这里的参数有三个:
var
:这是循环变量的名称,在text
中引用。list
:这是一个由空格分隔的值列表,foreach
函数会遍历这个列表。text
:这是在每次迭代中执行的文本或表达式,其中可以使用$(var)
来引用当前的列表元素。
例如,假设我们有一个源文件列表 srcs
,我们想要生成相应的目标文件列表 objs
,可以使用 foreach
函数来完成:
srcs := a.c b.c c.c
objs := $(foreach src, $(srcs), $(src:.c=.o))
这段代码将 objs
设置为 a.o b.o c.o
。
总的来说,foreach
函数为在 Makefile 中处理列表提供了强大的功能,使得我们可以更方便地处理复杂的构建任务。 foreach
是一个非常强大的函数,除了基本的列表遍历之外,它还可以配合其他函数来实现更复杂的逻辑。以下是一些 foreach
的高级用法:
嵌套使用 foreach
:foreach
可以嵌套使用来处理更复杂的列表结构。例如,假设我们有一个二维的源文件列表,我们可以使用嵌套的 foreach
来生成所有的目标文件列表:
src_lists := list1:a.c b.c list2:c.c d.c
objs := $(foreach src_list, $(src_lists), \
$(foreach src, $(wordlist 2, $(words $(src_list)), $(src_list)), \
$(src:.c=.o)))
结合 call
函数:foreach
可以结合 call
函数来调用用户自定义的函数。例如,假设我们定义了一个函数 compile
来编译源文件,我们可以使用 foreach
和 call
来编译所有的源文件:
src_lists := list1:a.c b.c list2:c.c d.c
objs := $(foreach src_list, $(src_lists), \
$(foreach src, $(wordlist 2, $(words $(src_list)), $(src_list)), \
$(src:.c=.o)))
生成规则:foreach
可以用于生成 Makefile 规则。例如,假设我们有多个目标需要生成,我们可以使用 foreach
来生成这些目标的规则:
targets := a b c
define rule
$(1):
@echo Building $(1)
endef
$(foreach target, $(targets), $(eval $(call rule, $(target))))
这段代码将为 a
、b
和 c
生成对应的构建规则。
这些只是 foreach
函数的一部分高级用法,实际上 foreach
的可能用法有很多,只要结合其他函数,你可以实现各种复杂的逻辑。
Makefile中$(eval ...) 和include的用法和区别
$(eval ...)
和include
都可以在 Makefile 中用于包含和执行其他 Makefile 文件的内容,但它们的工作方式和用途有些区别。
1.include
:在处理 Makefile 时,Make 会在当前的上下文中直接包含include
指定的文件。被包含的文件中的所有规则、变量定义等都将被加入到当前 Makefile 中。如果被包含的文件不存在,Make 会尝试查找隐含规则来创建它,如果仍然找不到,那么 Make 将会报错。
2.$(eval ...)
:eval
函数会将其参数解析为 Makefile 语法,并执行。与include
不同,eval
可以处理动态生成的 Makefile 代码。比如,可以先定义一个变量,然后使用eval
来解析和执行这个变量的值。 总结一下,include
更常用于包含静态的 Makefile 文件,而$(eval ...)
更适合处理动态生成的 Makefile 代码。如果你需要在 Makefile 中处理复杂的逻辑,可能需要用到$(eval ...)
。
比较项 | include | $(eval ...) |
---|---|---|
目的 | 用于包含静态的 Makefile 文件,这些文件中的所有规则、变量定义等都将被加入到当前 Makefile 中。 | 用于解析和执行 Makefile 语法。更适合处理动态生成的 Makefile 代码。 |
处理方式 | 在处理 Makefile 时,直接在当前的上下文中包含 include 指定的文件。 | 会将其参数解析为 Makefile 语法,并执行。 |
错误处理 | 如果被包含的文件不存在,Make 会尝试查找隐含规则来创建它。如果仍然找不到,那么 Make 将会报错。 | eval 不会处理文件的存在性。如果 eval 的参数不能被正确解析为 Makefile 语法,那么会导致错误。 |
使用场景 | 当你有一些公共的规则或变量定义,需要在多个 Makefile 中共享时,可以使用 include。 | 当你需要在 Makefile 中处理复杂的逻辑,可能需要用到 $(eval ...)。比如,先定义一个变量,然后使用 eval 来解析和执行这个变量的值。 |
灵活性 | 对于静态的 Makefile 文件,include 是一个简单而直接的解决方案。 | $(eval ...) 提供了更高的灵活性,可以处理动态生成的 Makefile 代码,但同时也更复杂。 |
请注意,这个对比表格并不是绝对的,使用 include
还是 $(eval ...)
取决于具体的应用场景。
Makefile中.PHONY和.SUFFIXES的含义
- 内容过长,放链接:Makefile中.SUFFIXES的含义
- 内容过长,放链接:Makefile中.PHONY的含义
Makefile中文件读写 file命令
file
是 GNU make 4.2 版本引入的一个新函数,用于在 Makefile 中进行文件读写操作。它的语法如下:bash $(file op filename[,text])
在这个函数中,op
是操作符,filename
是要操作的文件的名称,text
是可选的文本参数。这个函数的返回值是空字符串。
操作符 op
可以是以下的一个:
<
:读取文件。这个操作符会读取指定文件的内容,并返回。例如,$(file <filename)
会读取filename
文件的内容。这个文件的内容会作为函数的返回值。>
:写入文件。这个操作符会将text
参数写入到filename
文件中,覆盖文件中原有的内容。例如,$(file >filename,text)
会将text
写入到filename
文件中。>>
:追加到文件。这个操作符会将text
参数追加到filename
文件的末尾。例如,$(file >>filename,text)
会将text
追加到filename
文件的末尾。
这个函数可以用于在 Makefile 中直接进行文件操作,而无需调用 shell 命令。但请注意,这个函数只在 GNU make 4.2 及更高版本中可用。
这是一个简单的例子:
all:
$(file >output.txt,This is some text.)
这个 Makefile 会创建一个名为 output.txt
的文件,并写入文本 "This is some text."。
在 GNU make 中,file
函数的高级用法主要涉及到它与其他 Makefile 特性的结合使用,以实现更复杂的文件操作。下面是一些可能的高级用法:
动态生成 Makefile 规则
有时,你可能希望在运行 make 命令时动态地生成一些 Makefile 规则。这可以通过将规则写入一个临时文件,然后使用 include
指令将其包含到当前 Makefile 中来实现。
generate-rules:
$(file >rules.mk,$(foreach obj,$(OBJECTS),$(obj): $(obj:.o=.c)\n\t$(CC) -c $< -o $@\n\n))
include rules.mk
在这个例子中,file
函数用于生成一个名为 rules.mk
的文件,该文件中包含了一组编译规则。然后,include
指令用于将这些规则包含到当前的 Makefile 中。
在文件中存储中间结果
在某些情况下,你可能希望在 Makefile 中存储一些中间结果,以便在后续的规则中使用。这可以通过 file
函数来实现:
intermediate:
@echo "Generating intermediate results..."
@$(file >$@,$(shell ./generate-results))
final: intermediate
@echo "Generating final results based on intermediate results..."
@$(file >$@,$(shell ./generate-final $(shell cat intermediate)))
在这个例子中,file
函数用于将一个中间结果存储到一个文件中。然后,这个文件在后续的规则中被读取,并用于生成最终的结果。
注意,以上示例为简化展示并未包含完整的错误处理和清理代码,在实际使用中需要进行相应的处理。
Makefile中patsubst(扩展通配符)的含义
- 内容过长,放链接:Makefile中patsubst(扩展通配符)的含义
最基本的Makefile示例
SRCS = $(wildcard *.c)
OBJS = $(patsubst %c, %o, $(Message_SRCS))
# wildcard 扩展通配符,指定目录 ./ 和 ./sub/ 下的所有后缀是c的文件全部展开
# patsubst 替换通配符
# $^ 代表所有依赖文件
# $@ 代表所有目标文件
# $< 代表第一个依赖文件
# % 代表通配符 CFLAGS/CXXFLAGS: #编译器会用到的一些优化参数 , 并指定头文件(.h文件)的路径,如:CFLAGS=-I/usr/include -I/path/include。
LDFLAGS:#指定库文件的位置。
LIBS: #告诉链接器要链接哪些库文件,如LIBS = -lpthread -liconv
$(CC) $(CFLAGS) $(LDFLAGS) main.c gfifo.c queue.c usbmonitor.c socket_rcv_360_server.c ./lib/srs_librtmp.a ./lib/libcrypto.a ./lib/libssl.a ./lib/libtinyalsa.a -o media_record -static -ldl -lstdc++ -lm -lpthread
#使用定的编译器、编译选项参数、链接选项参数,编译.c文件,并使用静态方式链接制定的库文件,以及编译器目录下的libdl.a、libstdc++.a、libm.a、libpthread.a库文件生成media_record 可执行目标文件。
Makefile模版 提供一个模版,里面内容按需修改.
#############################################################
# Generic Makefile for C/C++ Program
#
# License: GPL (General Public License)
# Description:
# ------------
# This is an easily customizable makefile template. The purpose is to
# provide an instant building environment for C/C++ programs.
#
# It searches all the C/C++ source files in the specified directories,
# makes dependencies, compiles and links to form an executable.
#
# Besides its default ability to build C/C++ programs which use only
# standard C/C++ libraries, you can customize the Makefile to build
# those using other libraries. Once done, without any changes you can
# then build programs using the same or less libraries, even if source
# files are renamed, added or removed. Therefore, it is particularly
# convenient to use it to build codes for experimental or study use.
#
# GNU make is expected to use the Makefile. Other versions of makes
# may or may not work.
#
# Usage:
# ------
# 1. Copy the Makefile to your program directory.
# 2. Customize in the "Customizable Section" only if necessary:
# * to use non-standard C/C++ libraries, set pre-processor or compiler
# options to <MY_CFLAGS> and linker ones to <MY_LIBS>
# (See Makefile.gtk+-2.0 for an example)
# * to search sources in more directories, set to <SRCDIRS>
# * to specify your favorite program name, set to <PROGRAM>
# 3. Type make to start building your program.
#
# Make Target:
# ------------
# The Makefile provides the following targets to make:
# $ make compile and link
# $ make NODEP=yes compile and link without generating dependencies
# $ make objs compile only (no linking)
# $ make tags create tags for Emacs editor
# $ make ctags create ctags for VI editor
# $ make clean clean objects and the executable file
# $ make cleanall clean objects, the executable, TAGS and dependencies
# $ make help get the usage of the makefile
#
#===========================================================================
## Customizable Section: adapt those variables to suit your program.
##==========================================================================
# The pre-processor and compiler options.
MY_CFLAGS = -I/usr/include
# The linker options.
MY_LIBS = -lpthread
-Wl,-rpath-link=
-Wl,-rpath=/usr/lib
# The pre-processor options used by the cpp (man cpp for more).
CPPFLAGS = -Wall
# The options used in linking as well as in any direct use of ld.
LDFLAGS =
# The directories in which source files reside.
# If not specified, only the current directory will be serached.
SRCDIRS = .
# The executable file name.
# If not specified, current directory name or `a.out' will be used.
PROGRAM = name_app
## Implicit Section: change the following only when necessary.
##==========================================================================
# The source file types (headers excluded).
# .c indicates C source files, and others C++ ones.
SRCEXTS = .c .C .cc .cpp .CPP .c++ .cxx .cp
# The header file types.
HDREXTS = .h .H .hh .hpp .HPP .h++ .hxx .hp
# The pre-processor and compiler options.
# Users can override those variables from the command line.
# The GCC default, if no C language dialect options are given, is -std=gnu17.
# The GCC default, if no C++ language dialect options are given, is -std=gnu++17.
CFLAGS = -O3 -std=gnu11
CXXFLAGS= -O3 -std=gnu++14
# The C program compiler.
CC = /usr/bin/gcc
# The C++ program compiler.
CXX = /usr/bin/g++
# Un-comment the following line to compile C programs as C++ ones.
#CC = $(CXX)
# The command used to delete file.
RM = rm -f
ETAGS = etags
ETAGSFLAGS =
CTAGS = ctags
CTAGSFLAGS =
## Stable Section: usually no need to be changed. But you can add more.
##==========================================================================
SHELL = /bin/sh
EMPTY =
SPACE = $(EMPTY) $(EMPTY)
ifeq ($(PROGRAM),)
CUR_PATH_NAMES = $(subst /,$(SPACE),$(subst $(SPACE),_,$(CURDIR)))
PROGRAM = $(word $(words $(CUR_PATH_NAMES)),$(CUR_PATH_NAMES))
ifeq ($(PROGRAM),)
PROGRAM = a.out
endif
endif
ifeq ($(SRCDIRS),)
SRCDIRS = .
endif
SOURCES = $(foreach d,$(SRCDIRS),$(wildcard $(addprefix $(d)/*,$(SRCEXTS))))
HEADERS = $(foreach d,$(SRCDIRS),$(wildcard $(addprefix $(d)/*,$(HDREXTS))))
SRC_CXX = $(filter-out %.c,$(SOURCES))
OBJS = $(addsuffix .o, $(basename $(SOURCES)))
DEPS = $(OBJS:.o=.d)
## Define some useful variables.
DEP_OPT = $(shell if `$(CC) --version | grep "GCC" >/dev/null`; then \
echo "-MM -MP"; else echo "-M"; fi )
DEPEND = $(CC) $(DEP_OPT) $(MY_CFLAGS) $(CFLAGS) $(CPPFLAGS)
DEPEND.d = $(subst -g ,,$(DEPEND))
COMPILE.c = $(CC) $(MY_CFLAGS) $(CFLAGS) $(CPPFLAGS) -c
COMPILE.cxx = $(CXX) $(MY_CFLAGS) $(CXXFLAGS) $(CPPFLAGS) -c
LINK.c = $(CC) $(MY_CFLAGS) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)
LINK.cxx = $(CXX) $(MY_CFLAGS) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS)
.PHONY: all objs tags ctags clean cleanall help show
# Delete the default suffixes
.SUFFIXES:
all: $(PROGRAM)
# Rules for creating dependency files (.d).
#------------------------------------------
%.d:%.c
@echo -n $(dir $<) > $@
@$(DEPEND.d) $< >> $@
%.d:%.C
@echo -n $(dir $<) > $@
@$(DEPEND.d) $< >> $@
%.d:%.cc
@echo -n $(dir $<) > $@
@$(DEPEND.d) $< >> $@
%.d:%.cpp
@echo -n $(dir $<) > $@
@$(DEPEND.d) $< >> $@
%.d:%.CPP
@echo -n $(dir $<) > $@
@$(DEPEND.d) $< >> $@
%.d:%.c++
@echo -n $(dir $<) > $@
@$(DEPEND.d) $< >> $@
%.d:%.cp
@echo -n $(dir $<) > $@
@$(DEPEND.d) $< >> $@
%.d:%.cxx
@echo -n $(dir $<) > $@
@$(DEPEND.d) $< >> $@
# Rules for generating object files (.o).
#----------------------------------------
objs:$(OBJS)
%.o:%.c
$(COMPILE.c) $< -o $@
%.o:%.C
$(COMPILE.cxx) $< -o $@
%.o:%.cc
$(COMPILE.cxx) $< -o $@
%.o:%.cpp
$(COMPILE.cxx) $< -o $@
%.o:%.CPP
$(COMPILE.cxx) $< -o $@
%.o:%.c++
$(COMPILE.cxx) $< -o $@
%.o:%.cp
$(COMPILE.cxx) $< -o $@
%.o:%.cxx
$(COMPILE.cxx) $< -o $@
# Rules for generating the tags.
#-------------------------------------
tags: $(HEADERS) $(SOURCES)
$(ETAGS) $(ETAGSFLAGS) $(HEADERS) $(SOURCES)
ctags: $(HEADERS) $(SOURCES)
$(CTAGS) $(CTAGSFLAGS) $(HEADERS) $(SOURCES)
# Rules for generating the executable.
#-------------------------------------
$(PROGRAM):$(OBJS)
ifeq ($(SRC_CXX),) # C program
$(LINK.c) $(OBJS) $(MY_LIBS) -o $@
@echo Type ./$@ to execute the program.
else # C++ program
$(LINK.cxx) $(OBJS) $(MY_LIBS) -o $@
@echo Type ./$@ to execute the program.
endif
ifndef NODEP
ifneq ($(DEPS),)
sinclude $(DEPS)
endif
endif
# Commands to clean and help etc.
#-------------------------------------
.PHONY: clean
clean:
$(RM) $(OBJS) $(PROGRAM)
.PHONY: cleanall
cleanall:
$(RM) $(DEPS) $(OBJS) TAGS $(PROGRAM)
# Show help.
.PHONY: help
help:
@echo 'Usage: make [TARGET]'
@echo 'TARGETS:'
@echo ' all (=make) compile and link.'
@echo ' NODEP=yes make without generating dependencies.'
@echo ' objs compile only (no linking).'
@echo ' tags create tags for Emacs editor.'
@echo ' ctags create ctags for VI editor.'
@echo ' clean clean objects and the executable file.'
@echo ' distclean clean objects, the executable and dependencies.'
@echo ' show show variables (for debug use only).'
@echo ' help print this message.'
@echo
@echo 'Report bugs to <whyglinux AT gmail DOT com>.'
# Show variables (for debug use only.)
.PHONY: show
show:
@echo 'PROGRAM :' $(PROGRAM)
@echo 'SRCDIRS :' $(SRCDIRS)
@echo 'HEADERS :' $(HEADERS)
@echo 'SOURCES :' $(SOURCES)
@echo 'SRC_CXX :' $(SRC_CXX)
@echo 'OBJS :' $(OBJS)
@echo 'DEPS :' $(DEPS)
@echo 'DEPEND :' $(DEPEND)
@echo 'COMPILE.c :' $(COMPILE.c)
@echo 'COMPILE.cxx :' $(COMPILE.cxx)
@echo 'link.c :' $(LINK.c)
@echo 'link.cxx :' $(LINK.cxx)
## End of the Makefile ## Suggestions are welcome ## All rights reserved ##
##############################################################
========= End