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

真正的变量:在规则和配方中使用shell命令和通配符 -> $(shell) $(wildcard)函数

  • $(shell)函数使用find命令实现匹配所有特定后缀文件的先决条件
    假如我们要匹配src目录下的所有.c文件,它们编译后生成一个libtest.so动态链接库文件,则规则如下:
libtest.so: $(shell find src -name '*.c')
    $(CC) -o $@ -shared -fPIC $^
  • $(wildcard)函数 * ** 通配符
    *匹配一个目录下的任意文件或目录(的部分字符)
    **递归匹配子目录,不过具有兼容性问题,建议不要使用
libtest.so: $(wildcard )
    $(CC) -o $@ -shared -fPIC $^

makefile代理

在不使用$(shell)和$(wildcard)函数的情况下,可以使用makefile代理,根据目录的stat属性检测一个目录的结构是否发生变化,从而生成一个新的makefile代理文件,由它来完成最终的make任务

make -f makefile_proxy

参考项目 https://github.com/develon2015/make


官方文档 [https://www.gnu.org/software/make/manual/make.html](https://www.gnu.org/software/make/manual/make.html)
原文地址:https://www.cnblogs.com/develon/p/11622537.html