Makefile入门

(1)Makefile

makefile规则?

target...:prerequisites...
	command  #每个command签名必须要有一个tab
	...
	...
	
  1. target:目标文件、可执行文件、标签
  2. prerequisites:生成这个target所需要的文件或者标签
  3. command:就是make需要执行的执行(任意的shell命令)

含义:生成target文件或者标签需要prerequisites所要求的文件或者标签,其生成规则在command中

换句话说:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-APOIsCTn-1654830083069)((1)]Makefile.assets/image-20220513152827087.png)
实例?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ePNv90n-1654830083071)((1)]Makefile.assets/image-20220513154048813-16524276498351-16524276554642.png)

target:可以是标签比如clean,可以是中间目标文件比如:utils.0,可以是执行程序比如:edit

把上面的内容保存在**"makefile"或”Makefile“**中;在该目录下 ”make“可以生成edit可执行文件(make不会执行clean后面的命令,clean不是一个文件名字只是一个动作名字);”make clean“ 会执行clean:后面的命令
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SltyLT3e-1654830083072)((1)]Makefile.assets/image-20220513154646208-16524280077113.png)
总结:prerequisites表明了target文件是由哪些依赖文件按照哪些规则command生成更新的,当prerequisites文件的时间大于target说明target需要重新更新了。

make是如何工作的?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gk0Pd57X-1654830083072)((1)]Makefile.assets/image-20220513160259562-16524289812224-16524289894595.png)
几个重要的点:

  1. make 时会找到makefile或者Makefile文件中第一个target,并作为最终目标文件。

  2. 在生成一个target时会先检查依赖prerequisites是不是存在或者比target旧(即prerequisites对应target中command生成的文件是不是更改了)

  3. 如果command部分没有生成任何文件,则检查到该依赖时都会运行该command部分如下:

    some_file:other_file
    	touch some_file
    
    other_file:
    	echo "nothing"
    	
    无论运行多少次make时这个两个target都会运行,因为other_file中没有创建任何文件,如果other_file更改为
    other_file:
    	echo "this will run first"
    	touch other_file
    	
    第一make会运行这两个target,第二次运行make时如果other_file没有更改时则不会重新创建some_file
    	
    以上注意command创建的文件名必须与target一致这样才能追踪到特定的文件,检查文件的修改情况	
    

变量?

变量只能是字符串,可以用**:=或者=赋值** ;可以用** 或 者 {}或者 ()引用**;可以理解为C语言的宏

多target:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U2o2uAi9-1654830083075)((1)]Makefile.assets/image-20220513220748147-16524508690576.png)
其中$@表示是当前rule的target名;

其他小规则?

Makefile中只有行注释#,如果想在Makefile中使用“#”字符,可以用反斜框进行转义,如:“#”

引用其他Makefile?

在Makefile使用include关键字可以把别的Makefile包含进来. 会把包含的Makefile文件中内容安置在当前的位置。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OZSL5FZP-1654830083076)((1)]Makefile.assets/image-20220514165439439-16525184812431-16525184904682.png)

以上包含的文件会在当前目录下首先寻找,如果当前目录下没有找到,那么,make还会在下面的几个目录中找。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vtdQn1re-1654830083078)((1)]Makefile.assets/image-20220514165703300-16525186244833.png)

(2)书写规则

书写规则分为两个部分:一个是依赖关系,另一个是生成目标的方法

1. 生成的最终目标:

​ 在一个Makefile文件中有很多规则,但是最终目标只有一个。一般是Makefile文件中的第一个规则的第一个目标作为最终目标

​ **注释:**上面这句话有两个第一个,第一个规则是指Makefile文件从上到下第一个规则,第一个目标是指由于一个规则中可能有多个目标,在多个目标中从左到右数第一个目标

  1. 规则举例:

    foo.o:foo.c defs.h # foo模块
    	cc -c -g foo.c
    

    解释:

    当依赖关系成立时会运行“cc -c -g foo.c”
    依赖关系成立的条件:foo.o文件不存在 或者 foo.c defs.h文件比foo.o新

    当依赖关系成立后就会执行command部分

2. 书写基本格式:

targets : prerequisites
	command
	...

targets是文件名,以空格分开,可以使用通配符。如果一行写不下可以用反斜杠""来作为换行符 一个规则告诉Makefile文件之间的依赖关系、如何生成目标文件的命令

3. 在规则文件名中使用通配符:

*:表示匹配零个或多个任意字符

?:表示匹配一个任意字符

[…] : 表示匹配[]中的一个特定字符

如果文件名中存在*,?等字符,可以是用反斜杠\来转义。

​ 举例说明:

 ```makefile
 clean: 
  rm -f *.o
 ## make clean 后会删除所有.o为后缀的所有文件
 
 print: *.c 
  lpr -p $? 
  touch print
 ## 依赖关系是后缀为.c的所有文件
 
 objects = *.o
 ##objects变量就是*.o没有进行展开,可以认为Makefile中的变量是C中的宏
 ```

4.文件搜寻:

  1. Makefile文件中特殊变量"VPATH"可以指明路径去寻找依赖文件和目标文件,否则只会在当前目录中去找寻依赖文件和目标文件

    VPATH=src:../headers
    ## 上面定义了两个文件搜寻目录其中一个是./src和./../headers两个,搜寻优先级:1 ./ 2 ./src 3 ./../headers 目录之间用冒号分隔开
    
  2. vpath关键字,比特殊变量VPATH更为灵活,它可以指定不同的文件在不同的搜索目录中
    在这里插入图片描述

​ 例子:

vpath %.h ../headers
# 要求make时以.h为后缀的所有文件在目录../headers中搜寻(如果某文件在当前目录没有找到的话)

vpath %.c foo 
vpath % blish 
vpath %.c bar
#make会按照vpath语句的先后顺序来执行搜索

5.伪目标:

"伪目标"并不是一个文件,只是一个标签,由于“伪目标”不是文件,所以make无法生成它的依赖关系和决定它是否要执行。当然,伪目标的取名不能和文件名重名,不然其就失去了“伪目标”的意义,伪目标的本质是在文件搜索目录中找不到该名字的文件

伪目标地特性:1. 总是被执行 2. 可以通过"make 伪目标名"执行伪目标 3. 伪目标可以有依赖关系,且依赖关系中可以是目标也可以是伪目标.

“.PHONY”来显示地指明一个目标时“伪目标”,想make说明,不管是否有这个文件,这个目标就是“伪目标”

.PHNOY : clean
clean:
	rm *.0 temp
  1. 伪目标中也可以有依赖关系(依赖关系中可以是目标也可以是伪目标),伪目标也可以作为最终目标(但是必须放在makefile文件中的第一个规则)

    all : prog1 prog2 prog3 
    .PHONY : all 
    
    prog1 : prog1.o utils.o 
    cc -o prog1 prog1.o utils.o 
     
    prog2 : prog2.o 
    cc -o prog2 prog2.o 
     
    prog3 : prog3.o sort.o utils.o 
    cc -o prog3 prog3.o sort.o utils.o
    
    ##make all
    
  2. 可以通过伪目标的方式,不同程度的编译或者说不同程度的执行

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BxLtbxl5-1654830213683)((2)]书写规则.assets/image-20220516142853297-16526825347524.png)

    make cleanall=>会执行"cleanobj"伪目标和"cleandiff"伪目标以及"rm program"指令

    make cleanobj=>会执行“rm *.o”指令

    make cleandiff=>会执行"rm *.diff"指令

    从而可以根据伪目标名实现不同程度的清理工作。

6.多目标:

Makefile的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖于一个文件,并且其生成的命令大体类似。

(3)书写命令

make一般是使用环境变量SHELL中所定义的系统shell来执行命令。

寻找命令解释器的过程: 1. 首先会在SHELL所指定的路径中找寻命令解释器 2.当前目录下找寻命令解释器 3.会在PATH环境变量中所定义的所有路径中寻找。

1. 显示命令:

通常make会把要执行的命令行在命令执行前输出到屏幕上,当我们用“@”字符在命令行前,那么,这个命令将不会被make显示出来

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XWSoSdDN-1654830311937)((3)]书写命令.assets/image-20220516145205333.png)

clean:
	@echo "正在编译xxx模块"

##make clean其结果为
## 正在编译xxx模块

由于一条指令在执行时会有两个步骤(1.命令行打印 2.命令执行信息)其中 make的参数有

  1. "-n"或者“–just-print”那么其只是显示命令,但不会执行命令。
  2. “-s”或者"–silent"则是只显示命令执行信息,不显示命令行

2. 命令执行

当依赖条件中的文件新于目标文件或者目标文件不存在时,命令command部分会被执行.

伪目标则命令总是会执行,由于伪目标在搜索目录中总是找不到与伪目标名相同的文件。

如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令.

exec:
	cd /home/hchen
	pwd

在这里插入图片描述

如上显示的是当前makefile文件存在的目录

exec:
	cd /home/hchen;pwd

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oby0kZYO-1654830311939)((3)]书写命令.assets/image-20220516151142635-16526851040412.png)

如上显示的是/home/hchen

3.命令出错

每当命令运行完后,make会检测每个命令的返回码,如果命令返回成功,那么make会执行下一条命令,当规则中所有的命令成功返回后,这个规则就算是成功完成了。只要当一条命令返回码非零时即命令出错,则会中止该规则的命令的继续运行,以及其他规则的继续运行。

为了忽略命令的出错,可以在Makefile的命令行签名加一个减号"-"(在tab键之后)

exec:
        @cd /home/panHY;pwd
        -@rm *.o

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LPHqZh5s-1654830311939)((3)]书写命令.assets/image-20220516154734217-16526872559963.png)

4.嵌套执行make

在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中,我们可以在每个目录中都书写一个该目录的Makefile.

例子:我们有一个子目录叫subdir,这个目录下有个Makefile文件,来指明了这个目录下文件的编译规则。

总控Makefile可以这样书写:

subsystem:
	cd subdir && ${MAKE}
#进入当前目录的subdir子目录,然后执行make执行

总控Makefile的变量可以传递到下一级Makefile中,但是不会覆盖下层的Makefile中所定义的变量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1otLFNTH-1654830311940)((3)]书写命令.assets/image-20220516160728771-16526884504744.png)
当export后面不加任何变量时表示所有的变量都可以传递给下一层.

5. 定义命令包:

如果Makefile中出现一些相同命令序列时,那么我们可以为这些相同的命令序列定义一个变量。

define xxx
yyy
yyyyy
endef 

#引用就是${xxx},引用的方式和引用变量的方式是一样的,所以不要和makefile中的变量c
(4)使用变量

在Makefile中定义的变量,就像是c/c++语言中的宏一样,他代表了一个文本子串

在makefile中执行的时候其会原模原样地展开在所使用的地方.

1.变量使用地方

变量可以使用在“目标”、“依赖目标”、“命令”或是makefile的其他部分中,变量的命名字可以包含字符、数字、下划线(可以是数字开头),但不应该含有“:”、“#”

2.变量基础

变量在声明时需要给予初值,而在使用时,需要给在变量名前加上**“$“符号,但最好用小括号”()“或是大括号”{}“把变量**给包括起来。

3.变量中的变量:

其中:=和=的区别

方法1:就是简单的使用”=“号,在”=“左侧是变量,右侧是变量的值。右侧变量的值可以定义在文件的任何一处,也就是说,右侧中的变量不一定非要是已定义好的值。

foo = ${bar}
bar =${ugh}
ugh = Hub?

方法2:使用”:=“号,则前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。

如何定义一个空格变量?
empty:=
space:=${empty} #end of the line
以上用empty变量表明变量的值开始,而后面采用"#"注释符来表明变量定义的终止。由于在${empty}和"#"之间有一个空格,所以space表示一个空格。
所以
Dir:=/foo/bar   #directory 此时Dir表示的是“/foo/bar   ”

3.变量高级用法

变量值的替换:${var: a=b }其意思是把变量var中所有以a字串“结尾”的“a”替换成“b”字串。这里结尾的意思是“空格”和“结束符”

foo := a.o b.o c.o
bar := ${foo:.o=.c}

把变量的值再当成变量

x = y
y = z
a := ${${x}} #引用变量x的值为y,则变量y的值为z所以a的值为z

可以有多层
x = y
y = z
z = u
a:=${${${x}}}

4.追加变量值:

使用"+="操作符给变量追加值

objects = main.o foo.o bar.o utils.o
objects += another.o

等价于
objects = main.o foo.o bar.o utils.o
objects := ${objects} another.o

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wJynsbni-1654830456047)((4)]使用变量.assets/image-20220516213402462-16527080440111.png)

5.自动变量

$@: 表示一个规则中的目标文件
$(@D):表示$@的目录
$^:表示一个规则中的所有依赖文件(如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份)
$<:表示一个规则中的第一个依赖文件
$?:表示一个规则中比目标文件要新的依赖文件列表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W6942kHO-1654830456048)((4)]使用变量.assets/image-20220517145757465-16527706784011.png)

在这里插入图片描述

例子:如果 @ = “ d i r / f o o . o ” , 那 么 @=“dir/foo.o”,那么 @=dir/foo.o,(@D)=“dir”,$(@F)=“foo.o”

(5)使用条件变量

可以根据运行时的不同情况选择不同的执行分支。条件表达式可以是比较变量的值、或是变量或常量的值

实例:

foo:${Objects}
ifeq (${CC},gcc)
	$(CC) -o foo $(objects) $(libs_for_gcc)
else
	$(CC) -o foo $(objects) $(normal_libs)
endif
目标"foo"可以根据变量“$(cc)”值来选取不同的函数库来编译程序
我们可以从上面的示例中看到三个关键字:ifeq、else和endif

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CWjS2XAi-1654830488471)((5)]使用条件变量.assets/image-20220516220854467.png)

语法:

<conditional-directive>
<text-if-true>
endif
以及
<conditional-directive>
	<text-if-true>
else
	<text-if-false>
endif

其中中有四个关键字ifeq、ifneq、ifdef以及ifndef

ifeq ( , )表示两个值是否相等

ifneq( , )表示两个值是否不相等

ifdef :如果变量的值非空,那到表达式为真。否则,表达式为假。

但是一般常用ifeq来判断变量是否为空由于如下例子中虽然foo的值为空但是由于将bar赋值给foo所以输出yes即判断foo不为空

ifndef :与ifdef相反

bar = 
foo = $(bar) 
ifdef foo 
	frobozz = yes 
else 
	frobozz = no 
endif

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jyuUmXOi-1654830488472)((5)]使用条件变量.assets/image-20220516222825270-16527113070591.png)

(6)使用函数

在Makefile中可以使用函数来处理变量,函数调用后,函数的返回值可以当作变量来使用

1.函数调用语法:

( < f u n c t i o n > < a r g u m e n t s > ) 或 者 (<function> <arguments>) 或者 (<function><arguments>){ }

<function>是函数名,<arguments>是函数的参数,之间用“,”分隔,函数名和参数之间用“空格”分隔。

2.字符串处理函数:

1. 
$(subst <from>,<to>,<text>)
名称:字符串替换函数-subst
功能:把字串<text>中的<from>字符串替换成<to>
返回:函数返回被替换后的字符串
$(subst ee,EE,feet on the street)
返回结果:fEEt on the strEEt

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

3.
$(strip <string>)
名称:去空格函数--strip
功能:去掉<string>字符串中开头和结尾的空字符
返回:返回被去掉空格的字符串值
$(strip a b c )返回为"a b c"

4.
$(sort <list>)
名称:排序函数--sort
功能:给字符串<list>中的单词排序(升序)
返回:返回排序后的字符串(会去掉<list>中相同的单词)
$(sort foo bar lose)返回“bar foo lose”

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I40XZlII-1654830520698)((6)]使用函数.assets/image-20220517102633514.png)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q5NFImDa-1654830520699)((6)]使用函数.assets/image-20220517103049613-16527546506441.png)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UP3CPxvR-1654830520700)((6)]使用函数.assets/image-20220517103121705-16527546827222-16527546860473.png)

$(wordlist ,, )

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JcnJPrNv-1654830520702)((6)]使用函数.assets/image-20220517103246663-16527547675184.png)

3. origin函数

origin函数不像其他的函数,他并不操作变量的值。他只是告诉你你的这个变量是哪里来的

$(origin ) 其中是变量的名字,不应该是引用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a7TvZ7cy-1654830520705)((6)]使用函数.assets/image-20220517103941937-16527551833095.png)

4.shell函数

shell函数也不像其他的函数。顾名思义,它的参数应该就是操作系统shell的命令。它和反引号“`”是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数返回。

(7)make的运行

一般来说,最简单的就是直接再命令行下输入make命令,make命令会找当前目录makefile来执行。

但也有时你也许只想让 make 重编译某些文件,而不是整个工程,而又有的时候你有几套编译规则

1.make的退出码:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oJQds7jj-1654830564342)((7)]make的运行.assets/image-20220517105234170.png)

2.指定Makefile:

默认情况下GNU make是在当前目录下依次找三个文件–“GNUmakefile”,“makefile”,“Makefile”。

当然我们也可以给makefile文件指定特殊的名字。要达到这个功能,我们要使用make的“-f”或者“–file”参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8QMJMLum-1654830564343)((7)]make的运行.assets/image-20220517105552398-16527561538191.png)

3.指定makefile目标:

一般来说,make的最终目标是makefile中的第一个目标(makefile文件中从上到下第一个规则,规则中的从左到右的第一个目标),而其他目标一般是由这个目标连带出来的。

可以根据make命令后直接跟目标的名字,就**可以完成你所指定的目标。**make的环境变量叫"MAKECMDGOALS",这个变量中会存放你所指定的终极目标的列表。

4.参照规则来写makefile中的目标:

"all:"这个伪目标是所有目标的目标,其功能一般是编译所有的目标。

“clean:”这个伪目标功能是删除所有被make创建的文件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hyfWCR2u-1654830564345)((7)]make的运行.assets/image-20220517143112741-16527691719533.png)

本博客根据《跟我一起学makefile》而做的学习笔记,只限于学习之用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Blockchain410

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值