yocto 编译流程分析

git clone 一份poky 的工程到本地。

source poky/oe-init-build-env your_build_path

看下 oe-init-build-env 这个shell 脚本都干了些什么:

[plain]  view plain copy
  1. if [ -z "$ZSH_NAME" ] && [ "x$0" = "x./oe-init-build-env" ]; then  
  2.    echo "Error: This script needs to be sourced. Please run as '. ./oe-init-build-env'"  
  3. else  
  4.    if [ -n "$BASH_SOURCE" ]; then  
  5.       OEROOT="`dirname $BASH_SOURCE`"  
  6.    elif [ -n "$ZSH_NAME" ]; then  
  7.       OEROOT="`dirname $0`"  
  8.    else  
  9.       OEROOT="`pwd`"  
  10.    fi  
  11.    OEROOT=`readlink -f "$OEROOT"`  
  12.    export OEROOT  
  13.    . $OEROOT/scripts/oe-buildenv-internal && \  
  14.         $OEROOT/scripts/oe-setup-builddir && \  
  15.         [ -n "$BUILDDIR" ] && cd $BUILDDIR  
  16.    unset OEROOT  
  17.    unset BBPATH  
  18. fi  

转载: OSChina 上一个博友分析的这段shell 脚本:

http://my.oschina.net/u/158589/blog/70921


第一步,判断该脚本是否是用source或者.的方法运行的。 但是

1 ["x$0"="x./oe-init-build-env"]
只能对./oe-init-build-env这种执行方式报错,对./yocto/oe-init-build-env是不会报错的。


第二步,设置OEROOT这个变量。当用.或者source去执行脚本时,BASH_SOURCE这个变量会被自动设置到源文件路径。于是dirname $BASH_SOURCE就获得了脚本所在目录。readlink -f $OEROOT获得了绝对路径。

 

第三步,执行oe-buildenv-internal和oe-setup-builddir这两个脚本,并且进入到build目录。

(注意这里的oe-buildenv-internal是用.执行的,而oe-setup-build是fork shell执行的。因为oe-setup-builddir中用到了OEROOT这个变量,所以在此之前,OEROOT必须被export,正如脚本中所做的那样。)

 

第四步,unset一些变量。因为.或者source的执行方式是在原shell中执行,并不fork shell,所以如果不unset,会一直留在该shell中。

 

chenqi@chenqi-laptop ~/MyPro/ShellScript/yocto $ . ./oe-init-build-env
BASH_SOURCE = ./oe-init-build-env, OEROOT = .
OEROOT = /home/chenqi/MyPro/ShellScript/yocto

 

chenqi@chenqi-laptop ~/MyPro/ShellScript $ source yocto/oe-init-build-env
BASH_SOURCE = yocto/oe-init-build-env, OEROOT = yocto
OEROOT = /home/chenqi/MyPro/ShellScript/yocto

 

可见,无论在哪个目录下执行,最后获得的OEROOT的绝对路径都是一致的。(主要利用BASH_SOURCE, dirname, readlink)。


[plain]  view plain copy
  1. . $OEROOT/scripts/oe-buildenv-internal  

oe-buildenv-internal 这个shell 脚本主要是设置环境变量(poky/bitbake/bin/ 和 poky/scripts 这两个路径加入到PATH中。bitbake 命令就在poky/bitbake/bin/路径下面。),如下:

[plain]  view plain copy
  1. PATH="${OEROOT}/scripts:$BITBAKEDIR/bin/:$PATH"  
  2. unset BITBAKEDIR  
  3.   
  4. # Used by the runqemu script  
  5. export BUILDDIR  
  6. export PATH  
  7. export BB_ENV_EXTRAWHITE="MACHINE DISTRO TCMODE TCLIBC HTTP_PROXY http_proxy \  
  8. HTTPS_PROXY https_proxy FTP_PROXY ftp_proxy FTPS_PROXY ftps_proxy ALL_PROXY \  
  9. all_proxy NO_PROXY no_proxy SSH_AGENT_PID SSH_AUTH_SOCK BB_SRCREV_POLICY \  
  10. SDKMACHINE BB_NUMBER_THREADS BB_NO_NETWORK PARALLEL_MAKE GIT_PROXY_COMMAND \  
  11. SOCKS5_PASSWD SOCKS5_USER SCREENDIR STAMPS_DIR"  

[plain]  view plain copy
  1. $OEROOT/scripts/oe-setup-builddir  

oe-setup-builddir 这个shell 脚本,创建编译目录,判断当前编译目录下面是否存在conf/local.conf 文件,如果不存在local.conf 配置文件的话,通过Template模板,sample 生成一个local.conf。

下面这段shell 脚本的意思是首先检查 meta-yocto 目录下面是否存在conf/ 如果存在的话就用meta-yocto/conf/  下面的 local.conf.sample 和 bblayers.conf, 如果不存在的话就到meta/conf 下面去找 local.conf.sample 和 bblayer.conf。


[plain]  view plain copy
  1. TEMPLATECONF=${TEMPLATECONF:-meta-yocto/conf}  
  2.   
  3. #   
  4. # $TEMPLATECONF can point to a directory for the template local.conf & bblayers.conf  
  5. #  
  6. if [ "x" != "x$TEMPLATECONF" ]; then  
  7.     if ! (test -d "$TEMPLATECONF"); then  
  8.         # Allow TEMPLATECONF=meta-xyz/conf as a shortcut  
  9.         if [ -d "$OEROOT/$TEMPLATECONF" ]; then  
  10.             TEMPLATECONF="$OEROOT/$TEMPLATECONF"  
  11.         fi  
  12.         if ! (test -d "$TEMPLATECONF"); then  
  13.             echo >&2 "Error: '$TEMPLATECONF' must be a directory containing local.conf & bblayers.conf"  
  14.             return  
  15.         fi  
  16.     fi  
  17.     OECORELAYERCONF="$TEMPLATECONF/bblayers.conf.sample"  
  18.     OECORELOCALCONF="$TEMPLATECONF/local.conf.sample"  
  19.     OECORENOTESCONF="$TEMPLATECONF/conf-notes.txt"  
  20. fi  
  21.   
  22. if [ "x" = "x$OECORELOCALCONF" ]; then  
  23.     OECORELOCALCONF="$OEROOT/meta/conf/local.conf.sample"  
  24. fi  
  25. if ! (test -r "$BUILDDIR/conf/local.conf"); then  
  26. cat <
  27. You had no conf/local.conf file. This configuration file has therefore been  
  28. created for you with some default values. You may wish to edit it to use a   
  29. different MACHINE (target hardware) or enable parallel build options to take   
  30. advantage of multiple cores for example. See the file for more information as   
  31. common configuration options are commented.  
  32.   
  33. The Yocto Project has extensive documentation about OE including a reference manual  
  34. which can be found at:  
  35.     http://yoctoproject.org/documentation  
  36.   
  37. For more information about OpenEmbedded see their website:  
  38.     http://www.openembedded.org/  
  39.   
  40. EOM  
  41.     cp -f $OECORELOCALCONF $BUILDDIR/conf/local.conf  
  42. fi  
  43. if [ "x" = "x$OECORELAYERCONF" ]; then  
  44.     OECORELAYERCONF="$OEROOT/meta/conf/bblayers.conf.sample"  
  45. fi  
  46. if ! (test -r "$BUILDDIR/conf/bblayers.conf"); then  
  47. cat <
  48. You had no conf/bblayers.conf file. The configuration file has been created for  
  49. you with some default values. To add additional metadata layers into your  
  50. configuration please add entries to this file.  
  51.   
  52. The Yocto Project has extensive documentation about OE including a reference manual  
  53. which can be found at:  
  54.     http://yoctoproject.org/documentation  
  55.   
  56. For more information about OpenEmbedded see their website:  
  57.     http://www.openembedded.org/  
  58.   
  59.   
  60. EOM  
  61.   
  62.     # Put the abosolute path to the layers in bblayers.conf so we can run  
  63.     # bitbake without the init script after the first run  
  64.     sed "s|##COREBASE##|$OEROOT|g" $OECORELAYERCONF > $BUILDDIR/conf/bblayers.conf  
  65. fi  

至此, build_path 下面的conf/local.conf 以及 bblayer.conf 文件就 创建好了,接下来就可以执行 bitbake target 编译了。



----------------------------------------------------------------  华丽丽的分割线 ---------------------------------------------------------


现在开始分析bitbake 编译流程:

bitbake --help 可以看到bitbake 后面可以跟的选项参数:

[plain]  view plain copy
  1. yocto_build$ bitbake --help  
  2. Usage: bitbake [options] [package ...]  
  3.   
  4. Executes the specified task (default is 'build') for a given set of BitBake files.  
  5. It expects that BBFILES is defined, which is a space separated list of files to  
  6. be executed.  BBFILES does support wildcards.  
  7. Default BBFILES are the .bb files in the current directory.  
  8.   
  9. Options:  
  10.   --version             show program's version number and exit  
  11.   -h, --help            show this help message and exit  
  12.   -b BUILDFILE, --buildfile=BUILDFILE  
  13.                         execute the task against this .bb file, rather than a  
  14.                         package from BBFILES. Does not handle any  
  15.                         dependencies.  
  16.   -k, --continue        continue as much as possible after an error. While the  
  17.                         target that failed, and those that depend on it,  
  18.                         cannot be remade, the other dependencies of these  
  19.                         targets can be processed all the same.  
  20.   -a, --tryaltconfigs   continue with builds by trying to use alternative  
  21.                         providers where possible.  
  22.   -f, --force           force run of specified cmd, regardless of stamp status  
  23.   -c CMD, --cmd=CMD     Specify task to execute. Note that this only executes  
  24.                         the specified task for the providee and the packages  
  25.                         it depends on, i.e. 'compile' does not implicitly call  
  26.                         stage for the dependencies (IOW: use only if you know  
  27.                         what you are doing). Depending on the base.bbclass a  
  28.                         listtasks tasks is defined and will show available  
  29.                         tasks  
  30.   -C INVALIDATE_STAMP, --clear-stamp=INVALIDATE_STAMP  
  31.                         Invalidate the stamp for the specified cmd such as  
  32.                         'compile' and run the default task for the specified  
  33.                         target(s)  
  34.   -r PREFILE, --read=PREFILE  
  35.                         read the specified file before bitbake.conf  
  36.   -R POSTFILE, --postread=POSTFILE  
  37.                         read the specified file after bitbake.conf  
  38.   -v, --verbose         output more chit-chat to the terminal  
  39.   -D, --debug           Increase the debug level. You can specify this more  
  40.                         than once.  
  41.   -n, --dry-run         don't execute, just go through the motions  
  42.   -S, --dump-signatures  
  43.                         don't execute, just dump out the signature  
  44.                         construction information  
  45.   -p, --parse-only      quit after parsing the BB files (developers only)  
  46.   -s, --show-versions   show current and preferred versions of all recipes  
  47.   -e, --environment     show the global or per-package environment (this is  
  48.                         what used to be bbread)  
  49.   -g, --graphviz        emit the dependency trees of the specified packages in  
  50.                         the dot syntax, and the pn-buildlist to show the build  
  51.                         list  
  52.   -I EXTRA_ASSUME_PROVIDED, --ignore-deps=EXTRA_ASSUME_PROVIDED  
  53.                         Assume these dependencies don't exist and are already  
  54.                         provided (equivalent to ASSUME_PROVIDED). Useful to  
  55.                         make dependency graphs more appealing  
  56.   -l DEBUG_DOMAINS, --log-domains=DEBUG_DOMAINS  
  57.                         Show debug logging for the specified logging domains  
  58.   -P, --profile         profile the command and print a report  
  59.   -u UI, --ui=UI        userinterface to use  
  60.   -t SERVERTYPE, --servertype=SERVERTYPE  
  61.                         Choose which server to use, none, process or xmlrpc  
  62.   --revisions-changed   Set the exit code depending on whether upstream  
  63.                         floating revisions have changed or not  
  64.   --server-only         Run bitbake without UI,  the frontend can connect with  
  65.                         bitbake server itself  
  66.   -B BIND, --bind=BIND  The name/address for the bitbake server to bind to  
  67.   --no-setscene         Do not run any setscene tasks, forces builds  

那么,bitbake 对于这些个选项参数是怎么处理的呢。 bitbake target -xxx -xxxx -xx  , 这样其实就是执行poky/bitbake/bitbake 这个python 脚本:

这个python 脚本相对简单,可以看下最后的这段代码:

通过下面这个 if 判断,调用 main 函数执行,使用 python 库中的

[python]  view plain copy
  1. optparse.OptionParser  
对选项参数进行处理。


[python]  view plain copy
  1. if __name__ == "__main__":  
  2.     try:  
  3.         ret = main()  

[python]  view plain copy
  1. #!/usr/bin/env python  
  2. # ex:ts=4:sw=4:sts=4:et  
  3. # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-  
  4.   
  5. import os  
  6. import sys, logging  
  7. sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(__file__)),  
  8.                                 'lib'))  
  9.   
  10. import optparse  
  11. import warnings  
  12. from traceback import format_exception  
  13. try:  
  14.     import bb  
  15. except RuntimeError as exc:  
  16.     sys.exit(str(exc))  
  17. from bb import event  
  18. import bb.msg  
  19. from bb import cooker  
  20. from bb import ui  
  21. from bb import server  
  22.   
  23. __version__ = "1.17.1"  
  24. logger = logging.getLogger("BitBake")  
  25.   
  26. # Unbuffer stdout to avoid log truncation in the event  
  27. # of an unorderly exit as well as to provide timely  
  28. # updates to log files for use with tail  
  29. try:  
  30.     if sys.stdout.name == '':  
  31.         sys.stdout = os.fdopen(sys.stdout.fileno(), 'w'0)  
  32. except:  
  33.     pass  
  34.   
  35. class BBConfiguration(object):  
  36.     """ 
  37.     Manages build options and configurations for one run 
  38.     """  
  39.   
  40.     def __init__(self, options):  
  41.         for key, val in options.__dict__.items():  
  42.             setattr(self, key, val)  
  43.         self.pkgs_to_build = []  
  44.   
  45. *** ***  
  46.   
  47. def main():  
  48.     parser = optparse.OptionParser(  
  49.         version = "BitBake Build Tool Core version %s, %%prog version %s" % (bb.__version__, __version__),  
  50.         usage = """%prog [options] [package ...] 
  51.  
  52. Executes the specified task (default is 'build') for a given set of BitBake files. 
  53. It expects that BBFILES is defined, which is a space separated list of files to 
  54. be executed.  BBFILES does support wildcards. 
  55. Default BBFILES are the .bb files in the current directory.""")  
  56.   
  57.     parser.add_option("-b""--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES. Does not handle any dependencies.",  
  58.                action = "store", dest = "buildfile", default = None)  
  59.   
  60.     parser.add_option("-k""--continue", help = "continue as much as possible after an error. While the target that failed, and those that depend on it, cannot be remade, the other dependencies of these targets can be processed all the same.",  
  61.                action = "store_false", dest = "abort", default = True)  
  62.   
  63.     parser.add_option("-a""--tryaltconfigs", help = "continue with builds by trying to use alternative providers where possible.",  
  64.                action = "store_true", dest = "tryaltconfigs", default = False)  
  65.   
  66.     parser.add_option("-f""--force", help = "force run of specified cmd, regardless of stamp status",  
  67.                action = "store_true", dest = "force", default = False)  
  68.   
  69.     parser.add_option("-c""--cmd", help = "Specify task to execute. Note that this only executes the specified task for the providee and the packages it depends on, i.e. 'compile' does not implicitly call stage for the dependencies (IOW: use only if you know what you are doing). Depending on the base.bbclass a listtasks tasks is defined and will show available tasks",  
  70.                action = "store", dest = "cmd")  
  71.   
  72.     parser.add_option("-C""--clear-stamp", help = "Invalidate the stamp for the specified cmd such as 'compile' and run the default task for the specified target(s)",  
  73.                 action = "store", dest = "invalidate_stamp")  
  74.   
  75.     parser.add_option("-r""--read", help = "read the specified file before bitbake.conf",  
  76.                action = "append", dest = "prefile", default = [])  
  77.   
  78.     parser.add_option("-R""--postread", help = "read the specified file after bitbake.conf",  
  79.                       action = "append", dest = "postfile", default = [])  
  80.   
  81.     parser.add_option("-v""--verbose", help = "output more chit-chat to the terminal",  
  82.                action = "store_true", dest = "verbose", default = False)  
  83.   
  84.     parser.add_option("-D""--debug", help = "Increase the debug level. You can specify this more than once.",  
  85.                action = "count", dest="debug", default = 0)  
  86.   
  87.     parser.add_option("-n""--dry-run", help = "don't execute, just go through the motions",  
  88.                action = "store_true", dest = "dry_run", default = False)  
  89.   
  90.     parser.add_option("-S""--dump-signatures", help = "don't execute, just dump out the signature construction information",  
  91.                action = "store_true", dest = "dump_signatures", default = False)  
  92.   
  93.     parser.add_option("-p""--parse-only", help = "quit after parsing the BB files (developers only)",  
  94.                action = "store_true", dest = "parse_only", default = False)  
  95.   
  96.     parser.add_option("-s""--show-versions", help = "show current and preferred versions of all recipes",  
  97.                action = "store_true", dest = "show_versions", default = False)  
  98.   
  99.     parser.add_option("-e""--environment", help = "show the global or per-package environment (this is what used to be bbread)",  
  100.                action = "store_true", dest = "show_environment", default = False)  
  101.   
  102.     parser.add_option("-g""--graphviz", help = "emit the dependency trees of the specified packages in the dot syntax, and the pn-buildlist to show the build list",  
  103.                 action = "store_true", dest = "dot_graph", default = False)  
  104.   
  105.     parser.add_option("-I""--ignore-deps", help = """Assume these dependencies don't exist and are already provided (equivalent to ASSUME_PROVIDED). Useful to make dependency graphs more appealing""",  
  106.                 action = "append", dest = "extra_assume_provided", default = [])  
  107.   
  108.     parser.add_option("-l""--log-domains", help = """Show debug logging for the specified logging domains""",  
  109.                 action = "append", dest = "debug_domains", default = [])  
  110.   
  111.     parser.add_option("-P""--profile", help = "profile the command and print a report",  
  112.                action = "store_true", dest = "profile", default = False)  
  113.   
  114.     parser.add_option("-u""--ui", help = "userinterface to use",  
  115.                action = "store", dest = "ui")  
  116.   
  117.     parser.add_option("-t""--servertype", help = "Choose which server to use, none, process or xmlrpc",  
  118.                action = "store", dest = "servertype")  
  119.   
  120.     parser.add_option("", "--revisions-changed", help = "Set the exit code depending on whether upstream floating revisions have changed or not",  
  121.                action = "store_true", dest = "revisions_changed", default = False)  
  122.   
  123.     parser.add_option("", "--server-only", help = "Run bitbake without UI,  the frontend can connect with bitbake server itself",  
  124.                action = "store_true", dest = "server_only", default = False)  
  125.   
  126.     parser.add_option("-B""--bind", help = "The name/address for the bitbake server to bind to",  
  127.                action = "store", dest = "bind", default = False)  
  128.     parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks, forces builds",  
  129.                action = "store_true", dest = "nosetscene", default = False)  
  130.     options, args = parser.parse_args(sys.argv)  
  131.       
  132.     print "+++++ qc test +++++ options:", options  
  133.     print "+++++ qc test +++++ args:", args  
  134.   
  135.     configuration = BBConfiguration(options)  
  136.     configuration.pkgs_to_build.extend(args[1:])  
  137.   
  138.     print "+++++ qc test +++++ pkgs_to_build:",configuration.pkgs_to_build  
  139.       
  140.     *** ***  
  141.      
  142.     # Ensure logging messages get sent to the UI as events  
  143.     handler = bb.event.LogHandler()  
  144.     logger.addHandler(handler)  
  145.   
  146.     # Before we start modifying the environment we should take a pristine  
  147.     # copy for possible later use  
  148.     initialenv = os.environ.copy()  
  149.     # Clear away any spurious environment variables while we stoke up the cooker  
  150.     cleanedvars = bb.utils.clean_environment()  
  151.   
  152.     server = server.BitBakeServer()  
  153.     if configuration.bind:  
  154.         server.initServer((configuration.bind, 0))  
  155.     else:  
  156.         server.initServer()  
  157.   
  158.     idle = server.getServerIdleCB()  
  159.   
  160.     cooker = bb.cooker.BBCooker(configuration, idle, initialenv)  
  161.     cooker.parseCommandLine()  
  162.   
  163.     server.addcooker(cooker)  
  164.     server.saveConnectionDetails()  
  165.     server.detach()  
  166.   
  167.     # Should no longer need to ever reference cooker  
  168.     del cooker  
  169.   
  170.     logger.removeHandler(handler)  
  171.   
  172.     if not configuration.server_only:  
  173.         # Setup a connection to the server (cooker)  
  174.         server_connection = server.establishConnection()  
  175.   
  176.         # Restore the environment in case the UI needs it  
  177.         for k in cleanedvars:  
  178.             os.environ[k] = cleanedvars[k]  
  179.   
  180.         try:  
  181.             return server.launchUI(ui_main, server_connection.connection, server_connection.events)  
  182.         finally:  
  183.             bb.event.ui_queue = []  
  184.             server_connection.terminate()  
  185.     else:  
  186.         print("server address: %s, server port: %s" % (server.serverinfo.host, server.serverinfo.port))  
  187.   
  188.     return 1  
  189.   
  190. if __name__ == "__main__":  
  191.     try:  
  192.         ret = main()  
  193.     except Exception:  
  194.         ret = 1  
  195.         import traceback  
  196.         traceback.print_exc(5)  
  197.     sys.exit(ret)  

 这里调用到了 parseConfigurationFiles() 函数,这个函数用于解析 prefiles, postfiles (这两个变量都是空,需要通过执行bitbake -r -R 指定才行),解析 layer.conf, bblayer.conf, bitbake.conf, 以及 bb 文件中 inherit 的 bbclass,以及包含base.bbclass。

[python]  view plain copy
  1. def parseConfigurationFiles(self, prefiles, postfiles):  
  2.      data = self.configuration.data  
  3.      bb.parse.init_parser(data)  
  4.   
  5.      # Parse files for loading *before* bitbake.conf and any includes  
  6.      for f in prefiles:  
  7.          data = _parse(f, data)  
  8.   
  9.      layerconf = self._findLayerConf()  
  10.      if layerconf:  
  11.          parselog.debug(2"Found bblayers.conf (%s)", layerconf)  
  12.          data = _parse(layerconf, data)  
  13.   
  14.          layers = (data.getVar('BBLAYERS'Trueor "").split()  
  15.   
  16.          data = bb.data.createCopy(data)  
  17.          for layer in layers:  
  18.              parselog.debug(2"Adding layer %s", layer)  
  19.              data.setVar('LAYERDIR', layer)  
  20.              data = _parse(os.path.join(layer, "conf""layer.conf"), data)  
  21.              data.expandVarref('LAYERDIR')  
  22.   
  23.          data.delVar('LAYERDIR')  
  24.   
  25.      if not data.getVar("BBPATH"True):  
  26.          raise SystemExit("The BBPATH variable is not set")  
  27.   
  28.      data = _parse(os.path.join("conf""bitbake.conf"), data)  
  29.   
  30.      # Parse files for loading *after* bitbake.conf and any includes  
  31.      for p in postfiles:  
  32.          data = _parse(p, data)  
  33.   
  34.      # Handle any INHERITs and inherit the base class  
  35.      bbclasses  = ["base"] + (data.getVar('INHERIT'Trueor "").split()  
  36.      for bbclass in bbclasses:  
  37.          data = _inherit(bbclass, data)  
  38.   
  39.      # Nomally we only register event handlers at the end of parsing .bb files  
  40.      # We register any handlers we've found so far here...  
  41.      for var in data.getVar('__BBHANDLERS'or []:  
  42.          bb.event.register(var, data.getVar(var))  
  43.   
  44.      if data.getVar("BB_WORKERCONTEXT"Falseis None:  
  45.          bb.fetch.fetcher_init(data)  
  46.      bb.codeparser.parser_cache_init(data)  
  47.      bb.event.fire(bb.event.ConfigParsed(), data)  
  48.   
  49.      if data.getVar("BB_INVALIDCONF"is True:  
  50.          data.setVar("BB_INVALIDCONF"False)  
  51.          self.parseConfigurationFiles(self.configuration.prefile,  
  52.                                       self.configuration.postfile)  
  53.      else:  
  54.          bb.parse.init_parser(data)  
  55.          data.setVar('BBINCLUDED',bb.parse.get_file_depends(data))  
  56.          self.configuration.data = data  
  57.          self.configuration.data_hash = data.get_hash()  

将所有的conf, bb, bbclass, etc 文件解析完毕之后,处理一下这些数据,提取出task list ,之后就是执行这些task 了。

下面以linux-yocto 为例看下如何找到这些task 的先后关系:

yocto_build/tmp/work/qemuppc-poky-linux/linux-yocto/3.4.36+gitAUTOINC+80b4b5110dca5184b57e85f1e775fb006a2e85ad_ddbc382cbc45a009e9ce17f7d448fcfd050ab5fc-r4.3/temp/log.task_order

这个文件显示了编译 linux-yocto 执行的task 以及 task 的执行顺序。

do_fetch (24333): log.do_fetch.24333
do_unpack (24359): log.do_unpack.24359
do_kernel_checkout (24371): log.do_kernel_checkout.24371
do_validate_branches (24399): log.do_validate_branches.24399
do_patch (24438): log.do_patch.24438
do_populate_lic (7751): log.do_populate_lic.7751
do_kernel_configme (7750): log.do_kernel_configme.7750
do_configure (18091): log.do_configure.18091
do_kernel_configcheck (18191): log.do_kernel_configcheck.18191
do_compile (23327): log.do_compile.23327
do_compile_kernelmodules (11394): log.do_compile_kernelmodules.11394
do_uboot_mkimage (11396): log.do_uboot_mkimage.11396
do_kernel_link_vmlinux (11397): log.do_kernel_link_vmlinux.11397
do_sizecheck (11395): log.do_sizecheck.11395
do_install (24128): log.do_install.24128
do_package (13631): log.do_package.13631
do_deploy (13632): log.do_deploy.13632
do_populate_sysroot (13633): log.do_populate_sysroot.13633
do_packagedata (16431): log.do_packagedata.16431
do_package_write_rpm (16452): log.do_package_write_rpm.16452
do_listtasks (7391): log.do_listtasks.7391

接下来介绍如何验证这些task 以及顺序:

poky/meta/recipes-kernel/linux/linux-yocto_3.4.bb

require recipes-kernel/linux/linux-yocto.inc

KBRANCH_DEFAULT = "standard/base"
KBRANCH = "${KBRANCH_DEFAULT}"

SRCREV_machine_qemuarm ?= "7cc80532306889b75619f8a1b713048e25f59e19"
SRCREV_machine_qemumips  ?= "debce6677988e03b50c369aba5861d4f9b2e557d"
SRCREV_machine_qemuppc ?= "ddbc382cbc45a009e9ce17f7d448fcfd050ab5fc"
SRCREV_machine_qemux86 ?= "c994390cfa28339cbc1ec3b56eeec83a5fa75bb7"
SRCREV_machine_qemux86-64 ?= "c994390cfa28339cbc1ec3b56eeec83a5fa75bb7"
SRCREV_machine ?= "c994390cfa28339cbc1ec3b56eeec83a5fa75bb7"
SRCREV_meta ?= "80b4b5110dca5184b57e85f1e775fb006a2e85ad"

SRC_URI = "git://git.yoctoproject.org/linux-yocto-3.4.git;protocol=git;bareclone=1;branch=${KBRANCH},${KMETA};name=machine,meta"

LINUX_VERSION ?= "3.4.36"

PR = "${INC_PR}.3"
PV = "${LINUX_VERSION}+git${SRCPV}"

KMETA = "meta"

COMPATIBLE_MACHINE = "qemuarm|qemux86|qemuppc|qemumips|qemux86-64"

# Functionality flags
KERNEL_FEATURES_append = " features/netfilter/netfilter.scc"
KERNEL_FEATURES_append_qemux86=" cfg/sound.scc"
KERNEL_FEATURES_append_qemux86-64=" cfg/sound.scc"
KERNEL_FEATURES_append_qemux86=" cfg/paravirt_kvm.scc"
KERNEL_FEATURES_append = " ${@bb.utils.contains("TUNE_FEATURES", "mx32", " cfg/x32.scc", "" ,d)}"


poky/meta/recipes-kernel/linux/linux-yocto.inc


DESCRIPTION = "Yocto Kernel"
SECTION = "kernel"
LICENSE = "GPLv2"

LIC_FILES_CHKSUM = "file://COPYING;md5=d7810fab7487fb0aad327b76f1be7cd7"

INC_PR = "r4"

# A KMACHINE is the mapping of a yocto $MACHINE to what is built
# by the kernel. This is typically the branch that should be built,
# and it can be specific to the machine or shared
# KMACHINE = "UNDEFINED"

LINUX_KERNEL_TYPE ?= "standard"

# KMETA ?= ""
KBRANCH ?= "master"
KMACHINE ?= "${MACHINE}"
SRCREV_FORMAT ?= "meta_machine"

LINUX_VERSION_EXTENSION ?= "-yocto-${LINUX_KERNEL_TYPE}"

do_patch[depends] = "kern-tools-native:do_populate_sysroot"

addtask kernel_configme before do_configure after do_patch

# Pick up shared functions
inherit kernel
inherit kernel-yocto
require linux-dtb.inc


B = "${WORKDIR}/linux-${MACHINE}-${LINUX_KERNEL_TYPE}-build"

do_install_append(){
        if [ -n "${KMETA}" ]; then
                rm -rf ${STAGING_KERNEL_DIR}/${KMETA}
        fi
}

# extra tasks
addtask kernel_link_vmlinux after do_compile before do_install
addtask validate_branches before do_patch after do_kernel_checkout
addtask kernel_configcheck after do_configure before do_compile


poky/meta/classes/base.bbclass

poky/meta/classes/kernel.bbclass

poky/meta/classes/kernel-yocto.bbclass

这是 linux-yocto 所有相关的bb, bbclass 文件,所有的task 都是通过addtasks 关键字添加的,它们之间先后关系构成了所谓的依赖关系,第一个task,在这几个bb, bbclass 文件里面肯定没有 addtask before the_1st_task 这样的语句。

poky/meta/classes$ grep -nr "addtask . | grep "patch"

就按照上面这样的方法不断的grep 就能验证出log.task_order 里面显示的task,以及 正确的执行顺序。

ex. $ bitbake linux-yocto -c cleanall 执行 cleanall task 需要运行的tasklist:

poky/meta/classes$ grep -nr "addtask" . | grep "clean"
./base.bbclass:636:addtask cleansstate after do_clean
./base.bbclass:637:addtask qc_test after do_cleansstate
./base.bbclass:648:addtask cleanall after do_cleansstate
./utility-tasks.bbclass:16:addtask clean

其中,qc_test task 是我自己加的测试 task:

addtask cleansstate after do_clean
addtask qc_test after do_cleansstate

do_qc_test() {
        echo "qc hello base.bbclass !"
        echo "qc   test !!!!!~~~~ "
}

这样,构建起来的tasklist 如下:

clean clean
cleansstate cleansstate
cleanall qc_test

如此,可以看出,执行 cleanall task 的话,clean, cleansstate task 都要执行,因为具有依赖关系,相反,qc_test 和 cleanall 虽然都是 after do_cleansstate ,但是二者之间没有依赖关系。其它的task 的 runqueue list 也是这样得到。


bitbake 如果不刻意指定要执行的task 的话,默认执行的是build 操作,而这个操作是针对一系列的bb 文件,这些文件是在BBFILES定义的。看一下BBFILES 这个变量的来历,方法如下

/poky/bitbake$ grep -nr "BBFILES" .

直接在bitbake 目录下面搜索有谁对BBFILES 这个变量进行了赋值操作,这样就能定位到./lib/bb/cooker.py

反向推理一下:

搜集bbfiles 函数:

[plain]  view plain copy
  1. def collect_bbfiles( self ):  
  2.     """Collect all available .bb build files"""  
  3.     parsed, cached, skipped, masked = 0, 0, 0, 0  
  4.   
  5.     collectlog.debug(1, "collecting .bb files")  
  6.   
  7.     files = (data.getVar( "BBFILES", self.configuration.data, True) or "").split()  
  8.     data.setVar("BBFILES", " ".join(files), self.configuration.data)  
  9.   
  10.     # Sort files by priority  
  11.     files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem) )  

[plain]  view plain copy
  1.     def matchFile(self, buildfile):  
  2.         """  
  3.         Find the .bb file which matches the expression in 'buildfile'.  
  4.         Raise an error if multiple files  
  5.         """  
  6.         matches = self.matchFiles(buildfile)  

[plain]  view plain copy
  1.    def buildFile(self, buildfile, task):  
  2.         """  
  3.         Build the file matching regexp buildfile  
  4.         """  
  5.   
  6.         # Too many people use -b because they think it's how you normally  
  7.         # specify a target to be built, so show a warning  
  8.         bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.")  
  9.   
  10.         # Parse the configuration here. We need to do it explicitly here since  
  11.         # buildFile() doesn't use the cache  
  12.         self.parseConfiguration()  
  13.   
  14.         # If we are told to do the None task then query the default task  
  15.         if (task == None):  
  16.             task = self.configuration.cmd  
  17.   
  18.         fn, cls = bb.cache.Cache.virtualfn2realfn(buildfile)  
  19.         fn = self.matchFile(fn)  
  20.   
  21.         self.buildSetVars()  


[plain]  view plain copy
  1. def parseCommandLine(self):  
  2.      # Parse any commandline into actions  
  3.      self.commandlineAction = {'action':None, 'msg':None}  
  4.      if self.configuration.show_environment:  
  5.          if 'world' in self.configuration.pkgs_to_build:  
  6.              self.commandlineAction['msg'] = "'world' is not a valid target for --environment."  
  7.          elif 'universe' in self.configuration.pkgs_to_build:  
  8.              self.commandlineAction['msg'] = "'universe' is not a valid target for --environment."  
  9.          elif len(self.configuration.pkgs_to_build) > 1:  
  10.              self.commandlineAction['msg'] = "Only one target can be used with the --environment option."  
  11.          elif self.configuration.buildfile and len(self.configuration.pkgs_to_build) > 0:  
  12.              self.commandlineAction['msg'] = "No target should be used with the --environment and --buildfile options."  
  13.          elif len(self.configuration.pkgs_to_build) > 0:  
  14.              self.commandlineAction['action'] = ["showEnvironmentTarget", self.configuration.pkgs_to_build]  
  15.              self.configuration.data.setVar("BB_CONSOLELOG", None)  
  16.          else:  
  17.              self.commandlineAction['action'] = ["showEnvironment", self.configuration.buildfile]  
  18.              self.configuration.data.setVar("BB_CONSOLELOG", None)  
  19.      elif self.configuration.buildfile is not None:  
  20.          self.commandlineAction['action'] = ["buildFile", self.configuration.buildfile, self.configuration.cmd]  
  21.      elif self.configuration.revisions_changed:  
  22.          self.commandlineAction['action'] = ["compareRevisions"]  
  23.      elif self.configuration.show_versions:  
  24.          self.commandlineAction['action'] = ["showVersions"]  
  25.      elif self.configuration.parse_only:  
  26.          self.commandlineAction['action'] = ["parseFiles"]  
  27.      elif self.configuration.dot_graph:  
  28.          if self.configuration.pkgs_to_build:  
  29.              self.commandlineAction['action'] = ["generateDotGraph", self.configuration.pkgs_to_build, self.configuration.cmd]  
  30.          else:  
  31.              self.commandlineAction['msg'] = "Please specify a package name for dependency graph generation."  
  32.      else:  
  33.          if self.configuration.pkgs_to_build:  
  34.              self.commandlineAction['action'] = ["buildTargets", self.configuration.pkgs_to_build, self.configuration.cmd]  
  35.          else:  
  36.              #self.commandlineAction['msg'] = "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information."  
  37.              self.commandlineAction = None  

仔细看下上面这个函数,是不是和最初在bitbake 这个python 根脚本中定义的main 函数有很多相似之处,这样就能猜到 bitbake -b xxx 这个指令回执行到buildFile 这个python 函数。同样可以知道 bitbake -e 可以看到所有的环境变量,包括BBFILES 变量的值,因为它执行了 showEnvrioment 这个python 函数。

强烈建议把 bitbake -e > ~/bitbake_-e.txt  重定向到文件中好好看看。

 

[plain]  view plain copy
  1. def main():  
  2.     parser = optparse.OptionParser(  
  3.         version = "BitBake Build Tool Core version %s, %%prog version %s" % (bb.__version__, __version__),  
  4.         usage = """%prog [options] [package ...]  
  5.   
  6. Executes the specified task (default is 'build') for a given set of BitBake files.  
  7. It expects that BBFILES is defined, which is a space separated list of files to  
  8. be executed.  BBFILES does support wildcards.  
  9. Default BBFILES are the .bb files in the current directory.""")  
  10.   
  11.     parser.add_option("-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES. Does not handle any dependencies.",  
  12.                action = "store", dest = "buildfile", default = None)  
  13.   
  14.     parser.add_option("-k", "--continue", help = "continue as much as possible after an error. While the target that failed, and those that depend on it, cannot be remade, the other dependencies of these targets can be processed all the same.",  
  15.                action = "store_false", dest = "abort", default = True)  
  16.   
  17.     parser.add_option("-a", "--tryaltconfigs", help = "continue with builds by trying to use alternative providers where possible.",  
  18.                action = "store_true", dest = "tryaltconfigs", default = False)  
  19.   
  20.     parser.add_option("-f", "--force", help = "force run of specified cmd, regardless of stamp status",  
  21.                action = "store_true", dest = "force", default = False)  
  22.   
  23.     parser.add_option("-c", "--cmd", help = "Specify task to execute. Note that this only executes the specified task for the providee and the packages it depends on, i.e. 'compile' does not implicitly call stage for the dependencies (IOW: use only if you know what you are doing). Depending on the base.bbclass a listtasks tasks is defined and will show available tasks",  
  24.                action = "store", dest = "cmd")  

OK.

再看一下 parseCommandLine 这个函数if elif elif ,,, else 通过不断的探测bitbake 的选项参数,如果没有选项参数,只有target 走到buildTargets 这个else 里面,接下来要执行的函数是buildTargets.

[plain]  view plain copy
  1. def parseCommandLine(self):  
  2.      # Parse any commandline into actions  
  3.      self.commandlineAction = {'action':None, 'msg':None}  
  4.      if self.configuration.show_environment:  
  5.          if 'world' in self.configuration.pkgs_to_build:  
  6.              self.commandlineAction['msg'] = "'world' is not a valid target for --environment."  
  7.          elif 'universe' in self.configuration.pkgs_to_build:  
  8.              self.commandlineAction['msg'] = "'universe' is not a valid target for --environment."  
  9.          elif len(self.configuration.pkgs_to_build) > 1:  
  10.              self.commandlineAction['msg'] = "Only one target can be used with the --environment option."  
  11.          elif self.configuration.buildfile and len(self.configuration.pkgs_to_build) > 0:  
  12.              self.commandlineAction['msg'] = "No target should be used with the --environment and --buildfile options."  
  13.          elif len(self.configuration.pkgs_to_build) > 0:  
  14.              self.commandlineAction['action'] = ["showEnvironmentTarget", self.configuration.pkgs_to_build]  
  15.              self.configuration.data.setVar("BB_CONSOLELOG", None)  
  16.          else:  
  17.              self.commandlineAction['action'] = ["showEnvironment", self.configuration.buildfile]  
  18.              self.configuration.data.setVar("BB_CONSOLELOG", None)  
  19.      elif self.configuration.buildfile is not None:  
  20.          self.commandlineAction['action'] = ["buildFile", self.configuration.buildfile, self.configuration.cmd]  
  21.      elif self.configuration.revisions_changed:  
  22.          self.commandlineAction['action'] = ["compareRevisions"]  
  23.      elif self.configuration.show_versions:  
  24.          self.commandlineAction['action'] = ["showVersions"]  
  25.      elif self.configuration.parse_only:  
  26.          self.commandlineAction['action'] = ["parseFiles"]  
  27.      elif self.configuration.dot_graph:  
  28.          if self.configuration.pkgs_to_build:  
  29.              self.commandlineAction['action'] = ["generateDotGraph", self.configuration.pkgs_to_build, self.configuration.cmd]  
  30.          else:  
  31.              self.commandlineAction['msg'] = "Please specify a package name for dependency graph generation."  
  32.      else:  
  33.          if self.configuration.pkgs_to_build:  
  34.              self.commandlineAction['action'] = ["buildTargets", self.configuration.pkgs_to_build, self.configuration.cmd]  
  35.          else:  
  36.              #self.commandlineAction['msg'] = "Nothing to do.  Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information."  
  37.              self.commandlineAction = None  

看一个特定的package 编译流程,拿kernel 看吧:

从bitbake_-e.txt 环境中搜到:

PREFERRED_PROVIDER_virtual/kernel="linux-yocto"

PREFERRED_VERSION_linux-yocto="3.4%"

这样就可以在poky 目录下面搜索 名字为 linux-yocto*.bb* 的 bb和bbappend, 搜出来以后再取 3.4版本的那些bb 和 bbappend。

/poky$ find -name "linux-yocto*.bb*"

./meta/recipes-kernel/linux/linux-yocto_3.8.bb
./meta/recipes-kernel/linux/linux-yocto-dev.bb
./meta/recipes-kernel/linux/linux-yocto-rt_3.4.bb
./meta/recipes-kernel/linux/linux-yocto-rt_3.8.bb
./meta/recipes-kernel/linux/linux-yocto-rt_3.2.bb
./meta/recipes-kernel/linux/linux-yocto-tiny_3.2.bb
./meta/recipes-kernel/linux/linux-yocto_3.4.bb
./meta/recipes-kernel/linux/linux-yocto_3.2.bb
./meta/recipes-kernel/linux/linux-yocto-tiny_3.4.bb
./meta/recipes-kernel/linux/linux-yocto-tiny_3.8.bb
./meta-yocto-bsp/recipes-kernel/linux/linux-yocto_3.8.bbappend
./meta-yocto-bsp/recipes-kernel/linux/linux-yocto_3.2.bbappend
./meta-yocto-bsp/recipes-kernel/linux/linux-yocto_3.4.bbappend
./meta-skeleton/recipes-kernel/linux/linux-yocto-custom.bb

查看./meta/recipes-kernel/linux/linux-yocto_3.4.bb

[plain]  view plain copy
  1. require recipes-kernel/linux/linux-yocto.inc  
  2.   
  3. KBRANCH_DEFAULT = "standard/base"  
  4. KBRANCH = "${KBRANCH_DEFAULT}"  
  5.   
  6. SRCREV_machine_qemuarm ?= "7cc80532306889b75619f8a1b713048e25f59e19"  
  7. SRCREV_machine_qemumips  ?= "debce6677988e03b50c369aba5861d4f9b2e557d"  
  8. SRCREV_machine_qemuppc ?= "ddbc382cbc45a009e9ce17f7d448fcfd050ab5fc"  
  9. SRCREV_machine_qemux86 ?= "c994390cfa28339cbc1ec3b56eeec83a5fa75bb7"  
  10. SRCREV_machine_qemux86-64 ?= "c994390cfa28339cbc1ec3b56eeec83a5fa75bb7"  
  11. SRCREV_machine ?= "c994390cfa28339cbc1ec3b56eeec83a5fa75bb7"  
  12. SRCREV_meta ?= "80b4b5110dca5184b57e85f1e775fb006a2e85ad"  
  13.   
  14. SRC_URI = "git://git.yoctoproject.org/linux-yocto-3.4.git;protocol=git;bareclone=1;branch=${KBRANCH},${KMETA};name=machine,meta"  
  15.   
  16. LINUX_VERSION ?= "3.4.36"  
  17.   
  18. PR = "${INC_PR}.3"  
  19. PV = "${LINUX_VERSION}+git${SRCPV}"  
  20.   
  21. KMETA = "meta"  
  22.   
  23. COMPATIBLE_MACHINE = "qemuarm|qemux86|qemuppc|qemumips|qemux86-64"  
  24.   
  25. # Functionality flags  
  26. KERNEL_FEATURES_append = " features/netfilter/netfilter.scc"  
  27. KERNEL_FEATURES_append_qemux86=" cfg/sound.scc"  
  28. KERNEL_FEATURES_append_qemux86-64=" cfg/sound.scc"  
  29. KERNEL_FEATURES_append_qemux86=" cfg/paravirt_kvm.scc"  
  30. KERNEL_FEATURES_append = " ${@bb.utils.contains("TUNE_FEATURES", "mx32", " cfg/x32.scc", "" ,d)}"  

先查看下 linux-yocto_3.4.bb require 的 recipes-kernel/linux/linux-yocto.inc 

[plain]  view plain copy
  1. DESCRIPTION = "Yocto Kernel"  
  2. SECTION = "kernel"  
  3. LICENSE = "GPLv2"  
  4.   
  5. LIC_FILES_CHKSUM = "file://COPYING;md5=d7810fab7487fb0aad327b76f1be7cd7"  
  6.   
  7. INC_PR = "r4"  
  8.   
  9. # A KMACHINE is the mapping of a yocto $MACHINE to what is built  
  10. # by the kernel. This is typically the branch that should be built,  
  11. # and it can be specific to the machine or shared  
  12. # KMACHINE = "UNDEFINED"  
  13.   
  14. LINUX_KERNEL_TYPE ?= "standard"  
  15.   
  16. # KMETA ?= ""  
  17. KBRANCH ?= "master"  
  18. KMACHINE ?= "${MACHINE}"  
  19. SRCREV_FORMAT ?= "meta_machine"  
  20.   
  21. LINUX_VERSION_EXTENSION ?= "-yocto-${LINUX_KERNEL_TYPE}"  
  22.   
  23. do_patch[depends] = "kern-tools-native:do_populate_sysroot"  
  24.   
  25. addtask kernel_configme before do_configure after do_patch  
  26.   
  27. # Pick up shared functions  
  28. inherit kernel  
  29. inherit kernel-yocto  
  30. require linux-dtb.inc  
  31.   
  32. B = "${WORKDIR}/linux-${MACHINE}-${LINUX_KERNEL_TYPE}-build"  
  33.   
  34. do_install_append(){  
  35.         if [ -n "${KMETA}" ]; then  
  36.                 rm -rf ${STAGING_KERNEL_DIR}/${KMETA}  
  37.         fi  
  38. }  
  39.   
  40. # extra tasks  
  41. addtask kernel_link_vmlinux after do_compile before do_install  
  42. addtask validate_branches before do_patch after do_kernel_checkout  
  43. addtask kernel_configcheck after do_configure before do_compile  
这里inherit kernel, inherit kernel-yocto, 在meta/class 里面可以看到 kernel.class ,kernel-yocto.class 文件,里面有kernel 公共的base 函数。

http://blog.chinaunix.net/uid-7652108-id-2047290.html

bitbake parse 的机理分析:

    Bitbake 这个 task execute tool的第一步就是parsing,也就是对BBFILES所定义的变量的内容,也就是bbfiles 进行读取data,然后分析,相关数据进行缓存(cache)
 
     出发是从 conf/bitbake.conf 这个文件开始,这个文件本身的内容处理是一部分,另外一部分是这个文件会include一些其它的conf文件,比如很重要的 conf/local.conf,分析处理的数据都会写到 CACHEDATA的文件中(bb_cache.dat) ,当然前提是你开启了cache机制
    处理完conf文件后,就是bbclass文件,这个时候是从 class/base.bbclass开始,然后分析其inherit的一些class。
    这两部分数据很重要,后来所有的bbfile 都依赖于这些conf 文件和class文件,当然这些文件中的变量也会实施到所有的bb file上,所以只要这些conf文件和class文件有改变,那么bitbake 就会重新parse
   那么,到现在,就开始进入parsing bb file了,当然第一次肯定要parse所有的bbfile了,这部分是最耗时的,第一次完了之后,那么就可以利用cache机制来判断是否重新parse了
 
   上面就是bitbake version 1版本parsing的机制,理解了code背后的故事,才能让我们更游刃有余地工作

膜拜这个大神,早在07年就把 bitbake 一个package 的流程分析的这么透彻了:

http://blog.chinaunix.net/uid-7652108-id-2047247.html


在 linux-yocto.inc 文件中 inherit kernel-yocto, 来看下 class/kernel-yocto.bbclass , 分析一下do_kernel_configme 这个函数。添加log语句打印一下这个函数中的一些变量。 ${S} 表示linux source code 的目录, ${B} 表示linux build 目录。

在 linux_source_code_path/meta/scripts/configme 执行这个shell 脚本。

[python]  view plain copy
  1. do_kernel_configme[dirs] = "${S} ${B}"  
  2. do_kernel_configme() {  
  3.         echo "[INFO] doing kernel configme"  
  4.         export KMETA=${KMETA}  
  5.   
  6.         if [ -n ${KCONFIG_MODE} ]; then  
  7.                 configmeflags=${KCONFIG_MODE}  
  8.         else  
  9.                 # If a defconfig was passed, use =n as the baseline, which is achieved  
  10.                 # via --allnoconfig  
  11.                 if [ -f ${WORKDIR}/defconfig ]; then  
  12.                         configmeflags="--allnoconfig"  
  13.                 fi  
  14.         fi  
  15.   
  16.         cd ${S}  
  17.         PATH=${PATH}:${S}/scripts/util  
  18.         configme ${configmeflags} --reconfig --output ${B} ${LINUX_KERNEL_TYPE} ${KMACHINE}  
  19.         if [ $? -ne 0 ]; then  
  20.                 echo "ERROR. Could not configure ${KMACHINE}-${LINUX_KERNEL_TYPE}"  
  21.                 exit 1  
  22.         fi  
  23.   
  24.         #qc added log  
  25.         echo "qc variables value: ${S} ${B} ${KMETA} ${KCONFIG_MODE} ${WORKDIR} ${LINUX_KERNEL_TYPE} ${KMACHINE}."  
  26.         echo "# Global settings from linux recipe" >> ${B}/.config  
  27.         echo "CONFIG_LOCALVERSION="\"${LINUX_VERSION_EXTENSION}\" >> ${B}/.config  
  28. }  

configme 这个 shell 脚本完成 生成.config 文件的工作:

[plain]  view plain copy
  1. # This is factored out into a function because for a given branch,  
  2. # there may be more than one user (i.e. big endian, little endian,  
  3. # or BSPs that use the same branch but differ only in kernel configs)  
  4. run_board_config()  
  5. {  
  6.     # Can't set these until we've unwound the checkpoint and have meta data.  
  7.     KVER=`cat ./$META_DIR/cfg/kernel-*cache/kver|sed 's/^v//'`  
  8.   
  9.     # Look for standard defines, with compatibility fallbacks  
  10.     KARCH=`grep KARCH $SCC | awk '{print $3}'`  
  11.     KPROFILE=`grep KMACHINE $SCC | awk '{print $3}'`  
  12.     KTYPE=`grep KTYPE $SCC | awk '{print $3}'`  
  13.   
  14.     META=./$META_DIR/meta-series  
  15.     META_ALT=./$META_DIR/cfg/scratch/`basename $SCC .scc`-meta  
  16.   
  17.     BUILD_DIR=$out_dir  
  18.     CFGFILE=$machine-$target-config-$KVER  
  19.     kgit-meta -v -k $META  
  20.     if [ $? != 0 ]; then  
  21.         echo Error running the meta series for collecting config data  
  22.         return 1  
  23.     fi  
  24.   
  25.     KTGT=`get_branch_name $META`  
  26.     mkdir -p ./$META_DIR/cfg/$KTGT  
  27.     if [ $? != 0 ]; then  
  28.         echo Failed to mkdir ./$META_DIR/cfg/$KTGT for config data  
  29.         return 1  
  30.     fi  
  31.   
  32.     frags=`cat $META_DIR/cfg/$KTGT/config_frag.txt | sed 's% .$ %'$META_DIR/cfg'\1%'`  
  33.     pre_config -l $META_DIR/cfg/$KTGT/ $frags > $META_DIR/cfg/$KTGT/config.log 2>&1  
  34.   
  35.     # remove any old assembled debug fragments  
  36.     rm -f $BUILD_DIR/.tmp.config*  
  37.   
  38.     merge_frags=`cat $META_DIR/cfg/$KTGT/config_frag.txt | sed 's% .$ %'$META_DIR/cfg'\1.sanitized%'`  
  39.     ARCH=$KARCH O=$BUILD_DIR merge_config.sh $allnoconfig -d $merge_frags  \  
  40.                                       > $META_DIR/cfg/$KTGT/merge_log.txt 2>&1  
  41.   
  42.     mv $BUILD_DIR/.tmp.config* $META_DIR/cfg/$KTGT/$CFGFILE  
  43.     if [ $? != 0 ]; then  
  44.         echo creation of pre-processed config data failed  
  45.         return 1  
  46.     fi  
  47.   
  48.     # break the merge log down into parts that can be processed later  
  49.     grep -A2 "^Value of" $META_DIR/cfg/$KTGT/merge_log.txt > $META_DIR/cfg/$KTGT/redefinition.txt  
  50.     grep -A2 "^Value requested" $META_DIR/cfg/$KTGT/merge_log.txt > $META_DIR/cfg/$KTGT/mismatch.txt  
  51.   
  52.     echo "[INFO] Pre-processed cfg file $CFGFILE created."  
  53.   
  54.    

拼接一系列的meta 目录下的config_frag 文件,最终通过 merge_config.sh 生成 .config 文件。

/yocto_build/tmp/work/qemuppc-poky-linux/linux-yocto/3.4.36+gitAUTOINC+80b4b5110dca5184b57e85f1e775fb006a2e85ad_ddbc382cbc45a009e9ce17f7d448fcfd050ab5fc-r4.3/linux/meta/cfg/standard/qemuppc/config_frag.txt

这个就是要处理的所有 config 的片段。

可以参考 config.log 和 merge_log.txt

/yocto_build/tmp/work/qemuppc-poky-linux/linux-yocto/3.4.36+gitAUTOINC+80b4b5110dca5184b57e85f1e775fb006a2e85ad_ddbc382cbc45a009e9ce17f7d448fcfd050ab5fc-r4.3/linux/meta/cfg/standard/qemuppc/config.log

/yocto_build/tmp/work/qemuppc-poky-linux/linux-yocto/3.4.36+gitAUTOINC+80b4b5110dca5184b57e85f1e775fb006a2e85ad_ddbc382cbc45a009e9ce17f7d448fcfd050ab5fc-r4.3/linux/meta/cfg/standard/qemuppc/merge_log.txt

至此,就知道 yocto 编译 linux kernel 的 .config 是从哪来的了!!!

了解更多关于 yocto kernel config 可以 git clone git://git.yoctoproject.org/yocto-kernel-cache,查看 00-README 文档。

http://blog.csdn.net/fmddlmyy/article/details/325403

在 bitbake 中加入 print log语句,方便调试:

[python]  view plain copy
  1. parser.add_option("-B""--bind", help = "The name/address for the bitbake server to bind to",  
  2.            action = "store", dest = "bind", default = False)  
  3. parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks, forces builds",  
  4.            action = "store_true", dest = "nosetscene", default = False)  
  5. options, args = parser.parse_args(sys.argv)  
  6.   
  7. #qc test code  
  8. print "qc test code: options:", options  
  9. print "qc test code: args", args  
  10.   
  11. configuration = BBConfiguration(options)  
  12. configuration.pkgs_to_build.extend(args[1:])  

=====================================================================================

分割线

======================================================================================================

  这也是behind the code重要的事情,为了达到这个理解,在没有manual,资料缺乏的情况下,是很困难的,但是一旦达到了这个境界,很多事情就好办了,我也就可以安心做其它的事情了,继续研究behind the code的故事了

bitbake demo

程序是从 bitbake/bin/bitbake开始,其中会调用 lib/bb/下的相关模块

BBCooker.cook()
这是开始的函数
一开始是对bitbake 的一些参数进行处理,比如:

if not self.configuration.cmd:
            self.configuration.cmd = "build"

就是说如果没有指定task,那么就默认为build,比如我 

bitbake demo

那么 self.configure.cmd就为空,所以就相当于 

bitbake -c build demo


继续下走

self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )

这是分析一些conf文件和继承的bbclass,并cache住,这个其实就是 __depends的值,一旦这个值有改动,那么就会重新parse所有的文件


if not bb.data.getVar("BUILDNAME", self.configuration.data):
            bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)

这个就是设置BUILDNAME,比如
NOTE: build  200710201006 : started

OE Build Configuration:
BB_VERSION     = "1.6.2"
OE_REVISION    = ""
TARGET_ARCH    = "powerpc"
TARGET_OS      = "linux-uclibc"
MACHINE        = "test"
DISTRO         = "test2-uclibc"
DISTRO_VERSION = "20071020"
TARGET_FPU     = ""

NOTE: build  200710201006 : completed
BUILDNAME的值每次在这里出现。


if (not self.configuration.show_environment) and (self.configuration.buildfile is not None):

...

 self.tryBuildPackage( bf, item, bbfile_data )

如果你不想parse所有的bb file,(可以节约时间),直接 build package,你就可以使用 bitbake -b

bitbake -b path/demo.bb

这是真实的,因为你当现在可以发现,还没有程序去parse所有的bbfile
 
go on....

self.status = BBParsingStatus()

这就实例化了一个 BBParsingStatus Class,这个class很重要,记录了处理每一个pacakge的一些变量

def __init__(self):
        self.providers = {} // package提供的providers,默认是3个
        self.rproviders = {}// packages提供的run providers
        self.packages = {}   // PACKAGES变量的值
        self.packages_dynamic = {} // 动态 PACKAGES的值
        self.bbfile_priority = {}  // bbfile的优先级
        self.bbfile_config_priorities = [] //多个分支的匹配模式和优先级
        self.ignored_dependencies = None //忽略的依赖关系
        self.possible_world = [] // 
        self.world_target = Set()// 分支里所有的package
        self.pkg_pn = {} // package->bb filename
        self.pkg_fn = {} // filename-> package
        self.pkg_pvpr = {}// pv pr
        self.pkg_dp = {}   // default_perference
        self.pn_provides = {}
        self.all_depends = Set()// 所有package总的依赖
        self.build_all = {}  // BUILD_ALL_DEPS
        self.deps = {} // depends list
        self.rundeps = {} // rundeps list
        self.runrecs = {} // run reccommend
        self.task_queues = {} //task 队列
        self.task_deps = {} // task 的依赖关系
        self.stamp = {}      // stamp file
        self.preferred = {}   // 优先选择的 virtual/package

在处理每个bbfile的时候,都会调用 handle_bb_data去填充上面成员变量,理解上面这些成员变量,对理解整个bitbake sources code 有着非常重要的意思,这些变量的意义,我是通过若干次的反复,才得到的。
好,我接着走


ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
        self.status.ignored_dependencies = Set( ignore.split() )

看到这里,我们就能明白在local.conf.sample,里ASSUME_PROVIDED的意思了吧,如果你一旦定义了ASSUME_PROVIDED的时候,bitbake就会把它放入 ignored_dependencies,也就是说,不会再编译这些package了。

self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )

这个就是为了填充bbfile_config_priority,关于这个,可以参考以前的日志,有详细的解释

pkgs_to_build = None
        if args:
            if not pkgs_to_build:
                pkgs_to_build = []
            pkgs_to_build.extend(args)
        if not pkgs_to_build:
                bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1)
                if bbpkgs:
                        pkgs_to_build = bbpkgs.split()
        if not pkgs_to_build and not self.configuration.show_versions \
                             and not self.configuration.interactive \
                             and not self.configuration.show_environment:
                print "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help'"
                print "for usage information."
                sys.exit(0)

pkg_to_build 就是 bitbake package1 package2 ...,这也是通过args传递过来的,如果我们只执行

bitbake

那么就会提示红色的信息
下面是关于psyco,是可以加快python速度的一个东东,python是解释性的语言,当然有解释性语言的通病,慢,一旦处理数量很大的时候,有些难以忍受.

if not self.configuration.disable_psyco:

如果没有安装 psyco,那么就走到

self.collect_bbfiles( self.myProgressCallback )

这个函数很重要,尽管意思很明了,就是收集所有分支的bbfile,然后parse,当然里面也包含这cache机制,参数是一个回调函数,为了不影响大的路线,我现在还不深入这个函数,等理解了整个bitbake流程,再进入看看

到了这里,我们就parse了所有的bbfile

self.buildDepgraph()

这个函数做2个方面的事情,一个是 PREFERRED_PROVIDERS,填充 self.preferred,二是计算上面所搜集的每个bbfile的优先级
下面是一个有趣的变量

if 'world' in pkgs_to_build:
                self.buildWorldTargetList()
                pkgs_to_build.remove('world')
                for t in self.status.world_target:
                    pkgs_to_build.append(t)

就是说如果你 bitbake world,那么就会build 所有分支里面的package,我想这个变量也来源于gentoo,

下面终于来到了build package,结束了前面的预处理工作

for k in pkgs_to_build:
                failed = False
                try:
                    self.pkg_to_build = k
                    if self.buildProvider( k , False ) == 0:

pkgs_to_build,就是build package列表,我们就假设bitbake 一个package,首先调用的是
buildProvider,我一开始没有理解这个意思,到现在才明白,其实走到现在,我们还没有到去compile package的时候,首先是要找到一个provider 提供 pkgs_to_build,然后才走到compile package 包括dependency,如前面一样,我们现在还不分析里面的东东,继续往下走
走,其实到这里 bitbake run one time就结束了

你可能感兴趣的:(yocto 编译流程分析)