如何通过阅读Makefile及编译过程分析软件的编译过程

在很多的嵌入式的软件中,我们拿到代码,如果有文档先看看文档,没有文档也只能从Makefile的角度去看看软件的基本架构了。有一些大型的嵌入式软件项目的Makefile写的很复杂,这样在看学习Makefile的时候,需要一些技巧去分析Makefile的流程。看Makefile主要可以看哪些代码都是编译到哪,是编译到动态库,还是通过静态链接的静态库中的。

第一个技巧:make -n

make -n 在编译的时候加上了-n的参数,执行该命令编译并不真正的执行,只是顺序的列出该编译过程都执行哪些编译命令。可以通过这个来查看编译的基本序列,知道make一层不会涉及具体的执行命令,例如gcc xxx。

第二个技巧:makefile的打印调试技巧

makefile存在 info warning error等的函数可以调用,可以在makefile执行到此处(函数所在处)的时候,打印出调试信息,方便查看变量的值,和预展开(eval 函数)的内容。

方法1:$(info  “here add the debug info”)

方法2:$(warning “here add the warning info”)

方法3:$(error “error:this will stop the compile”)  //在打印编译处,停止编译的进程,相当于跳出当前的编译,该操作比较有用,在加了适当打印的地方停住,能非常方便的查看当前所打印出来的值。分析好当前位置的Makefile去掉该语句(error)在继续向下进行后续的编译。

在makefile中常常见到有类似于 $(eval $(call stampfile,$(curdir),tools,compile,,_$(subst $(space),,$(tools_enabled))))  ----  openwrt tools/Makefile中的一行内容。

这行内容表示,在make执行的时候会根据call所调用的函数生成一段Makefile文件内容放到这行代码的地方,我们把这个叫做展开。展开的文件中包含若干个目标,依赖,和执行的命令,和正常的makefile一样的。对于这种我们不能很明显的看到它所展开的makefile文件是什么样的,需把展开的内容打印出来查看。我们可以采用上边介绍的命令对其进行展开 例如:

$(warning $(eval $(call stampfile,$(curdir),tools,compile,,_$(subst $(space),,$(tools_enabled)))))用makefile的warning语句将上边eval语句展开的内容打印出来。

tools/stamp-compile:=/home/quzhifeng/workspace/lede_wallys_mtk/staging_dir/target-mipsel_24kc_musl/stamp/.tools_compile_yynyyyyynyyyyynyynnyyyynyyyyyyyyyyyyyyynyynynnyyynny
  $(tools/stamp-compile): /home/quzhifeng/workspace/lede_wallys_mtk/tmp/.build 
        @+/home/quzhifeng/workspace/lede_wallys_mtk/scripts/timestamp.pl -n $(tools/stamp-compile) tools  || make --no-print-directory $(tools/flags-compile) tools/compile
        @mkdir -p $$(dirname $(tools/stamp-compile))
        @touch $(tools/stamp-compile)

  $(if ,,.SILENT: $(tools/stamp-compile))

  .PRECIOUS: $(tools/stamp-compile) # work around a make bug

  tools//clean:=tools/stamp-compile/clean
  tools/stamp-compile/clean: FORCE
        @rm -f $(tools/stamp-compile)
可以通过查看展开来分析makefile文件

 warning 可以放在makefile总任何位置的实例:

$(warning A top-level warning)     1
FOO := $(warning Right-hand side of a simple variable)bar    2
BAZ = $(warning Right-hand side of a recursive variable)boo   3

$(warning A target)target: $(warning In a prerequisite list)makefile $(BAZ)  5
$(warning In a command script)   6
ls  7
$(BAZ):  8


$ make
makefile:1: A top-level warning 
makefile:2: Right-hand side of a simple variable 
makefile:5: A target   
makefile:5: In a prerequisite list
makefile:5: Right-hand side of a recursive variable
makefile:8: Right-hand side of a recursive variable
makefile:6: In a command script
ls   //执行命令
makefile   //显示命令的执行结果

第三个技巧:make --debug

对于一个比较复杂的工程,有超多的makefile文件,有很多的目标需要编译,例如 openwrt的makefile编译系统。这个时候需要你打开makefile的 --debug 选项功能,查看各个目标在编译过程的依赖关系,可以更好的分析make的执行流程和makefile的关系。

从上边可以看出,在编译world的时候,需要优先编译出prepare(是world的依赖目标),在编译prepare还需要依赖于那一长串的目标等等。可以清晰的查看目标的依赖关系,及目标和Makefile之间的关系。 

第四个技巧:显示实际执行的命令的方法。make VERBOSE=1 

对于GNU的项目一般在执行的make命令后加VERBOSE=1 可以打印出信息的命令执行信息。

对于非GNU项目,一般的隐藏执行命令的方法都是在命令的开头添加了@,这样命令执行的时候并不显示所执行的命令。

$(BIN_DIR)/$(PROG): $(OBJS) $(DEP_LIBS)
	@echo "LD  => $(subst $(BLD_DIR),build.$(CPU).$(VER),$@)" && \
	if [ ! -d $(dir $@) ]; then mkdir -p $(dir $@); fi && \
	$(CC) $(LD_FLAGS) -o $@ $(OBJS) $(LD_LIBS)

因为在命令的最开头echo前边加了@,并且三条命令用 \ 做了拼接,所以是一行的命令,因此$(CC) 的详细执行命令不会被真正的显示出来的。只会显示 LD  => build.p1010.r/bin/cdbctl 信息。如果去掉@ 能显示出详细的编译命令,如下:

通过这样的信息,非常有利于对Makefile做进一步的分析。 

加了VERBOSE=1为什么能打印出详细的命令信息?

是因为内核里加了如下的过度语句,没有直接使用@,而是用Q变量来代替是使用@还是不使用@。

ifeq ($(KBUILD_VERBOSE),1)
  quiet =
  Q =
else
  quiet=quiet_
  Q = @
endif

 

scripts_basic:
	$(Q)$(MAKE) $(build)=scripts/basic
	$(Q)rm -f .tmp_quiet_recordmcount

可以通过make VERBOSE=1 显示出编译是所执行的命令了,对于私有的Makefile也可以通过这样的方法做这样的改造。 

在openwrt里还可以V=s V=99,都是同样的道理。

第五个技巧:如何找到编译出的某个程序是在哪里链接进去的?

一个最为直接的方法就是,将编译出改目标的地方去掉例如:想知道libdrvs.a 的静态库是在哪里链接进去的?

该命令生成libdrvs.a的静态库,用#注释后就不再生成了。在编译会报错,报错的地方就是链接该静态库的地方。

第六个技巧:动态链接库于静态链接库

动态链接库 so:可以通过编译选项把程序编译为 so文件,例如  libcjson.so,动态链接库的编译不会被编译到使用程序中,而是在执行的时候动态的加载运行。 

静态链接库 a:也可以把程序编译为.a文件,例如libdrvs.a,在链接时将程序直接和应用程序链接在一起,形成一个程序。

-L/lib/libpath -lcjson //链接动态库 libcjson.so  /lib/libpath 为动态库所在的路径

-L/lib/libpath -ldrvs  //链接静态库 libdrvs.a  该库放在/lib/libpath路径下 

第七个技巧: 如何在编译时抓取编译的日志出来分析?

make V=s 2>&1 | tee build.log | grep -i error

make V=s 2>&1 | tee build.log 

 

  • 12
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
晶视(SemiDrive)是一个公司,他们开发了一种智能算法和芯片技术。根据引用[2],在《SemiDrive源码分析》一文中提到了关于SemiDrive的YOCTO内核编译过程分析。这表明晶视的内核编译与YOCTO项目有关。YOCTO是一个开源的嵌入式Linux构建系统,它可以用于为各种嵌入式设备构建自定义的Linux发行版。 然而,在提到晶视内核编译时,并没有提供具体的内容。所以,我无法提供有关晶视内核编译的详细信息。但是,一般来说,内核编译是指将Linux内核源代码编译成可执行的内核映像文件的过程。这个过程可能包括配置内核选项、编译内核代码、生成内核映像等步骤。为了编译内核,通常需要使用特定的工具链和构建系统,具体的步骤可能因系统而异。 如果您需要更详细的关于晶视内核编译的信息,建议您参考晶视官方文档或与晶视公司联系以获取准确的信息。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [高性能计算(HPC)和智能计算理解](https://blog.csdn.net/tugouxp/article/details/119054810)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [Yocto Kernel编译过程分析完整log](https://download.csdn.net/download/Ciellee/85306136)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值