Makefile入门(介绍、规则、语法、函数、实例)

Makefile的基本使用

Makefile的介绍和规则

问:之前使用单片机开发的时候,使用Keil工具程序点击一下鼠标就可以进行编译了,那么它为什么可以这样如此轻松进行编译?如何组织管理大量的工程文件?如何确定编译哪一个文件?

答:实际上windows工具管理程序的内部机制,也是Makefile。当我们在Linux下来开发裸板程序的时候,使用Makefile组织管理这些程序,这些文件。

程序的编译和链接过程

一个C/C++文件要经过预处理(preprocessing)、编译(compilation)、汇编(assembly)和链接(linking)等4步才能变成可执行文件。
在这里插入图片描述

一般来说,我们会把前三个步骤即预处理、编译和汇编统称为编译。

编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件(.h 文件)中应该只是声明,而定义应该放在C/C++文件(.c 文件)中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。

链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File)。在大多数的时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“*库文件”(Library File)*,也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。

总结一下,源文件首先会生成中间目标文件(O文件或是OBJ文件),再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error)。

Makefile的介绍

What:什么是Makefile?

​ 在 Linux(unix )环境下使用GNU 的make工具能够比较容易的构建一个属于你自己的工程,整个工程的编译只需要一个命令就可以完成编译、连接以至于最后的执行。不过这需要我们投入一些时间去完成一个或者多个称之为Makefile 文件的编写。

​ 所要完成的Makefile 文件描述了整个工程的编译、连接等规则。其中包括:工程中的哪些源文件需要编译以及如何编译、需要创建哪些库文件以及如何创建这些库文件、如何最后产生我们想要的可执行文件。

​ make是一个命令工具,它解释Makefile 中的指令。在Makefile文件中描述了整个工程所有文件的编译顺序、编译规则。Makefile 有自己的书写格式、关键字、函数。像C 语言有自己的格式、关键字和函数一样。

​ make命令执行的时候,需要一个Makefile文件(此文件不需要任何后缀),这个Makefile文件就告诉了make命令如何去编译和链接程序。后续就能够使用一个make命令就能快速自动对某些文件进行重编译和链接目标程序。而要想快速编译和链接,就需要写好Makefile文件。

注意:Makefile文件有以下的书写规则

​ 1.如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。

​ 2.如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。

​ 3.如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。

Why:为什么使用Makefile?

​ 编写一个Makefile文件尽管看起来可能是很复杂的事情,但是为工程编写Makefile 的好处是能够使用一行命令来完成“自动化编译”,一旦提供一个(通常对于一个工程来说会是多个)正确的 Makefile。编译整个工程你所要做的事就是在shell 提示符下输入make命令。整个工程完全自动编译,极大提高了效率。

Makefile的规则

Makefile最基本的语法就是规则,规则如下:

目标 : 依赖1 依赖2 ...
[TAB]命令

当“依赖”比“目标”新,执行它们下面的命令。这就是Makefile最基本的语法规则。

下面,将写一个程序来实验一下:

文件a.c

02	#include <stdio.h>
03
04	int main()
05	{
06	func_b();
07	return 0;
08}

文件b.c

2	#include <stdio.h>
3
4	void func_b()
5	{
6		printf("This is B\n");
7	}

编译:

gcc -o test a.c b.c

运行:

./test

结果:

It is B

gcc -o test a.c b.c 这条命令虽然简单,但是它完成的功能不简单。

(提醒:gcc -o test a.c b.c -v :加上一个**‘-v’**选项可以看到它们的处理过程,包括编译和链接的过程,include的路径等等信息。)

从上文的程序的编译和链接就可以知道 .c 程序—> 可执行程序要按顺序经过四个步骤:预处理、编译、汇编、;链接。对gcc -o test a.c b.c 命令进行具体分析,可知:

①对于a.c:执行:预处理 编译 汇编 的过程,a.c ==>xxx.s ==>xxx.o 文件。

②对于b.c:执行:预处理 编译 汇编 的过程,b.c ==>yyy.s ==>yyy.o 文件。

③最后:xxx.oyyy.o链接在一起得到一个test应用程序。

第一次编译 a.c 得到 xxx.o 文件,这是很合乎情理的。

执行完第一次之后,如果修改 a.c 又再次执行:gcc -o test a.c b.c,这一条命令,对于 a.c 应该重新生成 xxx.o,但是对于 b.c 又会重新编译一次,这完全没有必要,因为b.c 根本没有修改,直接使用第一次生成的 yyy.o 文件就可以了。

缺点:使用gcc -o test a.c b.c 则会对所有的文件都会再处理一次,即使 b.c 没有经过修改,b.c 也会重新编译一次,当文件较少时,这还没有什么问题。当文件非常多的时候,就会导致效率太低,编译时间过长。如果文件非常多的时候,我们,若仅仅只修改了少量的文件,但所有的文件都会重新处理一次,编译的时候就会等待很长时间。

解决方法:对于这些源文件,我们应该分别处理,执行:预处理 编译 汇编,先分别编译它们,最后再把它们链接在一起,比如:

编译:

gcc -o a.o a.c
gcc -o b.o b.c

链接:

gcc -o test a.o b.o

比如:上面的例子,当我们修改a.c之后,a.c会重现编译,得到新的a.o 文件,然后再把新的a.o 和 旧的 b.o 文件链接在一起就可以了。而b.c就不需要重新编译,节省时间了。

那么问题又来了,怎么知道哪些文件被更新了/被修改了?

比较时间,比较 a.o 和 a.c 的时间,如果a.c的时间比 a.o 的时间更加新的话,就表明 a.c 被修改了,同理b.o和b.c也会进行同样的比较。比较test和 a.o,b.o 的时间,如果a.o或者b.o的时间比test更加新的话,就表明应该重新生成test。而由Makefile的基本书写规则得知,它就是这样做的。

How:如何使用Makefile?

写一个基本的Makefile:

根据Makefile的基本规则:

目标 : 依赖1 依赖2 ...
[TAB]命令

当“依赖”比“目标”新,就执行它们下面的命令。我们要把上面三个命令写成makefile规则,如下:

test :a.o b.o          #test是目标,它依赖于a.o b.o文件,一旦a.o或者b.o比                              #test新的时候,就需要执行下面的命令,重新生成test可执行程序。
       gcc -o test a.o b.o         

a.o : a.c                  #a.o依赖于a.c,当a.c更加新的话,执行下面的命令来生成a.o
      gcc -c -o a.o a.c


b.o : b.c                 #b.o依赖于b.c,当b.c更加新的话,执行下面的命令,来生成b.o
      gcc -c -o b.o b.c

我们来作一下实验:

在a.c 文件和b.c 文件所在的目录下,建立一个Makefile文件(注意不添加任何后缀):

文件:Makefile

1	test:a.o b.o
2		gcc -o test a.o b.o
3	
4	a.o : a.c
5		gcc -c -o a.o a.c
6
7	b.o : b.c
8		gcc -c -o b.o b.c

上面是Makefile中的三条规则。Makefile,就是名字为“Makefile”的文件。

当我们想编译程序时,直接执行make命令就可以了。

一执行make命令它想生成第一个目标test可执行程序。如果发现a.o 或者b.o没有,就要先生成a.o或者b.o,发现a.o依赖a.c,有a.c但是没有a.o,他就会认为a.c比a.o新,就会执行它们下面的命令来生成a.o,同理b.o和b.c的处理关系也是这样的。

在这里插入图片描述

​ 我们第一次执行make的时候,所有命令都执行,由Makefile第一条书写规则可知(如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接)

在这里插入图片描述

​ 我们再次执行make就会判断Makefile文件中的依赖,发现依赖没有更新,所以目标文件就不会重现生成,就会有上面的提示“make: `test’ is up to date.”

​ 若只是修改了a.c 文件,a.c文件就比a.o文件更加新,所以就会执行gcc -c -o a.o a.c 来更新a.o文件。而a.o比test新,所以又要使用gcc -o test a.o b.o 来重新链接生成test可执行程序。

在这里插入图片描述

Makefile的核心就是其里面的规则!!!

执行make命令的时候,就会在当前目录下面找到名字为:Makefile的文件,根据里面的内容来执行里面的判断/命令。

Makefile的基础语法

​ 在这里仅仅只是展示最基础语法,若想深入了解Makefile,

可学习官方文档: http://www.gnu.org/software/make/manual/

或查阅书籍:GNU Make 使用手册(中译版)https://file.elecfans.com/web1/M00/7D/E7/o4YBAFwQthSADYCWAAT9Q1w_4U0711.pdf

基础语法之通配符

what:什么是通配符?

​ 通配符是一种特殊语句,主要有星号(*)和问号(?),用来模糊搜索文件。当查找文件夹时,可以使用它来代替一个或多个真正字符;当不知道真正字符或者懒得输入完整名字时,常常使用通配符代替一个或多个真正的字符。

why:为什么要使用通配符?

​ 假如一个目标文件所依赖的依赖文件很多,那样岂不是我们要写很多规则,这显然是不合乎常理的,而且十分麻烦,所以我们可以使用通配符,来解决这些问题。

how:如何使用通配符?

基本的通配符:

%.o:表示所用的.o文件

%.c:表示所有的.c文件

$@:表示目标文件

$<:表示第1个依赖文件

$^:表示所有依赖文件

$*: 表示目标文件的名称,不包含扩展名

$?: 依赖项中,所有比目标文件新的依赖文件

在上面的程序基础下,在该目录下增加一个 c.c 文件,代码如下:

#include <stdio.h>

void func_c()
{
	printf("This is C\n");
}

然后在main函数中调用函数 ‘func_c’,再修改Makefile,修改后的代码如下:

test: a.o b.o c.o
	gcc -o test $^
	
%.o : %.c
	gcc -c -o $@ $<

执行:

make

结果:

gcc -c -o a.o a.c
gcc -c -o b.o b.c
gcc -c -o c.o c.c
gcc -o test a.o b.o c.o

运行:

./test

结果:

This is B
This is C

在这里插入图片描述

基础语法之伪目标: .PHONY

what:什么是伪目标?

要搞清楚什么是伪目标,前提是明白目标是什么?Makefile中的目标究竟是什么?实际上,在默认情况下:

①make命令将Makefile的目标认为是一个文件;

②make解释器比较目标文件和依赖文件的新旧关系,决定是否执行命令;

③make以文件作为第一优先级。

​ 如果不进行特殊的Makefile控制,make解释器在解析Makefile文件时,在解析到一个规则时,会将这个规则中的目标认为是一个文件,并进一步判断目标文件和依赖文件的新旧关系。

​ 编写以下的makefile文件,并执行make clean。
在这里插入图片描述

​ 正常情况下,当前目录下的*.o hello.out文件全部被删除了,没有任何错误,但当我们在当前目录下新建一个名字为clean的文件,然后再执行make clean,此时会提示clean文件是最新的。如图所示:

在这里插入图片描述

​ 这是因为make解释器默认将clean目标当作一个文件处理,而不是一个标签。而将clean当作一个文件的时候,make发现当前目录下有此文件,而且此目标没有依赖,即认为目标是最新的。最终make给出了clean是最新的结论。

​ 那么怎么解决这个问题呢?幸好,gun make中提供了关键字.PHONY,这个关键字用于定义一个伪目标,此时,伪目标不再对应任何实际的文件,仅仅只是作为一个标签,命令也总会执行。

伪目标实质:伪目标是make中特殊目标.PHONY的依赖。

why:为什么要使用伪目标?

​ 当同一目录下出现了与命令同名的文件,就会导致命令无法执行,为了避免出现这个问题,从而使用伪目标来解决这个问题。此时make不再将伪目标当作文件处理,而是当成一个标签。不管伪目标的依赖是否更新,命令总是会被执行。

how:如何使用假想目标?

​ 在Makfile结尾添加.PHONY: xxx语句即可,xxx为你想要的总是会被执行的命令。需要的时候,直接输入命令make xxx ,就可以执行此条命令了。

​ 举例,还是接上文的程序,在Makefile文件中写下:

在这里插入图片描述

​ 使用make命令运行

在这里插入图片描述

​ 可以看到,正常使用。

基础语法之变量

what:有什么变量?

在Makefile中有两种变量:简单(即时)变量、延时变量。

①简单(即时)变量

​ 变量的值即刻确定,在定义的时候就已经确定了。

②延时变量

​ 变量要使用到的的时候才会确定,在定义等于时并不存在

常用的变量的定义如下:

:=      # 即时变量
=       # 延时变量
?=      # 延时变量, 如果是第1次定义才起效, 如果在前面该变量已定义则忽略这句
\+=     # 附加, 它是即时变量还是延时变量取决于前面的定义
?=:     # 如果这个变量在前面已经被定义了,这句话就会不会起效果,

‘#’后面可以添加注释,不会对代码造成影响。

why:为什么要使用变量?

因为我们可以把变量的真实值推到后面来定义。

how:如何使用变量?

​ 实例:

A := $(C)
B = $(C)
C = abc

#D = 100ask
D ?= weidongshan

all:
	@echo A = $(A)
	@echo B = $(B)
	@echo D = $(D)

C += 123

代码符号解释:

‘@’ 符号的使用
通常makefile会将其执行的命令行在执行前输出到屏幕上。如果将‘@’添加到命令行前,这个命令将不被make回显出来。
例如:

@echo ABC;

// 屏幕输出 ABC

echo ABC

// 没有@ 屏幕输出echo ABC

$ '符号的使用
美元符号 ,主要打开 M a k e f i l e 中定义的变量。注: m a k e 定义了很多默认变量, ,主要打开Makefile中定义的变量。 注:make 定义了很多默认变量, ,主要打开Makefile中定义的变量。注:make定义了很多默认变量,(MAKE)就是预设的 make这个命令的名称(或者路径)

执行:

make

结果如图所示:

在这里插入图片描述

A =
B = abc 123
D = weidongshan

分析:

  1. A := $©:

A为即使变量,在定义时即确定,由于刚开始C的值为空,所以A的值也为空。

  1. B = $©:
    B为延时变量,只有使用到时它的值才确定,当执行make时,会解析Makefile里面的所用变量,所以先解析C= abc,然后解析C += 123,此时,C = abc 123,当执行:@echo B = $(B) B的值为 abc 123。

  2. D ?= weidongshan:

D变量在前面没有定义,所以D的值为weidongshan,如果在前面添加D = 100ask,最后D的值为100ask。

我们还可以通过命令行存入变量的值 例如:

执行:make D=123456 里面的 D ?= weidongshan 这句话就不起作用了。

结果:

A =
B = abc 123
D = 123456

Makefile的函数

Why:为什么我们要使用函数?

​ 在Makefile中可以使用它内部的函数来处理文本,从而让我们的命令或规则更加智能。函数调用之后,函数的返回值可以当作变量来使用。

What&&How:Makefile自带的函数有什么以及如何使用?

若想详细了解相关的内容,可查看官方文档:

https://www.gnu.org/software/make/manual/make.pdf

前奏:函数的调用语法

​ Makefile里面包含了一些函数,这些函数都是make本身实现的。

​ Makefile中调用一个函数就用’$‘符号。

$(<function><arguments>)     
或者
${<function><arguments>}   

​ 函数调用以‘$‘开头,用‘()’(圆括号)或 ‘{}’(花括号)括起来,像是对一个变量的引用,函数中参数可以使用变量。其中, 为函数名,为函数参数。参数间用逗号‘ ,’分离,而函数名和参数间用空格分离。

字符串替换与分析函数

patsubst

函数 patsubst 语法如下:

$(patsubst <pattern>,<replacement>,<text>)

功能:查找 中以空白符分隔的单词是否符合模式,如果匹配的话,就可以使用进行替换。

​ 可以包括通配符%,表示任意长度的字符串。

​ 中若包含%,那么这个%是中的那个%所代表的字符串

返回:函数返回被替换过后的字符串。

举例:

files2  = a.c b.c c.c d.c e.c abc

dep_files = $(patsubst %.c,%.d,$(files2))

all:
        @echo dep_files = $(dep_files)

结果:

dep_files = a.d b.d c.d d.d e.d abc

findstring

函数 findstring 语法如下:

$(findstring <FIND>,<IN>)

功能:从字符串中查找指定的字符串,找到就返回,没找到返回空。

举例:

$(findstring a,b c) 
$(findstring a,a b c) 

结果:


a

第一个函数结果返回空,第二个函数结果为字符串‘a’。

filter filter-out

函数 filter 语法如下:

$(filter <pattern...>,<text>)    # 在text中取出符合patten格式的值

功能:以模式过滤 字符串中的内容,保留符合模式的内容,可有多个模式。

函数filter-out 语法如下:

$(filter-out <pattern...>,text)   # 在text中取出不符合patten格式的值

功能:以模式过滤 字符串中的内容,保留不符合模式的内容,可有多个模式。

实例:

C = a b c d/

D = $(filter %/, $(C))
E = $(filter-out %/, $(C))

all:
        @echo D = $(D)
        @echo E = $(E)

结果:

D = d/
E = a b c
文件名称处理函数

wildcard

函数Wildcard语法如下:

$(wildcard <pattern...>) 

功能:这个函数 wildcard 会以 这个格式,去寻找所有存在的文件,返回存在文件的名字,文件名以空格分隔。若不存在任何符合此格式的文件,则返回空。

实例:

在该目录下创建三个文件:a.c b.c c.c

files = $(wildcard *.c)

all:
        @echo files = $(files)

结果:

files = a.c b.c c.c

会返回make工作目录下所有以 ’ .c ’ 为后缀的文件名。

make控制函数

info

函数info语法如下:

$(info <text>) 

功能:这个函数会标准输出打印文本 ,用于输出调试信息。

实例:

$(info some debug info)

结果:

some debug info

warning

函数warning语法如下:

$(warning <text>) 

功能:这个函数会向标准输出打印文本 ,用于输出警告信息。make继续执行。

实例:

$(warning some warning info)

结果:

some warning info

error

函数error语法如下:

$(error <text>) 

功能:这个函数会向标准错误输出打印文本 ,用于输出指明错误信息。make停止执行。

实例:

ERROR="can't find commad g++"
ifdef ERROR
$(error error is $(ERROR1))
endif

结果:

makefile:3: *** error is "can't find commad g++".  Stop.

解析:因为ERROR值非空,所以输出错误信息如下错误信息,并停止make的执行。

其他函数

foreach

函数foreach语法如下:

$(foreach <var>,<list>,<text>) 

功能:把参数中的单词逐一取出放到参数所指定的变量中,然后再执行 所包含的表达式。每一次 会返回一个字符串,循环过程中, 的所返回的每个字符串会以空格分隔,最后当整个循环结束时,text所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。

所以,var是一个变量名,list是一个元素列表,而text中会使用var这个参数依次枚举list中的元素。

实例:

A = a b c
B = $(foreach f, $(A), $(f).o)

all:
	@echo B = $(B)

结果:

B = a.o b.o c.o

代码解释:$(A)中的单词会被挨个取出,并且是存到变量 ‘ f ’ 中,‘ $(f).o ’ ,每次根据‘ ( f ) ’计算出一个值,这些值以空格分隔,最后作为 f o r e a c h 函数的返回,所以, (f) ’计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以, (f)计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以,(B)的值是 “a.o b.o c.o”。

注意:foreach中的参数是一个临时的局部变量,foreach函数执行完后,参数的变量将不在作用,其作用域只在foreach函数当中。

Makefile的实例

头文件依赖(生成*.d依赖文件及与-M相关参数介绍)

1. 为什么要使用自动依赖文件?

在Makefile中,目标文件的依赖关系需要包含一系列的头文件。

假设有如下程序:

c.c:

#include <stdio.h>
#include <c.h>

void func_c()
{
	printf("This is C = %d\n", C);
}

c.h:

#define C 10086

那么有如下的依赖关系:

c.o:c.c  stdio.h  c.h 

如果在使用Makefile的时,编写目标文件的依赖关系没有加上c.h文件,当c.h中的内容改变的时候,根本不会重新编译新的c.o文件,这会发生十分致命的错误,因为目标文件c.o内部引用了c.h文件中的宏定义。

若是大型的工程,我们必须清楚每个源文件中包含了哪些头文件,一旦增加或删除某些头文件,又要修改相应的Makefile,这就是很繁琐且很容易出错的工作。

因此,为了避免出现以上问题,我们需要做出改变,让它自动获取源文件中包含的头文件,并生成一个依赖关系,此时就使用后缀名为.d 的依赖文件。

这样做的优点就是:

  • 不必手动书写若干目标文件的依赖关系,由编译器自动生成
  • 不管是源文件还是头文件有更新,目标文件都会重新编译
2、让编译器自动生产依赖关系的相关参数

部分参数如下:

  • -M
  • -MF
  • -MD

-M

生成文件的依赖关系,同时也把一些标准库的头文件包含了进来。其本质是告诉预处理器输出一个适合 make 的规则,用于描述各目标文件的依赖关系。

举例:

gcc -M c.c    #打印出依赖

则在终端中就会输出:

在这里插入图片描述

这个参数同时可以查看某个源文件所包含的所有头文件。可以检查自己希望程序包含的头文件有没有包含进去。

-MF File

当同时使用了 “-M”选项时,则把依赖关系写入名为 “File” 的文件中。若同时也使用了 “-MD” 或 “-MMD”,“-MF” 将覆写输出的依赖文件的名称 。

举例:

gcc -M -MF c.d c.c  #把依赖写入文件c.d

结果:

“-M”输出的内容就保存在c.d文件中。可使用“cat c.d”命令查看依赖。

在这里插入图片描述

-MD

等同于 -M -MF File,但是默认关闭了 -E 选项。其输出的文件名是基于 -o 选项,若给定了 -o 选项,则输出的文件名是 -o 指定的文件名,并添加 .d 后缀,若没有给定,则输入的文件名作为输出的文件名,并添加 .d 后缀,同时继续指定的编译工作。

举例:

gcc -c -o tmp.o -MD main.c

本目录下生成了以下文件:
tmp.d tmp.o

另一个例子:

gcc -c -o c.o c.c -MD -MF c.d  #可以编译生成c.o, 并生成依赖写入文件c.d中

利用上文所说的参数,修改原本的Makefile。

修改Makefile如下:

objs = a.o b.o c.o

dep_files := $(patsubst %,.%.d, $(objs))
dep_files := $(wildcard $(dep_files))

test: $(objs)
	gcc -o test $^

ifneq ($(dep_files),)
include $(dep_files)
endif

%.o : %.c
	gcc -c -o $@ $< -MD -MF .$@.d

clean:
	rm *.o test

distclean:
	rm $(dep_files)
	
.PHONY: clean	

首先用objs变量将.o文件放在一块。
利用前面讲到的函数,把objs里所有文件都变为.%.d格式,并用变量dep_files表示。
利用前面介绍的wildcard函数,判断dep_files是否存在。

然后是目标文件test依赖所有的.o文件。

如果dep_files变量不为空,就将其包含进来。

然后就是所有的.o文件都依赖.c文件,且通过-MD -MF生成.d依赖文件。

clean清理所有的.o文件和目标文件
distclean清理依赖.d文件。

现在不论修改了任何.h文件,最终都不会影响最后生成的文件,也没任何手工添加.h、.c、.o文件,完成了支持头文件依赖。

添加CFLAGS

What:什么是CFLAGS?

Makefile中有选项CFLAGS,LDFLAGS,LIBS。

CFLAGS 表示用于 C 编译器的选项。CXXFLAGS 表示用于 C++ 编译器的选项。
这两个变量实际上涵盖了编译和汇编两个步骤。

CFLAGS: 指定头文件(.h文件)的路径,如:CFLAGS=-I/usr/include -I/path/include。同样地,安装一个包时会在安装路径下建立一个include目录,当安装过程中出现问题时,试着把以前安装的包的include目录加入到该变量中来。

LDFLAGS:gcc 等编译器会用到的一些优化参数,也可以在里面指定库文件的位置。

​ 用法:LDFLAGS=-L/usr/lib -L/path/to/your/lib。每安装一个包都几乎一定的会在安装目录里建立一个lib目录。如果明明安装了某个包,而安装另一个包时,它愣是说找不到,可以给那个包的lib路径加入的LDFALGS中试一下。

LIBS:告诉链接器要链接哪些库文件,如LIBS = -lpthread -liconv

简单地说,LDFLAGS是告诉链接器从哪里寻找库文件,而LIBS是告诉链接器要链接哪些库文件。不过使用时链接阶段这两个参数都会加上,所以你即使将这两个的值互换,也没有问题。

How:如何使用CFLAGS?

CFLAGS部分参数如下:

后 缀 名所对应的语言
-S只是编译不汇编,生成汇编代码
-E只进行预编译,不做其他处理
-g在可执行程序中包含标准调试信息
-o file把输出文件输出到file里
-v打印出编译器内部编译各过程的命令行信息和编译器的版本
-I dir头文件的搜索路径列表中添加dir目录
-L dir在库文件的搜索路径列表中添加dir目录
-static链接静态库
-llibrary连接名为library的库文件

-I 参数使用的比较多。因为Linux下的大多数函数都默认:

  • 头文件放到/usr/include/目录下

  • 库文件则放到/usr/lib/目录下

GCC在编译时必须有自己的办法来查找所需要的头文件和库文件。

-I选项可以向GCC的头文件搜索路径中添加新的目录。

Gcc的告警和出错选项:

选 项含 义
-ansi支持符合ANSI标准的C程序
-pedantic允许发出ANSI C标准所列的全部警告信息
-pedantic-error允许发出ANSI C标准所列的全部错误信息
-w关闭所有告警
-Wall允许发出Gcc提供的所有有用的报警信息
-Werror把所有的告警信息转化为错误信息,并在告警发生时终止编译过程

建议:编译程序的时候,可以加上参数-Werror,因为很多警告都会隐藏错误。

一般来说,可以使用CFLAGS加上编译参数-Werror,把所有的警告当成错误。使用-Iinclude,指定include目录是编译器搜索的,默认的文件目录,此时,包含某个.h文件时,可以不用“”(双引号),可以使用<>(尖括号)。使用双引号时是当前目录下,使用尖括号时是去编译器下面所指定的路径查找头文件,当然也会去gcc默认目录下查找文件。

CFLAGS = -Werror -Iinclude
…………
%.o : %.c
	gcc $(CFLAGS) -c -o $@ $< -MD -MF .$@.d

现在重新make,发现以前的警告就变成了错误,必须要解决这些错误编译才能进行。可以在a.c里面声明一下函数:

void func_b();
void func_c();

重新make,错误就没有了。

除了编译参数-Werror,还可以加上-I参数,指定头文件路径,-Iinclude表示当前的inclue文件夹下,一般来说include文件夹下放所有的.h文件。
此时就可以把c.c文件里的#include ".h"改为#include <c.h>,前者表示当前目录,后者表示编译器指定的路径和GCC路径。

参考资料

①百问网资料:http://download.100ask.net/#

②博主:天道酬勤: https://www.cnblogs.com/wanmeishenghuo/p/8409176.html

③博主:Dabelv:https://cloud.tencent.com/developer/article/1406069

④GUN make官方文档: https://www.gnu.org/software/make/manual/make.pdf

⑤博主:Jerry.yl : https://blog.csdn.net/qq1452008/article/details/50855810

⑥博主:mutes:http://blog.chinaunix.net/uid-20672257-id-3408132.html

  • 11
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
跟我一起写 Makefile 作者:陈皓 整理:祝冬华 来源网络,希望能与大家分享这份学习资料,资源分数也设置了最低值,如有侵权,请联系我删除文件。 第一部分、概述 (6) 第二部分、关于程序的编译和链接 (6) 第三部分、Makefile 介绍 (7) 一、Makefile规则 (7) 二、一个示例 (8) 三、make是如何工作的 (9) 四、makefile中使用变量 (10) 五、让make自动推导 (11) 六、另类风格的makefile (12) 七、清空目标文件的规则 (13) 第四部分、Makefile 总述 (13) 一、Makefile里有什么? (13) 1、显式规则。 (14) 2、隐晦规则。 (14) 3、变量的定义。 (14) 4、文件指示。 (14) 5、注释。 (14) 二、Makefile的文件名 (15) 三、引用其它的Makefile (15) 四、环境变量 MAKEFILES (16) 五、make的工作方式 (16) 第五部分、书写规则 (17) 一、规则举例 (17) 二、规则语法 (17) 三、在规则中使用通配符 (18) 四、文件搜寻 (19) 五、伪目标 (20) 六、多目标 (22) 七、静态模式 (22) 八、自动生成依赖性 (24) 第六部分书写命令 (25) 一、显示命令 (26) 二、命令执行 (26) 三、命令出错 (27) 四、嵌套执行make (28) 五、定义命令包 (30) 第七部分使用变量 (30) 一、变量的基础 (31) 二、变量中的变量 (32) 三、变量高级用法 (34) 四、追加变量值 (37) 五、override 指示符 (37) 六、多行变量 (38) 八、目标变量 (39) 九、模式变量 (40) 第八部分使用条件判断 (40) 一、示例 (40) 二、语法 (42) 第九部分使用函数 (43) 一、函数的调用语法 (44) 二、字符串处理函数 (44) 1、subst (44) 2、patsubst (45) 3、strip (45) 4、findstring (46) 5、filter (46) 6、filter-out (46) 7、sort (47) 8、word (47) 9、wordlist (47) 10、words (47) 11、firstword (48) 12、字符串函数实例 (48) 三、文件名操作函数 (48) 1、dir (48) 2、notdir (48) 3、suffix (49) 4、basename (49) 5、addsuffix (49) 6、addprefix (49) 7、join (50) 四、foreach 函数 (50) 五、if 函数 (50) 六、call函数 (51) 七、origin函数 (51) “undefined” (52) “default” (52) “file” (52) “command line” (52) “override” (52) “automatic” (52) 八、shell函数 (53) 九、控制make的函数 (53) 1、error (53) 2、warning (54) 第十部分 make 的运行 (54) 二、指定Makefile (54) 三、指定目标 (55) “all” (56) “clean” (56) “install” (56) “print” (56) “tar” (56) “dist” (56) “TAGS” (56) “check”和“test” (56) 四、检查规则 (57) 五、make的参数 (57) 第十一部分隐含规则 (61) 一、使用隐含规则 (61) 二、隐含规则一览 (62) 1、编译C程序的隐含规则 (63) 2、编译C++程序的隐含规则 (63) 3、编译Pascal程序的隐含规则 (63) 4、编译Fortran/Ratfor程序的隐含规则 (63) 5、预处理Fortran/Ratfor程序的隐含规则 (63) 6、编译Modula-2程序的隐含规则 (63) 7、汇编和汇编预处理的隐含规则 (64) 8、链接Object文件的隐含规则 (64) 9、Yacc C程序时的隐含规则 (64) 10、Lex C程序时的隐含规则 (64) 11、Lex Ratfor程序时的隐含规则 (65) 12、从C程序、Yacc文件或Lex文件创建Lint库的隐含规则 (65) 三、隐含规则使用的变量 (65) 1、关于命令的变量。 (65) 2、关于命令参数的变量 (66) 四、隐含规则链 (67) 五、定义模式规则 (68) 1、模式规则介绍 (68) 2、模式
### 回答1: 陈皓的makefile教程pdf是一本关于makefile的实用工具书,它详细介绍makefile的基本语法规则和常见用法,涵盖了多种操作系统的编译环境和工具链,适用于各类软件开发的实际需求。 该教程的优点在于它提供了很多实用的示例和详细的讲解,使读者能够很快地理解并掌握makefile的编写方法,并且可以根据自己的需求进行灵活的配置和扩展。同时,教程也讲解了一些高级技巧和优化方法,让读者了解和熟练运用makefile来提高程序的编译效率和可维护性。 针对初学者,该教程从基础入手,介绍makefile的起源和作用,十分易于理解。然后逐步深入到常用的命令、变量、条件判断、函数等详细讲解,并提供大量的实例来演示不同的编译方式和场景。对于有经验的开发者,该教程也提供了很多有用的技巧和经验分享,可以使其更好地利用makefile进行项目管理和优化。 总体来说,陈皓的makefile教程pdf是一本值得推荐的宝典,它可以帮助读者快速学习makefile的编写方法,并提供大量的实用技巧和经验分享,从而提高软件开发的效率和质量。 ### 回答2: 陈皓的makefile教程是一份极为详尽和实用的教程,它适合想要初步学习和使用makefile的程序员和系统管理员。该教程包含了很多有关makefile的基本概念和语法的解释,并提供了实用的例子和模板来帮助学习者快速上手。 陈皓的教程以实践为重,内容非常具体,详细讲解了makefile的各种常见用法,如变量、条件语句、循环语句等等,帮助学习者可以很好地掌握makefile的应用。同时,教程还包含了一些高级使用技巧,如自动化依赖关系、多目标构建、命令行参数等等,使学习者可以更加深入地使用makefile。 此外,陈皓的教程还提供了很多有用的工具和库,这些工具和库可以协助使用makefile实现更加高级的目标。尤其是对互联网公司的程序员而言,使用陈皓的教程可以快速学会如何构建和发布复杂的应用程序,降低代码管理和部署的成本和工作量,提高工作效率。 总之,陈皓的makefile教程pdf是一份非常好用的资源,对程序员来说是一份不可多得的学习资料,它能够为初学者提供深入浅出的指导,同时也适用于有经验的Makefile用户。 ### 回答3: 陈皓是一位知名的计算机程序员和教育家,他在网络上分享了许多优质的教程和资料,其中就包括了makefile教程的PDF文件。makefile是一种代码构建工具,可以帮助开发者在编写大型项目时更加高效地管理代码,将源文件编译成可执行文件。陈皓的makefile教程PDF详尽地介绍makefile的使用方法、语法结构和示例代码,帮助读者深入理解makefile的原理和实现方式。这份教程不仅适合初学者学习,也可以供经验丰富的开发者参考进一步提高技能。在PDF文件中,陈皓以简洁明了的语言和大量的案例说明了如何正确编写makefile,特别是在软件开发中需要考虑的各种问题和实践经验。如果你正在学习或掌握makefile的使用,这份教程是非常推荐的学习资料。陈皓的教程PDF可以在互联网上免费获取,学习者可以自主下载或者在线阅读。相信通过学习这份教程后,你一定能更好地掌握makefile的技术,并在日常开发中得到应用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值