Android源码编译详解 get_make_command

唉!最近忙新项目,周末把笔记整理了下忘了发出来了,今天继续分享下源码编译的第三阶段,make阶段,由于这个阶段的工作主要是编译工具进行的,像gcc这种其内部实现是相当复杂,个人功力有限,暂时没法深入去了解!但是我们可以从大概看下的过程,然后再去看看make,m,mm等命令的具体区别!!

注意:不了解第一第二个阶段的可以先看看下面这两篇文章

Android源码编译详解(一)

Android源码编译详解(二)

一、make函数

1、make函数

从envsetup.sh脚本中我们知道make函数被定义,这也是我们最终执行make操作的入口,那么我们看看它的具体实现:

 
  1. function make()

  2. {

  3. local start_time=$(date +"%s")

  4. $(get_make_command) "$@"

  5. local ret=$?

  6. local end_time=$(date +"%s")

  7. local tdiff=$(($end_time-$start_time))

  8. local hours=$(($tdiff / 3600 ))

  9. local mins=$((($tdiff % 3600) / 60))

  10. local secs=$(($tdiff % 60))

  11. local ncolors=$(tput colors 2>/dev/null)

  12. if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then

  13. color_failed="\e[0;31m"

  14. color_success="\e[0;32m"

  15. color_reset="\e[00m"

  16. else

  17. color_failed=""

  18. color_success=""

  19. color_reset=""

  20. fi

  21. echo

  22. if [ $ret -eq 0 ] ; then

  23. echo -n -e "${color_success}#### make completed successfully "

  24. else

  25. echo -n -e "${color_failed}#### make failed to build some targets "

  26. fi

  27. if [ $hours -gt 0 ] ; then

  28. printf "(%02g:%02g:%02g (hh:mm:ss))" $hours $mins $secs

  29. elif [ $mins -gt 0 ] ; then

  30. printf "(%02g:%02g (mm:ss))" $mins $secs

  31. elif [ $secs -gt 0 ] ; then

  32. printf "(%s seconds)" $secs

  33. fi

  34. echo -e " ####${color_reset}"

  35. echo

  36. return $ret

  37. }

这一堆脚本代码我们需要关注的其实只有$(get_make_command) "$@"这一句,而其它的只不过是获取下时间计算下编译所需时间而已,最后根据$?获取到返回的编译结果判断下,然后就提示是否编译成功等等!

那么我们看下$(get_make_command) "$@",其中$()这是个命令替换符,不认识也没关系,我们只需要知道他里面的get_make_command函数会被执行即可,那么按照惯例去看下get_make_command函数吧:

 
  1. function get_make_command()

  2. {

  3. echo command make

  4. }

呵呵,你没有看错,确实就这一句代码,其实make过程之所以不好理解,就是因为连代码都没得看。。。。不过没关系,既然make过程是从command make这句代码开始的,那么他就是突破点。不太了解shell可以查看下linux相关的知识,这里的command make的意思就是接下来会执行make这个命令,而这里的make并不是指我们前面所说的envsetup.sh中定义的make()函数,这个make是linux下的一个编译工具。

2、make工具

对于make编译工具,这里建议大家就不深入去看了,毕竟内部涉及的东西比较晦涩,但是我们需要知道的是make工具最基本的功能是调用makefile文件,通过makefile文件来描述源程序之间的相互依赖关系并自动维护编译工作,那么我们不妨先去看下源码根目录下的makefile文件:

 
  1. ### DO NOT EDIT THIS FILE ###

  2. include build/core/main.mk

  3. ### DO NOT EDIT THIS FILE ###

可以看到,加载了main.mk文件,其实到了这里,如果我们只是想编译个系统玩玩,那么我们也就没必要去深究main.mk到底都做了些什么,因为对mk语法不熟悉的话看起来也是一头雾水,有兴趣的朋友可以自行去查看相关的资料!

至此,我们的编译其实就已经开始了,但是我们平时使用最多的并非make这个全编指令,其实我们使用更多的是mm、mmm这样的指令,当然有的人也喜欢用m,所以我们很有必要去了解m、mm这些指令到底都做了些什么。

3、首先我们看看m

我们之前介绍过,m、mm、mmm这些函数都是在envsetup.sh中定义的,所以要了解他们的实现原理,还是需要去看源码,那么我们先看看m的源码:

 
  1. function m()

  2. {

  3. local T=$(gettop)

  4. local DRV=$(getdriver $T)

  5. if [ "$T" ]; then

  6. $DRV make -C $T -f build/core/main.mk $@

  7. else

  8. echo "Couldn't locate the top of the tree. Try setting TOP."

  9. return 1

  10. fi

  11. }

m函数其实并不复杂,他首先是利用gettop函数去获取源码根目录,其内部获取原理其实都是通过寻找build/core/envsetup.mk这个文件实现的,只要找到该文件,那么就表示当前目录是源码的根目录。至于getdriver函数,这个我们暂时不知道干什么用的,返回了一堆字符串,不过我看其他的源码有时候也没有这一句,暂时不管它吧!!

其实最主要的也就这一句而已:make -C $T -f build/core/main.mk $@,这里我们先对T进行判断,gettop如果返回源码根目录给我们的话就执行make命令,并且通过-C跳转到根目录,并且指定main.mk文件,如果有参数的话还会通过$@获取参数。其实m做的事情就是对make的一个简单的封装,并没有实际的功能,所以我们平时直接用make即可,没必要m!

4、接着看mm

mm这个就很重要了,记得刚开始编译android源码的时候,不管干了什么,只要动了源码,都是直接make,虽然说公司电脑比较好出来第一次之外后来make也就花十来二十分钟时间,但是二十分中也是时间啊!!!后来同事跟我说有些改动可以mm的,够尴尬的。一句话,mm其实就是模块编译,也就是只编译我们想要编译的模块(当然也有所限制)

惯例我们先看看源码:

 
  1. function mm()

  2. {

  3. local T=$(gettop)

  4. local DRV=$(getdriver $T)

  5. # If we're sitting in the root of the build tree, just do a

  6. # normal make.

  7. if [ -f build/core/envsetup.mk -a -f Makefile ]; then

  8. $DRV make $@

  9. else

  10. # Find the closest Android.mk file.

  11. local M=$(findmakefile)

  12. local MODULES=

  13. local GET_INSTALL_PATH=

  14. local ARGS=

  15. # Remove the path to top as the makefilepath needs to be relative

  16. local M=`echo $M|sed 's:'$T'/::'`

  17. if [ ! "$T" ]; then

  18. echo "Couldn't locate the top of the tree. Try setting TOP."

  19. return 1

  20. elif [ ! "$M" ]; then

  21. echo "Couldn't locate a makefile from the current directory."

  22. return 1

  23. else

  24. for ARG in $@; do

  25. case $ARG in

  26. GET-INSTALL-PATH) GET_INSTALL_PATH=$ARG;;

  27. esac

  28. done

  29. if [ -n "$GET_INSTALL_PATH" ]; then

  30. MODULES=

  31. ARGS=GET-INSTALL-PATH

  32. else

  33. MODULES=all_modules

  34. ARGS=$@

  35. fi

  36. ONE_SHOT_MAKEFILE=$M $DRV make -C $T -f build/core/main.mk $MODULES $ARGS

  37. fi

  38. fi

  39. }

if [ -f build/core/envsetup.mk -a -f Makefile ]

我们看这个判断,先判断build/core/envsetup.mk以及当前目录下的Makefile文件是否合法,这里的-f表示常规文件判断,-a其实是and(与)的意思,当这个添加成立时,认为处于源码根目录,直接执行make,这跟正常的全编一致。

local M=$(findmakefile)

如果上面的判断不成立,那么就会走到else中,执行findmakefile函数:

 
  1. function findmakefile()

  2. {

  3. TOPFILE=build/core/envsetup.mk

  4. local HERE=$PWD

  5. T=

  6. while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do

  7. T=`PWD= /bin/pwd`

  8. if [ -f "$T/Android.mk" ]; then

  9. echo $T/Android.mk

  10. \cd $HERE

  11. return

  12. fi

  13. \cd ..

  14. done

  15. \cd $HERE

  16. }

顾名思义,这个函数是用于查找makefile文件的,在findmakefile首先HERE保存了当前目录的路径,软后通过 !\(−f$TOPFILE

\) -a $PWD!="/"

 判断,如果不处于系统根目录以及编译跟目录的话就查找Android.mk文件,找到了就返回绝对路径,找不到就cd ..到上一级继续找,直到条件不满足为止,不管最后有没有找到,都会返回到执行findmakefile()函数的目录,也就是HERE。

好了,看完findmakefile函数我们回到mm函数中。

local M=`echo $M|sed 's:'$T'/::'`

这里我根据findmakefile()函数的实现得知,如果找到了Android.mk文件,返回的是绝对路径,所以$M是以文件系统跟目录开始的,而这里就是将M转换成以源码跟目录为标准的相对路径。

if[!"$T"]、if[!"$M"]

紧接着这两个判断是判断是否找到了源码跟目录,以及是否找到了Android.mk文件,如果两样都找不到,那么就打印错误提示,并退出编译程序。

for ARG in $@;

接下来通过这个循环处理所有参数,并保存到GET_INSTALL_PATH中,其实最后都会保存到ARGS中,然后通过判断GET_INSTALL_PATH是否为空,如果不为空就就赋值给ARGS,当然平时我们执行mm命令的时候,多数是不会带参数的,所以这里我们先假设会走到另一个判断中,导致MODULES=all_modules,也就是编译所有当前目录下所有模块。

ONE_SHOT_MAKEFILE=$M $DRV make -C $T -f build/core/main.mk $MODULES $ARGS

最后,准备好一切之后就开始执行make命令了!此时我们需要特别关注的是ONE_SHOT_MAKEFILE,它保存了$M,也就是上面找到的Android.mk文件,紧接着make会通过-C $T跳到源码跟目录中进行,同时加载main.mk文件,如果你去耐心的看main.mk文件的话会发现在其中会加载ONE_SHOT_MAKEFILE中保存的Android.mk文件的。

好了,到这里mm的过程粗略的介绍了下,也比较喜欢是用mm,至于mmm,只不过是指定了编译路径而已,并没做什么很特殊的事情,如果有朋友想了解的话可自己肯下脚本,其实耐心点的话还是可以理清楚的!

 

二、总结

到这篇文章为止,我们理论上已经能够正常的去编译一个android系统的源码了,包括整个流程中的几个步骤也有了大致了解,但是这里仅仅是编译而已,并没有涉及到定制!比如说我们如何针对某个版子定制一个系统?驱动文件该如何放置?需要修改哪些配置文件?如何重新编译内核?这一系列的工作虽然难度不是很大,但是都是非常繁琐的,一个不小心很有可能你就把系统搞挂了。。。。不过没关系,做事情总得一步步来。后续会继续把笔记分享出来,大家一起学习共同进步!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值