[关闭]
@phper 2019-05-17T14:44:21.000000Z 字数 3949 阅读 2709

Makefile学习笔记

linux


Makefile 是Linux系统中用来生成文件、整理和管理命令的配置文件。你可以这样理解,它就是一个key:value 的shell 命令(系统或者软件的命令)集合。你通过执行和管理key,来执行 value (shell命令)。

Makefile 配置文件生成

一般我们会在项目跟目录下,直接新建一个名字叫做 Makefile 的文件,它没有文件后缀类型。

这样就生成了1个文Makefile配置文件。

Makefile 命令使用

Makefile 文件里的命令书写方式,类似于yaml。每一个命令规则,单独用一个区块表示,比如下面,就有好几个命令。

  1. result.txt: source.txt
  2. @echo "building result.txt from source.txt"
  3. cp source.txt result.txt
  4. source.txt:
  5. @echo "touch source.txt"
  6. echo '1222222' > source.txt
  7. txt = hello world
  8. test:
  9. @echo ${txt}
  10. @echo 11223

Makefile中每一个命令格式如下:

  1. <target>: <prerequisites...>
  2. <commands>
  1. 目标:前置条件(依赖)
  2. TAB 命令

需要注意的是,命令前面必须加个Tab键,不能用多个空格替代,不然会报错:

  1. Makefile:2: *** missing separator. Stop.

另外有些编辑器,比如phpstrom或者golandJB的编辑器,默认的Tab是有问题的,如果用来编写Makefile文件,会报错。所以,你可以直接用Vim来编辑,或者安装Makefile插件来解决Tab的问题。

"目标"是必需要申明的,不能省略;"前置条件"和"命令"都是可选的,但是两者之中必须至少存在一个。

如何运行

我们写好了Markfile文件,我们想去执行或者运行其中的一个target,如何运行呢?用make target 这样子来运行,比如:

  1. make result.txt
  2. make source.txt
  3. make test

如果只是用make关键字,后面不加target,它会默认去执行第一个target,比如:下面是等价的。

  1. make
  2. make result.txt

下面来一个一个来讲一下这3个如何搭配使用。

target(目标)

这个相当于key:value的key。一个目标(target)就构成一条规则,它代表你需要执行的目标或者需要生成的文件。目标通常是文件名,指明Make命令所要构建的对象,比如上文的 result.txt,则表示:"我需要创建result.txt文件"

目标可以是一个文件名,也可以是多个文件名,之间用空格分隔。

比如:

  1. result.txt: source.txt
  2. @echo "building result.txt from source.txt"
  3. cp source.txt result.txt

目标还可以是某个操作的名字,这称为"伪目标"(phony target)。比如:

  1. tutorial:
  2. @echo "Please read the 'Makefile' file to go through this tutorial"

如果这个伪目标的名字和文件系统中的文件名一样,那么make执行,则会不生效,并且会报错: " 这个文件已经生成了,并比较新,没必要再生成了"。

Mrakfile文件如下:

  1. list:
  2. echo 123456

查看文件列表,有一个名字也为list的文件:

  1. $ ll
  2. total 2
  3. -rw-r--r-- 1 yangyi staff 152B 5 15 20:06 Makefile
  4. -rw-r--r-- 1 yangyi staff 0B 5 15 20:05 list

make 执行一下,报错了:

  1. $ make list
  2. make: `list' is up to date.

所以,我们得把list加入到伪目标名单里,通过.PHONY关键字来添加,告诉make命令,别去文件系统中找文件是都存在了,直接执行吧:

  1. list:
  2. echo 123456
  3. .PHONY:list install

再执行一下:

  1. $ make list
  2. 123456

成功了。

prerequisites 前置条件(依赖)

前置条件,通常是一组文件名,之间用空格分隔。它指定了"目标"是否重新构建("命令"被执行)的判断标准:

prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。

这是makefile 的核心。我们还是用例子来分别说明下。

  1. result.txt: source.txt
  2. @echo "building result.txt from source.txt"
  3. cp source.txt result.txt

上面这个例子,我们的目的是想生成result.txt文件,它是通过拷贝source.txt的内容来生成的。

通过prerequisites 前置条件(依赖)的定义,我们可以知道,result.txt是依赖于 source.txt。需要满足下面2条规则其一,才会生效,cp命令才会被执行:

  1. source.txt存在,source.txt 的最近一次修改时间,比 result.txt 要新。
  2. source.txt文件不存在,但是存在一个命令规则,名子就叫source.txt

那么就根据这2种情况,来分析。

假设 source.txt 文件在目录里不存在。它会自动去寻找 规则中,是否有source.txt 规则是否存在,如果不存在,那么直接报错:

  1. make: *** No rule to make target `source.txt', needed by `result.txt'. Stop.

如果,我们有有一条规则是source.txt,是创建者文件。

  1. source.txt:
  2. echo 1111 > source.txt

那么就会被自动执行。再接着回到result流程去执行命令,然后整个流程就走完了。2个target都会执行成功,并输出:

  1. echo 1111 > source.txt
  2. building result.txt from source.txt
  3. cp source.txt result.txt

假设 source.txt 文件在目录里存在。就会去对比 目标和依赖文件的更新时间,如果 source.txt 的最近一次修改时间,比 result.txt 要新。那么就会执行cp命令成功,否则就会报错:

  1. make: `result.txt' is up to date.

下面这张流程图就会看的很清楚了:

在这里插入图片描述

prerequisites 前置条件(依赖)为空

如果前置条件为空,只有target 和 commend。那如何对比更新时间呢?

比如下面这个规则:

  1. source.txt:
  2. echo 1111 > source.txt

上面代码中,source.txt后面没有前置条件,就意味着它跟其他文件都无关,只要这个文件还不存在,每次调用make source.txt,它都会生成。

我们执行一下:

  1. $ make source.txt
  2. echo 1111 > source.txt
  3. $ make source.txt
  4. make: `source.txt' is up to date.

他对比的是自身的文件是否存在,来作为是否去执行命令的依据

还有一种情况,target 不是文件名,是一个伪目标。比如:

  1. help:
  2. @echo 'help'
  3. .PHONY help

那么,就不会去对比任何时间以及文件是否存在,每次都会往下执行。

  1. $ make help
  2. help
  3. $ make help
  4. help
  5. $ make help
  6. help
  7. $ make help
  8. help

command 命令

这个命令就是普通的shell命令。规则通过后,会执行这里的命令。

每行命令之前必须有一个tab键。如果想用其他键,可以用内置变量.RECIPEPREFIX声明。

  1. .RECIPEPREFIX = >
  2. hello:
  3. > echo world

make 输出的时候,会把命令也会原封不动的输出出来,再去执行,这就叫做回声(echoing)。

如果我们想屏蔽掉,可以在前面加个@

  1. hello:
  2. echo world
  3. hello2:
  4. @echo world

我们执行一下:

  1. make hello
  2. echo 'world'
  3. world
  4. make hello2
  5. world

可以看出,加了@ 就会屏蔽了这条命令自生的输出,而只会去执行命令。

其他

注释是用# 来表示。但是注释在 命令里,也会被输出,可以在前面加 #来屏蔽回声。

  1. #list:
  2. # ehco 'list'
  3. install:
  4. @#doing something
  5. @echo 122

例子

看一下golang 编译的例子,把golang的各个环节,用Makefile来管理和执行

  1. all: gotool
  2. @go build -v .
  3. clean:
  4. rm -f apiserver
  5. find . -name "[._]*.s[a-w][a-z]" | xargs -i rm -f {}
  6. gotool:
  7. gofmt -w .
  8. go tool vet . |& grep -v vendor;true
  9. ca:
  10. openssl req -new -nodes -x509 -out conf/server.crt -keyout conf/server.key -days 3650 -subj "/C=DE/ST=NRW/L=Earth/O=Random Company/OU=IT/CN=127.0.0.1/emailAddress=xxxxx@qq.com"
  11. help:
  12. @echo "make - compile the source code"
  13. @echo "make clean - remove binary file and vim swp files"
  14. @echo "make gotool - run go tool 'fmt' and 'vet'"
  15. @echo "make ca - generate ca files"
  16. .PHONY: clean gotool ca help
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注