前言
在Linux开发过程中,经常会遇到各种编译问题。比较简单的问题一目了然,但有些比较复杂,较难分析。本文将编译中常见的调试手段、技巧做一总结,供各位参考。
常用手段和技巧
错误代码
经常遇到某个宏不生效的问题,这种情况,可以在宏控制的部分增加错误,看是否会有编译报错。
部分编译
如果一个项目比较庞大,直接make比较耗时间,此时,有两个选择:
- 只编译出问题的模块或目录;
- 注掉部分暂时无问题的模块(比如,有的sdk驱动和用户态app在一起编译,在调试app的编译的过程中,就可以把sdk驱动暂时注掉);
gcc相关
- -E选项
– 当需要确定预处理相关的信息,如头文件包含、宏展开等情况,可使用这个选项;
– 只激活预处理,不生成文件;
– 仅对依赖关系做初步解析,没有-M等全面深入;
gcc -E hello.c #默认输出到屏幕上; gcc -E hello.c -o hello.i #将输出重定向到文件; gcc -E hello.c > hello.txt #将输出重定向到文件; gcc -E -C hello.c -o hello.i #在第二条的基础上,可阻止预处理器删除源文件和头文件中的注释;
- 编译器依赖生成选项,-M选项、-MM选项、-MMD选项
其作用在于自动找寻源文件中包含的头文件,并生成一个依赖关系。
gcc -M obj.c #获取目标obj.c的完整依赖关系 gcc -MD obj.c #在-M的基础上,将输出导出到.d文件; gcc -MM obj.c #在-M的基础上,去除标准库的头文件; gcc -MMD obj.c #在-MM的基础上,将输出将导到.d文件: gcc -MM -MF "obj.d" obj.c #-MF可以指定输出文件,前面几个命令默认用obj.d文件;
例1、一个简单例子:
[qxhgd@localhost]$ gcc -M test.c test.o: test.c /usr/include/stdc-predef.h /usr/include/stdio.h \ /usr/include/features.h /usr/include/sys/cdefs.h \
例2、一个完整例子:
SRCS=$(wildcard *.c) OBJS=$(SRCS:.c=.o) DEPS=$(SRCS:.c=.d) .PHONY: all clean all: main -include $(DEPS) %.o:%.c gcc -c -g -Wall $< -o $@ -MD -MF $*.d -MP #“-MF $*.d ”可以去除; main: $(OBJS) gcc $^ -o $@ clean: rm -f *.d *.o main
shell命令
- echo方法
echo some info #显示echo这条命令本身和后面要输出的内容 @echo some info #不会显示echo这条命令,只会显示后面要输出的内容
例1、打印一个变量值:
@echo "########### VAR=" $(VAR)
例2、将宏定义输入到h文件中:
@echo "#define BUILD_DATE \"`date +%Y-%m-%d`\"" > $(INCDIR)/version.h
Makefile内置函数
$(info ...) #不打印当前makefile名和行号 $(warning ...) #不中断makefile的执行,打印信息,并打印当前makefile文件名和行号 $(error ...) #含warning的功能,同时会中断makefile的执行并退出
例1、打印信息
$(warning These ARMv8 Crypto Extensions modules need binutils 2.23 or higher)
例2、打印变量
$(warning The '$(SPHINXBUILD)' command was not found.)
编译重定向
make xxx > build_output.txt #将编译过程重定向到txt文件,错误在屏幕会显示; make xxx 2> build_output.txt #仅将make输出中的错误(及警告)信息输出到文件中 make xxx 1> build_output.txt #仅需要把make输出中的正常(非错误,非警告)的信息输出到文件 make xxx 1> build_output_normal.txt 2>build_output_error.txt #正常信息和错误信息,都输出到对应文件中 make xxx > build_output_all.txt 2>&1 #所有的信息都输出到同一个文件中
make命令行选项
--just-print(-n) #输出makefile中的所有数据,包括所有的规则和变量;只显示命令,但不会执行命令; --dry-run #和-n类似,只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行 --recon #和-n类似,只是打印命令,不管目标是否更新,把规则和连带规则下的命令打印出来,但不执行 --print-data-base(-p) #显示GNU版权信息以及make 所运行的命令,然后输出它的内部数据库; --debug #当你需要知道make如何分析你的依存图时,可以使用--debug 选项。共计五个调试级别和一个修饰符可用,basic、verbose、implicit、jobs、all 以及makefile; --trace #类似于Shell 里头的 set -x; -W <file> # 这个参数需要指定一个文件。一般是是源文件(或依赖文件),Make会根据规则推导来运行依赖于这个文件的命令,一般来说,可以和“-n”参数一同使用,来查看这个依赖文件所发生的规则命令。另外一个很有意思的用法是结合“-p”和“-v”来输出makefile被执行时的信息 --what-if=<file> #类似于-W; --assume-new=<file> #类似于-W; --new-file=<file> #类似于-W; --touch(-t) #把目标文件的时间更新,但不更改目标文件。也就是说,make假装编译目标,但不是真正的编译目标,只是把目标变成已编译过的状态。
例子:
make --trace xxx #展开xxx目标代码的执行过程; make --debug xxx #展开整个make解析和执行xxx的过程;
remake
- remake is an enahanced version of GNU Make that adds improved error reporting, better tracing, profiling and a debugger.
小结
上述这些方法,基本可以覆盖Makefile常用的调试场景。