Skip to content

Commit 242f38d

Browse files
committed
Add support for external Hackage repositories
Currently haskell.nix is not able to build Cabal projects that depend on packages from private Hackage repositories, as it make only main Hackage available to cabal. This is unfortunate. This commit adds this functionality, by allowing the user to pass `extra-hackages` and `extra-hackage-tarballs` to `mkPkgSet` and `callCabalToNix` respectively, to add as much extra repositories as needed. This repositories are first made available to Cabal when calling `v2-configure`, resulting in correct plans. Later they are combined with global Hackage when building dependencies of the local packages.
1 parent 3bd0157 commit 242f38d

File tree

3 files changed

+53
-20
lines changed

3 files changed

+53
-20
lines changed

lib/call-cabal-project-to-nix.nix

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# If the tests and benchmarks are not needed and they
1717
# causes the wrong plan to be choosen, then we can use
1818
# `configureArgs = "--disable-tests --disable-benchmarks";`
19+
, extra-hackage-tarballs ? []
1920
, ...
2021
}@args:
2122
# cabal-install versions before 2.4 will generate insufficient plan information.
@@ -201,7 +202,7 @@ let
201202
export SSL_CERT_FILE=${cacert}/etc/ssl/certs/ca-bundle.crt
202203
export GIT_SSL_CAINFO=${cacert}/etc/ssl/certs/ca-bundle.crt
203204
HOME=${dotCabal {
204-
inherit cabal-install nix-tools;
205+
inherit cabal-install nix-tools extra-hackage-tarballs;
205206
index-state =
206207
builtins.trace ("Using index-state: ${index-state-found}" + (if name == null then "" else " for " + name))
207208
index-state-found;

mk-local-hackage-repo/default.nix

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
# nix would be pointless as we'd have to hardcode them to produce the same output
1515
# reproducably.
1616
#
17-
{ pkgs, hackageTarball }:
18-
{ index-state, sha256, ... }@args:
19-
let index = hackageTarball args; in
20-
pkgs.runCommand "hackage-repo-${builtins.replaceStrings [":"] [""] index-state}" { nativeBuildInputs = [ pkgs.buildPackages.nix ]; } ''
17+
pkgs:
18+
{ name, index }:
19+
20+
pkgs.runCommand "hackage-repo-${name}" { nativeBuildInputs = [ pkgs.buildPackages.nix ]; } ''
2121
mkdir -p $out
2222
export expires="4000-01-01T00:00:00Z"
2323

overlays/haskell.nix

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,19 @@ self: super: {
7070
{ pkg-def # Base package set. Either from stackage (via stack-to-nix) or from a cabal projects plan file (via plan-to-nix)
7171
, pkg-def-extras ? [] # Additional packages to augment the Base package set `pkg-def` with.
7272
, modules ? []
73+
, extra-hackages ? [] # Extra Hackage repositories to use besides main one.
7374
}@args:
7475

75-
import ../package-set.nix (args // {
76+
let
77+
hackageAll = builtins.foldl' (base: extra: base // extra) hackage extra-hackages;
78+
in
79+
80+
import ../package-set.nix {
81+
inherit (args) pkg-def pkg-def-extras;
7682
modules = defaultModules ++ modules;
7783
pkgs = self;
78-
inherit hackage pkg-def;
79-
});
84+
hackage = hackageAll;
85+
};
8086

8187
# Some boot packages (libiserv) are in lts, but not in hackage,
8288
# so we should not try to get it from hackage based on the stackage
@@ -128,6 +134,7 @@ self: super: {
128134
{ plan-pkgs # Path to the output of plan-to-nix
129135
, pkg-def-extras ? []
130136
, modules ? []
137+
, extra-hackages ? []
131138
}@args:
132139

133140
let
@@ -146,6 +153,7 @@ self: super: {
146153
modules = [ { doExactConfig = true; } patchesModule ]
147154
++ modules
148155
++ plan-pkgs.modules or [];
156+
inherit extra-hackages;
149157
};
150158

151159
# Package sets for all stackage snapshots.
@@ -169,33 +177,56 @@ self: super: {
169177
nix-tools = self.buildPackages.haskell-nix.nix-tools-cross-compiled;
170178
# TODO perhaps there is a cleaner way to get a suitable nix-tools.
171179

172-
# Produce a fixed output derivation from a moving target (hackage index tarball)
180+
# Produce a fixed output derivation from a moving target (hackage index tarball)
181+
# Takes desired index-state and sha256 and produces a set { name, index }, where
182+
# index points to "01-index.tar.gz" file downloaded from hackage.haskell.org.
173183
hackageTarball = { index-state, sha256, nix-tools ? self.haskell-nix.nix-tools, ... }:
174184
assert sha256 != null;
175-
self.fetchurl {
176-
name = "01-index.tar.gz-at-${builtins.replaceStrings [":"] [""] index-state}";
185+
let at = builtins.replaceStrings [":"] [""] index-state; in
186+
{ name = "hackage.haskell.org-at-${at}";
187+
index = self.fetchurl {
188+
name = "01-index.tar.gz-at-${at}";
177189
url = "https://hackage.haskell.org/01-index.tar.gz";
178190
downloadToTemp = true;
179191
postFetch = "${nix-tools}/bin/truncate-index -o $out -i $downloadedFile -s ${index-state}";
180192

181193
outputHashAlgo = "sha256";
182194
outputHash = sha256;
195+
};
183196
};
184197

185-
mkLocalHackageRepo = import ../mk-local-hackage-repo { inherit hackageTarball; pkgs = self; };
198+
# Creates Cabal local repository from { name, index } set.
199+
mkLocalHackageRepo = import ../mk-local-hackage-repo self;
186200

187-
dotCabal = { index-state, sha256, cabal-install, ... }@args:
201+
dotCabal = { index-state, sha256, cabal-install, extra-hackage-tarballs ? [], ... }@args:
202+
let
203+
allTarballs = [ (hackageTarball args) ] ++ extra-hackage-tarballs;
204+
in
188205
self.runCommand "dot-cabal-at-${builtins.replaceStrings [":"] [""] index-state}" { nativeBuildInputs = [ cabal-install ]; } ''
189206
mkdir -p $out/.cabal
190207
cat <<EOF > $out/.cabal/config
191-
repository cached
192-
url: file:${mkLocalHackageRepo args}
193-
secure: True
194-
root-keys:
195-
key-threshold: 0
208+
${self.lib.concatStrings (
209+
map (tarball:
210+
''
211+
repository ${tarball.name}
212+
url: file:${mkLocalHackageRepo tarball}
213+
secure: True
214+
root-keys:
215+
key-threshold: 0
216+
217+
'') allTarballs
218+
)}
196219
EOF
197-
mkdir -p $out/.cabal/packages/cached
198-
HOME=$out cabal new-update cached
220+
221+
# All repositories must be mkdir'ed before calling new-update on any repo,
222+
# otherwise it fails.
223+
${self.lib.concatStrings (map ({ name, ... }: ''
224+
mkdir -p $out/.cabal/packages/${name}
225+
'') allTarballs)}
226+
227+
${self.lib.concatStrings (map ({ name, ... }: ''
228+
HOME=$out cabal new-update ${name}
229+
'') allTarballs)}
199230
'';
200231

201232
checkMaterialization = false; # This is the default. Use an overlay to set it to true and test all the materialized files
@@ -419,6 +450,7 @@ self: super: {
419450
pkg-def-extras = args.pkg-def-extras or [];
420451
modules = (args.modules or [])
421452
++ self.lib.optional (args ? ghc) { ghc.package = args.ghc; };
453+
extra-hackages = args.extra-hackages or [];
422454
};
423455
in { inherit (pkg-set.config) hsPkgs; plan-nix = plan.nix; };
424456

0 commit comments

Comments
 (0)