# to update:
# - first, figure the rev for `koreader-base`:
#   - inside `koreader` repo:
#     - `git submodule status base`
#     - or `git log base`
# - inside `koreader-base` repo:
#   - `git diff old-rev..new-rev thirdparty`
#
# koreader's native build process
# 1. git clone each dependency lib into base/thirdparty/$lib
# 2. git checkout a specific rev into base/thirdparty/$lib/build/$platform
# 3. invoke cmake in the checkout dir (including `make install` equivalent), which seems to not write outside the dir
# 4. manually copy the "installed" files in that checkout to some other place for koreader to use
#    - see: koreader/base/Makefile.third
# ...
# koreader and all its deps are installed to $out/lib/koreader
# - i.e. it vendors its runtime deps
#
# a good way to substitute nixpkgs deps in place of KOReader's vendored deps is to
# inject them into base/Makefile.third, via makeFlags.
# failing that, we can patch the source of each vendored library into base/thirdparty/*/CMakeLists.txt
# and KOReader will build them from-source perfectly, but that's more involved on our end.
#
# TODO:
# - don't vendor fonts
{ lib
, autoPatchelfHook
, autoconf
, automake
, buildPackages
, cmake
, fetchFromGitHub
, fetchFromGitLab
, fetchurl
, gettext
, git
, libtool
, luajit
, makeWrapper
, pkg-config
, stdenv
, symlinkJoin

# third-party dependencies which KOReader would ordinarily vendor
, curl
, czmq
, djvulibre
, dropbear
, freetype
, fribidi
# , gettext
, giflib
, glib
, gnutar
, harfbuzz
, libiconvReal
, libjpeg_turbo
, libpng
, libunibreak
, libwebp
, nanosvg
, openssl
, openssh
, sdcv
, SDL2  # koreader doesn't actually vendor this, just expects it'll magically be available
, sqlite
, utf8proc
, zlib
, zeromq4
, zstd
, zsync
}:
let
  version = "2024.03";
  src = fetchFromGitHub {
    owner = "koreader";
    repo = "koreader";
    name = "koreader";  # needed because `srcs = ` in the outer derivation is a list
    fetchSubmodules = true;
    rev = "v${version}";
    hash = "sha256-/51pOGSAoaS0gOKlqNKruwaKY5qylzCpeNUrWyzYTpA=";
  };

  fbink-src-ko = fetchFromGitHub {
    owner = "NiLuJe";
    repo = "FBInk";
    name = "fbink";  # where to unpack this in `srcs`
    rev = "1a989b30a195ca240a3cf37f9de61b4b3c7e891c";
    hash = "sha256-lXjAX0BoHW3L1E54d5J+wiAlAZXVmj9Y1Un8yaCwO8w=";
  };

  kobo-usbms-src-ko = fetchFromGitHub {
    owner = "koreader";
    repo = "KoboUSBMS";
    name = "kobo-usbms";  # where to unpack this in `srcs`
    rev = "v1.3.9";
    hash = "sha256-91B0FUnmpE6TP4Lg5mj6z/U1DZQTKiPhG3ccCSgY4mQ=";
  };

  leptonica-src-ko = fetchFromGitHub {
    # k2pdf needs leptonica src, because it actually patches it and builds it itself:
    # - `cp -f $(LEPTONICA_MOD)/dewarp2.c $(LEPTONICA_DIR)/src/dewarp2.c`
    # -  i.e. cp -f /build/koreader/base/thirdparty/libk2pdfopt/build/aarch64-unknown-linux-gnu/libk2pdfopt-prefix/src/libk2pdfopt/leptonica_mod/dewarp2.c ...
    # k2pdf uses an old leptonica -- like 2015-2017-ish (1.74.1).
    # seems it can be at least partially updated, by replacing `numaGetMedianVariation` with `numaGetMedianDevFromMedian` (drop-in replacement)
    # and replacing references to `liblept.so` with `libleptonica.so`,
    # but eventually this requires patching the tesseract Makefiles. could get intense, idk.
    owner = "DanBloomberg";
    repo = "leptonica";
    name = "leptonica";  # where to unpack this in `srcs`
    rev = "1.74.1";
    hash = "sha256-SDXKam768xvZZvTbXe3sssvZyeLEEiY97Vrzx8hoc6g=";
  };

  libk2pdfopt-src-ko = fetchFromGitHub {
    owner = "koreader";
    repo = "libk2pdfopt";
    name = "libk2pdfopt";  # where to unpack this in `srcs`
    rev = "09f1e011a618c8ec06b4caa67079682119d2aaa7";
    hash = "sha256-37sZ46dG6Z1Wk7NrhKAKl5j9r1bN6g01cd5Iyt/2coM=";
  };

  lodepng-src-ko = fetchFromGitHub {
    owner = "lvandeve";
    repo = "lodepng";
    name = "lodepng";  # where to unpack this in `srcs`
    rev = "d398e0f10d152a5d17fa30463474dc9f56523f9c";
    hash = "sha256-ApOHUgU6X1rHwyjAHA/0Nt+buDFqY2ttXEnEvdrRl3A=";
  };

  lunasvg-src-ko = fetchFromGitHub {
    owner = "sammycage";
    repo = "lunasvg";
    name = "lunasvg";  # where to unpack this in `srcs`
    rev = "59d6f6ba835c1b7c7a0f9d4ea540ec3981777885";
    hash = "sha256-gW2ikakS6Omz5upmy26nAo/jkGHYO2kjlB3UmKJBh1k=";
  };

  minizip-src-ko = fetchFromGitHub {
    # this is actually just a very old version (2015) of `minizip-ng`
    owner = "nmoinvaz";
    repo = "minizip";
    name = "minizip";  # where to unpack this in `srcs`
    rev = "0b46a2b4ca317b80bc53594688883f7188ac4d08";
    hash = "sha256-P/3MMMGYDqD9NmkYvw/thKpUNa3wNOSlBBjANHSonAg=";
  };

  mupdf-src-ko = fetchFromGitHub {
    owner = "ArtifexSoftware";
    repo = "mupdf";
    name = "mupdf";  # where to unpack this in `srcs`
    fetchSubmodules = true;  # specifically for jbig2dec, mujs, openjpeg
    rev = "1.13.0";
    hash = "sha256-pQejRon9fO9A1mhz3oLjBr1j4HveDLcQIWjR1/Rpy5Q=";
  };

  nanosvg-headers-ko = symlinkJoin {
    # koreader's heavily-patched mupdf is dependent on a koreader-specific `stb_image_write` extension to nanosvg.
    # nanosvg is used as a header-only library, so just patch that extension straight into the src.
    name = "nanosvg-headers-ko";
    paths = [
      "${nanosvg.src}/src"
      "${src}/base/thirdparty/nanosvg"
    ];
  };

  popen-noshell-src-ko = fetchFromGitHub {
    owner = "famzah";
    repo = "popen-noshell";
    name = "popen-noshell";
    rev = "e715396a4951ee91c40a98d2824a130f158268bb";
    hash = "sha256-JeBZMsg6ZUGSnyZ4eds4w63gM/L73EsAnLaHOPpL6iM=";
  };

  tesseract-src-ko = fetchFromGitHub {
    # TODO: try using nixpkgs' tesseract.src (i doubt it will work)
    owner = "tesseract-ocr";
    repo = "tesseract";
    name = "tesseract";
    rev = "60176fc5ae5e7f6bdef60c926a4b5ea03de2bfa7";
    hash = "sha256-FQvlrJ+Uy7+wtUxBuS5NdoToUwNRhYw2ju8Ya8MLyQw=";
  };

  turbo-src-ko = fetchFromGitHub {
    owner = "kernelsauce";
    repo = "turbo";
    name = "turbo";
    rev = "v2.1.3";
    hash = "sha256-vBRkFdc5a0FIt15HBz3TnqMZ+GGsqjEefnfJEpuVTBs=";
  };

  # XXX: for some inscrutable reason, `enable52Compat` is *partially* broken, only when cross compiling.
  # `table.unpack` is non-nil, but `table.pack` is nil.
  # the normal path is for `enable52Compat` to set `env.NIX_CFLAGS_COMPILE = "-DLUAJIT_ENABLE_LUA52COMPAT";`
  # which in turn sets `#define LJ_52 1`, and gates functions like `table.pack`, `table.unpack`.
  # instead, koreader just removes the `#if LJ_52` gates. doing the same in nixpkgs seems to work.
  # luajit52 = luajit.override { enable52Compat = true; self = luajit52; };
  luajit52 = (luajit.override { self = luajit52; }).overrideAttrs (super: {
    patches = (super.patches or []) ++ [
      "${src}/base/thirdparty/luajit/koreader-luajit-enable-table_pack.patch"
    ];
  });

  overlayedLuaPkgs = luaPkgs: let
    ps = with ps; {
      luajson = buildLuarocksPackage rec {
        # needed by KOReader's lua-Spore
        pname = "luajson";
        version = "1.3.4-1";
        src = fetchFromGitHub {
          owner = "harningt";
          repo = "luajson";
          # rev = "1.3.4";
          # 1.3.4 (released 2017) has some incompatible bugs with lpeg library.
          # see: <https://github.com/harningt/luajson/commit/6ecaf9bea8b121a9ffca5a470a2080298557b55d>
          rev = "6ecaf9bea8b121a9ffca5a470a2080298557b55d";
          hash = "sha256-56G0NqIpavKHMQWUxy+Bp7G4ZKrQwUZ2C5e7GJxUJeg=";
        };
        knownRockspec = (fetchurl {
          url = "mirror://luarocks/${pname}-${version}.rockspec";
          hash = "sha256-+S4gfa6QaOMmOCDX8TxBq3kFWlbaEeiSMxCfefYakv0=";
        }).outPath;
        propagatedBuildInputs = [ lpeg ];
      };
      htmlparser = buildLuarocksPackage rec {
        pname = "htmlparser";  #< name of the rockspec, not the repo
        version = "0.3.9-1";
        src = fetchFromGitHub {
          owner = "msva";
          repo = "lua-htmlparser";
          # the rockspec was added to the repo *after* v0.3.9 was tagged
          rev = "5ce9a775a345cf458c0388d7288e246bb1b82bff";
          hash = "sha256-aSTLSfqz/MIDFVRwtBlDNBUhPb7KqOl32/Y62Hdec1s=";
        };
        knownRockspec = "${src}/rockspecs/${pname}-${version}.rockspec";
      };
      lua-spore = buildLuarocksPackage rec {
        pname = "lua-spore";  #< name of the rockspec, not the repo
        version = "0.3.3-1";
        src = fetchFromGitLab {
          domain = "framagit.org";
          owner = "fperrad";
          repo = "lua-Spore";
          rev = "0.3.3";
          hash = "sha256-wb7ykJsndoq0DazHpfXieUcBBptowYqD/eTTN/EK/6g=";
        };
        knownRockspec = "${src}/rockspec/${pname}-${version}.rockspec";
        propagatedBuildInputs = [
          luajson
          luasocket
        ];
      };
    } // luaPkgs;
  in ps;

  luaEnv = luajit52.withPackages (ps: with (overlayedLuaPkgs ps); [
    luajson
    htmlparser
    lua-spore
    lpeg
    luasec
    luasocket
    rapidjson
  ]);

  rockspecFor = luaPkgName: let
    pkg = (overlayedLuaPkgs luaEnv.pkgs)."${luaPkgName}";
  in
    "${luaEnv}/${pkg.rocksSubdir}/${luaPkgName}/${pkg.rockspecVersion}/${luaPkgName}-${pkg.rockspecVersion}.rockspec";

  crossTargets = {
    # koreader-base Makefile targets to use when compiling for the given host platform
    # only used when cross compiling
    aarch64 = "debian-arm64";
  };
  target = if stdenv.buildPlatform == stdenv.hostPlatform then
    "debian"
  else
    crossTargets."${stdenv.hostPlatform.parsed.cpu.name}";

  getContrib = pkg: stdenv.mkDerivation {
    inherit (pkg) name src;
    dontConfigure = true;
    dontBuild = true;
    installPhase = ''
      mkdir "$out"
      cp -R ./contrib/ "$out/contrib"
    '';
  };

  lib' = lib;
  fhsLib = pkg: { lib ? true, include ? true, flatLib ? false, flatInclude ? false, contrib ? false }: symlinkJoin {
    inherit (pkg) name;
    paths = (lib'.optionals lib [
      "${lib'.getLib pkg}"
    ]) ++ (lib'.optionals include [
      "${lib'.getDev pkg}"
    ]) ++ (lib'.optionals flatLib [
      "${lib'.getLib pkg}/lib"
    ]) ++ (lib'.optionals flatInclude [
      "${lib'.getDev pkg}/include"
    ]) ++ (lib'.optionals contrib [
      "${getContrib pkg}"
    ]);
  };

  # mostly for k2pdf, which expects lib/ and include/ for each dep to live side-by-side
  libAndDev = pkg: fhsLib pkg { lib = true; include = true; };

  # these probably have more dirs than they really need.
  djvulibreAll = fhsLib djvulibre { lib=true; include=true; flatInclude=true; };
  opensslAll = fhsLib openssl { lib=false; include=true; flatLib=true; };
  utf8procAll = fhsLib utf8proc { lib=true; include=false; flatInclude=true; };
  # KOreader uses ZLIB_DIR as:
  # - -L${ZLIB_DIR}
  # - -I${ZLIB_DIR}
  # - -I${ZLIB_DIR}/include
  zlibAll = fhsLib zlib { lib=false; include=true; flatLib=true; flatInclude=true; contrib=true; };

  # values to provide to koreader/base/Makefile.defs.
  # should be ok to put this in `makeFlags` array, but i can't get that to work!
  makefileDefs = ''
    CURL_LIB="${lib.getLib curl}/lib/libcurl.so" \
    CURL_DIR="${lib.getDev curl}" \
    CZMQ_LIB="${lib.getLib czmq}/lib/libczmq.so" \
    CZMQ_DIR="${lib.getDev czmq}" \
    DJVULIBRE_LIB="${lib.getLib djvulibre}/lib/libdjvulibre.so" \
    DJVULIBRE_LIB_LINK_FLAG="-L ${lib.getLib djvulibre}/lib -l:libdjvulibre.so" \
    DJVULIBRE_DIR="${djvulibreAll}" \
    FBINK_DIR="$NIX_BUILD_TOP/fbink" \
    FREETYPE_LIB="${lib.getLib freetype}/lib/libfreetype.so" \
    FREETYPE_LIB_LINK_FLAG="-L ${lib.getLib freetype}/lib -l:libfreetype.so" \
    FREETYPE_DIR="${lib.getDev freetype}" \
    FRIBIDI_LIB="${lib.getLib fribidi}/lib/libfribidi.so" \
    FRIBIDI_LIB_LINK_FLAG="-L ${lib.getLib fribidi}/lib -l:libfribidi.so" \
    FRIBIDI_DIR="${lib.getDev fribidi}" \
    GETTEXT_DIR="${lib.getDev gettext}" \
    LIBGETTEXT="${lib.getLib gettext}/lib/preloadable_libintl.so" \
    GIF_LIB="${lib.getLib giflib}/lib/libgif.so" \
    GIF_DIR="${lib.getDev giflib}" \
    GLIB="${lib.getLib glib}/lib/libglib-2.0.so" \
    GLIB_DIR="${lib.getDev glib}" \
    HARFBUZZ_LIB="${lib.getLib harfbuzz}/lib/libharfbuzz.so" \
    HARFBUZZ_LIB_LINK_FLAG="-L ${lib.getLib harfbuzz}/lib -l:libharfbuzz.so" \
    HARFBUZZ_DIR="${lib.getDev harfbuzz}" \
    JPEG_LIB="${lib.getLib libjpeg_turbo}/lib/libjpeg.so" \
    JPEG_LIB_LINK_FLAG="-L ${lib.getLib libjpeg_turbo}/lib -l:libjpeg.so" \
    JPEG_DIR="${lib.getDev libjpeg_turbo}" \
    TURBOJPEG_LIB="${lib.getLib libjpeg_turbo}/lib/libturbojpeg.so" \
    K2PDFOPT_DIR="$NIX_BUILD_TOP/libk2pdfopt" \
    KOBO_USBMS_DIR="$NIX_BUILD_TOP/kobo-usbms" \
    LEPTONICA_DIR="$NIX_BUILD_TOP/leptonica" \
    LIBICONV="${lib.getLib libiconvReal}/lib/libiconv.so" \
    LIBICONV_DIR="${lib.getDev libiconvReal}" \
    LIBUNIBREAK_LIB="${lib.getLib libunibreak}/lib/libunibreak.so" \
    LIBUNIBREAK_DIR="${libAndDev libunibreak}" \
    LIBUNIBREAK_LIB_LINK_FLAG="-L ${lib.getLib libunibreak}/lib -l:libunibreak.so" \
    LIBWEBP_LIB="${lib.getLib libwebp}/lib/libwebp.so" \
    LIBWEBPDEMUX_LIB="${lib.getLib libwebp}/lib/libwebpdemux.so" \
    LIBWEBPSHARPYUV_LIB="${lib.getLib libwebp}/lib/libwebpsharpyuv.so" \
    LIBWEBP_DIR="${lib.getDev libwebp}" \
    LODEPNG_DIR="$NIX_BUILD_TOP/lodepng" \
    LPEG_ROCK="${rockspecFor "lpeg"}" \
    LUNASVG_DIR="$NIX_BUILD_TOP/lunasvg" \
    LUAJIT="${luaEnv}/bin/luajit" \
    LUAJIT_JIT="${luaEnv}/share/lua/5.1/jit" \
    LUAJIT_LIB="${lib.getLib luaEnv}/lib/libluajit-5.1.so" \
    LUASEC="${luaEnv}/share/lua/5.1/ssl/" \
    LUASOCKET="${luaEnv}/share/lua/5.1/socket/" \
    LUA_HTMLPARSER_ROCK="${rockspecFor "htmlparser"}" \
    LUA_INCDIR="${lib.getDev luaEnv}/include" \
    LUA_LIBDIR="${lib.getLib luaEnv}/lib/libluajit-5.1.so" \
    LUA_RAPIDJSON_ROCK="${rockspecFor "rapidjson"}" \
    LUA_SPORE_ROCK="${rockspecFor "lua-spore"}" \
    MINIZIP_DIR="$NIX_BUILD_TOP/minizip" \
    MUPDF_DIR="$NIX_BUILD_TOP/mupdf" \
    NANOSVG_HEADERS="${nanosvg-headers-ko}" \
    NANOSVG_INCLUDE_DIR="${nanosvg-headers-ko}" \
    OPENSSL_LIB="${lib.getLib openssl}/lib/libssl.so" \
    OPENSSL_DIR="${opensslAll}" \
    SSL_LIB="${lib.getLib openssl}/lib/libssl.so.3" \
    CRYPTO_LIB="${lib.getLib openssl}/lib/libcrypto.so" \
    PNG_LIB="${lib.getLib libpng}/lib/libpng.so" \
    PNG_DIR="${libAndDev libpng}" \
    POPEN_NOSHELL_DIR="$NIX_BUILD_TOP/popen-noshell" \
    SQLITE_LIB="${lib.getLib sqlite}/lib/libsqlite3.so" \
    SQLITE_DIR="${lib.getDev sqlite}" \
    TESSERACT_DIR="$NIX_BUILD_TOP/tesseract" \
    TURBO_DIR="$NIX_BUILD_TOP/turbo" \
    UTF8PROC_LIB="${lib.getLib utf8proc}/lib/libutf8proc.so" \
    UTF8PROC_DIR="${utf8procAll}" \
    ZLIB="${lib.getLib zlib}/lib/libz.so" \
    ZLIB_DIR="${zlibAll}" \
    ZLIB_STATIC="${zlib.static}/lib/libz.a" \
    ZMQ_LIB="${lib.getLib zeromq4}/lib/libzmq.so" \
    ZMQ_DIR="${lib.getDev zeromq4}" \
    ZSTD_LIB="${lib.getLib zstd}/lib/libzstd.so" \
    ZSTD_DIR="${lib.getDev zstd}" \
    ZSTD_DESTDIR="${lib.getDev zstd}" \
  '';

  # DO_STRIP=0 else it'll try to strip our externally built libraries, and error because those live in the nix store.
  makeFlags = ''
    TARGET=${target} DEBIAN=1 SHELL=sh VERBOSE=1 \
      DO_STRIP=0 \
      ${makefileDefs} \
  '';

  symlinkThirdpartyBins = outdir: ''
    ln -sf "${lib.getBin dropbear}/bin/dropbear" "${outdir}/dropbear"
    ln -sf "${lib.getExe gnutar}" "${outdir}/tar"
    ln -sf "${lib.getBin openssh}/libexec/sftp-server" "${outdir}/sftp-server"
    ln -sf "${lib.getBin sdcv}/bin/sdcv" "${outdir}/sdcv"
    ln -sf "${lib.getBin zsync}/bin/zsync" "${outdir}/zsync2"
  '';

  thirdparty = [
    curl
    czmq
    djvulibre
    dropbear
    freetype
    fribidi
    gettext
    giflib
    glib
    gnutar
    harfbuzz
    libiconvReal
    libjpeg_turbo
    libpng
    libunibreak
    libwebp
    openssl
    openssh
    sdcv
    SDL2
    sqlite
    utf8proc
    zlib
    zeromq4
    zstd
    zsync
  ];
in
stdenv.mkDerivation rec {
  pname = "koreader-from-src";
  inherit version;
  srcs = [
    src
    fbink-src-ko
    kobo-usbms-src-ko
    leptonica-src-ko
    libk2pdfopt-src-ko
    lodepng-src-ko
    lunasvg-src-ko
    minizip-src-ko
    mupdf-src-ko
    popen-noshell-src-ko
    tesseract-src-ko
    turbo-src-ko
  ];

  patches = [
    # ./debug.patch  #< not needed to build, just helps debug packaging issues
    ./rss-no-interrupt-on-image-failure.patch  # just a preference
  ];

  sourceRoot = "koreader";

  nativeBuildInputs = [
    buildPackages.stdenv.cc
    autoconf  # autotools is used by some thirdparty libraries
    automake
    autoPatchelfHook  # used by us, in fixupPhase, to ensure substituted thirdparty deps can be loaded at runtime
    cmake  # for koreader/base submodule
    git
    libtool
    makeWrapper
    pkg-config
  ];
  buildInputs = [
    luaEnv  #< specifically for lua.h
  ];

  postPatch = ''
    # patch for newer openssl
    substituteInPlace base/ffi/crypto.lua \
      --replace-fail 'ffi.load("libs/libcrypto.so.1.1")' 'ffi.load("libcrypto.so")'

    # dlopen libraries by name only, allowing them to be found via LD_LIBRARY_PATH
    # instead of just via $out/libs. this is required whenever we direct KOreader to use system libs instead of its vendored libs.
    for f in $(shopt -s globstar; ls **/*.lua) ; do
      substituteInPlace "$f" \
        --replace-quiet 'ffi.load("libs/' 'ffi.load("'
    done

    # don't force-rebuild third-party components, else we can't replace them with our own
    substituteInPlace base/Makefile.third \
      --replace-fail '	-rm ' '	# -rm'

    # make some sources writable, particularly so koreader can apply its patches (by default only the `sourceRoot` is writable)
    chmod -R u+w "$NIX_BUILD_TOP"/{fbink,kobo-usbms,leptonica,libk2pdfopt,lodepng,lunasvg,minizip,mupdf,popen-noshell,tesseract,turbo}
    # koreader builds these deps itself: we mock out the download stage, and it does the rest
    for dep in fbink kobo-usbms libk2pdfopt lodepng lunasvg minizip mupdf popen-noshell turbo; do
      # sed -i 's/DOWNLOAD_COMMAND .*/DOWNLOAD_COMMAND ""/' "base/thirdparty/$dep/CMakeLists.txt"
      sed -i "s:DOWNLOAD_COMMAND .*:DOWNLOAD_COMMAND rm -fd $dep $dep-build \\&\\& ln -s $NIX_BUILD_TOP/$dep $dep \\&\\& ln -s $NIX_BUILD_TOP/$dep $dep-build :" "base/thirdparty/$dep/CMakeLists.txt"
    done

    # lots of places in Makefile.third (incorrectly) assume lib paths are relative to CURDIR,
    # so link /nix into CURDIR to allow them to work anyway
    ln -s /nix base/nix
  '';

  dontConfigure = true;
  buildPhase = ''
    # outDir should match OUTPUT_DIR in koreader-base
    outDir="$NIX_BUILD_TOP/koreader/base/build/${stdenv.hostPlatform.config}"
    mkdir -p "$outDir"
    ${symlinkThirdpartyBins "$outDir"}

    make ${makeFlags}
  '';

  env = lib.optionalAttrs (stdenv.buildPlatform != stdenv.hostPlatform) {
    CHOST = stdenv.hostPlatform.config;
  };

  installPhase = ''
    # XXX: build without INSTALL_DIR="$out" as make arg because that conflicts with vars used by third-party libs.
    # instead, `make` and then manually install koreader to $out ourselves.
    # TODO: might be safe to specify `INSTALL_DIR` here as an env var instead, though?
    make debianupdate ${makeFlags}

    mv koreader-${target}-${stdenv.hostPlatform.config}/debian/usr $out

    # XXX: nixpkgs' `koreader` adds glib and gtk3-x11 to LD_LIBRARY_PATH as well.
    wrapProgram $out/bin/koreader --prefix LD_LIBRARY_PATH : ${
      (lib.makeLibraryPath thirdparty) + ":$out/lib/koreader/libs"
    }
  '';

  preFixup = ''
    # koreader installation copies the thirdparty binaries like tar, sdcv, which we injected earlier.
    # but we specifically want these to live in the nix store and be symlinked into koreader instead.
    # TODO: this might symlink binaries which koreader doesn't actually use...
    #       i should either only overwrite the binaries which DO exist at this point,
    #       or find some other way to inject them (i can maybe just put them on KOreader's PATH?).
    ${symlinkThirdpartyBins "$out/lib/koreader"}
    ${lib.concatStringsSep "\n" (builtins.map (dep: ''
        if [ -e "${lib.getLib dep}/lib" ]; then
          addAutoPatchelfSearchPath "${lib.getLib dep}/lib"
        fi
      '') thirdparty)
    }
  '';

  passthru = {
    # exposed for debugging
    inherit luajit52 luaEnv mupdf-src-ko nanosvg-headers-ko rockspecFor;
    inherit (overlayedLuaPkgs luaEnv.pkgs)
      luajson
      htmlparser
      lua-spore
    ;
  };

  meta = with lib; {
    homepage = "https://github.com/koreader/koreader";
    description =
      "An ebook reader application supporting PDF, DjVu, EPUB, FB2 and many more formats, running on Cervantes, Kindle, Kobo, PocketBook and Android devices";
    sourceProvenance = with sourceTypes; [ fromSource ];
    platforms = platforms.linux;
    license = licenses.agpl3Only;
    maintainers = with maintainers; [ colinsane contrun neonfuz];
  };
}
