QEMU 构建

qemu 的 configure 并没有使用 Autotools 那一套,而是手写的 configure shell 脚本,但是命令行选项模拟了 Autotools 的实现,如显式启用、禁用特性:

--enable-foo, --disable-foo

configure 原理

特性的控制当前分两部分,一部分是 configure 脚本实现,一部分是 meson 构建工具实现,但最终会统一至 meson,如对 rbd 特性的修改:

rbd: convert to meson
https://github.com/qemu/qemu/commit/fabd1e93d93ef90ddf8574a42aee406314cc47c4

configure 脚本实现的开关

yes/no/""/default_feature,如:libpmem="$default_feature"

default_feature 默认为 ""

--without-default-features 选项将设置 default_featureno

default_feature=""
# parse CC options second
for opt do
  optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)')
  case "$opt" in
      --without-default-features)
          default_feature="no"
  ;;
  esac
done

以 rdma 为例:

## qemu/configure

rdma="$default_feature"

--enable-rdma) rdma="yes"
;;
--disable-rdma) rdma="no"
;;

# RDMA needs OpenFabrics libraries
if test "$rdma" != "no" ; then
  cat > $TMPC <<EOF
#include <rdma/rdma_cma.h>
int main(void) { return 0; }
EOF
  rdma_libs="-lrdmacm -libverbs -libumad"
  if compile_prog "" "$rdma_libs" ; then
    rdma="yes"
  else
    if test "$rdma" = "yes" ; then
        error_exit \
            " OpenFabrics librdmacm/libibverbs/libibumad not present." \
            " Your options:" \
            "  (1) Fast: Install infiniband packages (devel) from your distro." \
            "  (2) Cleanest: Install libraries from www.openfabrics.org" \
            "  (3) Also: Install softiwarp if you don't have RDMA hardware"
    fi
    rdma="no"
  fi
fi

meson 构建选项实现的开关

enabled/disabled/auto,如:vnc="enabled"

If the value of a feature option is set to auto, that value is overridden by the global auto_features option (which defaults to auto). This is intended to be used by packagers who want to have full control on which dependencies are required and which are disabled, and not rely on build-deps being installed (at the right version) to get a feature enabled. They could set auto_features=enabled to enable all features and disable explicitly only the few they don’t want, if any.

Build options
https://mesonbuild.com/Build-options.html

## qemu/configure

NINJA=$ninja $meson setup \
    $(if test "$default_feature" = no; then echo "-Dauto_features=disabled"; fi)

configure: Fix –without-default-features propagation to meson
https://github.com/qemu/qemu/commit/332008e0b9da33dee2c765db3e4e16b3c3ba3a92

显然,--without-default-features 选项决定了 auto_features 的行为。

以 rbd 为例:

## qemu/configure

rbd="auto"

--disable-rbd) rbd="disabled"
;;
--enable-rbd) rbd="enabled"
;;

NINJA=$ninja $meson setup \
    -Drbd=$rbd
## qemu/meson_options.txt

option('rbd', type : 'feature', value : 'auto',
       description: 'Ceph block device driver')
## qemu/configure

echo "TARGET_DIRS=$target_list" >> $config_host_mak
## qemu/meson.build

target_dirs = config_host['TARGET_DIRS'].split()
have_user = false
have_system = false
foreach target : target_dirs
  have_user = have_user or target.endswith('-user')
  have_system = have_system or target.endswith('-softmmu')
endforeach
have_tools = 'CONFIG_TOOLS' in config_host
have_block = have_system or have_tools

rbd = not_found
if not get_option('rbd').auto() or have_block
  librados = cc.find_library('rados', required: get_option('rbd'),
                             kwargs: static_kwargs)
  librbd = cc.find_library('rbd', has_headers: ['rbd/librbd.h'],
                           required: get_option('rbd'),
                           kwargs: static_kwargs)
  if librados.found() and librbd.found()
    if cc.links('''
      #include <stdio.h>
      #include <rbd/librbd.h>
      int main(void) {
        rados_t cluster;
        rados_create(&cluster, NULL);
        return 0;
      }''', dependencies: [librbd, librados])
      rbd = declare_dependency(dependencies: [librbd, librados])
    elif get_option('rbd').enabled()
      error('could not link librados')
    else
      warning('could not link librados, disabling')
    endif
  endif
endif

上面的判断 if not get_option('rbd').auto() 让人有点意外,看上去是个明显的错误(特别是对照 configure 版本的处理来看),当然,由于 have_block 的存在使得这个条件存在与否基本上无关紧要。

## qemu/block/meson.build

block_modules = {}

modsrc = []
foreach m : [
  [curl, 'curl', [curl, glib], 'curl.c'],
  [glusterfs, 'gluster', glusterfs, 'gluster.c'],
  [libiscsi, 'iscsi', libiscsi, 'iscsi.c'],
  [libnfs, 'nfs', libnfs, 'nfs.c'],
  [libssh, 'ssh', libssh, 'ssh.c'],
  [rbd, 'rbd', rbd, 'rbd.c'],
]
  if m[0].found()
    if enable_modules
      modsrc += files(m[3])
    endif
    module_ss = ss.source_set()
    module_ss.add(when: m[2], if_true: files(m[3]))
    block_modules += {m[1] : module_ss}
  endif
endforeach

同时需要注意的是,configure 脚本与 meson 构建选项之间通过 meson_option_parse bash 函数建立了联系:

# everything else has the same name in configure and meson
--enable-* | --disable-*) meson_option_parse "$opt" "$optarg"
;;

因此,并不是所有选项都需要在 configure 脚本中进行显式的定义和解析。

v7.1.0+ 版本代码稍有改动如下:

# everything else has the same name in configure and meson
  --*) meson_option_parse "$opt" "$optarg"

configure & build

$ wget https://download.qemu.org/qemu-6.1.0.tar.xz
$ tar xJf qemu-6.1.0.tar.xz
$ cd qemu-6.1.0/
$ mkdir build
$ cd build/
$ ../configure --prefix=$PWD/qemu-bin --target-list='x86_64-softmmu,aarch64-softmmu' \
--enable-kvm --enable-tools --without-default-features --enable-linux-aio --enable-rbd \
--enable-vnc --enable-vnc-png
$ make
$ make install
$ tree -L 2 qemu-bin/
qemu-bin/
├── bin
│   ├── qemu-edid
│   ├── qemu-img
│   ├── qemu-io
│   ├── qemu-nbd
│   ├── qemu-pr-helper
│   ├── qemu-storage-daemon
│   ├── qemu-system-aarch64
│   └── qemu-system-x86_64
├── libexec
│   └── qemu-bridge-helper
└── share
    ├── applications
    ├── icons
    └── qemu
$ tree -L 1 qemu-bin/share/qemu/
qemu-bin/share/qemu/
├── bamboo.dtb
├── bios-256k.bin
├── bios.bin
├── bios-microvm.bin
├── canyonlands.dtb
├── edk2-aarch64-code.fd
├── edk2-arm-code.fd
├── edk2-arm-vars.fd
├── edk2-i386-code.fd
├── edk2-i386-secure-code.fd
├── edk2-i386-vars.fd
├── edk2-licenses.txt
├── edk2-x86_64-code.fd
├── edk2-x86_64-secure-code.fd
...

由于 --without-default-features 将可选特性都设置成了禁用,因此需要显式的使能 kvm, tools, aio, rbd, vnc 等可选特性。

qemu-bin/ 文件夹拷贝至合适的地方,然后既可以将 qemu-bin/bin/ 加入 PATH,也可以直接以绝对定位的方式运行 qemu 工具。


最后修改于 2021-09-08