makefile

makefile #

make 不带参数,只会执行 makefile 中第一条 target 声明(带冒号的语句)

简介 #

Makefile 用于定义如何创建目标文件,比如如何从源码到可执行文件。 创建这一工具的目标是 减少不必要的编译或者任务。 传说 Stuart Feldman 在 1976 年花了一个周末写出来的, 而今仍然使用广泛,特别是在 Unix 和 Linux 系统上。

基础 #

语法 #

  • 注释:#
  • 文件名: 必须是 Makefile
    • 区分大小写
  • make <target> 生成 target
    • 重命名:make -f "filename" <target>
  • 只认识 TAB,不认识空格
    • 但是在 GNU Make 3.82 之后, 可以通过设置参数 .RECIPEPREFIX 进行修改
  • shell 符号
    • @:不把命令打印到 stdout
    • -:发生错误了也没关系
  • $$
    • $ 是 make 变量
    • $$ 是 shell 变量

target 声明 #

  • 创建一个 target
    targets : prerequisites
        recipe
    
    • prerequisites(依赖) 是可选的, recipe(做法) 也可以多个或者不给
      • 没有给 prerequisites, 只会在目标文件文件不存在时执行
    • targets 和 prerequisites 都可以是多个, 以空格分割
      file2.txt file3.txt: file0.txt file1.txt
          touch file2.txt
          touch file3.txt
      
  • target 的声明顺序不重要
    • 上面的依赖可以下面再声明
  • 如果声明重复的 target,make 会给一个 warning,后面会覆盖前面的
    • 但是如果不定义任何 recipe, 就不会冲突, 只是多了依赖关系
      file2.txt: file0.txt file3.txt
      
  • Phony(假的) Targets
    • 意思是 tagets 并不是文件, 可以想象成一个任务的名字而已
    • 因为不是文件, 无法比对是否有更新, 所以每次 make 都会执行
    • 依赖于 phony target 的 target 也会每次 make 都执行, 即使 target 是文件
  • .PHONY
    • 如果定义的 phony target 与文件名重名, 可以用 .PHONY 显式地指明哪些 targets 是 phony
常用 phony target #
  • all
  • clean
  • install
  • uninstall

变量与通配符 #

  • $^: 代表 prerequisites
    # 即便分开定义依赖, $^ 依然能拿到
    process: file*.txt
    # 非常智能的, ex1.txt 会被找到, file0.txt 会被去重
    process: ex1.txt file0.txt
      @echo $^
    
  • $@: 代表 target, 如果 target 为多个, $@ 代表当前执行的那个
  • $<: prerequisite 中的第一个
  • $?: 需要更新的 prerequisite 文件列表
  • $+: 所有依赖, 包括重复的
  • $|: 竖线后面的 order-only prerequisites
  • $*: target % 那部分, 包括路径
    a.%.b:
        # $* match 的target % 那部分, 包括路径, 比如 `make dir/a.foo.b` 会打出 `dir/foo`
        @echo $*
    

模式匹配 #

  • make 会找到最具体的匹配
    • make small/foo.png 则会匹配下面这个规则(在这之前要先有 small/foo.svg 这个文件)
      %.png: %.svg
          inkscape --export-png $^
      small/%.png: %.svg
          inkscape --export-png --export-dpi 30 $^
      
  • make 已经有一些内置的规则, 比如从 _.c 到 _.o

竖线 #

  • 竖线左边为:正常前提目标(Normal Prerequisites
    • 当正常前提目标变化时,target 重新生成
    • 可以为空
  • 竖线右边为:命令前提目标(order-only Prerequisites
    • 当命令前提目标变化时,target 重新生成
process: file*.txt | dir/a.foo.b

变量 #

  • 变量都是字符串类型

    # 这俩是一样一样的
    name = Ted
    name2="Sarah"
    
  • 设置变量,按以下顺序由高到低:

    1. 命令行参数. 比如试试 make echo name3=JICHAO
    2. Makefile 里面的
    3. shell 中的环境变量
    4. make 预设的一些变量
  • ?=

    # 如果 name 被设置过了, 就不设置了
    name ?= Jean
    
  • override

    # 用 override 可以防止命令行参数设置的覆盖
    override name = David
    ``
    
  • +

    # 用加号可以连接 (中间用空格分割)
    name4 +=grey
    
  • 内置的变量

    echo_inbuilt:
        echo $(CC)
        echo ${CXX)}
        echo $(FC)
        echo ${CFLAGS)}
        echo $(CPPFLAGS)
        echo ${CXXFLAGS}
        echo $(LDFLAGS)
        echo ${LDLIBS}
    
  • :=

    • 等号声明时 recursively expanded 递归扩展
    • 加个冒号可以声明 Simply expanded variables 即时扩展变量, 即只在声明时扩展一次
    # var3 声明时找不到 var4, var3 会扩展成 `and good luck`,直接忽视 var4
    var3 := $(var4) and good luck
    # var5 是正常的,扩展为 `good night and good luck`
    var5 = $(var4) and good luck
    var4 := good night
    

函数 #

  • 函数调用格式

    $(func arg0,arg1,arg2...)
    
  • wildcard:将后面的通配符变成一串文件路径

  • patsubst:做替换

    # 把所有 markdown 后缀的文件重命名为 md 后缀
    substitue: *
        @echo $(patsubst %.markdown,%.md,$* $^)
    

指令 #

  • include:引入别的 Makefile 文件
  • 流程控制语句顶格写
    sport = tennis
    # 流程控制语句 (如if else 等等) 顶格写
    report:
    ifeq ($(sport),tennis)
        @echo 'game, set, match'
    else
        @echo "They think it's all over; it is now"
    endif
    

分支和变体 #

GNU make #


进阶 #


原理 #


本文访问量

本站总访问量

本站总访客数