【操作系统】Makefile

make 是一个根据指定的 Shell 命令进行构建的工具——你规定要构建哪个文件、它依赖哪些源文件,当那些文件有变动时,如何重新构建它。


Makefile 格式

<target> : <prerequisites> 
[tab]  <commands>

目标

  • 目标可以是一个文件名,也可以是多个文件名,之间用空格分隔;除了文件名,目标还可以是某个操作的名字。
  • 如果 make 命令运行时没有指定目标,默认会执行 Makefile 文件的第一个目标。
  • 伪目标 .PHONY:声明某个“伪目标”后,make 就不会去检查是否存在一个叫做“伪目标”的文件,也不会检查其依赖的修改时间,每次运行都执行对应的命令。

依赖

  • 前置文件(依赖)通常是一组文件名,之间用空格分隔。
  • 指定了"目标"是否重新构建的判断标准:只要有一个前置文件不存在,或者有过更新(前置文件的 last modification 时间戳比目标的时间戳新),"目标"就需要重新构建。
  • 依赖可以省略。
all: test.txt
	echo hello
test.txt:
	echo world > test.txt

若当前目录中没有 test.txt,在 Shell 中执行 make 将会先执行 echo world > test.txt,随后执行 echo hello


规则

  • 规则是构建"目标"的具体指令。

  • 每行命令在一个单独的 Shell 中执行,这些 Shell 之间没有继承关系;若想让命令间有继承关系:

    • 将两行命令写在一行,中间用分号分隔;

    • 或在换行符前加反斜杠转义;

      all:
      	mkdir a;
      	cd a; \
      	touch b;
      
    • 或使用 .ONESHELL: 命令,Mac 自带 Make 3.81 不支持 .ONESHELL:,需要:

      • brew instal make
      • open ~/zshrc
      • export PATH 中添加 /usr/local/bin/usr/local/opt/make/libexec/gnubin,使用 : 分隔,如:export PATH=/usr/local/bin:/usr/local/opt/make/libexec/gnubin:$PATH
  • 每行命令之前必须有一个[tab] 键。如果想用其他键,可以用内置变量 .RECIPEPREFIX 声明。

.RECIPEPREFIX=>
.ONESHELL:
all:
>mkdir a
>cd a
>touch b

Makefile 语法


回声

  • 正常情况下,make 会打印每条命令,然后再执行,这就叫做回声(echoing)。
  • 在命令的前面加上 @,就可以关闭回声。

通配符

  • 用来指定一组符合条件的文件名。
  • Makefile 的通配符与 Bash 一致,主要有星号 *、问号 ?[...]
    • 通配符 ?:匹配一个任意字符。
    • 通配符 *: 匹配0个或任意多个字符,也就是可以匹配任何内容。
      • 例:对于文件 foo, foo1, foo2, foo10, bar:
      • rm foo? 删除 foo1 和 foo2。
      • rm foo* 删除除 bar 外的所有文件。
    • 通配符 [ ]:匹配中括号中任意一个字符。[-] 代表范围,[^] 代表逻辑非。
      • 例:[abc] 匹配 abc[a-z] 匹配所有小写字母;[^0-9] 匹配一个不是数字的字符。

模式匹配

  • make 命令允许对文件名,进行类似正则运算的匹配,主要用到的匹配符是 %
  • 使用匹配符 %,可以将大量同类型的文件,只用一条规则就完成构建。
  • 咕咕咕~

变量和赋值

赋值:

  • VAR=value:在执行时(引用时)扩展,允许递归扩展。

    varA = hi
    varB = $(varA)
    varA = hello
    all:
    	@echo $(varB)
    # 输出 hello
    
  • VAR:=value:在定义时(赋值时)扩展。

    varA := hi
    varB := $(varA)
    varA := hello
    all:
    	@echo $(varB)
    # 输出 hi
    
  • VAR?=value:只有在该变量为空时才设置值。

  • VAR+=value:将值追加到变量的尾端。

调用:

  • 变量需要放在 $( ) 之中。
  • 调用 Shell 变量,需要在美元符号前,再加一个美元符号,如 $$HOME

内置变量

  • $(CC) 指向当前使用的编译器。
  • $(MAKE) 指向当前使用的 make 工具。

自动变量

  • $@:指代当前“目标”。
  • $<:指代第一个“依赖”。
  • $?:指代比“目标”新的所有“依赖”。
  • $^:指代所有“依赖”。
  • $*:指代匹配符 % 匹配的部分。
  • $(@D):指向 $@ 的目录名。
  • $(@F):指向 $@ 的文件名。
  • $(<D):指向 $< 的目录名。
  • $(<F):指向 $< 的文件名。

判断和循环

判断:

  • 使用 ifeqelse endif,没有缩进。
all:
ifeq ($(CC),gcc)
	@echo hi
else
	@echo hello
endif
# 输出 hello
  • 使用 Bash 语法,有缩进。
.ONESHELL:
all:
	@if [ 1 == 2 ]
	@then
		@echo hi
	@else
		@echo hello
	@fi
# 输出 hello

循环:

.ONESHELL:
LIST = one two three
all:
	@for i in $(LIST)
	@do 
		@echo $$i; 
	@done

函数

  • 调用方法:$(function arguments)${function arguments}

  • 内置函数:咕咕咕~


参考

posted @ 2022-03-16 17:06  空白4869  阅读(80)  评论(0编辑  收藏  举报