makefile 精粹

  • SHELL = /bin/bash # 设置执行shell, 默认为sh, sh又怎么能发挥 Linux 的全部威力呢?

    要想知道执行 make 命令的 shell, 使用代码
SHELL := /bin/bash

.PHONY: info
info:
    @echo 当前shell: $$0

d@MyServer: ~/git/make $ make info
当前shell: /bin/bash
  • Rule 速记 --> $@: $< $? $^ | 仅指示的先决条件

    自动变量 含义
    $@ 目标的完整名称。
    $* 避免使用, 在模式规则下匹配不包含扩展名的目标文件名称。etc
    $< 第一个依赖文件的名称。
    $^ 所有的依赖文件,以空格分开,不包含重复的依赖文件。
    $+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
    $? 更新的依赖文件,这些依赖文件的修改日期比目标的创建日期晚。
    $% 如果目标是归档成员,则该变量表示目标的归档成员名称。例如,如果目标名称
    为 ar.a(a.o, b.o),则 $@ 为 ar.a,而 $% 为 a.o b.o。
    AR 归档维护程序的名称,默认值为 ar。
    ARFLAGS 归档维护程序的选项。
    AS 汇编程序的名称,默认值为 as。
    ASFLAGS 汇编程序的选项。
    CC C 编译器的名称,默认值为 cc。
    CCFLAGS C 编译器的选项。
    CPP C 预编译器的名称,默认值为 $(CC) -E。
    CPPFLAGS C 预编译的选项。
    CXX C++ 编译器的名称,默认值为 g++。
    CXXFLAGS C++ 编译器的选项。

  • 仅指示的先决条件(order-only-prerequisites)

    在构建目标前, 可能需要先创建目录, 这些目录的时间戳经常会由于普通的文件操作而改变, 但是却令规则被触发, 这是无法容忍的
    因此使用仅指示的先决条件来指示这些依赖, 只要它们存在, 即使它们比target新, 也不会触发规则, 如果不存在就会先构建这个先决目标
    相应的自动变量为 $|
all: src/main.c src/lib.c | bin
    gcc -o $|/target $^

bin: 
    mkdir bin
  • $ 是 makefile 关键字, 要在 shell 中使用 '$' 字符, 一律使用 "$$" 转义

  • 使用同一个Shell

    默认情况下, 每个命令运行在单独的 shell 进程中, 执行完成后 shell 被销毁, 变量无法被导出
.PHONY: info
info:
        @echo $$$$
        @echo $$$$

d@MyServer: ~/git/make $ make info
16207
16208

这非常不方便共享数据, 比如无法传递环境变量, 使用同一个Shell不仅提高性能, 还能方便配方编程
只需在非配方区域声明一行代码:

.ONESHELL:

例如

.ONESHELL:
SHELL := /bin/bash

.PHONY: info
info:
        @echo $$$$
        @echo $$$$

d@MyServer: ~/git/make $ make info
16225
16225

使用一个Shell进程还有一个好处, 要屏蔽所有的命令回显, 只需在第一行开头的命令上写@即可, 甚至直接将@作为第一行配方命令
缺陷: 事实上是将换行隐式转义了, 这样一来所有的配方都变成了一条命令, 某一条配方失败了仍然会继续...


官方文档
https://www.gnu.org/software/make/manual/make.html


你可能感兴趣的:(makefile 精粹)