{ lib , stdenv , callPackage , dpkg , fetchurl , recurseIntoAttrs , sqlite , elfutils }: let inherit (builtins) elem attrNames attrValues concatMap filter fromJSON getAttr groupBy head isNull listToAttrs map mapAttrs readFile replaceStrings splitVersion ; inherit (lib) converge findFirst groupBy' hasPrefix optional pipe take toInt toList versionAtLeast versionOlder ; aptData = fromJSON (readFile ./packages.json); # Compare versions in debian control file syntax # See: https://www.debian.org/doc/debian-policy/ch-relationships.html#syntax-of-relationship-fields # # NOTE: this is not a proper version comparison # # A proper version solver, should aggregate dependencies with the same name # and compute the constraint (e.g. a (>= 2) a (<< 5) -> 2 <= a << 5) # # But in the intel repo, there are no such "duplicated" dependencies to specify # upper limits, which leads to issues when intel-hpckit-2021 depends on things # like intel-basekit >= 2021.1.0-2403 and we end up installing the newest # basekit instead of the one from 2021. # # To mitigate this, >= is set to take the latest version with matching major # and minor (only revision and patch are allowed to change) compareVersions = got: kind: want: let g0 = take 2 (splitVersion got); w0 = take 2 (splitVersion want); in if isNull want then true else if kind == "=" then got == want else if kind == "<<" then versionOlder got want else if kind == "<=" then versionAtLeast want got else if kind == ">>" then versionOlder want got else if kind == ">=" then (g0 == w0) && versionAtLeast got want # always match major version else throw "unknown operation: ${kind}"; findMatching = { pname, kind, version }: findFirst (x: pname == x.pname && compareVersions x.version kind version) null aptData; isIntel = pkg: (hasPrefix "intel-" pkg.pname); expandDeps = pkg: (map findMatching (filter isIntel pkg.dependencies)) ++ (optional (pkg.size != 0) pkg); # get the oldest by major version. If they have the same major version, take # the newest. This prevents most issues with resolutions # versionOlder b a -> true if b is older than a (b `older` a) getNewerInMajor = a: b: let va = a.version; vb = b.version; va0 = head (splitVersion va); vb0 = head (splitVersion vb); in if isNull a then b else if va0 != vb0 then if va0 > vb0 then b else a else if versionOlder vb va then a else b; removeDups = l: attrValues (groupBy' getNewerInMajor null (getAttr "provides") l); _resolveDeps = converge (l: removeDups (concatMap expandDeps l)); resolveDeps = pkg: let deps = _resolveDeps (toList pkg); namedDeps = (map (x: "${x.pname}-${x.version}") deps); in builtins.traceVerbose (builtins.deepSeq namedDeps namedDeps) deps; blacklist = [ "intel-basekit-env" "intel-basekit-getting-started" "intel-hpckit-env" "intel-hpckit-getting-started" "intel-oneapi-advisor" "intel-oneapi-common-licensing" "intel-oneapi-common-oneapi-vars" "intel-oneapi-common-vars" "intel-oneapi-compiler-cpp-eclipse-cfg" "intel-oneapi-compiler-dpcpp-eclipse-cfg" "intel-oneapi-condaindex" "intel-oneapi-dev-utilities-eclipse-cfg" "intel-oneapi-dpcpp-ct-eclipse-cfg" "intel-oneapi-eclipse-ide" "intel-oneapi-hpc-toolkit-getting-started" "intel-oneapi-icc-eclipse-plugin-cpp" "intel-oneapi-vtune" "intel-oneapi-vtune-eclipse-plugin-vtune" ]; isInBlacklist = pkg: elem pkg.provides blacklist; removeBlacklist = filter (e: !(isInBlacklist e)); dpkgExtractAll = pname: version: {srcs, deps}: stdenv.mkDerivation { inherit pname version srcs; nativeBuildInputs = [ dpkg ]; phases = [ "installPhase" ]; passthru = { inherit deps; }; installPhase = '' mkdir -p $out for src in $srcs; do echo "Unpacking $src" dpkg -x $src $out done ''; }; apthost = "https://apt.repos.intel.com/oneapi/"; fetchDeb = p: fetchurl { url = apthost + p.filename; inherit (p) sha256; }; buildIntel = pkg: pipe pkg [ resolveDeps removeBlacklist (l: {srcs = map fetchDeb l; deps = l; }) (dpkgExtractAll "${pkg.provides}-extracted" pkg.version) ]; findHpcKit = year: findMatching { pname = "intel-hpckit"; kind = "<<"; version = toString (year+1); }; years = map toInt (attrNames components); patchIntel = callPackage ./patch_intel.nix { }; # Version information for each hpckit. This is used to normalize the paths # so that files are in $out/{bin,lib,include...} instead of all over the place # in $out/opt/intel/oneapi/*/*/{...}. # # The most important is the compiler component, which is used to build the # stdenv for the hpckit. # # NOTE: this have to be manually specified, so we can avoid IFD. To add a # new version, add a new field with an empty attrset, (e.g. "2026" = {}; ), # build hpckit_2026.unpatched and use the values from # result/opt/intel/oneapi/* to populate the attrset. # # WARN: if there are more than one version in the folders of the unpatched # components, our dependency resolution hacks have probably failed and the # package set may be broken. components = { "2025" = { ishmem = "1.4"; pti = "0.13"; tcm = "1.4"; umf = "0.11"; ccl = "2021.16"; compiler = "2025.2"; dal = "2025.8"; debugger = "2025.2"; dev-utilities = "2025.2"; dnnl = "2025.2"; dpcpp-ct = "2025.2"; dpl = "2022.9"; ipp = "2022.2"; ippcp = "2025.2"; mkl = "2025.2"; mpi = "2021.16"; tbb = "2022.2"; }; "2024" = { tcm = "1.1"; ccl = "2021.13"; compiler = "2024.2"; dal = "2024.6"; debugger = "2024.2"; dev-utilities = "2024.2"; diagnostics = "2024.2"; dnnl = "2024.2"; dpcpp-ct = "2024.2"; dpl = "2022.6"; ipp = "2021.12"; ippcp = "2021.12"; mkl = "2024.2"; mpi = "2021.13"; tbb = "2021.13"; extraPackages = [ sqlite elfutils ]; }; }; replaceDots = replaceStrings ["."] ["_"]; in recurseIntoAttrs (listToAttrs (map (year: let year_str = toString year; in { name = "hpckit_${year_str}"; value = patchIntel {unpatched = buildIntel (findHpcKit year); components = components.${year_str}; }; }) years)) // { apt = pipe aptData [ (groupBy (p: replaceDots p.provides)) (mapAttrs (_: l: listToAttrs (map (pkg: { name = replaceDots ("v" + pkg.version); value = pkg; }) l))) ] ; inherit resolveDeps patchIntel buildIntel; }