@@ -122,14 +122,39 @@ let
122122 else builtins.trace ("Using latest index state" + (if name == null then "" else " for " + name) + "!")
123123 (pkgs.lib.last (builtins.attrNames index-state-hashes));
124124
125+in
126+ assert (if index-state-found == null
127+ then throw "No index state passed and none found in ${cabalProjectFileName}" else true);
128+
129+ assert (if index-sha256 == null && !(pkgs.lib.hasSuffix "Z" index-state-found)
130+ then throw "Index state found was ${index-state-found} and no `index-sha256` was provided. "
131+ "The index hash lookup code requires zulu time zone (ends in a Z)" else true);
132+
133+let
134+ # If a hash was not specified find a suitable cached index state to
135+ # use that will contain all the packages we need. By using the
136+ # first one after the desired index-state we can avoid recalculating
137+ # when new index-state-hashes are added.
138+ # See https://github.com/input-output-hk/haskell.nix/issues/672
139+ cached-index-state = if index-sha256 != null
140+ then index-state-found
141+ else
142+ let
143+ suitable-index-states =
144+ builtins.filter
145+ (s: s >= index-state-found) # This compare is why we need zulu time
146+ (builtins.attrNames index-state-hashes);
147+ in
148+ if builtins.length suitable-index-states == 0
149+ then index-state-found
150+ else pkgs.lib.head suitable-index-states;
151+
125152 # Lookup hash for the index state we found
126153 index-sha256-found = if index-sha256 != null
127154 then index-sha256
128− else index-state-hashes.${index-state-found} or null;
155+ else index-state-hashes.${cached-index-state} or null;
129156
130157in
131− assert (if index-state-found == null
132− then throw "No index state passed and none found in ${cabalProjectFileName}" else true);
133158 assert (if index-sha256-found == null
134159 then throw "Unknown index-state ${index-state-found}, the latest index-state I know about is ${pkgs.lib.last (builtins.attrNames index-state-hashes)}. You may need to update to a newer hackage.nix." else true);
135160
@@ -389,12 +414,23 @@ let
389414 ''}
390415 export SSL_CERT_FILE=${cacert}/etc/ssl/certs/ca-bundle.crt
391416 export GIT_SSL_CAINFO=${cacert}/etc/ssl/certs/ca-bundle.crt
392− HOME=${dotCabal {
393− inherit cabal-install nix-tools extra-hackage-tarballs;
394− index-state =
395− builtins.trace ("Using index-state: ${index-state-found}" + (if name == null then "" else " for " + name))
396− index-state-found;
397− sha256 = index-sha256-found; }} cabal v2-configure \
417+ HOME=${
418+ # This creates `.cabal` directory that is as it would have
419+ # been at the time `cached-index-state`. We may include
420+ # some packages that will be excluded by `index-state-found`
421+ # which is used by cabal (cached-index-state >= index-state-found).
422+ dotCabal {
423+ inherit cabal-install nix-tools extra-hackage-tarballs;
424+ index-state = cached-index-state;
425+ sha256 = index-sha256-found;
426+ }
427+ } cabal v2-configure \
428+ --index-state=${
429+ # Setting the desired `index-state` here in case it was not
430+ # from the cabal.project file. This will further restrict the
431+ # packages used by the solver (cached-index-state >= index-state-found).
432+ builtins.trace ("Using index-state: ${index-state-found}" + (if name == null then "" else " for " + name))
433+ index-state-found} \
398434 --with-ghc=${ghc.targetPrefix}ghc \
399435 --with-ghc-pkg=${ghc.targetPrefix}ghc-pkg \
400436 --enable-tests \
@@ -437,4 +473,9 @@ let
437473 # move pkgs.nix to default.nix ensure we can just nix `import` the result.
438474 mv $out${subDir'}/pkgs.nix $out${subDir'}/default.nix
439475 '');
440−in { projectNix = plan-nix; inherit src; inherit (fixedProject) sourceRepos; }
476+in {
477+ projectNix = plan-nix;
478+ index-state = index-state-found;
479+ inherit src;
480+ inherit (fixedProject) sourceRepos;
481+}