OpenBMC v2.8 devtool
在 Ubuntu 20.04 docker 环境构建 OpenBMC v2.8 时遇到了一个 devtool 使用的问题,记录一下。

问题现象

$ devtool modify u-boot

File: '/usr/lib/python3.8/sysconfig.py', lineno: 431, function: _init_posix
     0427:def _init_posix(vars):
     0428:    """Initialize the module as appropriate for POSIX systems."""
     0429:    # _sysconfigdata is generated at build time, see _generate_posix_vars()
     0430:    name = _get_sysconfigdata_name()
 *** 0431:    _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0)
     0432:    build_time_vars = _temp.build_time_vars
     0433:    vars.update(build_time_vars)
     0434:
     0435:def _init_non_posix(vars):
Exception: ModuleNotFoundError: No module named '_sysconfigdata'

分析定位

通过 --keep-temp 选项保留日志等临时文件以辅助定位:

$ devtool modify --keep-temp u-boot

ERROR: Logfile of failure stored in: /home/runsisi/working/bmc/openbmc-2.8.0/build/romulus/tmp/work/romulus-openbmc-linux-gnueabi/u-boot-aspeed/1_v2016.07+gitAUTOINC+59428fe010-r0/devtooltmp-68hlmktt/temp/log.do_unpack.2424525
NOTE: Tasks Summary: Attempted 114 tasks of which 112 didn't need to be rerun and 1 failed.
INFO: Preserving temporary directory /home/runsisi/working/bmc/openbmc-2.8.0/build/romulus/tmp/work/romulus-openbmc-linux-gnueabi/u-boot-aspeed/1_v2016.07+gitAUTOINC+59428fe010-r0/devtooltmp-68hlmktt
ERROR: Extracting source for u-boot-aspeed failed

但是即使分析日志,也看不出来太多问题。

devtool 实际上只是 bitbake 的一个客户端,会自动启动 bitbake 服务端(如果没有启动的话),我们也可以手工启动:

$ bitbake -m
$ bitbake --server-only

$ ls build/romulus/bitbake.sock 
build/romulus/bitbake.sock

$ ps aux | grep bitbake
runsisi  2425021  0.0  0.0  60216 47664 ?        S    00:13   0:00 python3 /home/runsisi/working/bmc/openbmc-2.8.0/poky/bitbake/bin/bitbake --server-only
-v, --verbose       Enable tracing of shell tasks (with 'set -x'). Also
                    print bb.note(...) messages to stdout (in addition to
                    writing them to ${T}/log.do_<task>).
-D, --debug         Increase the debug level. You can specify this more
                    than once. -D sets the debug level to 1, where only
                    bb.debug(1, ...) messages are printed to stdout; -DD
                    sets the debug level to 2, where both bb.debug(1, ...)
                    and bb.debug(2, ...) messages are printed; etc.
                    Without -D, no debug messages are printed. Note that
                    -D only affects output to stdout. All debug messages
                    are written to ${T}/log.do_taskname, regardless of the
                    debug level.
--server-only       Run bitbake without a UI, only starting a server
                    (cooker) process.

但 bitbake 是多进程架构,没法前台运行:

# bitbake/lib/bb/server/process.py

class BitBakeServer(object):
    bb.daemonize.createDaemon(self._startServer, logfile)

devtool 的请求最终发给 bitbake 服务进程进行处理:

# scripts/lib/devtool/standard.py

def modify():
    tinfoil = setup_tinfoil
    _extract_source(tinfoil)

def _extract_source(tinfoil):
    tinfoil.build_targets()

但是,可以通过使用增加日志打印的手段进行调试,修改代码之后需要执行 bitbake -m 杀掉已有的 bitbake 进程,否则新代码不会加载。

通过打印可以确认在 devtool_post_unpack 中调用 sysconfig.get_config_vars() 会出错:

# meta/classes/devtool-source.bbclass

python devtool_post_unpack()
    import sysconfig
    bb.warn(str(sysconfig.get_config_vars()))

再进一步,是 _get_sysconfigdata_name 拿到模块名是 _sysconfigdata,通过显式的将 name 赋值为 _sysconfigdata__x86_64-linux-gnu 问题得以规避:

# /usr/lib/python3.8/sysconfig.py

def _get_sysconfigdata_name():
    return os.environ.get('_PYTHON_SYSCONFIGDATA_NAME',
        '_sysconfigdata_{abi}_{multiarch}'.format(
        abi=sys.abiflags,
        multiarch=getattr(sys.implementation, '_multiarch', ''),
    ))

def _init_posix(vars):
    """Initialize the module as appropriate for POSIX systems."""
    # _sysconfigdata is generated at build time, see _generate_posix_vars()
    name = _get_sysconfigdata_name()
    name = '_sysconfigdata__x86_64-linux-gnu'
    _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0)
    build_time_vars = _temp.build_time_vars
    vars.update(build_time_vars)

通过打印可知 sys.implementation 的值并不为空,问题显然出在 _PYTHON_SYSCONFIGDATA_NAME 环境变量上,该环境变量被显示赋值成了 _sysconfigdata,实际上,在代码里能找到对该环境变量相关的处理和说明(注意 u-boot-aspeed.inc 显式 inherit 了 python3native):

# meta/lib/oe/prservice.py

def prserv_make_conn(d, check = False):
    # Otherwise this fails when called from recipes which e.g. inherit python3native (which sets _PYTHON_SYSCONFIGDATA_NAME) with:
    # No module named '_sysconfigdata'
    if '_PYTHON_SYSCONFIGDATA_NAME' in os.environ:
        del os.environ['_PYTHON_SYSCONFIGDATA_NAME']

解决方案

照葫芦画瓢,对 devtool_post_unpack 增加相应的处理(新版本 OpenBMC python3native.bbclass 已没有对 _PYTHON_SYSCONFIGDATA_NAME 的处理):

# meta/classes/devtool-source.bbclass

python devtool_post_unpack() {
    # Otherwise this fails when called from recipes which e.g. inherit python3native (which sets _PYTHON_SYSCONFIGDATA_NAME) with:
    # No module named '_sysconfigdata'
    if '_PYTHON_SYSCONFIGDATA_NAME' in os.environ:
        del os.environ['_PYTHON_SYSCONFIGDATA_NAME']

至此,问题得以解决。

当然,通过上面的分析可知,还有一个更简单的规避手段:

$ sudo ln -s /usr/lib/python3.8/_sysconfigdata__x86_64-linux-gnu.py /usr/lib/python3.8/_sysconfigdata.py

记得执行 bitbake -m 以启动新的 bitbake 服务进程。


最后修改于 2024-01-03