Makefile基本使用

Makefile基本使用
泡泡Makefile简介
Makefile 是一个文本文件(默认情况下使用名为 Makefile 的文件),其中列出了按所谓 “target”分组的命令,你可以将其视为应一起执行的命令组。我们用 TAB 缩进字符来表示属于某个目标的命令。
默认情况下,make 会同时打印执行的命令和命令本身的结果。如果需要,你可以关闭这种行为,不过通常情况下,你确实希望看到被执行的内容。一开始,同时看到命令和输出结果可能会让人有点困惑,但你的眼睛很快就会习惯的。
Makefiles支持我们开发工作流,我们希望可以实现以下功能:
- 依赖管理—-跳过已经完成的步骤
- 重新进入—-从特定点重新启动工作流
- 自动化—-使用相同的代码并行运行多个相同的分析
一个Makefile文件的基本结构如下:
1 | foo: |
执行全部代码:
1 | make -f Makefile |
执行指定代码:
1 | make -f Makefile foo |
Tip: 如果文件名字是Makefile,也可以直接省略-f参数和文件名。
需要注意的是,Makefile 中的命令必须使用 Tab 键缩进,不能使用空格。不过我们可以通过设置 .RECIPEPREFIX 变量来修改这个行为,例如:
1 | # Sets the prefix for commands. |
Makefile使用技巧
好的默认设置
1 | SHELL := bash |
1 | # Deletes dependencies if the command fails. |
作用:
• 这个指示符告诉 make 如果在执行目标的命令时发生错误,应该删除目标文件。
好处:
• 保持构建环境的整洁:如果某个构建目标在执行过程中失败,它会删除部分生成的文件,从而避免留下半成品或不完整的文件。
• 避免误用不完整的构建:当构建失败时,目标文件可能是部分生成的,保留这些文件可能会导致后续的构建过程误以为这些文件是完整的,从而造成错误。删除这些文件可以避免这种情况。
1 | # Warns about undefined variables. |
作用:
• —warn-undefined-variables: 这个选项会使 make 在使用未定义变量时发出警告。
• —no-builtin-rules: 这个选项会禁用所有的内建规则和变量,使用者需要在 Makefile 中明确地定义所有需要的规则。
好处:
• 提高可调试性:
• —warn-undefined-variables: 当使用未定义变量时发出警告,有助于在构建过程中快速发现和修复拼写错误或遗漏定义的变量。这对于维护大型和复杂的 Makefile 特别有用。
• 增强可控性和可预测性:
• —no-builtin-rules: 禁用内建规则,确保所有的构建规则都是显式定义的,从而提高 Makefile 的可读性和可维护性。这样可以避免一些隐式规则导致的意外行为,使构建过程更加透明和可预测。
1 | SHELL := bash |
作用:
• 这个指示符告诉 make 使用 bash 作为 shell。
1 | .ONESHELL: |
作用:
• 这个指示符告诉 make 在执行目标的命令时,将所有的命令放在同一个 shell 进程中执行。
1 | .SHELLFLAGS := -eu -o pipefail -c |
作用:
-e
: 如果任何命令行返回非零状态(失败),则立即退出脚本。
-u
: 当执行到未设置的变量时,shell将会报错并退出。
-o pipefail
: 这个选项会导致管道(|)中的命令如果有任何一个失败了,整个管道的返回状态都会是失败的。默认情况下,只有最后一个命令的状态会影响整个管道的状态。
-c
: 这个标志告诉shell从字符串中读取命令。
“Dry-run”模式
如果你想要查看 make 命令将执行的命令,而不实际执行它们,可以使用 -n
或 --just-print
选项。这个选项会显示 make 将执行的命令,但不会实际执行它们。
1 | make fastq align -n |
变量命名
Makefile一般通过${foo}来引用变量,这和bash脚本中是一样的。
但在Makefile中,可以使用?=
来定义变量,如此定义的变量我们可以在命令控制行中进行控制。
例如:
1 | # Set the value of FOO to bar (if not already set). |
我们可以对NAME进行赋值,如:
1 | make usage NAME=Joe |
隐藏命令
一般情况下,make会打印出执行的命令,但有时候我们不希望看到这些命令,可以使用@
符号来隐藏命令。
1 | # Sets the prefix for commands. |
文本替换
有时候我们想从路径中提取目录名或文件名,如:
- PATH = data/reads/abc.txt
则: - DIR = $(dir ${PATH})将包含data/reads/
- FNAME = $(notdir ${PATH})将包含abc.txt
一些常见的使用如下:
1 | # |
打印说明
有时候我们想知道自己编写的Makefiles中有哪些作用,我们可以编写一个说明来帮助我们理解。
1 | # Set the prefix from tabs to > |
我们可以运行make usage
来查看说明。另外,如果我们没有指定“target“,则默认执行第一个“target”。
依赖管理
Makefile中的依赖管理是非常重要的,我们可以通过依赖管理来跳过已经完成的步骤,从而提高构建速度。
1 | # Set the prefix from tabs to > |
如果不存在results.txt文件,make会先生成counts.txt和names.txt文件,然后再生成results.txt文件。