Skip to content
Snippets Groups Projects
meson.build 158 KiB
Newer Older
Paolo Bonzini's avatar
Paolo Bonzini committed
project('qemu', ['c'], meson_version: '>=0.63.0',
        default_options: ['warning_level=1', 'c_std=gnu11', 'cpp_std=gnu++11', 'b_colorout=auto',
                          'b_staticpic=false', 'stdsplit=false', 'optimization=2', 'b_pie=true'],
add_test_setup('quick', exclude_suites: ['slow', 'thorough'], is_default: true)
add_test_setup('slow', exclude_suites: ['thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow'])
add_test_setup('thorough', env: ['G_TEST_SLOW=1', 'SPEED=thorough'])
meson.add_postconf_script(find_program('scripts/symlink-install-tree.py'))

not_found = dependency('', required: false)
ss = import('sourceset')
targetos = host_machine.system()
sh = find_program('sh')
config_host = keyval.load(meson.current_build_dir() / 'config-host.mak')
cc = meson.get_compiler('c')
all_languages = ['c']
if targetos == 'windows' and add_languages('cpp', required: false, native: false)
  all_languages += ['cpp']
  cxx = meson.get_compiler('cpp')
endif
if targetos == 'darwin' and \
   add_languages('objc', required: get_option('cocoa'), native: false)
  all_languages += ['objc']
  objc = meson.get_compiler('objc')
endif

# Temporary directory used for files created while
# configure runs. Since it is in the build directory
# we can safely blow away any previous version of it
# (and we need not jump through hoops to try to delete
# it when configure exits.)
tmpdir = meson.current_build_dir() / 'meson-private/temp'

if get_option('qemu_suffix').startswith('/')
  error('qemu_suffix cannot start with a /')
endif

qemu_confdir = get_option('sysconfdir') / get_option('qemu_suffix')
qemu_datadir = get_option('datadir') / get_option('qemu_suffix')
qemu_docdir = get_option('docdir') / get_option('qemu_suffix')
qemu_moddir = get_option('libdir') / get_option('qemu_suffix')

qemu_desktopdir = get_option('datadir') / 'applications'
qemu_icondir = get_option('datadir') / 'icons'

config_host_data = configuration_data()
genh = []
bsd_oses = ['gnu/kfreebsd', 'freebsd', 'netbsd', 'openbsd', 'dragonfly', 'darwin']
supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux']
supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv32', 'riscv64', 'x86', 'x86_64',
  'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc64']

cpu = host_machine.cpu_family()

target_dirs = config_host['TARGET_DIRS'].split()
have_linux_user = false
have_bsd_user = false
have_system = false
foreach target : target_dirs
  have_linux_user = have_linux_user or target.endswith('linux-user')
  have_bsd_user = have_bsd_user or target.endswith('bsd-user')
  have_system = have_system or target.endswith('-softmmu')
endforeach
have_user = have_linux_user or have_bsd_user
have_tools = get_option('tools') \
  .disable_auto_if(not have_system) \
  .allowed()
have_ga = get_option('guest_agent') \
  .disable_auto_if(not have_system and not have_tools) \
  .require(targetos in ['sunos', 'linux', 'windows', 'freebsd', 'netbsd', 'openbsd'],
           error_message: 'unsupported OS for QEMU guest agent') \
  .allowed()
enable_modules = get_option('modules') \
  .require(targetos != 'windows',
           error_message: 'Modules are not available for Windows') \
  .require(not get_option('prefer_static'),
           error_message: 'Modules are incompatible with static linking') \
  .allowed()
have_block = have_system or have_tools

python = import('python').find_installation()

if cpu not in supported_cpus
  host_arch = 'unknown'
elif cpu == 'x86'
  host_arch = 'i386'
elif cpu == 'mips64'
  host_arch = 'mips'
elif cpu in ['riscv32', 'riscv64']
  host_arch = 'riscv'
if cpu in ['x86', 'x86_64']
  kvm_targets = ['i386-softmmu', 'x86_64-softmmu']
elif cpu == 'aarch64'
  kvm_targets = ['aarch64-softmmu']
elif cpu == 's390x'
  kvm_targets = ['s390x-softmmu']
elif cpu in ['ppc', 'ppc64']
  kvm_targets = ['ppc-softmmu', 'ppc64-softmmu']
elif cpu in ['mips', 'mips64']
  kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu']
elif cpu in ['riscv32']
  kvm_targets = ['riscv32-softmmu']
elif cpu in ['riscv64']
  kvm_targets = ['riscv64-softmmu']
if get_option('kvm').allowed() and targetos == 'linux'
  kvm_targets_c = '"' + '" ,"'.join(kvm_targets) + '"'
endif
config_host_data.set('CONFIG_KVM_TARGETS', kvm_targets_c)

accelerator_targets = { 'CONFIG_KVM': kvm_targets }

if cpu in ['aarch64']
  accelerator_targets += {
    'CONFIG_HVF': ['aarch64-softmmu']
  }
endif

if cpu in ['x86', 'x86_64', 'arm', 'aarch64']
  # i386 emulator provides xenpv machine type for multiple architectures
  accelerator_targets += {
    'CONFIG_XEN': ['i386-softmmu', 'x86_64-softmmu', 'aarch64-softmmu'],
if cpu in ['x86', 'x86_64']
  accelerator_targets += {
    'CONFIG_HVF': ['x86_64-softmmu'],
    'CONFIG_NVMM': ['i386-softmmu', 'x86_64-softmmu'],
    'CONFIG_WHPX': ['i386-softmmu', 'x86_64-softmmu'],
  }
endif

modular_tcg = []
# Darwin does not support references to thread-local variables in modules
if targetos != 'darwin'
  modular_tcg = ['i386-softmmu', 'x86_64-softmmu']
endif
edk2_targets = [ 'arm-softmmu', 'aarch64-softmmu', 'i386-softmmu', 'x86_64-softmmu' ]
unpack_edk2_blobs = false
foreach target : edk2_targets
  if target in target_dirs
    bzip2 = find_program('bzip2', required: get_option('install_blobs'))
    unpack_edk2_blobs = bzip2.found()
    break
  endif
endforeach
dtrace = not_found
stap = not_found
if 'dtrace' in get_option('trace_backends')
  dtrace = find_program('dtrace', required: true)
  stap = find_program('stap', required: false)
  if stap.found()
    # Workaround to avoid dtrace(1) producing a file with 'hidden' symbol
    # visibility. Define STAP_SDT_V2 to produce 'default' symbol visibility
    # instead. QEMU --enable-modules depends on this because the SystemTap
    # semaphores are linked into the main binary and not the module's shared
    # object.
    add_global_arguments('-DSTAP_SDT_V2',
                         native: false, language: all_languages)
if get_option('iasl') == ''
  iasl = find_program('iasl', required: false)
else
  iasl = find_program(get_option('iasl'), required: true)
endif

##################
# Compiler flags #
##################

foreach lang : all_languages
  compiler = meson.get_compiler(lang)
  if compiler.get_id() == 'gcc' and compiler.version().version_compare('>=7.4')
    # ok
  elif compiler.get_id() == 'clang' and compiler.compiles('''
      #ifdef __apple_build_version__
      # if __clang_major__ < 12 || (__clang_major__ == 12 && __clang_minor__ < 0)
      #  error You need at least XCode Clang v12.0 to compile QEMU
      # endif
      #else
      # if __clang_major__ < 10 || (__clang_major__ == 10 && __clang_minor__ < 0)
      #  error You need at least Clang v10.0 to compile QEMU
      # endif
      #endif''')
    # ok
  else
    error('You either need GCC v7.4 or Clang v10.0 (or XCode Clang v12.0) to compile QEMU')
  endif
endforeach

# default flags for all hosts
# We use -fwrapv to tell the compiler that we require a C dialect where
# left shift of signed integers is well defined and has the expected
# 2s-complement style results. (Both clang and gcc agree that it
# provides these semantics.)

qemu_common_flags = [
  '-D_GNU_SOURCE', '-D_FILE_OFFSET_BITS=64', '-D_LARGEFILE_SOURCE',
  '-fno-strict-aliasing', '-fno-common', '-fwrapv' ]
if targetos == 'darwin'
  # Disable attempts to use ObjectiveC features in os/object.h since they
  # won't work when we're compiling with gcc as a C compiler.
  if compiler.get_id() == 'gcc'
    qemu_common_flags += '-DOS_OBJECT_USE_OBJC=0'
  endif
elif targetos == 'sunos'
  # needed for CMSG_ macros in sys/socket.h
  qemu_common_flags += '-D_XOPEN_SOURCE=600'
  # needed for TIOCWIN* defines in termios.h
  qemu_common_flags += '-D__EXTENSIONS__'
elif targetos == 'haiku'
  qemu_common_flags += ['-DB_USE_POSITIVE_POSIX_ERRORS', '-D_BSD_SOURCE', '-fPIC']
endif

# __sync_fetch_and_and requires at least -march=i486. Many toolchains
# use i686 as default anyway, but for those that don't, an explicit
# specification is necessary
if host_arch == 'i386' and not cc.links('''
  static int sfaa(int *ptr)
  {
    return __sync_fetch_and_and(ptr, 0);
  }

  int main(void)
  {
    int val = 42;
    val = __sync_val_compare_and_swap(&val, 0, 1);
    sfaa(&val);
    return val;
  }''')
  qemu_common_flags = ['-march=i486'] + qemu_common_flags
endif

if get_option('prefer_static')
  qemu_ldflags += get_option('b_pie') ? '-static-pie' : '-static'
endif

# Meson currently only handles pie as a boolean for now, so if the user
# has explicitly disabled PIE we need to extend our cflags.
#
# -no-pie is supposedly a linker flag that has no effect on the compiler
# command line, but some distros, that didn't quite know what they were
# doing, made local changes to gcc's specs file that turned it into
# a compiler command-line flag.
#
# What about linker flags?  For a static build, no PIE is implied by -static
# which we added above (and if it's not because of the same specs patching,
# there's nothing we can do: compilation will fail, report a bug to your
# distro and do not use --disable-pie in the meanwhile).  For dynamic linking,
# instead, we can't add -no-pie because it overrides -shared: the linker then
# tries to build an executable instead of a shared library and fails.  So
# don't add -no-pie anywhere and cross fingers. :(
if not get_option('b_pie')
  qemu_common_flags += cc.get_supported_arguments('-fno-pie', '-no-pie')
if not get_option('stack_protector').disabled()
  stack_protector_probe = '''
    int main(int argc, char *argv[])
    {
      char arr[64], *p = arr, *c = argv[argc - 1];
      while (*c) {
          *p++ = *c++;
      }
      return 0;
    }'''
  have_stack_protector = false
  foreach arg : ['-fstack-protector-strong', '-fstack-protector-all']
    # We need to check both a compile and a link, since some compiler
    # setups fail only on a .c->.o compile and some only at link time
    if cc.compiles(stack_protector_probe, args: ['-Werror', arg]) and \
       cc.links(stack_protector_probe, args: ['-Werror', arg])
      have_stack_protector = true
      qemu_cflags += arg
      qemu_ldflags += arg
      break
    endif
  endforeach
  get_option('stack_protector') \
    .require(have_stack_protector, error_message: 'Stack protector not supported')
endif

coroutine_backend = get_option('coroutine_backend')
ucontext_probe = '''
  #include <ucontext.h>
  #ifdef __stub_makecontext
  #error Ignoring glibc stub makecontext which will always fail
  #endif
  int main(void) { makecontext(0, 0, 0); return 0; }'''

# On Windows the only valid backend is the Windows specific one.
# For POSIX prefer ucontext, but it's not always possible. The fallback
# is sigcontext.
supported_backends = []
if targetos == 'windows'
  supported_backends += ['windows']
else
  if targetos != 'darwin' and cc.links(ucontext_probe)
    supported_backends += ['ucontext']
  endif
  supported_backends += ['sigaltstack']
endif

if coroutine_backend == 'auto'
  coroutine_backend = supported_backends[0]
elif coroutine_backend not in supported_backends
  error('"@0@" backend requested but not available.  Available backends: @1@' \
        .format(coroutine_backend, ', '.join(supported_backends)))
endif

# Compiles if SafeStack *not* enabled
safe_stack_probe = '''
  int main(void)
  {
  #if defined(__has_feature)
  #if __has_feature(safe_stack)
  #error SafeStack Enabled
  #endif
  #endif
      return 0;
  }'''
if get_option('safe_stack') != not cc.compiles(safe_stack_probe)
  safe_stack_arg = get_option('safe_stack') ? '-fsanitize=safe-stack' : '-fno-sanitize=safe-stack'
  if get_option('safe_stack') != not cc.compiles(safe_stack_probe, args: safe_stack_arg)
    error(get_option('safe_stack') \
          ? 'SafeStack not supported by your compiler' \
          : 'Cannot disable SafeStack')
  endif
  qemu_cflags += safe_stack_arg
  qemu_ldflags += safe_stack_arg
endif
if get_option('safe_stack') and coroutine_backend != 'ucontext'
  error('SafeStack is only supported with the ucontext coroutine backend')
endif

if get_option('sanitizers')
  if cc.has_argument('-fsanitize=address')
    qemu_cflags = ['-fsanitize=address'] + qemu_cflags
    qemu_ldflags = ['-fsanitize=address'] + qemu_ldflags
  endif

  # Detect static linking issue with ubsan - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84285
  if cc.links('int main(int argc, char **argv) { return argc + 1; }',
              args: [qemu_ldflags, '-fsanitize=undefined'])
    qemu_cflags = ['-fsanitize=undefined'] + qemu_cflags
    qemu_ldflags = ['-fsanitize=undefined'] + qemu_ldflags
  endif
endif

# Thread sanitizer is, for now, much noisier than the other sanitizers;
# keep it separate until that is not the case.
if get_option('tsan')
  if get_option('sanitizers')
    error('TSAN is not supported with other sanitizers')
  endif
  if not cc.has_function('__tsan_create_fiber',
                         args: '-fsanitize=thread',
                         prefix: '#include <sanitizer/tsan_interface.h>')
    error('Cannot enable TSAN due to missing fiber annotation interface')
  endif
  qemu_cflags = ['-fsanitize=thread'] + qemu_cflags
  qemu_ldflags = ['-fsanitize=thread'] + qemu_ldflags
endif

# Detect support for PT_GNU_RELRO + DT_BIND_NOW.
# The combination is known as "full relro", because .got.plt is read-only too.
qemu_ldflags += cc.get_supported_link_arguments('-Wl,-z,relro', '-Wl,-z,now')

if targetos == 'windows'
  qemu_ldflags += cc.get_supported_link_arguments('-Wl,--no-seh', '-Wl,--nxcompat')
  qemu_ldflags += cc.get_supported_link_arguments('-Wl,--dynamicbase', '-Wl,--high-entropy-va')
# Exclude --warn-common with TSan to suppress warnings from the TSan libraries.
if targetos != 'sunos' and not get_option('tsan')
  qemu_ldflags += cc.get_supported_link_arguments('-Wl,--warn-common')
if get_option('fuzzing')
  # Specify a filter to only instrument code that is directly related to
  # virtual-devices.
  configure_file(output: 'instrumentation-filter',
                 input: 'scripts/oss-fuzz/instrumentation-filter-template',
                 copy: true)

  if cc.compiles('int main () { return 0; }',
                  name: '-fsanitize-coverage-allowlist=/dev/null',
                 args: ['-fsanitize-coverage-allowlist=/dev/null',
                        '-fsanitize-coverage=trace-pc'] )
    qemu_common_flags += ['-fsanitize-coverage-allowlist=instrumentation-filter']

  if get_option('fuzzing_engine') == ''
    # Add CFLAGS to tell clang to add fuzzer-related instrumentation to all the
    # compiled code.  To build non-fuzzer binaries with --enable-fuzzing, link
    # everything with fsanitize=fuzzer-no-link. Otherwise, the linker will be
    # unable to bind the fuzzer-related callbacks added by instrumentation.
    qemu_common_flags += ['-fsanitize=fuzzer-no-link']
    qemu_ldflags += ['-fsanitize=fuzzer-no-link']
    # For the actual fuzzer binaries, we need to link against the libfuzzer
    # library. They need to be configurable, to support OSS-Fuzz
    fuzz_exe_ldflags = ['-fsanitize=fuzzer']
  else
    # LIB_FUZZING_ENGINE was set; assume we are running on OSS-Fuzz, and
    # the needed CFLAGS have already been provided
    fuzz_exe_ldflags = get_option('fuzzing_engine').split()
  endif
add_global_arguments(qemu_common_flags, native: false, language: all_languages)
add_global_link_arguments(qemu_ldflags, native: false, language: all_languages)

# Collect warnings that we want to enable

warn_flags = [
  '-Wundef',
  '-Wwrite-strings',
  '-Wmissing-prototypes',
  '-Wstrict-prototypes',
  '-Wredundant-decls',
  '-Wold-style-declaration',
  '-Wold-style-definition',
  '-Wtype-limits',
  '-Wformat-security',
  '-Wformat-y2k',
  '-Winit-self',
  '-Wignored-qualifiers',
  '-Wempty-body',
  '-Wnested-externs',
  '-Wendif-labels',
  '-Wexpansion-to-defined',
  '-Wimplicit-fallthrough=2',
  '-Wmissing-format-attribute',
  '-Wno-initializer-overrides',
  '-Wno-missing-include-dirs',
  '-Wno-shift-negative-value',
  '-Wno-string-plus-int',
  '-Wno-typedef-redefinition',
  '-Wno-tautological-type-limit-compare',
  '-Wno-psabi',
  '-Wno-gnu-variable-sized-type-not-at-end',
]

if targetos != 'darwin'
  warn_flags += ['-Wthread-safety']
endif

if 'cpp' in all_languages
  qemu_cxxflags = ['-D__STDC_LIMIT_MACROS', '-D__STDC_CONSTANT_MACROS', '-D__STDC_FORMAT_MACROS'] + qemu_cflags
add_project_arguments(qemu_cflags, native: false, language: 'c')
add_project_arguments(cc.get_supported_arguments(warn_flags), native: false, language: 'c')
if 'cpp' in all_languages
  add_project_arguments(qemu_cxxflags, native: false, language: 'cpp')
  add_project_arguments(cxx.get_supported_arguments(warn_flags), native: false, language: 'cpp')
endif
if 'objc' in all_languages
  # Note sanitizer flags are not applied to Objective-C sources!
  add_project_arguments(objc.get_supported_arguments(warn_flags), native: false, language: 'objc')
endif
if targetos == 'linux'
  add_project_arguments('-isystem', meson.current_source_dir() / 'linux-headers',
                        '-isystem', 'linux-headers',
                        language: all_languages)
add_project_arguments('-iquote', '.',
                      '-iquote', meson.current_source_dir(),
                      '-iquote', meson.current_source_dir() / 'include',
                      language: all_languages)

# If a host-specific include directory exists, list that first...
host_include = meson.current_source_dir() / 'host/include/'
if fs.is_dir(host_include / host_arch)
  add_project_arguments('-iquote', host_include / host_arch,
                        language: all_languages)
endif
# ... followed by the generic fallback.
add_project_arguments('-iquote', host_include / 'generic',
                      language: all_languages)
sparse = find_program('cgcc', required: get_option('sparse'))
if sparse.found()
  run_target('sparse',
             command: [find_program('scripts/check_sparse.py'),
                       'compile_commands.json', sparse.full_path(), '-Wbitwise',
                       '-Wno-transparent-union', '-Wno-old-initializer',
                       '-Wno-non-pointer-null'])
###########################################
# Target-specific checks and dependencies #
###########################################

if get_option('fuzzing') and get_option('fuzzing_engine') == '' and \
    not cc.links('''
          #include <stdint.h>
          #include <sys/types.h>
          int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
          int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; }
        ''',
        args: ['-Werror', '-fsanitize=fuzzer'])
  error('Your compiler does not support -fsanitize=fuzzer')
endif

if 'ftrace' in get_option('trace_backends') and targetos != 'linux'
  error('ftrace is supported only on Linux')
endif
if 'syslog' in get_option('trace_backends') and not cc.compiles('''
    #include <syslog.h>
    int main(void) {
        openlog("qemu", LOG_PID, LOG_DAEMON);
        syslog(LOG_INFO, "configure");
        return 0;
    }''')
  error('syslog is not supported on this system')
endif

# Miscellaneous Linux-only features
get_option('mpath') \
  .require(targetos == 'linux', error_message: 'Multipath is supported only on Linux')
multiprocess_allowed = get_option('multiprocess') \
  .require(targetos == 'linux', error_message: 'Multiprocess QEMU is supported only on Linux') \
  .allowed()
vfio_user_server_allowed = get_option('vfio_user_server') \
  .require(targetos == 'linux', error_message: 'vfio-user server is supported only on Linux') \
  .allowed()

have_tpm = get_option('tpm') \
  .require(targetos != 'windows', error_message: 'TPM emulation only available on POSIX systems') \
  .allowed()

have_vhost_user = get_option('vhost_user') \
  .disable_auto_if(targetos != 'linux') \
  .require(targetos != 'windows',
           error_message: 'vhost-user is not available on Windows').allowed()
have_vhost_vdpa = get_option('vhost_vdpa') \
  .require(targetos == 'linux',
           error_message: 'vhost-vdpa is only available on Linux').allowed()
have_vhost_kernel = get_option('vhost_kernel') \
  .require(targetos == 'linux',
           error_message: 'vhost-kernel is only available on Linux').allowed()
have_vhost_user_crypto = get_option('vhost_crypto') \
  .require(have_vhost_user,
           error_message: 'vhost-crypto requires vhost-user to be enabled').allowed()

have_vhost = have_vhost_user or have_vhost_vdpa or have_vhost_kernel

have_vhost_net_user = have_vhost_user and get_option('vhost_net').allowed()
have_vhost_net_vdpa = have_vhost_vdpa and get_option('vhost_net').allowed()
have_vhost_net_kernel = have_vhost_kernel and get_option('vhost_net').allowed()
have_vhost_net = have_vhost_net_kernel or have_vhost_net_user or have_vhost_net_vdpa
# Target-specific libraries and flags
libm = cc.find_library('m', required: false)
threads = dependency('threads')
util = cc.find_library('util', required: false)
version_res = []
coref = []
iokit = []
emulator_link_args = []
pathcch = not_found
if targetos == 'windows'
  midl = find_program('midl', required: false)
  widl = find_program('widl', required: false)
  pathcch = cc.find_library('pathcch')
  socket = cc.find_library('ws2_32')
  winmm = cc.find_library('winmm')

  win = import('windows')
  version_res = win.compile_resources('version.rc',
                                      depend_files: files('pc-bios/qemu-nsis.ico'),
                                      include_directories: include_directories('.'))
elif targetos == 'darwin'
  coref = dependency('appleframeworks', modules: 'CoreFoundation')
  iokit = dependency('appleframeworks', modules: 'IOKit', required: false)
  host_dsosuf = '.dylib'
elif targetos == 'sunos'
  socket = [cc.find_library('socket'),
            cc.find_library('nsl'),
            cc.find_library('resolv')]
elif targetos == 'haiku'
  socket = [cc.find_library('posix_error_mapper'),
            cc.find_library('network'),
            cc.find_library('bsd')]
elif targetos == 'openbsd'
  if get_option('tcg').allowed() and target_dirs.length() > 0
    # Disable OpenBSD W^X if available
    emulator_link_args = cc.get_supported_link_arguments('-Wl,-z,wxneeded')
  endif
# Target-specific configuration of accelerators
accelerators = []
if get_option('kvm').allowed() and targetos == 'linux'
  accelerators += 'CONFIG_KVM'
endif
if get_option('whpx').allowed() and targetos == 'windows'
  if get_option('whpx').enabled() and host_machine.cpu() != 'x86_64'
    error('WHPX requires 64-bit host')
  elif cc.has_header('winhvplatform.h', required: get_option('whpx')) and \
       cc.has_header('winhvemulation.h', required: get_option('whpx'))
    accelerators += 'CONFIG_WHPX'
  endif
endif
if get_option('hvf').allowed()
  hvf = dependency('appleframeworks', modules: 'Hypervisor',
                   required: get_option('hvf'))
  if hvf.found()
    accelerators += 'CONFIG_HVF'
  endif
endif
if targetos == 'netbsd'
  nvmm = cc.find_library('nvmm', required: get_option('nvmm'))
  if nvmm.found()
    accelerators += 'CONFIG_NVMM'
  endif
endif
tcg_arch = host_arch
if get_option('tcg').allowed()
  if host_arch == 'unknown'
    if not get_option('tcg_interpreter')
      error('Unsupported CPU @0@, try --enable-tcg-interpreter'.format(cpu))
    endif
    warning('Use of the TCG interpreter is not recommended on this host')
    warning('architecture. There is a native TCG execution backend available')
    warning('which provides substantially better performance and reliability.')
    warning('It is strongly recommended to remove the --enable-tcg-interpreter')
    warning('configuration option on this architecture to use the native')
    warning('backend.')
  if get_option('tcg_interpreter')
    tcg_arch = 'tci'
  elif host_arch == 'x86_64'
    tcg_arch = 'i386'
  elif host_arch == 'ppc64'
    tcg_arch = 'ppc'
  endif
  add_project_arguments('-iquote', meson.current_source_dir() / 'tcg' / tcg_arch,
                        language: all_languages)
  accelerators += 'CONFIG_TCG'
endif

if 'CONFIG_KVM' not in accelerators and get_option('kvm').enabled()
  error('KVM not available on this platform')
endif
if 'CONFIG_HVF' not in accelerators and get_option('hvf').enabled()
  error('HVF not available on this platform')
endif
if 'CONFIG_NVMM' not in accelerators and get_option('nvmm').enabled()
  error('NVMM not available on this platform')
endif
if 'CONFIG_WHPX' not in accelerators and get_option('whpx').enabled()
  error('WHPX not available on this platform')
endif
################
# Dependencies #
################

# When bumping glib minimum version, please check also whether to increase
# the _WIN32_WINNT setting in osdep.h according to the value from glib
glib_req_ver = '>=2.56.0'
glib_pc = dependency('glib-2.0', version: glib_req_ver, required: true,
                    method: 'pkg-config')
glib_cflags = []
  gmodule = dependency('gmodule-export-2.0', version: glib_req_ver, required: true,
                       method: 'pkg-config')
elif get_option('plugins')
  gmodule = dependency('gmodule-no-export-2.0', version: glib_req_ver, required: true,
                       method: 'pkg-config')
else
  gmodule = not_found
endif

# This workaround is required due to a bug in pkg-config file for glib as it
# doesn't define GLIB_STATIC_COMPILATION for pkg-config --static
if targetos == 'windows' and get_option('prefer_static')
  glib_cflags += ['-DGLIB_STATIC_COMPILATION']
endif

# Sanity check that the current size_t matches the
# size that glib thinks it should be. This catches
# problems on multi-arch where people try to build
# 32-bit QEMU while pointing at 64-bit glib headers

if not cc.compiles('''
  #include <glib.h>
  #include <unistd.h>

  #define QEMU_BUILD_BUG_ON(x) \
  typedef char qemu_build_bug_on[(x)?-1:1] __attribute__((unused));

  int main(void) {
     QEMU_BUILD_BUG_ON(sizeof(size_t) != GLIB_SIZEOF_SIZE_T);
     return 0;
  }''', dependencies: glib_pc, args: glib_cflags)
  error('''sizeof(size_t) doesn't match GLIB_SIZEOF_SIZE_T.
        You probably need to set PKG_CONFIG_LIBDIR" to point
        to the right pkg-config files for your build target.''')
endif

# Silence clang warnings triggered by glib < 2.57.2
if not cc.compiles('''
  #include <glib.h>
  typedef struct Foo {
    int i;
  } Foo;
  static void foo_free(Foo *f)
  {
    g_free(f);
  }
  G_DEFINE_AUTOPTR_CLEANUP_FUNC(Foo, foo_free)
  int main(void) { return 0; }''', dependencies: glib_pc, args: ['-Wunused-function', '-Werror'])
  glib_cflags += cc.get_supported_arguments('-Wno-unused-function')
endif
glib = declare_dependency(dependencies: [glib_pc, gmodule],
                          compile_args: glib_cflags,
                          version: glib_pc.version())

# Check whether glib has gslice, which we have to avoid for correctness.
# TODO: remove this check and the corresponding workaround (qtree) when
# the minimum supported glib is >= 2.75.3
glib_has_gslice = glib.version().version_compare('<2.75.3')

# override glib dep to include the above refinements
meson.override_dependency('glib-2.0', glib)

# The path to glib.h is added to all compilation commands.
add_project_dependencies(glib.partial_dependency(compile_args: true, includes: true),
                         native: false, language: all_languages)
gdbus_codegen = not_found
gdbus_codegen_error = '@0@ requires gdbus-codegen, please install libgio'
if not get_option('gio').auto() or have_system
  gio = dependency('gio-2.0', required: get_option('gio'),
                   method: 'pkg-config')
  if gio.found() and not cc.links('''
    #include <gio/gio.h>
    int main(void)
    {
      g_dbus_proxy_new_sync(0, 0, 0, 0, 0, 0, 0, 0);
      return 0;
    }''', dependencies: [glib, gio])
    if get_option('gio').enabled()
      error('The installed libgio is broken for static linking')
    endif
    gio = not_found
  endif
  if gio.found()
    gdbus_codegen = find_program(gio.get_variable('gdbus_codegen'),
                                 required: get_option('gio'))
    gio_unix = dependency('gio-unix-2.0', required: get_option('gio'),
                          method: 'pkg-config')
    gio = declare_dependency(dependencies: [gio, gio_unix],
                             version: gio.version())
  endif
if gdbus_codegen.found() and get_option('cfi')
  gdbus_codegen = not_found
  gdbus_codegen_error = '@0@ uses gdbus-codegen, which does not support control flow integrity'
endif
xml_pp = find_program('scripts/xml-preprocess.py')

lttng = not_found
if 'ust' in get_option('trace_backends')
  lttng = dependency('lttng-ust', required: true, version: '>= 2.1',
                     method: 'pkg-config')
pixman = not_found
if not get_option('pixman').auto() or have_system or have_tools
  pixman = dependency('pixman-1', required: get_option('pixman'), version:'>=0.21.8',
                      method: 'pkg-config')
zlib = dependency('zlib', required: true)
libaio = not_found
if not get_option('linux_aio').auto() or have_block
  libaio = cc.find_library('aio', has_headers: ['libaio.h'],
                           required: get_option('linux_aio'))

linux_io_uring_test = '''
  #include <liburing.h>
  #include <linux/errqueue.h>

  int main(void) { return 0; }'''

linux_io_uring = not_found
if not get_option('linux_io_uring').auto() or have_block
  linux_io_uring = dependency('liburing', version: '>=0.3',
                              required: get_option('linux_io_uring'),
                              method: 'pkg-config')
  if not cc.links(linux_io_uring_test)
    linux_io_uring = not_found
  endif
endif
libnfs = not_found
Paolo Bonzini's avatar
Paolo Bonzini committed
if not get_option('libnfs').auto() or have_block
  libnfs = dependency('libnfs', version: '>=1.9.3',
                      required: get_option('libnfs'),
                      method: 'pkg-config')
endif

libattr_test = '''
  #include <stddef.h>
  #include <sys/types.h>
  #ifdef CONFIG_LIBATTR
  #include <attr/xattr.h>
  #else
  #include <sys/xattr.h>
  #endif
  int main(void) { getxattr(NULL, NULL, NULL, 0); setxattr(NULL, NULL, NULL, 0, 0); return 0; }'''

libattr = not_found
have_old_libattr = false
if get_option('attr').allowed()
  if cc.links(libattr_test)
    libattr = declare_dependency()
  else
    libattr = cc.find_library('attr', has_headers: ['attr/xattr.h'],
                              required: get_option('attr'))
    if libattr.found() and not \
      cc.links(libattr_test, dependencies: libattr, args: '-DCONFIG_LIBATTR')
      libattr = not_found
      if get_option('attr').enabled()
        error('could not link libattr')
      else
        warning('could not link libattr, disabling')
      endif
    else
      have_old_libattr = libattr.found()
    endif
  endif
cocoa = dependency('appleframeworks', modules: ['Cocoa', 'CoreVideo'],
                   required: get_option('cocoa'))
vmnet = dependency('appleframeworks', modules: 'vmnet', required: get_option('vmnet'))
if vmnet.found() and not cc.has_header_symbol('vmnet/vmnet.h',
                                              'VMNET_BRIDGED_MODE',
                                              dependencies: vmnet)
  vmnet = not_found
  if get_option('vmnet').enabled()
    error('vmnet.framework API is outdated')
  else
    warning('vmnet.framework API is outdated, disabling')
  endif
endif

seccomp = not_found
if not get_option('seccomp').auto() or have_system or have_tools
  seccomp = dependency('libseccomp', version: '>=2.3.0',
                       required: get_option('seccomp'),
                       method: 'pkg-config')
  if seccomp.found()
    seccomp_has_sysrawrc = cc.has_header_symbol('seccomp.h',
                                                'SCMP_FLTATR_API_SYSRAWRC',
                                                dependencies: seccomp)
  endif
libcap_ng = not_found
Paolo Bonzini's avatar
Paolo Bonzini committed
if not get_option('cap_ng').auto() or have_system or have_tools
  libcap_ng = cc.find_library('cap-ng', has_headers: ['cap-ng.h'],
                              required: get_option('cap_ng'))
Paolo Bonzini's avatar
Paolo Bonzini committed
endif
if libcap_ng.found() and not cc.links('''
   #include <cap-ng.h>
   int main(void)
   {
     capng_capability_to_name(CAPNG_EFFECTIVE);
     return 0;
   }''', dependencies: libcap_ng)
  libcap_ng = not_found
  if get_option('cap_ng').enabled()
    error('could not link libcap-ng')
  else
    warning('could not link libcap-ng, disabling')
  endif
if get_option('xkbcommon').auto() and not have_system and not have_tools
  xkbcommon = not_found
else
  xkbcommon = dependency('xkbcommon', required: get_option('xkbcommon'),
                         method: 'pkg-config')
slirp = not_found
if not get_option('slirp').auto() or have_system
  slirp = dependency('slirp', required: get_option('slirp'),
                     method: 'pkg-config')
  # slirp < 4.7 is incompatible with CFI support in QEMU.  This is because
  # it passes function pointers within libslirp as callbacks for timers.
  # When using a system-wide shared libslirp, the type information for the
  # callback is missing and the timer call produces a false positive with CFI.
  # Do not use the "version" keyword argument to produce a better error.
  # with control-flow integrity.
  if get_option('cfi') and slirp.found() and slirp.version().version_compare('<4.7')
    if get_option('slirp').enabled()
      error('Control-Flow Integrity requires libslirp 4.7.')
    else
      warning('Cannot use libslirp since Control-Flow Integrity requires libslirp >= 4.7.')
      slirp = not_found
    endif
  endif
endif

if not get_option('vde').auto() or have_system or have_tools
  vde = cc.find_library('vdeplug', has_headers: ['libvdeplug.h'],
                           required: get_option('vde'))
endif
if vde.found() and not cc.links('''
   #include <libvdeplug.h>
   int main(void)
   {
     struct vde_open_args a = {0, 0, 0};
     char s[] = "";
     vde_open(s, s, &a);
     return 0;
   }''', dependencies: vde)
  vde = not_found
  if get_option('cap_ng').enabled()
    error('could not link libvdeplug')
  else
    warning('could not link libvdeplug, disabling')
  endif
pulse = not_found
if not get_option('pa').auto() or (targetos == 'linux' and have_system)
  pulse = dependency('libpulse', required: get_option('pa'),
                     method: 'pkg-config')
endif
alsa = not_found
if not get_option('alsa').auto() or (targetos == 'linux' and have_system)
  alsa = dependency('alsa', required: get_option('alsa'),
                    method: 'pkg-config')
endif
jack = not_found
if not get_option('jack').auto() or have_system
  jack = dependency('jack', required: get_option('jack'),
                    method: 'pkg-config')
pipewire = not_found
if not get_option('pipewire').auto() or (targetos == 'linux' and have_system)
  pipewire = dependency('libpipewire-0.3', version: '>=0.3.60',
                    required: get_option('pipewire'),
                    method: 'pkg-config')