{ pkgs ? import (fetchTarball {
    url = "https://github.com/NixOS/nixpkgs/archive/1dab772dd4a68a7bba5d9460685547ff8e17d899.tar.gz";
    sha256 = "18qwmspcsmi0ryhcg66p9i42qwismm7l0706m39zw5ziq6n11fc9";
  }) {}
, CONFIG ? "XSNoCTopConfig" # "DefaultConfig"
, EMU_THREADS ? "4"
, NUM_CORES ? "1"
, EMU_TRACE ? "fst" # "fst" or "vcd"
, genRTL ? false
}:
let
  xs-src = (pkgs.fetchFromGitHub {
    owner = "OpenXiangShan";
    repo = "XiangShan";
    rev = "ff4344e4e52d4e58ba1570da9ace0dafaca74de0";
    hash = "sha256-2vaeqiEOGRlEkcSZLzneqwVe1IldhPMK552ydUnKS+s=";
    forceFetchGit = true;
  }).overrideAttrs (old: {
    # NOTE: Why not use leaveDotGit/deepClone here?
    # see pkgs/build-support/fetchgit/nix-prefetch-git:
    # # > # shallow with leaveDotGit will change hashes
    # In other words: If deepClone/leaveDotGit co-exists with fetchSubmodules
    # (here we only fetch selected submodules by NIX_PREFETCH_GIT_CHECKOUT_HOOK)
    # then hash changes every time.
    #
    # old nixpkgs.fetchgit not support preFetch
    # preFetch = ''export NIX_PREFETCH_GIT_CHECKOUT_HOOK="make -C $out init"'';
    NIX_PREFETCH_GIT_CHECKOUT_HOOK="make -C $out init";
  });

  # mill deps refer to https://github.com/com-lihaoyi/mill/discussions/1170
  millDeps = pkgs.stdenv.mkDerivation {
    name = "xs-mill-cache";
    src = xs-src;
    nativeBuildInputs = [ pkgs.mill ];
    impureEnvVars = pkgs.lib.fetchers.proxyImpureEnvVars;
    # [COURSIER_CACHE with relative path cause artifacts download into process sandbox folder](https://github.com/com-lihaoyi/mill/issues/3946)
    # Thus, COURSIER_CACHE uses absolute path like below
    buildPhase = ''
      export COURSIER_CACHE=$PWD/.cache/coursier
      mill -D http.proxyHost=$(echo $http_proxy | sed -E 's,(.*://)?([^:/]+):([0-9]*).*,\2,') \
           -D http.proxyPort=$(echo $http_proxy | sed -E 's,(.*://)?([^:/]+):([0-9]*).*,\3,') \
           -D https.proxyHost=$(echo $https_proxy | sed -E 's,(.*://)?([^:/]+):([0-9]*).*,\2,') \
           -D https.proxyPort=$(echo $https_proxy | sed -E 's,(.*://)?([^:/]+):([0-9]*).*,\3,') \
        __.prepareOffline
    '';
    installPhase = ''
      cp -r $COURSIER_CACHE $out
    '';
    outputHashMode = "recursive";
    outputHash = "sha256-9qL+2W2KSHIV4KxhfY9F+2j1xP8NCW7mC/CO2EWbYh8=";
  };
in pkgs.stdenv.mkDerivation {
  name = "xs";
  src = xs-src;
  nativeBuildInputs = [
    pkgs.makeWrapper
    pkgs.mill
    pkgs.time
    pkgs.espresso
    pkgs.verilator
    pkgs.sqlite
    pkgs.zlib
    pkgs.zstd
    pkgs.python3
  ];
  COURSIER_CACHE = millDeps;

  postPatch = ''
    patchShebangs scripts/
  ''
  # The .git/ folder format is not stable, thus hard to achieve deterministic.
  # For more information about .git/ and deterministic see the `leaveDotGit` option in
  # [nixpkgs manual: fetchgit](https://nixos.org/manual/nixpkgs/stable/#fetchgit).
  # 1. Clean vcs.version scala lib, which needs the .git/ folder.
  + ''
    find -name build.sc -exec sed -i '/vcs.version/d' {} \;
    sed -i '/def publishVersion/,/^  )$/c\  def publishVersion() = "deterministic-version"' build.sc
    find -name build.sc -exec sed -i 's/def publishVersion =.*vcs.version.*/def publishVersion = "miao"/' {} \;
  ''
  # 2. Clean git related operations in build.sc
  + ''
    sed -i '/def gitStatus:/,/^  }/c\  def gitStatus() = "SHA=${xs-src.rev}\\ndirty=0"' build.sc
    sed -i 's/"git", "ls-files"/"ls", "-1"/' build.sc
    sed -i 's/os.proc("git", "submodule", "status").call().out.text()/""/' build.sc
  ''
  # 3. Clean git in Makefile
  + ''
    sed -i '/@git log/,/rm .__head__/d' Makefile
  '';

  # current version of Chisel (6.6.0) was published against firtool version 1.62.1
  CHISEL_FIRTOOL_PATH = "${(import (pkgs.fetchFromGitHub {
    owner = "NixOS";
    repo = "nixpkgs";
    rev = "771b079bb84ac2395f3a24a5663ac8d1495c98d3";
    sha256 = "0l1l9ms78xd41xg768pkb6xym200zpf4zjbv4kbqbj3z7rzvhpb7";
  }){}).circt}/bin/";
  shellHook = ''
    export NOOP_HOME=$(realpath .)
  '';
  buildPhase = ''
    eval "$shellHook"
    make emu CONFIG=${CONFIG} EMU_THREADS=${EMU_THREADS} NUM_CORES=${NUM_CORES} EMU_TRACE=${EMU_TRACE} -j $NIX_BUILD_CORES
  '';

  outputs = ["out"] ++ pkgs.lib.optional genRTL "rtl";
  installPhase = ''
    mkdir -p $out/bin
    cp build/emu $out/bin/
    wrapProgram $out/bin/emu --prefix PATH : ${pkgs.lib.makeBinPath [pkgs.spike]}
  '' + pkgs.lib.optionalString genRTL ''
    mkdir -p $rtl
    cp -r build/rtl/* $rtl/
  '';
}