Skip to content

Makefile

  • makefile 文件的基本格式
        target: dependencies
               steps to build target with dependencies
    
  • 目标、源文件、生成方式(shell)

  • 例c多文件编译

    .PHONY: all clean depen
    
    depen=clean main.o libmulti_div.so libadd_minus.a
    
    all: $(depen)
        sudo cp libmulti_div.so /usr/lib
        gcc -o main4 main.o -L./ -ladd_minus -lmulti_div
    libadd_minus.a:
        gcc -c add_minus.c
        ar rc libadd_minus.a add_minus.o
    libmulti_div.so:
        gcc multi_div.c -fPIC -shared -o libmulti_div.so
    main.o:
        patch -p2 < v3.0.patch
        gcc -c main.c
    clean:
        -rm $(depen) add_minus.o main4 main.c.\*
    

基本概念

依赖的执行顺序

  • GNU make 会按默认的优先级查找当前文件夹的文件,查找的优先级为: 「GNUmakefile」> 「makefile」> 「Makefile」。(三种不同名称的 makefile)
    • 手动指定 make -f name
  • 使用时间戳来判断文件是否需要更新是一种高效的方式,避免不必要的重新编译。
    • 依赖文件不存在:如果 make 检测到一个目标的依赖文件不存在,它会查找 Makefile 中定义的规则来创建这个依赖文件。
    • 依赖文件的时间戳比目标文件新:如果依赖文件存在且其修改时间比目标文件的修改时间要新make 会认为目标文件已经过时。因此,make 会执行更新目标文件所需要的命令。这通常涉及重新编译源代码或执行其他更新目标文件所需的步骤。
    • 依赖文件的时间戳比目标文件老:如果所有依赖文件的修改时间都比目标文件的修改时间要旧,make 会判断目标文件是最新的,没有必要重新构建。因此,它会忽略对应于这些依赖文件的命令。
  • make 按照从左到右的顺序检查依赖 main:main.o testa testb

  • 目标重建的规则:

    • 目标是否为伪目标,若是则需要执行重建,否则参考第 2 条
    • 目标是否存在,若存在,则参考第 3 条,若不存在则需要执行重建
    • 目标文件是否比其全部依赖项都,若比其任一依赖项文件要旧则需要执行重建
  • 依赖的分类

    • 常规依赖:告诉 make 目标重建所需的依赖文件以及何时需要更新目标。
    • order-only 依赖:告诉 make 目标重建所需的依赖文件,这些依赖文件的更新不会导致目标被重建。order-only 依赖以管道符号「|」开始,管道符号左边为常规依赖项,管道符号右边为 order-only 依赖项。
    • aim_1:test_1 |test_2 test_3

自动推导规

  • 对于 xxx. o 类型的目标会默认使用命令 cc -c xxx. c -o xxx. o 进行编译。
  • 对于 xxx 类型的目标会默认使用命令 cc xxx. o -o xxx 进行编译。
  • 即使没有 makefile 文件,自动推导也会进行

最终目标

  • 一般情况下 makefile 的最终目标是第一条规则的目标。
  • 当目标名以符号「.」开头且其后跟的不是斜线符号「/」(即不被解析为目录符号)时,此目标无法作为最终目标。如 .PHONY
    • 需要手动选择执行 make -f phony_make .PHONY
  • 模式规则中的目标不会被作为最终目标
    • %: 可以匹配任何未显示定义的目标,是一种模式规则,不会被作为最终目目标
  • 环境变量指定的文件会被 make 首先读入,但其中的目标无法作为最终目标。
  • 关键字 include 所引用进 makefile 中的规则,它的目标会被作为最终目标

基本概念

  • 注意缩进必须使用制表符不能用空格代替

  • 忽略错误:添加 - 作为前缀 -rm $(depen),即使命令执行出现错误也继续完成 makefile

  • 执行 make file:make targetname

    main: main.o gcc -o main main.o main.o: main.c gcc -c main.c
    

  • 可以使用 make main.omake main
  • 省略 name 时默认以第一条规则作为目标

  • 在命令前添加 @ 不会把命令本身输出,如 @cd

include

  • make 读入 makefile 时遇见 include 指示符会暂停读入当前文件,转而读入 include 指定的文件,之后才继续读入当前文件。

  • makefile 中可以使用 include 指令来包含另一个文件。(如引入变量、依赖的构建方式)

  • include ./makefile_dir/inc_a
  • 当 include 指示符包含的文件不包含绝对路径,且在当前路径下也无法寻找到时,make 会按以下优先级寻找文件:

    • -I 指定的目录
      • make -I ./makefile_dir/
    • /usr/gnu/include
    • /usr/local/include
    • /usr/include
    • 指定 makefile 的 include 路径
  • makefile 的执行分为两个阶段,一阶段是读入所有 makefile 文件,include 导入的文件以及环境变量指定的文件,同名变量会被 include 的覆盖

环境变量

  • 功能上与 include 类似,允许读取环境变量中的 makefile 文件
  • export MAKEFILES=./makefile_dir/c_inc(在 bash 中执行)

重载

  • makefile 重载另一个 makefile 时,不允许有规则名重名。对于两个文件中同名的规则,make 后读入的规则会重写先读入的规则
  • 对于存在重名方法时不要直接 include 防止覆盖
    AAim:
        make -f AMake intro
    
    BAim:
        make -f BMake intro
    

变量

  • 变量的定义 depen=main.o testa testb clean

    • 使用 $(depen)
  • 声明为目标(即名称代表要执行的命令、阶段而不是实际的文件),类似声明变量名 .PHONY: all clean depen

  • 输入时提供参数 make -f eq.mk a=6,在 makefile 内直接使用 ${a}

特殊变量

  • $@:当前执行目标的名称
  • $< :第一个依赖文件名
  • $^ :所有的依赖文件名,以空格分隔
  • $?:比目标新的依赖文件名,以空格分隔
  • $*:目标模式中 % 匹配的部分

通配符

  • image.png|450
  • 在规则的目标或依赖中使用%而不是*aim_%: test_%

函数方法

  • $(wildcard *.c):这个 wildcard 函数扩展为当前目录下所有匹配规则的文件名的列表(以 .c 结尾的文件名列表)
  • $(patsubst %.c,%.o,$(wildcard *.c))patsubst 函数用于模式替换。它的作用是将第三个参数(这里是通过 wildcard 得到的文件列表)中匹配第一个参数模式(%.c)的部分替换为第二个参数指定的模式(%.o)。
    • obj = $(patsubst %.c,%.o,$(wildcard *.c))

流程控制

  • 注意每一条指令都是相互独立的(并不是在相同的一个 shell 环境)
  • 如果想要连续执行代码(如结合 cd),那么要将代码写在同一行 @pwd; cd .. ; pwd
    • 可以使用\分行
      @echo "cmd1 process \
          id is :" $$$$; \
          echo "cmd2 process id is :" $$$$
      

条件判断语句

  • 判断关键字
    • image.png|235
  • 格式

    ifeq (ARG1, ARG2)
    ifeq 'ARG1' 'ARG2'
    ifeq "ARG1" "ARG2"
    ifeq "ARG1" 'ARG2'
    ifeq 'ARG1' "ARG2"
    

  • 条件语句

    CONDITIONAL-DIRECTIVE
    TEXT-IF-TRUE
    endif
    
    CONDITIONAL-DIRECTIVE
    TEXT-IF-TRUE
    else
    TEXT-IF-FALSE
    endif
    

高级