Linux Makefile 全面教程:使用 Makefile 进行项目管理和构建

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Linux Makefile 全面教程:使用 Makefile 进行项目管理和构建

什么是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 的目录文件,源文件之间相互关联。在 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 函数的基本语法如下:

$(foreach var, list, text)

这里的参数有三个:

  1. var:这是循环变量的名称,在 text 中引用。
  2. list:这是一个由空格分隔的值列表,foreach 函数会遍历这个列表。
  3. 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 的高级用法:

  1. 嵌套使用 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)))
  1. 这段代码将 objs 设置为 a.o b.o c.o d.o
  2. 结合 call 函数:
    foreach 可以结合 call 函数来调用用户自定义的函数。例如,假设我们定义了一个函数 compile 来编译源文件,我们可以使用 foreachcall 来编译所有的源文件:
define compile
$(1:.c=.o): $(1)
  $(CC) -c $(CFLAGS) $< -o $@
endef
srcs := a.c b.c
$(foreach src, $(srcs), $(eval $(call compile, $(src))))
  1. 这段代码将为 a.cb.c 生成对应的编译规则。
  2. 生成规则:
    foreach 可以用于生成 Makefile 规则。例如,假设我们有多个目标需要生成,我们可以使用 foreach 来生成这些目标的规则:
targets := a b c
define rule
$(1):
  @echo Building $(1)
endef
$(foreach target, $(targets), $(eval $(call rule, $(target))))
  1. 这段代码将为 abc 生成对应的构建规则。

这些只是 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中文件读写 file命令

file 是 GNU make 4.2 版本引入的一个新函数,用于在 Makefile 中进行文件读写操作。它的语法如下:

$(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 特性的结合使用,以实现更复杂的文件操作。下面是一些可能的高级用法:

  1. 动态生成 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
  1. 在这个例子中,file 函数用于生成一个名为 rules.mk 的文件,该文件中包含了一组编译规则。然后,include 指令用于将这些规则包含到当前的 Makefile 中。
  2. 在文件中存储中间结果
    在某些情况下,你可能希望在 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)))
  1. 在这个例子中,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 ##
##############################################################


目录
相关文章
|
2月前
|
Linux 网络安全 Python
linux centos上安装python3.11.x详细完整教程
这篇文章提供了在CentOS系统上安装Python 3.11.x版本的详细步骤,包括下载、解压、安装依赖、编译配置、解决常见错误以及版本验证。
698 1
linux centos上安装python3.11.x详细完整教程
|
15天前
|
安全 Linux 编译器
探索Linux内核的奥秘:从零构建操作系统####
本文旨在通过深入浅出的方式,带领读者踏上一段从零开始构建简化版Linux操作系统的旅程。我们将避开复杂的技术细节,以通俗易懂的语言,逐步揭开Linux内核的神秘面纱,探讨其工作原理、核心组件及如何通过实践加深理解。这既是一次对操作系统原理的深刻洞察,也是一场激发创新思维与实践能力的冒险。 ####
|
1月前
|
Linux Docker 容器
Centos安装docker(linux安装docker)——超详细小白可操作手把手教程,包好用!!!
本篇博客重在讲解Centos安装docker,经博主多次在不同服务器上测试,极其的稳定,尤其是阿里的服务器,一路复制命令畅通无阻。
243 4
Centos安装docker(linux安装docker)——超详细小白可操作手把手教程,包好用!!!
|
1月前
|
存储 数据可视化 Java
震惊!如何在linux下部署项目,部署/运行jar包 超详细保姆级教程!
如何在Linux系统下部署和运行Java项目jar包,包括传输文件到Linux、使用nohup命令运行jar包、查看端口状态、杀死进程和查看项目运行状态,以及如何解决“没有主清单属性”的错误。
310 1
震惊!如何在linux下部署项目,部署/运行jar包 超详细保姆级教程!
|
15天前
|
关系型数据库 MySQL Linux
基于阿里云服务器Linux系统安装Docker完整图文教程(附部署开源项目)
基于阿里云服务器Linux系统安装Docker完整图文教程(附部署开源项目)
146 2
|
30天前
|
Linux C++
Linux c/c++之makefile的基础使用
Linux下C/C++项目中makefile的基本使用,包括基础、进阶和高级用法,以及如何创建和使用makefile来自动化编译过程。
14 0
Linux c/c++之makefile的基础使用
|
1月前
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
164 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
2月前
|
存储 Linux 5G
Linux 基于 LVM 逻辑卷的磁盘管理【简明教程】
这篇文章介绍了LVM(逻辑卷管理)如何提供灵活的磁盘管理方式,允许动态调整逻辑卷的大小而不会丢失数据。
Linux 基于 LVM 逻辑卷的磁盘管理【简明教程】
|
1月前
|
Linux 开发工具 Docker
各个类linux服务器安装docker教程
各个类linux服务器安装docker教程
55 0
|
3月前
|
安全 Linux
Linux查看和剔除当前登录用户详细教程
Linux查看和剔除当前登录用户详细教程
124 0
Linux查看和剔除当前登录用户详细教程