Debug
出错类型
开发人员在编写出一个完整的版本之前,可能需要解决很多怪异的 make 问题。可能会引发错误的情况包括:
- 隐式规则
- 没想到的变量替换
- 嵌入式
shell
脚本中的语法错误 - 调用软件的路径错误。如,使用了绝对路径的
MakeFile
更换了编译环境 - 系统时钟设置错误
处理方法
仔细阅读错误消息
如果有一个嵌套的编译,可能会需要通过对一组错误消息来仔细进行分析,才能找到确切的错误。自问:
- 这是 make 自己产生的消息么?
- 还是 make 所调用的东西产生的消息?
确定make
正在编译的目标
在调试 makefile 时,目标是找到 make 正在试图编译什么东西,以及它认为哪些命令可以用来编译。如果 make 使用了正确的命令,但命令却出现了故障,那么这可能意味着完成了 make 调试 —— 但也许并不完全是。举例来说,如果试图编译程序时由于存在无法解析的符号而失败了,那么就可能是编译过程前面某个步骤出现了问题!如果不能定位命令中哪儿出现了问题,并且它看起来应该正常工作,那么很可能是 make 前面创建的某个文件没有被正确创建。
添加make
选项编译
调试标记可能会非常有用。对于GNU make
,-d
标记会提供大量的信息,其中有些是非常有用的。对于 Berkeley make
,-d
标记有一组标记;-d A
表示完整的集合,或者可以使用其中的一些子集;举例来说,-d vx
会给出有关变量赋值(v
)的调试信息,这会导致通过 sh -x
来运行所有的命令,这样 shell
就会精确地回显自己接收到的命令。-n
调试标记会导致 make
打印它认为需要做的事情的一个列表;这并不总是正确的,不过通常可以为思考哪些地方出现了问题而提供一些思路。
检查调用软件的路径
调用软件的路径错误。如,使用了绝对路径的MakeFile
更换了编译环境。
检查系统时钟
更重要的是,要检查编译树中文件的日期、系统中其他文件的日期以及系统的时钟。在面临输入数据的时间顺序不一致的情况时,make 的行为可能是无害的,也可能是不现实的。如果碰到了时钟问题(例如有些 “新” 文件被标记成 1970 年的),那么就需要修整这个问题了。 “touch” 工具是一个很好的帮手。在时钟问题中产生的错误消息通常都不太明显。
尝试使用其他make
工具
如果看到的错误消息显示有一些语法错误,或者有很多变量没有设置,或设置得不正确,那么可以尝试试验一下其他版本的 make;举例来说,有些程序在使用 gmake 编译时会产生一些非常含糊的错误,而使用 smake 时就能很好地进行编译。有些非常怪异的错误会说明正在使用 GNU make 来运行一个 Berkeley 的 makefile,反之亦然。Linux 特有的程序通常会假设使用 GNU make,使用其他 make 工具可能会碰到莫名其妙的错误,有些甚至在文档中都没有任何提示。
判断文件是否存在
-
调用shell的函数进行判断
exist = $(shell if [ -f $(FILE) ]; then echo “exist”; else echo “notexist”; fi;)
ifeq (exist, “exist”)
#do something here
endif -
使用makefile的函数进行判断
ifeq ($(FILE), $(wildcard $(FILE)))
#do something here
endif
visualizing the dependencies in a Makefile
Update 2014: I wrote a C version at https://github.com/lindenb/makefile2graph.
I’ve just coded a tool to visualize the dependencies in a Makefile. The java source code is available on github at : https://github.com/lindenb/jsandbox/blob/master/src/sandbox/MakeGraphDependencies.java. This simple tool parses the ouput of
make -dq
( here option ‘-d’ is ‘Print lots of debugging information’ and ‘-q’ is ‘Run no commands’) and prints a graphiz-dot file.
Example
Below is a simple NGS workflow:
%.bam.bai : %.bamfile.vcf: merged.bam.bai ref.fa
merged.bam : sorted1.bam sorted2.bam
sorted1.bam: lane1_1.fastq lane1_2.fastq ref.fa
sorted2.bam: lane2_1.fastq lane2_2.fastq ref.fa
Invoking the program:
make -d --dry-run | java -jar makegraphdependencies.jar
generates the following graphiz-dot file:
digraph G {
n9[label="sorted2.bam" ];
n3[label="merged.bam.bai" ];
n10[label="lane2_1.fastq" ];
n11[label="lane2_2.fastq" ];
n2[label="file.vcf" ];
n4[label="merged.bam" ];
n6[label="lane1_1.fastq" ];
n8[label="ref.fa" ];
n7[label="lane1_2.fastq" ];
n0[label="[ROOT]" ];
n5[label="sorted1.bam" ];
n1[label="Makefile" ];
n10->n9;
n11->n9;
n8->n9;
n4->n3;
n3->n2;
n8->n2;
n9->n4;
n5->n4;
n2->n0;
n1->n0;
n6->n5;
n8->n5;
n7->n5;
}
The result: (here using the google chart API for Graphviz)
reference
- visualizing the dependencies in a Makefile
- lindenb/jsandbox
- lindenb/makefile2graph