|
| 1 | +# Creates a haskell.nix module that adds the `extraHackage` options for specifying Cabal sources as additional compile dependencies. |
| 2 | +compiler-nix-name: |
| 3 | +let |
| 4 | + mylib = { pkgs, compiler-nix-name }: rec { |
| 5 | + mkPackageSpec = src: |
| 6 | + with pkgs.lib; |
| 7 | + let |
| 8 | + cabalFiles = concatLists (mapAttrsToList |
| 9 | + (name: type: if type == "regular" && hasSuffix ".cabal" name then [ name ] else [ ]) |
| 10 | + (builtins.readDir src)); |
| 11 | + |
| 12 | + cabalPath = |
| 13 | + if length cabalFiles == 1 |
| 14 | + then src + "/${builtins.head cabalFiles}" |
| 15 | + else builtins.abort "Could not find unique file with .cabal suffix in source: ${src}"; |
| 16 | + cabalFile = builtins.readFile cabalPath; |
| 17 | + parse = field: |
| 18 | + let |
| 19 | + lines = filter (s: if builtins.match "^${field} *:.*$" (toLower s) != null then true else false) (splitString "\n" cabalFile); |
| 20 | + line = |
| 21 | + if lines != [ ] |
| 22 | + then head lines |
| 23 | + else builtins.abort "Could not find line with prefix ''${field}:' in ${cabalPath}"; |
| 24 | + in |
| 25 | + replaceStrings [ " " ] [ "" ] (head (tail (splitString ":" line))); |
| 26 | + pname = parse "name"; |
| 27 | + version = parse "version"; |
| 28 | + in |
| 29 | + { inherit src pname version; }; |
| 30 | + |
| 31 | + mkHackageDirFor = { pname, version, src }: |
| 32 | + pkgs.runCommand "${pname}-${version}-hackage" { } |
| 33 | + '' |
| 34 | + set -e |
| 35 | + mkdir -p $out/${pname}/${version} |
| 36 | + md5=11111111111111111111111111111111 |
| 37 | + sha256=1111111111111111111111111111111111111111111111111111111111111111 |
| 38 | + length=1 |
| 39 | + cat <<EOF > $out/"${pname}"/"${version}"/package.json |
| 40 | + { |
| 41 | + "signatures" : [], |
| 42 | + "signed" : { |
| 43 | + "_type" : "Targets", |
| 44 | + "expires" : null, |
| 45 | + "targets" : { |
| 46 | + "<repo>/package/${pname}-${version}.tar.gz" : { |
| 47 | + "hashes" : { |
| 48 | + "md5" : "$md5", |
| 49 | + "sha256" : "$sha256" |
| 50 | + }, |
| 51 | + "length" : $length |
| 52 | + } |
| 53 | + }, |
| 54 | + "version" : 0 |
| 55 | + } |
| 56 | + } |
| 57 | + EOF |
| 58 | + cp ${src}/*.cabal $out/"${pname}"/"${version}"/ |
| 59 | + ''; |
| 60 | + |
| 61 | + mkHackageTarballFromDirsFor = hackageDirs: |
| 62 | + let |
| 63 | + f = dir: '' |
| 64 | + echo ${dir} |
| 65 | + ln -s ${dir}/* hackage/ |
| 66 | + ''; |
| 67 | + in |
| 68 | + pkgs.runCommand "01-index.tar.gz" { } '' |
| 69 | + mkdir hackage |
| 70 | + ${builtins.concatStringsSep "" (map f hackageDirs)} |
| 71 | + cd hackage |
| 72 | + tar --sort=name --owner=root:0 --group=root:0 --mtime='UTC 2009-01-01' -hczvf $out */*/* |
| 73 | + ''; |
| 74 | + |
| 75 | + mkHackageTarballFor = pkg-specs: |
| 76 | + mkHackageTarballFromDirsFor (map mkHackageDirFor pkg-specs); |
| 77 | + |
| 78 | + mkHackageNixFor = hackageTarball: |
| 79 | + pkgs.runCommand "hackage-nix" { } '' |
| 80 | + set -e |
| 81 | + export LC_CTYPE=C.UTF-8 |
| 82 | + export LC_ALL=C.UTF-8 |
| 83 | + export LANG=C.UTF-8 |
| 84 | + cp ${hackageTarball} 01-index.tar.gz |
| 85 | + ${pkgs.gzip}/bin/gunzip 01-index.tar.gz |
| 86 | + ${pkgs.haskell-nix.nix-tools.${compiler-nix-name}}/bin/hackage-to-nix $out 01-index.tar "https://mkHackageNix/" |
| 87 | + ''; |
| 88 | + |
| 89 | + copySrc = src: builtins.path { |
| 90 | + path = src; |
| 91 | + name = "copied-src-${builtins.baseNameOf (builtins.unsafeDiscardStringContext src)}"; |
| 92 | + }; |
| 93 | + |
| 94 | + mkModuleFor = pkg-specs: { lib, ... }: { |
| 95 | + # Prevent nix-build from trying to download the packages |
| 96 | + packages = pkgs.lib.listToAttrs (map |
| 97 | + (spec: { |
| 98 | + name = spec.pname; |
| 99 | + value = { src = lib.mkOverride 99 (copySrc spec.src); }; |
| 100 | + }) |
| 101 | + pkg-specs); |
| 102 | + }; |
| 103 | + |
| 104 | + mkHackageFromSpecFor = pkg-specs: rec { |
| 105 | + extra-hackage-tarball = mkHackageTarballFor pkg-specs; |
| 106 | + extra-hackage = mkHackageNixFor extra-hackage-tarball; |
| 107 | + module = mkModuleFor pkg-specs; |
| 108 | + }; |
| 109 | + |
| 110 | + mkHackageFor = srcs: mkHackageFromSpecFor (map mkPackageSpec srcs); |
| 111 | + }; |
| 112 | +in |
| 113 | + |
| 114 | +{ lib, config, pkgs, ... }: |
| 115 | +let |
| 116 | + l = mylib { inherit pkgs; inherit compiler-nix-name; }; |
| 117 | + # FIXME: We have only one Hackage now |
| 118 | + # FIXME: Do copySrc here, but for some reason Nix shits itself |
| 119 | + theHackages = [ (l.mkHackageFor config.extraHackage) ]; |
| 120 | + ifd-parallel = pkgs.runCommandNoCC "ifd-parallel" { myInputs = builtins.foldl' (b: a: b ++ [ a.extra-hackage a.extra-hackage-tarball ]) [ ] theHackages; } "echo $myInputs > $out"; |
| 121 | + ifdseq = x: builtins.seq (builtins.readFile ifd-parallel.outPath) x; |
| 122 | + nlib = pkgs.lib; |
| 123 | +in |
| 124 | +{ |
| 125 | + _file = "lambda-buffers/extras/haskell.nix/extra-hackage.nix"; |
| 126 | + options = with lib.types; { |
| 127 | + extraHackage = lib.mkOption { |
| 128 | + type = listOf str; # FIXME: Allow passing in a tuple of the src and cabal file instead. |
| 129 | + default = [ ]; |
| 130 | + description = "List of paths to cabal projects to include as extra hackages"; |
| 131 | + }; |
| 132 | + }; |
| 133 | + config = lib.mkIf (config.extraHackage != [ ]) { |
| 134 | + modules = ifdseq (builtins.map (x: x.module) theHackages); |
| 135 | + extra-hackage-tarballs = ifdseq ( |
| 136 | + nlib.listToAttrs (nlib.imap0 |
| 137 | + (i: x: { |
| 138 | + name = "_" + builtins.toString i; |
| 139 | + value = x.extra-hackage-tarball; |
| 140 | + }) |
| 141 | + theHackages)); |
| 142 | + extra-hackages = ifdseq (builtins.map (x: import x.extra-hackage) theHackages); |
| 143 | + }; |
| 144 | +} |
0 commit comments