git clone 一份poky 的工程到本地。 source poky/oe-init-build-env your_build_path 看下 oe-init-build-env 這個(gè)shell 腳本都干了些什么: [plain] view plaincopy if [ -z "$ZSH_NAME" ] && [ "x$0" = "x./oe-init-build-env" ]; then echo "Error: This script needs to be sourced. Please run as '. ./oe-init-build-env'" else if [ -n "$BASH_SOURCE" ]; then OEROOT="`dirname $BASH_SOURCE`" elif [ -n "$ZSH_NAME" ]; then OEROOT="`dirname $0`" else OEROOT="`pwd`" fi OEROOT=`readlink -f "$OEROOT"` export OEROOT . $OEROOT/scripts/oe-buildenv-internal && \ $OEROOT/scripts/oe-setup-builddir && \ [ -n "$BUILDDIR" ] && cd $BUILDDIR unset OEROOT unset BBPATH fi 轉(zhuǎn)載: OSChina 上一個(gè)博友分析的這段shell 腳本: http://my.oschina.net/u/158589/blog/70921 第一步,,判斷該腳本是否是用source或者.的方法運(yùn)行的,。 但是 1 ["x$0"="x./oe-init-build-env"]只能對(duì)./oe-init-build-env這種執(zhí)行方式報(bào)錯(cuò),,對(duì)./yocto/oe-init-build-env是不會(huì)報(bào)錯(cuò)的,。 第二步,,設(shè)置OEROOT這個(gè)變量,。當(dāng)用.或者source去執(zhí)行腳本時(shí),BASH_SOURCE這個(gè)變量會(huì)被自動(dòng)設(shè)置到源文件路徑,。于是dirname $BASH_SOURCE就獲得了腳本所在目錄。readlink -f $OEROOT獲得了絕對(duì)路徑,。 第三步,執(zhí)行oe-buildenv-internal和oe-setup-builddir這兩個(gè)腳本,,并且進(jìn)入到build目錄,。 (注意這里的oe-buildenv-internal是用.執(zhí)行的,,而oe-setup-build是fork shell執(zhí)行的。因?yàn)閛e-setup-builddir中用到了OEROOT這個(gè)變量,,所以在此之前,,OEROOT必須被export,,正如腳本中所做的那樣,。) 第四步,,unset一些變量,。因?yàn)?或者source的執(zhí)行方式是在原shell中執(zhí)行,,并不fork shell,,所以如果不unset,,會(huì)一直留在該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 可見,,無論在哪個(gè)目錄下執(zhí)行,,最后獲得的OEROOT的絕對(duì)路徑都是一致的,。(主要利用BASH_SOURCE, dirname, readlink)。 [plain] view plaincopy . $OEROOT/scripts/oe-buildenv-internal oe-buildenv-internal 這個(gè)shell 腳本主要是設(shè)置環(huán)境變量(poky/bitbake/bin/ 和 poky/scripts 這兩個(gè)路徑加入到PATH中,。bitbake 命令就在poky/bitbake/bin/路徑下面,。),,如下: [plain] view plaincopy PATH="${OEROOT}/scripts:$BITBAKEDIR/bin/:$PATH" unset BITBAKEDIR # Used by the runqemu script export BUILDDIR export PATH export BB_ENV_EXTRAWHITE="MACHINE DISTRO TCMODE TCLIBC HTTP_PROXY http_proxy \ HTTPS_PROXY https_proxy FTP_PROXY ftp_proxy FTPS_PROXY ftps_proxy ALL_PROXY \ all_proxy NO_PROXY no_proxy SSH_AGENT_PID SSH_AUTH_SOCK BB_SRCREV_POLICY \ SDKMACHINE BB_NUMBER_THREADS BB_NO_NETWORK PARALLEL_MAKE GIT_PROXY_COMMAND \ SOCKS5_PASSWD SOCKS5_USER SCREENDIR STAMPS_DIR" [plain] view plaincopy $OEROOT/scripts/oe-setup-builddir oe-setup-builddir 這個(gè)shell 腳本,,創(chuàng)建編譯目錄,判斷當(dāng)前編譯目錄下面是否存在conf/local.conf 文件,,如果不存在local.conf 配置文件的話,,通過Template模板,sample 生成一個(gè)local.conf,。 下面這段shell 腳本的意思是首先檢查 meta-yocto 目錄下面是否存在conf/ 如果存在的話就用meta-yocto/conf/ 下面的 local.conf.sample 和 bblayers.conf,, 如果不存在的話就到meta/conf 下面去找 local.conf.sample 和 bblayer.conf。 [plain] view plaincopy TEMPLATECONF=${TEMPLATECONF:-meta-yocto/conf} # # $TEMPLATECONF can point to a directory for the template local.conf & bblayers.conf # if [ "x" != "x$TEMPLATECONF" ]; then if ! (test -d "$TEMPLATECONF"); then # Allow TEMPLATECONF=meta-xyz/conf as a shortcut if [ -d "$OEROOT/$TEMPLATECONF" ]; then TEMPLATECONF="$OEROOT/$TEMPLATECONF" fi if ! (test -d "$TEMPLATECONF"); then echo >&2 "Error: '$TEMPLATECONF' must be a directory containing local.conf & bblayers.conf" return fi fi OECORELAYERCONF="$TEMPLATECONF/bblayers.conf.sample" OECORELOCALCONF="$TEMPLATECONF/local.conf.sample" OECORENOTESCONF="$TEMPLATECONF/conf-notes.txt" fi if [ "x" = "x$OECORELOCALCONF" ]; then OECORELOCALCONF="$OEROOT/meta/conf/local.conf.sample" fi if ! (test -r "$BUILDDIR/conf/local.conf"); then cat <<eom < p=""> You had no conf/local.conf file. This configuration file has therefore been created for you with some default values. You may wish to edit it to use a different MACHINE (target hardware) or enable parallel build options to take advantage of multiple cores for example. See the file for more information as common configuration options are commented. The Yocto Project has extensive documentation about OE including a reference manual which can be found at: http:///documentation For more information about OpenEmbedded see their website: http://www./ EOM cp -f $OECORELOCALCONF $BUILDDIR/conf/local.conf fi if [ "x" = "x$OECORELAYERCONF" ]; then OECORELAYERCONF="$OEROOT/meta/conf/bblayers.conf.sample" fi if ! (test -r "$BUILDDIR/conf/bblayers.conf"); then cat <<eom < p=""> You had no conf/bblayers.conf file. The configuration file has been created for you with some default values. To add additional metadata layers into your configuration please add entries to this file. The Yocto Project has extensive documentation about OE including a reference manual which can be found at: http:///documentation For more information about OpenEmbedded see their website: http://www./ EOM # Put the abosolute path to the layers in bblayers.conf so we can run # bitbake without the init script after the first run sed "s|##COREBASE##|$OEROOT|g" $OECORELAYERCONF > $BUILDDIR/conf/bblayers.conf fi 至此,, build_path 下面的conf/local.conf 以及 bblayer.conf 文件就 創(chuàng)建好了,,接下來就可以執(zhí)行 bitbake target 編譯了,。 ---------------------------------------------------------------- 華麗麗的分割線 --------------------------------------------------------- 現(xiàn)在開始分析bitbake 編譯流程: bitbake --help 可以看到bitbake 后面可以跟的選項(xiàng)參數(shù): [plain] view plaincopy yocto_build$ bitbake --help Usage: bitbake [options] [package ...] Executes the specified task (default is 'build') for a given set of BitBake files. It expects that BBFILES is defined, which is a space separated list of files to be executed. BBFILES does support wildcards. Default BBFILES are the .bb files in the current directory. Options: --version show program's version number and exit -h, --help show this help message and exit -b BUILDFILE, --buildfile=BUILDFILE execute the task against this .bb file, rather than a package from BBFILES. Does not handle any dependencies. -k, --continue 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. -a, --tryaltconfigs continue with builds by trying to use alternative providers where possible. -f, --force force run of specified cmd, regardless of stamp status -c CMD, --cmd=CMD 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 -C INVALIDATE_STAMP, --clear-stamp=INVALIDATE_STAMP Invalidate the stamp for the specified cmd such as 'compile' and run the default task for the specified target(s) -r PREFILE, --read=PREFILE read the specified file before bitbake.conf -R POSTFILE, --postread=POSTFILE read the specified file after bitbake.conf -v, --verbose output more chit-chat to the terminal -D, --debug Increase the debug level. You can specify this more than once. -n, --dry-run don't execute, just go through the motions -S, --dump-signatures don't execute, just dump out the signature construction information -p, --parse-only quit after parsing the BB files (developers only) -s, --show-versions show current and preferred versions of all recipes -e, --environment show the global or per-package environment (this is what used to be bbread) -g, --graphviz emit the dependency trees of the specified packages in the dot syntax, and the pn-buildlist to show the build list -I EXTRA_ASSUME_PROVIDED, --ignore-deps=EXTRA_ASSUME_PROVIDED Assume these dependencies don't exist and are already provided (equivalent to ASSUME_PROVIDED). Useful to make dependency graphs more appealing -l DEBUG_DOMAINS, --log-domains=DEBUG_DOMAINS Show debug logging for the specified logging domains -P, --profile profile the command and print a report -u UI, --ui=UI userinterface to use -t SERVERTYPE, --servertype=SERVERTYPE Choose which server to use, none, process or xmlrpc --revisions-changed Set the exit code depending on whether upstream floating revisions have changed or not --server-only Run bitbake without UI, the frontend can connect with bitbake server itself -B BIND, --bind=BIND The name/address for the bitbake server to bind to --no-setscene Do not run any setscene tasks, forces builds 那么,bitbake 對(duì)于這些個(gè)選項(xiàng)參數(shù)是怎么處理的呢,。bitbake target -xxx -xxxx -xx ,,這樣其實(shí)就是執(zhí)行poky/bitbake/bitbake 這個(gè)python 腳本: 這個(gè)python 腳本相對(duì)簡(jiǎn)單,,可以看下最后的這段代碼: 通過下面這個(gè) if 判斷,調(diào)用 main 函數(shù)執(zhí)行,,使用 python 庫(kù)中的 [python] view plaincopy optparse.OptionParser 對(duì)選項(xiàng)參數(shù)進(jìn)行處理。 [python] view plaincopy if __name__ == "__main__": try: ret = main() [python] view plaincopy #!/usr/bin/env python # ex:ts=4:sw=4:sts=4:et # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- import os import sys, logging sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(__file__)), 'lib')) import optparse import warnings from traceback import format_exception try: import bb except RuntimeError as exc: sys.exit(str(exc)) from bb import event import bb.msg from bb import cooker from bb import ui from bb import server __version__ = "1.17.1" logger = logging.getLogger("BitBake") # Unbuffer stdout to avoid log truncation in the event # of an unorderly exit as well as to provide timely # updates to log files for use with tail try: if sys.stdout.name == '': sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) except: pass class BBConfiguration(object): """ Manages build options and configurations for one run """ def __init__(self, options): for key, val in options.__dict__.items(): setattr(self, key, val) self.pkgs_to_build = [] *** *** def main(): parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % (bb.__version__, __version__), usage = """%prog [options] [package ...] Executes the specified task (default is 'build') for a given set of BitBake files. It expects that BBFILES is defined, which is a space separated list of files to be executed. BBFILES does support wildcards. Default BBFILES are the .bb files in the current directory.""") parser.add_option("-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES. Does not handle any dependencies.", action = "store", dest = "buildfile", default = None) 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.", action = "store_false", dest = "abort", default = True) parser.add_option("-a", "--tryaltconfigs", help = "continue with builds by trying to use alternative providers where possible.", action = "store_true", dest = "tryaltconfigs", default = False) parser.add_option("-f", "--force", help = "force run of specified cmd, regardless of stamp status", action = "store_true", dest = "force", default = False) 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", action = "store", dest = "cmd") 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)", action = "store", dest = "invalidate_stamp") parser.add_option("-r", "--read", help = "read the specified file before bitbake.conf", action = "append", dest = "prefile", default = []) parser.add_option("-R", "--postread", help = "read the specified file after bitbake.conf", action = "append", dest = "postfile", default = []) parser.add_option("-v", "--verbose", help = "output more chit-chat to the terminal", action = "store_true", dest = "verbose", default = False) parser.add_option("-D", "--debug", help = "Increase the debug level. You can specify this more than once.", action = "count", dest="debug", default = 0) parser.add_option("-n", "--dry-run", help = "don't execute, just go through the motions", action = "store_true", dest = "dry_run", default = False) parser.add_option("-S", "--dump-signatures", help = "don't execute, just dump out the signature construction information", action = "store_true", dest = "dump_signatures", default = False) parser.add_option("-p", "--parse-only", help = "quit after parsing the BB files (developers only)", action = "store_true", dest = "parse_only", default = False) parser.add_option("-s", "--show-versions", help = "show current and preferred versions of all recipes", action = "store_true", dest = "show_versions", default = False) parser.add_option("-e", "--environment", help = "show the global or per-package environment (this is what used to be bbread)", action = "store_true", dest = "show_environment", default = False) 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", action = "store_true", dest = "dot_graph", default = False) 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""", action = "append", dest = "extra_assume_provided", default = []) parser.add_option("-l", "--log-domains", help = """Show debug logging for the specified logging domains""", action = "append", dest = "debug_domains", default = []) parser.add_option("-P", "--profile", help = "profile the command and print a report", action = "store_true", dest = "profile", default = False) parser.add_option("-u", "--ui", help = "userinterface to use", action = "store", dest = "ui") parser.add_option("-t", "--servertype", help = "Choose which server to use, none, process or xmlrpc", action = "store", dest = "servertype") parser.add_option("", "--revisions-changed", help = "Set the exit code depending on whether upstream floating revisions have changed or not", action = "store_true", dest = "revisions_changed", default = False) parser.add_option("", "--server-only", help = "Run bitbake without UI, the frontend can connect with bitbake server itself", action = "store_true", dest = "server_only", default = False) parser.add_option("-B", "--bind", help = "The name/address for the bitbake server to bind to", action = "store", dest = "bind", default = False) parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks, forces builds", action = "store_true", dest = "nosetscene", default = False) options, args = parser.parse_args(sys.argv) print "+++++ qc test +++++ options:", options print "+++++ qc test +++++ args:", args configuration = BBConfiguration(options) configuration.pkgs_to_build.extend(args[1:]) print "+++++ qc test +++++ pkgs_to_build:",configuration.pkgs_to_build *** *** # Ensure logging messages get sent to the UI as events handler = bb.event.LogHandler() logger.addHandler(handler) # Before we start modifying the environment we should take a pristine # copy for possible later use initialenv = os.environ.copy() # Clear away any spurious environment variables while we stoke up the cooker cleanedvars = bb.utils.clean_environment() server = server.BitBakeServer() if configuration.bind: server.initServer((configuration.bind, 0)) else: server.initServer() idle = server.getServerIdleCB() cooker = bb.cooker.BBCooker(configuration, idle, initialenv) cooker.parseCommandLine() server.addcooker(cooker) server.saveConnectionDetails() server.detach() # Should no longer need to ever reference cooker del cooker logger.removeHandler(handler) if not configuration.server_only: # Setup a connection to the server (cooker) server_connection = server.establishConnection() # Restore the environment in case the UI needs it for k in cleanedvars: os.environ[k] = cleanedvars[k] try: return server.launchUI(ui_main, server_connection.connection, server_connection.events) finally: bb.event.ui_queue = [] server_connection.terminate() else: print("server address: %s, server port: %s" % (server.serverinfo.host, server.serverinfo.port)) return 1 if __name__ == "__main__": try: ret = main() except Exception: ret = 1 import traceback traceback.print_exc(5) sys.exit(ret) 這里調(diào)用到了 parseConfigurationFiles() 函數(shù),,這個(gè)函數(shù)用于解析 prefiles, postfiles (這兩個(gè)變量都是空,需要通過執(zhí)行bitbake -r -R 指定才行),,解析 layer.conf, bblayer.conf, bitbake.conf, 以及 bb 文件中 inherit 的 bbclass,,以及包含base.bbclass,。 [python] view plaincopy def parseConfigurationFiles(self, prefiles, postfiles): data = self.configuration.data bb.parse.init_parser(data) # Parse files for loading *before* bitbake.conf and any includes for f in prefiles: data = _parse(f, data) layerconf = self._findLayerConf() if layerconf: parselog.debug(2, "Found bblayers.conf (%s)", layerconf) data = _parse(layerconf, data) layers = (data.getVar('BBLAYERS', True) or "").split() data = bb.data.createCopy(data) for layer in layers: parselog.debug(2, "Adding layer %s", layer) data.setVar('LAYERDIR', layer) data = _parse(os.path.join(layer, "conf", "layer.conf"), data) data.expandVarref('LAYERDIR') data.delVar('LAYERDIR') if not data.getVar("BBPATH", True): raise SystemExit("The BBPATH variable is not set") data = _parse(os.path.join("conf", "bitbake.conf"), data) # Parse files for loading *after* bitbake.conf and any includes for p in postfiles: data = _parse(p, data) # Handle any INHERITs and inherit the base class bbclasses = ["base"] + (data.getVar('INHERIT', True) or "").split() for bbclass in bbclasses: data = _inherit(bbclass, data) # Nomally we only register event handlers at the end of parsing .bb files # We register any handlers we've found so far here... for var in data.getVar('__BBHANDLERS') or []: bb.event.register(var, data.getVar(var)) if data.getVar("BB_WORKERCONTEXT", False) is None: bb.fetch.fetcher_init(data) bb.codeparser.parser_cache_init(data) bb.event.fire(bb.event.ConfigParsed(), data) if data.getVar("BB_INVALIDCONF") is True: data.setVar("BB_INVALIDCONF", False) self.parseConfigurationFiles(self.configuration.prefile, self.configuration.postfile) else: bb.parse.init_parser(data) data.setVar('BBINCLUDED',bb.parse.get_file_depends(data)) self.configuration.data = data self.configuration.data_hash = data.get_hash() 將所有的conf, bb, bbclass, etc 文件解析完畢之后,處理一下這些數(shù)據(jù),,提取出task list ,之后就是執(zhí)行這些task 了,。 下面以linux-yocto 為例看下如何找到這些task 的先后關(guān)系: yocto_build/tmp/work/qemuppc-poky-linux/linux-yocto/3.4.36+gitAUTOINC+80b4b5110dca5184b57e85f1e775fb006a2e85ad_ddbc382cbc45a009e9ce17f7d448fcfd050ab5fc-r4.3/temp/log.task_order 這個(gè)文件顯示了編譯 linux-yocto 執(zhí)行的task 以及 task 的執(zhí)行順序。 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 接下來介紹如何驗(yàn)證這些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./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 所有相關(guān)的bb, bbclass 文件,,所有的task 都是通過addtasks 關(guān)鍵字添加的,,它們之間先后關(guān)系構(gòu)成了所謂的依賴關(guān)系,,第一個(gè)task,,在這幾個(gè)bb, bbclass 文件里面肯定沒有 addtask before the_1st_task 這樣的語(yǔ)句,。 poky/meta/classes$ grep -nr "addtask . | grep "patch" 就按照上面這樣的方法不斷的grep 就能驗(yàn)證出log.task_order 里面顯示的task,,以及 正確的執(zhí)行順序。 ex. $ bitbake linux-yocto -c cleanall 執(zhí)行 cleanall task 需要運(yùn)行的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 是我自己加的測(cè)試 task: addtask cleansstate after do_clean addtask qc_test after do_cleansstate do_qc_test() { echo "qc hello base.bbclass !" echo "qc test !!!!!~~~~ " } 這樣,,構(gòu)建起來的tasklist 如下: clean clean cleansstate cleansstate cleanall qc_test 如此,,可以看出,執(zhí)行 cleanall task 的話,,clean, cleansstate task 都要執(zhí)行,,因?yàn)榫哂幸蕾囮P(guān)系,相反,,qc_test 和 cleanall 雖然都是 after do_cleansstate ,,但是二者之間沒有依賴關(guān)系,。其它的task 的 runqueue list 也是這樣得到。 bitbake 如果不刻意指定要執(zhí)行的task 的話,,默認(rèn)執(zhí)行的是build 操作,而這個(gè)操作是針對(duì)一系列的bb 文件,,這些文件是在BBFILES定義的??匆幌翨BFILES 這個(gè)變量的來歷,方法如下 /poky/bitbake$ grep -nr "BBFILES" . 直接在bitbake 目錄下面搜索有誰(shuí)對(duì)BBFILES 這個(gè)變量進(jìn)行了賦值操作,,這樣就能定位到./lib/bb/cooker.py 反向推理一下: 搜集bbfiles 函數(shù): [plain] view plaincopy def collect_bbfiles( self ): """Collect all available .bb build files""" parsed, cached, skipped, masked = 0, 0, 0, 0 collectlog.debug(1, "collecting .bb files") files = (data.getVar( "BBFILES", self.configuration.data, True) or "").split() data.setVar("BBFILES", " ".join(files), self.configuration.data) # Sort files by priority files.sort( key=lambda fileitem: self.calc_bbfile_priority(fileitem) ) [plain] view plaincopy def matchFile(self, buildfile): """ Find the .bb file which matches the expression in 'buildfile'. Raise an error if multiple files """ matches = self.matchFiles(buildfile) [plain] view plaincopy def buildFile(self, buildfile, task): """ Build the file matching regexp buildfile """ # Too many people use -b because they think it's how you normally # specify a target to be built, so show a warning bb.warn("Buildfile specified, dependencies will not be handled. If this is not what you want, do not use -b / --buildfile.") # Parse the configuration here. We need to do it explicitly here since # buildFile() doesn't use the cache self.parseConfiguration() # If we are told to do the None task then query the default task if (task == None): task = self.configuration.cmd fn, cls = bb.cache.Cache.virtualfn2realfn(buildfile) fn = self.matchFile(fn) self.buildSetVars() [plain] view plaincopy def parseCommandLine(self): # Parse any commandline into actions self.commandlineAction = {'action':None, 'msg':None} if self.configuration.show_environment: if 'world' in self.configuration.pkgs_to_build: self.commandlineAction['msg'] = "'world' is not a valid target for --environment." elif 'universe' in self.configuration.pkgs_to_build: self.commandlineAction['msg'] = "'universe' is not a valid target for --environment." elif len(self.configuration.pkgs_to_build) > 1: self.commandlineAction['msg'] = "Only one target can be used with the --environment option." elif self.configuration.buildfile and len(self.configuration.pkgs_to_build) > 0: self.commandlineAction['msg'] = "No target should be used with the --environment and --buildfile options." elif len(self.configuration.pkgs_to_build) > 0: self.commandlineAction['action'] = ["showEnvironmentTarget", self.configuration.pkgs_to_build] self.configuration.data.setVar("BB_CONSOLELOG", None) else: self.commandlineAction['action'] = ["showEnvironment", self.configuration.buildfile] self.configuration.data.setVar("BB_CONSOLELOG", None) elif self.configuration.buildfile is not None: self.commandlineAction['action'] = ["buildFile", self.configuration.buildfile, self.configuration.cmd] elif self.configuration.revisions_changed: self.commandlineAction['action'] = ["compareRevisions"] elif self.configuration.show_versions: self.commandlineAction['action'] = ["showVersions"] elif self.configuration.parse_only: self.commandlineAction['action'] = ["parseFiles"] elif self.configuration.dot_graph: if self.configuration.pkgs_to_build: self.commandlineAction['action'] = ["generateDotGraph", self.configuration.pkgs_to_build, self.configuration.cmd] else: self.commandlineAction['msg'] = "Please specify a package name for dependency graph generation." else: if self.configuration.pkgs_to_build: self.commandlineAction['action'] = ["buildTargets", self.configuration.pkgs_to_build, self.configuration.cmd] else: #self.commandlineAction['msg'] = "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information." self.commandlineAction = None 仔細(xì)看下上面這個(gè)函數(shù),,是不是和最初在bitbake 這個(gè)python 根腳本中定義的main 函數(shù)有很多相似之處,,這樣就能猜到 bitbake -b xxx 這個(gè)指令回執(zhí)行到buildFile 這個(gè)python 函數(shù),。同樣可以知道 bitbake -e 可以看到所有的環(huán)境變量,,包括BBFILES 變量的值,因?yàn)樗鼒?zhí)行了 showEnvrioment 這個(gè)python 函數(shù),。 強(qiáng)烈建議把 bitbake -e > ~/bitbake_-e.txt 重定向到文件中好好看看,。 [plain] view plaincopy def main(): parser = optparse.OptionParser( version = "BitBake Build Tool Core version %s, %%prog version %s" % (bb.__version__, __version__), usage = """%prog [options] [package ...] Executes the specified task (default is 'build') for a given set of BitBake files. It expects that BBFILES is defined, which is a space separated list of files to be executed. BBFILES does support wildcards. Default BBFILES are the .bb files in the current directory.""") parser.add_option("-b", "--buildfile", help = "execute the task against this .bb file, rather than a package from BBFILES. Does not handle any dependencies.", action = "store", dest = "buildfile", default = None) 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.", action = "store_false", dest = "abort", default = True) parser.add_option("-a", "--tryaltconfigs", help = "continue with builds by trying to use alternative providers where possible.", action = "store_true", dest = "tryaltconfigs", default = False) parser.add_option("-f", "--force", help = "force run of specified cmd, regardless of stamp status", action = "store_true", dest = "force", default = False) 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", action = "store", dest = "cmd") OK. 再看一下 parseCommandLine 這個(gè)函數(shù)if elif elif ,,, else 通過不斷的探測(cè)bitbake 的選項(xiàng)參數(shù),如果沒有選項(xiàng)參數(shù),,只有target 走到buildTargets 這個(gè)else 里面,,接下來要執(zhí)行的函數(shù)是buildTargets. [plain] view plaincopy def parseCommandLine(self): # Parse any commandline into actions self.commandlineAction = {'action':None, 'msg':None} if self.configuration.show_environment: if 'world' in self.configuration.pkgs_to_build: self.commandlineAction['msg'] = "'world' is not a valid target for --environment." elif 'universe' in self.configuration.pkgs_to_build: self.commandlineAction['msg'] = "'universe' is not a valid target for --environment." elif len(self.configuration.pkgs_to_build) > 1: self.commandlineAction['msg'] = "Only one target can be used with the --environment option." elif self.configuration.buildfile and len(self.configuration.pkgs_to_build) > 0: self.commandlineAction['msg'] = "No target should be used with the --environment and --buildfile options." elif len(self.configuration.pkgs_to_build) > 0: self.commandlineAction['action'] = ["showEnvironmentTarget", self.configuration.pkgs_to_build] self.configuration.data.setVar("BB_CONSOLELOG", None) else: self.commandlineAction['action'] = ["showEnvironment", self.configuration.buildfile] self.configuration.data.setVar("BB_CONSOLELOG", None) elif self.configuration.buildfile is not None: self.commandlineAction['action'] = ["buildFile", self.configuration.buildfile, self.configuration.cmd] elif self.configuration.revisions_changed: self.commandlineAction['action'] = ["compareRevisions"] elif self.configuration.show_versions: self.commandlineAction['action'] = ["showVersions"] elif self.configuration.parse_only: self.commandlineAction['action'] = ["parseFiles"] elif self.configuration.dot_graph: if self.configuration.pkgs_to_build: self.commandlineAction['action'] = ["generateDotGraph", self.configuration.pkgs_to_build, self.configuration.cmd] else: self.commandlineAction['msg'] = "Please specify a package name for dependency graph generation." else: if self.configuration.pkgs_to_build: self.commandlineAction['action'] = ["buildTargets", self.configuration.pkgs_to_build, self.configuration.cmd] else: #self.commandlineAction['msg'] = "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information." self.commandlineAction = None 看一個(gè)特定的package 編譯流程,拿kernel 看吧: 從bitbake_-e.txt 環(huán)境中搜到: 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 plaincopy 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./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)}" 先查看下 linux-yocto_3.4.bb require 的 recipes-kernel/linux/linux-yocto.inc [plain] view plaincopy 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 這里inherit kernel, inherit kernel-yocto, 在meta/class 里面可以看到 kernel.class ,kernel-yocto.class 文件,里面有kernel 公共的base 函數(shù),。 http://blog./uid-7652108-id-2047290.html bitbake parse 的機(jī)理分析: Bitbake 這個(gè) task execute tool的第一步就是parsing,,也就是對(duì)BBFILES所定義的變量的內(nèi)容,也就是bbfiles 進(jìn)行讀取data,,然后分析,,相關(guān)數(shù)據(jù)進(jìn)行緩存(cache) 出發(fā)是從 conf/bitbake.conf 這個(gè)文件開始,這個(gè)文件本身的內(nèi)容處理是一部分,,另外一部分是這個(gè)文件會(huì)include一些其它的conf文件,,比如很重要的 conf/local.conf,分析處理的數(shù)據(jù)都會(huì)寫到 CACHEDATA的文件中(bb_cache.dat) ,,當(dāng)然前提是你開啟了cache機(jī)制 處理完conf文件后,,就是bbclass文件,,這個(gè)時(shí)候是從 class/base.bbclass開始,,然后分析其inherit的一些class。 這兩部分?jǐn)?shù)據(jù)很重要,,后來所有的bbfile 都依賴于這些conf 文件和class文件,,當(dāng)然這些文件中的變量也會(huì)實(shí)施到所有的bb file上,所以只要這些conf文件和class文件有改變,,那么bitbake 就會(huì)重新parse 那么,,到現(xiàn)在,,就開始進(jìn)入parsing bb file了,當(dāng)然第一次肯定要parse所有的bbfile了,,這部分是最耗時(shí)的,,第一次完了之后,,那么就可以利用cache機(jī)制來判斷是否重新parse了 上面就是bitbake version 1版本parsing的機(jī)制,,理解了code背后的故事,才能讓我們更游刃有余地工作 膜拜這個(gè)大神,,早在07年就把 bitbake 一個(gè)package 的流程分析的這么透徹了: http://blog./uid-7652108-id-2047247.html 在 linux-yocto.inc 文件中 inherit kernel-yocto,, 來看下 class/kernel-yocto.bbclass , 分析一下do_kernel_configme 這個(gè)函數(shù)。添加log語(yǔ)句打印一下這個(gè)函數(shù)中的一些變量,。 ${S} 表示linux source code 的目錄,, ${B} 表示linux build 目錄。 在 linux_source_code_path/meta/scripts/configme 執(zhí)行這個(gè)shell 腳本,。 [python] view plaincopy do_kernel_configme[dirs] = "${S} ${B}" do_kernel_configme() { echo "[INFO] doing kernel configme" export KMETA=${KMETA} if [ -n ${KCONFIG_MODE} ]; then configmeflags=${KCONFIG_MODE} else # If a defconfig was passed, use =n as the baseline, which is achieved # via --allnoconfig if [ -f ${WORKDIR}/defconfig ]; then configmeflags="--allnoconfig" fi fi cd ${S} PATH=${PATH}:${S}/scripts/util configme ${configmeflags} --reconfig --output ${B} ${LINUX_KERNEL_TYPE} ${KMACHINE} if [ $? -ne 0 ]; then echo "ERROR. Could not configure ${KMACHINE}-${LINUX_KERNEL_TYPE}" exit 1 fi #qc added log echo "qc variables value: ${S} ${B} ${KMETA} ${KCONFIG_MODE} ${WORKDIR} ${LINUX_KERNEL_TYPE} ${KMACHINE}." echo "# Global settings from linux recipe" >> ${B}/.config echo "CONFIG_LOCALVERSION="\"${LINUX_VERSION_EXTENSION}\" >> ${B}/.config } configme 這個(gè) shell 腳本完成 生成.config 文件的工作: [plain] view plaincopy # This is factored out into a function because for a given branch, # there may be more than one user (i.e. big endian, little endian, # or BSPs that use the same branch but differ only in kernel configs) run_board_config() { # Can't set these until we've unwound the checkpoint and have meta data. KVER=`cat ./$META_DIR/cfg/kernel-*cache/kver|sed 's/^v//'` # Look for standard defines, with compatibility fallbacks KARCH=`grep KARCH $SCC | awk '{print $3}'` KPROFILE=`grep KMACHINE $SCC | awk '{print $3}'` KTYPE=`grep KTYPE $SCC | awk '{print $3}'` META=./$META_DIR/meta-series META_ALT=./$META_DIR/cfg/scratch/`basename $SCC .scc`-meta BUILD_DIR=$out_dir CFGFILE=$machine-$target-config-$KVER kgit-meta -v -k $META if [ $? != 0 ]; then echo Error running the meta series for collecting config data return 1 fi KTGT=`get_branch_name $META` mkdir -p ./$META_DIR/cfg/$KTGT if [ $? != 0 ]; then echo Failed to mkdir ./$META_DIR/cfg/$KTGT for config data return 1 fi frags=`cat $META_DIR/cfg/$KTGT/config_frag.txt | sed 's%.?$%'$META_DIR/cfg'\1%'` pre_config -l $META_DIR/cfg/$KTGT/ $frags > $META_DIR/cfg/$KTGT/config.log 2>&1 # remove any old assembled debug fragments rm -f $BUILD_DIR/.tmp.config* merge_frags=`cat $META_DIR/cfg/$KTGT/config_frag.txt | sed 's%.?$%'$META_DIR/cfg'\1.sanitized%'` ARCH=$KARCH O=$BUILD_DIR merge_config.sh $allnoconfig -d $merge_frags \ > $META_DIR/cfg/$KTGT/merge_log.txt 2>&1 mv $BUILD_DIR/.tmp.config* $META_DIR/cfg/$KTGT/$CFGFILE if [ $? != 0 ]; then echo creation of pre-processed config data failed return 1 fi # break the merge log down into parts that can be processed later grep -A2 "^Value of" $META_DIR/cfg/$KTGT/merge_log.txt > $META_DIR/cfg/$KTGT/redefinition.txt grep -A2 "^Value requested" $META_DIR/cfg/$KTGT/merge_log.txt > $META_DIR/cfg/$KTGT/mismatch.txt echo "[INFO] Pre-processed cfg file $CFGFILE created." 拼接一系列的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 這個(gè)就是要處理的所有 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 是從哪來的了!??! 了解更多關(guān)于 yocto kernel config 可以 git clone git://git./yocto-kernel-cache,查看 00-README 文檔,。 http://blog.csdn.net/fmddlmyy/article/details/325403 在 bitbake 中加入 print log語(yǔ)句,,方便調(diào)試: [python] view plaincopy parser.add_option("-B", "--bind", help = "The name/address for the bitbake server to bind to", action = "store", dest = "bind", default = False) parser.add_option("", "--no-setscene", help = "Do not run any setscene tasks, forces builds", action = "store_true", dest = "nosetscene", default = False) options, args = parser.parse_args(sys.argv) #qc test code print "qc test code: options:", options print "qc test code: args", args configuration = BBConfiguration(options) configuration.pkgs_to_build.extend(args[1:]) |
|