{ lib, stdenv, fetchurl, fetchpatch, python, zlib, pkg-config, glib
, perl, pixman, vde2, alsaLib, texinfo, flex
, bison, lzo, snappy, libaio, gnutls, nettle, curl, ninja, meson
, makeWrapper, autoPatchelfHook
, attr, libcap, libcap_ng
, CoreServices, Cocoa, Hypervisor, rez, setfile
, numaSupport ? stdenv.isLinux && !stdenv.isAarch32, numactl
, seccompSupport ? stdenv.isLinux, libseccomp
, alsaSupport ? lib.hasSuffix "linux" stdenv.hostPlatform.system && !nixosTestRunner
, pulseSupport ? !stdenv.isDarwin && !nixosTestRunner, libpulseaudio
, sdlSupport ? !stdenv.isDarwin && !nixosTestRunner, SDL2, SDL2_image
, gtkSupport ? !stdenv.isDarwin && !xenSupport && !nixosTestRunner, gtk3, gettext, vte, wrapGAppsHook
, vncSupport ? !nixosTestRunner, libjpeg, libpng
, smartcardSupport ? !nixosTestRunner, libcacard
, spiceSupport ? !stdenv.isDarwin && !nixosTestRunner, spice, spice-protocol
, ncursesSupport ? !nixosTestRunner, ncurses
, usbredirSupport ? spiceSupport, usbredir
, xenSupport ? false, xen
, cephSupport ? false, ceph
, glusterfsSupport ? false, glusterfs, libuuid
, openGLSupport ? sdlSupport, mesa, epoxy, libdrm
, virglSupport ? openGLSupport, virglrenderer
, libiscsiSupport ? true, libiscsi
, smbdSupport ? false, samba
, tpmSupport ? true
, hostCpuOnly ? false
, hostCpuTargets ? (if hostCpuOnly
                    then (lib.optional stdenv.isx86_64 "i386-softmmu"
                          ++ ["${stdenv.hostPlatform.qemuArch}-softmmu"])
                    else null)
, nixosTestRunner ? false
}:

with lib;
let
  audio = optionalString alsaSupport "alsa,"
    + optionalString pulseSupport "pa,"
    + optionalString sdlSupport "sdl,";

in

stdenv.mkDerivation rec {
  version = "6.0.0";
  pname = "qemu"
    + lib.optionalString xenSupport "-xen"
    + lib.optionalString hostCpuOnly "-host-cpu-only"
    + lib.optionalString nixosTestRunner "-for-vm-tests";

  src = fetchurl {
    url= "https://download.qemu.org/qemu-${version}.tar.xz";
    sha256 = "1f9hz8rf12jm8baa7kda34yl4hyl0xh0c4ap03krfjx23i3img47";
  };

  nativeBuildInputs = [ python python.pkgs.sphinx pkg-config flex bison meson ninja ]
    ++ optionals gtkSupport [ wrapGAppsHook ]
    ++ optionals stdenv.isLinux [ autoPatchelfHook ];
  buildInputs =
    [ zlib glib perl pixman
      vde2 texinfo makeWrapper lzo snappy
      gnutls nettle curl
    ]
    ++ optionals ncursesSupport [ ncurses ]
    ++ optionals stdenv.isDarwin [ CoreServices Cocoa Hypervisor rez setfile ]
    ++ optionals seccompSupport [ libseccomp ]
    ++ optionals numaSupport [ numactl ]
    ++ optionals pulseSupport [ libpulseaudio ]
    ++ optionals sdlSupport [ SDL2 SDL2_image ]
    ++ optionals gtkSupport [ gtk3 gettext vte ]
    ++ optionals vncSupport [ libjpeg libpng ]
    ++ optionals smartcardSupport [ libcacard ]
    ++ optionals spiceSupport [ spice-protocol spice ]
    ++ optionals usbredirSupport [ usbredir ]
    ++ optionals stdenv.isLinux [ alsaLib libaio libcap_ng libcap attr ]
    ++ optionals xenSupport [ xen ]
    ++ optionals cephSupport [ ceph ]
    ++ optionals glusterfsSupport [ glusterfs libuuid ]
    ++ optionals openGLSupport [ mesa epoxy libdrm ]
    ++ optionals virglSupport [ virglrenderer ]
    ++ optionals libiscsiSupport [ libiscsi ]
    ++ optionals smbdSupport [ samba ];

  dontUseMesonConfigure = true; # meson's configurePhase isn't compatible with qemu build

  outputs = [ "out" "ga" ];

  patches = [
    ./fix-qemu-ga.patch
    ./9p-ignore-noatime.patch
    (fetchpatch {
      name = "CVE-2021-3545.patch";
      url = "https://gitlab.com/qemu-project/qemu/-/commit/121841b25d72d13f8cad554363138c360f1250ea.patch";
      sha256 = "13dgfd8dmxcalh2nvb68iv0kyv4xxrvpdqdxf1h3bjr4451glag1";
    })
    (fetchpatch {
      name = "CVE-2021-3546.patch";
      url = "https://gitlab.com/qemu-project/qemu/-/commit/9f22893adcb02580aee5968f32baa2cd109b3ec2.patch";
      sha256 = "1vkhm9vl671y4cra60b6704339qk1h5dyyb3dfvmvpsvfyh2pm7n";
    })
    (fetchpatch {
      name = "CVE-2021-3527-patch1.patch";
      url = "https://gitlab.com/qemu-project/qemu/-/commit/05a40b172e4d691371534828078be47e7fff524c.patch";
      sha256 = "19hwwyb3vh7pli921dx74i4bgpnlc7s43jma5mqzfp6wc158g5zl";
    })
    (fetchpatch {
      name = "CVE-2021-3527-patch2.patch";
      url = "https://gitlab.com/qemu-project/qemu/-/commit/7ec54f9eb62b5d177e30eb8b1cad795a5f8d8986.patch";
      sha256 = "1qakkb7i4gx3x4rrp7500yxqrcnvc2h6a8g916csynscbprlvl97";
    })
    (fetchpatch {
      name = "CVE-2021-3544-patch1.patch";
      url = "https://gitlab.com/qemu-project/qemu/-/commit/86dd8fac2acc366930a5dc08d3fb1b1e816f4e1e.patch";
      sha256 = "0kl0jfs7681fymz2b46a8anyzbmp9lv6k43bkscq7nsh8972b7s1";
    })
    (fetchpatch {
      name = "CVE-2021-3544-patch2.patch";
      url = "https://gitlab.com/qemu-project/qemu/-/commit/b9f79858a614d95f5de875d0ca31096eaab72c3b.patch";
      sha256 = "1wxd4q5q24lr8yhjxjayxli6kpkq4cd3q953kk4sw793wywivsb3";
    })
    (fetchpatch {
      name = "CVE-2021-3544-patch3.patch";
      url = "https://gitlab.com/qemu-project/qemu/-/commit/b7afebcf9e6ecf3cf9b5a9b9b731ed04bca6aa3e.patch";
      sha256 = "05yg9a4khan7hmxv2ssv4l6rvvw4vxf9l225g0kc7a947giwmqjy";
    })
    (fetchpatch {
      name = "CVE-2021-3544-patch4.patch";
      url = "https://gitlab.com/qemu-project/qemu/-/commit/f6091d86ba9ea05f4e111b9b42ee0005c37a6779.patch";
      sha256 = "1xxjd1fz21p88x0fp1bk4pd15w873cxm13av45i2xv7j9v510vsj";
    })
    (fetchpatch {
      name = "CVE-2021-3544-patch5.patch";
      url = "https://gitlab.com/qemu-project/qemu/-/commit/63736af5a6571d9def93769431e0d7e38c6677bf.patch";
      sha256 = "1h4hij602fsplgmppvgv51rghhkchq14akd8x7jb3w4hv241rsz1";
    })
    (fetchpatch {
      name = "CVE-2021-3682.patch";
      url = "https://gitlab.com/qemu-project/qemu/-/commit/5e796671e6b8d5de4b0b423dce1b3eba144a92c9.patch";
      sha256 = "0g87arqvjff1vzgzb87h67ws51y033slhzlqx1yy4fw9dzkszj9k";
    })
    (fetchpatch {
      name = "CVE-2021-3713.patch";
      url = "https://gitlab.com/qemu-project/qemu/-/commit/13b250b12ad3c59114a6a17d59caf073ce45b33a.patch";
      sha256 = "0lkzfc7gdlvj4rz9wk07fskidaqysmx8911g914ds1jnczgk71mf";
    })
  ] ++ optional nixosTestRunner ./force-uid0-on-9p.patch
    ++ optionals stdenv.hostPlatform.isMusl [
    (fetchpatch {
      url = "https://raw.githubusercontent.com/alpinelinux/aports/2bb133986e8fa90e2e76d53369f03861a87a74ef/main/qemu/xattr_size_max.patch";
      sha256 = "1xfdjs1jlvs99hpf670yianb8c3qz2ars8syzyz8f2c2cp5y4bxb";
    })
    (fetchpatch {
      url = "https://raw.githubusercontent.com/alpinelinux/aports/2bb133986e8fa90e2e76d53369f03861a87a74ef/main/qemu/musl-F_SHLCK-and-F_EXLCK.patch";
      sha256 = "1gm67v41gw6apzgz7jr3zv9z80wvkv0jaxd2w4d16hmipa8bhs0k";
    })
    ./sigrtminmax.patch
    (fetchpatch {
      url = "https://raw.githubusercontent.com/alpinelinux/aports/2bb133986e8fa90e2e76d53369f03861a87a74ef/main/qemu/fix-sigevent-and-sigval_t.patch";
      sha256 = "0wk0rrcqywhrw9hygy6ap0lfg314m9z1wr2hn8338r5gfcw75mav";
    })
  ];

  postPatch = ''
    # Otherwise tries to ensure /var/run exists.
    sed -i "/install_subdir('run', install_dir: get_option('localstatedir'))/d" \
        qga/meson.build

    # TODO: On aarch64-darwin, we automatically codesign everything, but qemu
    # needs specific entitlements and does its own signing. This codesign
    # command fails, but we have no fix at the moment, so this disables it.
    # This means `-accel hvf` is broken for now, on aarch64-darwin only.
    substituteInPlace meson.build \
      --replace 'if exe_sign' 'if false'
  '';

  preConfigure = ''
    unset CPP # intereferes with dependency calculation
    # this script isn't marked as executable b/c it's indirectly used by meson. Needed to patch its shebang
    chmod +x ./scripts/shaderinclude.pl
    patchShebangs .
    # avoid conflicts with libc++ include for <version>
    mv VERSION QEMU_VERSION
    substituteInPlace configure \
      --replace '$source_path/VERSION' '$source_path/QEMU_VERSION'
    substituteInPlace meson.build \
      --replace "'VERSION'" "'QEMU_VERSION'"
  '' + optionalString stdenv.hostPlatform.isMusl ''
    NIX_CFLAGS_COMPILE+=" -D_LINUX_SYSINFO_H"
  '';

  configureFlags =
    [ "--audio-drv-list=${audio}"
      "--enable-docs"
      "--enable-tools"
      "--enable-guest-agent"
      "--localstatedir=/var"
      "--sysconfdir=/etc"
    ]
    ++ optional numaSupport "--enable-numa"
    ++ optional seccompSupport "--enable-seccomp"
    ++ optional smartcardSupport "--enable-smartcard"
    ++ optional spiceSupport "--enable-spice"
    ++ optional usbredirSupport "--enable-usb-redir"
    ++ optional (hostCpuTargets != null) "--target-list=${lib.concatStringsSep "," hostCpuTargets}"
    ++ optional stdenv.isDarwin "--enable-cocoa"
    ++ optional stdenv.isDarwin "--enable-hvf"
    ++ optional stdenv.isLinux "--enable-linux-aio"
    ++ optional gtkSupport "--enable-gtk"
    ++ optional xenSupport "--enable-xen"
    ++ optional cephSupport "--enable-rbd"
    ++ optional glusterfsSupport "--enable-glusterfs"
    ++ optional openGLSupport "--enable-opengl"
    ++ optional virglSupport "--enable-virglrenderer"
    ++ optional tpmSupport "--enable-tpm"
    ++ optional libiscsiSupport "--enable-libiscsi"
    ++ optional smbdSupport "--smbd=${samba}/bin/smbd";

  doCheck = false; # tries to access /dev
  dontWrapGApps = true;

  postFixup = ''
    # the .desktop is both invalid and pointless
    rm -f $out/share/applications/qemu.desktop

    # copy qemu-ga (guest agent) to separate output
    mkdir -p $ga/bin
    cp $out/bin/qemu-ga $ga/bin/
  '' + optionalString gtkSupport ''
    # wrap GTK Binaries
    for f in $out/bin/qemu-system-*; do
      wrapGApp $f
    done
  '';
  preBuild = "cd build";

  # Add a ‘qemu-kvm’ wrapper for compatibility/convenience.
  postInstall = ''
    if [ -x $out/bin/qemu-system-${stdenv.hostPlatform.qemuArch} ]; then
      makeWrapper $out/bin/qemu-system-${stdenv.hostPlatform.qemuArch} \
                  $out/bin/qemu-kvm \
                  --add-flags "\$([ -e /dev/kvm ] && echo -enable-kvm)"
    fi
  '';

  passthru = {
    qemu-system-i386 = "bin/qemu-system-i386";
  };

  # Builds in ~3h with 2 cores, and ~20m with a big-parallel builder.
  requiredSystemFeatures = [ "big-parallel" ];

  meta = with lib; {
    homepage = "http://www.qemu.org/";
    description = "A generic and open source machine emulator and virtualizer";
    license = licenses.gpl2Plus;
    maintainers = with maintainers; [ eelco qyliss ];
    platforms = platforms.unix;
  };
}