前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >技术栈系列基础篇2-Makefile

技术栈系列基础篇2-Makefile

原创
作者头像
AndreKzWu
修改2022-09-21 17:14:52
5620
修改2022-09-21 17:14:52
举报
文章被收录于专栏:AndrekzWu的专栏

什么是Makefile

Makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,文件之间有哪些依赖等。Makefile有自己的书写格式、关键字、函数。像C 语言有自己的格式、关键字和函数一样。而且在Makefile中可以使用系统shell所提供的任何命令来完成想要的工作。Makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率

Makefile里有什么

Makefile里包含了:显示规则、隐晦规则、变量定义、文件指示和注释

  • 显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
  • 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。
  • 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
  • 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
  • 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜框进行转义,如:“#”。

Makefile的文件名

Makefile的文件名有两种方式:

  • 默认的文件名 Makefile 或makefile
  • 指定文件名 例如 Make.Linux,Make.Mac等
代码语言:txt
复制
# 默认的文件名,Makefile、makefike
> make
# 指定文件名,Make.Linux
> make -f Make.linux

Makefile规则

显式规则

代码语言:txt
复制
target ... : prerequisites ...
    command
  • target: 需要生成目标,或者是标签
  • prerequisites:生成目标所依赖的列表
  • command:shell指令,<font color=red>格式为Tab开头</font>

Makefile规则的target 和prerequisites 存在依赖关系,target是依赖于prerequisites;Makefile 存在自动推导的能力;不断向上<font color=red>自动推导</font>;

参考示例:

代码语言:txt
复制
test : test1.o test2.o
	cc -o test test1.o test2.o
	
test1.o : test1.c test1.h
	cc -c test1.c
	
test2.o : test2.c test2.h
	cc -c test2.c
	
clean :
	rm test test1.o test2.o

Makefile会进行自动推到,层层依赖、推导关系如下,

  • test 依赖于 test1.o 、test2.o
  • test1.o 依赖于 test1.c 、test1.h
  • test2.o 依赖于 test2.c 、test2.h

makefile会把所有依赖关系列举出来,执行make命令的时候,会根据依赖关系自动编译

隐晦规则

每个.o文件的依赖文件默认会有同名的.c文件,比如有一个target是test.o,那么test.c默认就是test.O的依赖文件,这个是makefile的隐晦规则,是make会自动推导出来的

make的工作方式

在默认命名的情况下,输入make命令做了什么?

  • make命令会找当前工作路径下的Makefile 或makefile文件
  • 找到文件,会寻找文件中,<font color=red>第一个目标文件(target)</font>,参考上面的示例就是”test“文件,并作为最终目标文件
  • 如果test文件不存在,就会根据test文件所依赖的.o文件,根据.o文件来生成test文件
  • 如果.o文件不存在,make会在makefile文件中,找到目标为.o文件,根据.o文件的依赖性,生成.o文件

make的依赖性,会自动推到一层层的依赖关系,最终编译出最终的目标。

makefile 变量

变量基础

例如:

代码语言:txt
复制
# 声明一个变量
objects = test1.o test2.o \
		test3.o
# 使用变量
test: $(objects)
	cc -o test $(objects)
  • 变量的引用可以使用 ${变量名} 或 $(变量名) 中括号或括号都可以
  • Makefile 中的变量的使用其实非常的简单,因为它并没有像其它语言那样定义变量的时候需要使用数据类型。变量的名称可以由大小写字母、阿拉伯数字和下划线构成。<font color=red>等号左右的空白符没有明确的要求</font>,因为在执行 make 的时候多余的空白符会被自动的删除。至于值列表,既可以是零项,又可以是一项或者是多项。

变量的基本赋值

  • 简单赋值 ( := ) 编程语言中常规理解的赋值方式,<font color=red>只对当前语句的变量有效。</font>
  • 递归赋值 ( = ) 赋值语句可能影响多个变量,所有目标变量相关的其他变量都受影响。
  • 条件赋值 ( ?= ) 如果变量未定义,则使用符号中的值定义变量。如果该变量已经赋值,则该赋值语句无效。
  • 追加赋值 ( += ) 原变量用空格隔开的方式追加一个新值。

变量中的变量

在定义变量的值时,我们可以使用其它变量来构造变量的值,在Makefile中有两种方式来在用变量定义变量的值。

例如:

代码语言:txt
复制
x := foo
y := $(x) 

追加变量值

代码语言:txt
复制
objects = main.o foo.o bar.o utils.o
objects += another.o

于是,我们的$(objects)值变成:“main.o foo.o bar.o utils.o another.o”(another.o被追加进去了)

如果变量之前没有定义过,那么,“+=”会自动变成“=”,如果前面有变量定义,那么“+=”会继承于前次操作的赋值符。如果前一次的是“:=”,那么“+=”会以“:=”作为其赋值符,如:

变量高级用法

变量值替换

<font color=red size=5> (var:a = b) 或{var:a = b }</font>

把变量“var”中所有以“a”字串“结尾”的“a”替换成“b”字串。这里的“结尾”意思是“空格”或是“结束符还是看一个示例吧:

代码语言:txt
复制
foo := a.o b.o c.o
bar := $(foo:.o=.c)
# 把“$(foo)”中所有以“.o”字串“结尾”全部替换成“.c”,最终bar的值是a.c b.c c.c

把变量值再当成变量

代码语言:txt
复制
x = y
y = z
a := $($(x))
# 在这个例子中,$(x)的值是“y”,所以$($(x))就是$(y),于是$(a)的值就是“z”

makefile注释

Makefile中只有行注释,注释使用”#“ 字符 例如:

代码语言:txt
复制
# 这是makefile的注释

引用其他的Makefile

在Makefile使用include关键字可以把别的Makefile包含进来,make命令开始时,会把找寻include所指出的其它Makefile,并把其内容安置在当前的位置。这很像C语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include的语法是:

代码语言:txt
复制
include <filename>
# 在include前面可以有一些空字符,但是绝不能是[Tab]键开始
# filename可以是当前操作系统Shell的文件模式(可以保含路径和通配符)

-include <filename>
# 无论include过程中出现什么错误,都不要报错继续执行。上面那条指令若是找不到include的目标文件,会报错

伪目标

代码语言:txt
复制
clean:
    rm *.o temp

<font color=red size=4>伪目标不会自动被执行,只能显式地调用执行。</font>但是上面伪目标的写法有一个缺陷,若是当前目录下存在有一个文件名为"clean",那么根据我们的规则,command将不会被执行,因为目标已经存在了,为了解决这个问题,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不管是否有这个文件,这个目标就是“伪目标”

代码语言:txt
复制
.PHONY : clean
clean:
    rm *.o temp

通过.PHONY,无论是否存在“clean”文件,我们的command都将会被执行了

常见伪目标

伪目标

描述

all

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

clean

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

install

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

print

这个伪目标的功能是例出改变过的源文件。

tar

这个伪目标功能是把源程序打包备份。也就是一个tar文件。

dist

这个伪目标功能是创建一个压缩文件,一般是把tar文件压成Z文件。或是gz文件。

TAGS

这个伪目标功能是更新所有的目标,以备完整地重编译使用。

check 和test

这两个伪目标一般用来测试makefile的流程。

常见问题

1. @echo,命令不回显

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

2. 立即赋值(:=) 和延迟赋值(=)

  • :=: 强制按先后顺序执行,立即赋值。
  • =:赋值的结果会等到整个路径执行完再决定,后面的会覆盖前面的,延迟赋值。

示例:

代码语言:txt
复制
$ cat Makefile

a = foo
b1 := $(a) bar
b2 = $(a) bar
a = xyz

all:
	@echo b1=$(b1)
	@echo b2=$(b2)

$ make
b1=foo bar
b2=xyz bar

3. 在 Makefile 表达式中使用逗号和空格变量

逗号和空格是 Makefile 表达式中的特殊符号

参考文档:

https://juejin.cn/post/6844904001872330760#heading-4

https://blog.csdn.net/weixin_38391755/article/details/80380786/

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是Makefile
    • Makefile里有什么
    • Makefile的文件名
    • Makefile规则
      • 显式规则
        • 隐晦规则
        • make的工作方式
        • makefile 变量
          • 变量基础
            • 变量中的变量
              • 追加变量值
              • 变量高级用法
                • 变量值替换
                  • 把变量值再当成变量
                  • makefile注释
                  • 引用其他的Makefile
                  • 伪目标
                  • 常见问题
                    • 1. @echo,命令不回显
                      • 2. 立即赋值(:=) 和延迟赋值(=)
                        • 3. 在 Makefile 表达式中使用逗号和空格变量
                        • 参考文档:
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档