@@ -74,17 +74,21 @@ let
7474 # --shar256: 003lm3pm0000hbfmii7xcdd9v20000flxf7gdl2pyxia7p014i8z
7575 # will be trated like a field and returned here
7676 # (used in call-cabal-project-to-nix.nix to create a fixed-output derivation)
77− extractRepoData = cabalProjectFileName: lookupSha256: repo: {
77+ extractSourceRepoPackageData = cabalProjectFileName: sha256map: repo: {
7878 url = repo.location;
7979 ref = repo.tag;
80− sha256 = repo."--sha256" or (lookupSha256 repo);
80+ sha256 = repo."--sha256" or (
81+ if sha256map != null
82+ then sha256map."${repo.location}"."${repo.tag}"
83+ else null);
8184 subdirs = if repo ? subdir
8285 then pkgs.lib.filter (x: x != "") (pkgs.lib.splitString " " repo.subdir)
8386 else ["."];
8487 };
8588
8689 # Parse a source-repository-package and return data of `type: git` repositories
87− parseBlock = cabalProjectFileName: lookupSha256: block:
90+ # See tests/unit.nix for examples of input and output.
91+ parseSourceRepositoryPackageBlock = cabalProjectFileName: sha256map: block:
8892 let
8993 x = span (pkgs.lib.strings.hasPrefix " ") (pkgs.lib.splitString "\n" block);
9094 attrs = parseBlockLines x.fst;
@@ -95,10 +99,106 @@ let
9599 otherText = "\nsource-repository-package\n" + block;
96100 }
97101 else {
98− sourceRepo = extractRepoData cabalProjectFileName lookupSha256 attrs;
102+ sourceRepo = extractSourceRepoPackageData cabalProjectFileName sha256map attrs;
99103 otherText = pkgs.lib.strings.concatStringsSep "\n" x.snd;
100104 };
101105
106+ parseSourceRepositoryPackages = cabalProjectFileName: sha256map: source-repo-override: projectFile:
107+ let
108+ blocks = pkgs.lib.splitString "\nsource-repository-package\n" ("\n" + projectFile);
109+ initialText = pkgs.lib.lists.take 1 blocks;
110+ repoBlocks = builtins.map (parseSourceRepositoryPackageBlock cabalProjectFileName sha256map) (pkgs.lib.lists.drop 1 blocks);
111+ overrideSourceRepo = sourceRepo: (source-repo-override.${sourceRepo.url} or (pkgs.lib.id)) sourceRepo;
112+ in {
113+ sourceRepos = pkgs.lib.lists.map (block: overrideSourceRepo block.sourceRepo) repoBlocks;
114+ otherText = pkgs.lib.strings.concatStringsSep "\n" (
115+ initialText
116+ ++ (builtins.map (x: x.otherText) repoBlocks));
117+ };
118+
119+ # Parse and replace repository
120+ # This works in a similar way to the `source-repository-package` but we are
121+ # able to simply replace the `repository` blocks with local `file:/nix/store` ones.
122+ # This works because `cabal configure` does not include any of the `/nix/sore/`
123+ # paths in the `plan.json` (so materialized plan-nix will still work as expeced).
124+ # See tests/unit.nix for examples of input and output.
125+ parseRepositoryBlock = cabalProjectFileName: sha256map: cabal-install: nix-tools: block:
126+ let
127+ lines = pkgs.lib.splitString "\n" block;
128+ # The first line will contain the repository name.
129+ x = span (pkgs.lib.strings.hasPrefix " ") (__tail lines);
130+ attrs = parseBlockLines x.fst;
131+ sha256 = attrs."--sha256" or (
132+ if sha256map != null
133+ then sha256map."${attrs.url}"
134+ else null);
135+ in rec {
136+ # This is `some-name` from the `repository some-name` line in the `cabal.project` file.
137+ name = __head lines;
138+ # The $HOME dir with `.cabal` sub directory after running `cabal new-update` to download the repository
139+ home =
140+ pkgs.evalPackages.runCommandLocal name ({
141+ nativeBuildInputs = [ cabal-install pkgs.evalPackages.curl nix-tools ];
142+ LOCALE_ARCHIVE = pkgs.lib.optionalString (pkgs.evalPackages.stdenv.buildPlatform.libc == "glibc") "${pkgs.evalPackages.glibcLocales}/lib/locale/locale-archive";
143+ LANG = "en_US.UTF-8";
144+ } // pkgs.lib.optionalAttrs (sha256 != null) {
145+ outputHashMode = "recursive";
146+ outputHashAlgo = "sha256";
147+ outputHash = sha256;
148+ }) ''
149+ mkdir -p $out/.cabal
150+ cat <<EOF > $out/.cabal/config
151+ repository ${name}
152+ url: ${attrs.url}
153+ ${pkgs.lib.optionalString (attrs ? secure) "secure: ${attrs.secure}"}
154+ ${pkgs.lib.optionalString (attrs ? root-keys) "root-keys: ${attrs.root-keys}"}
155+ ${pkgs.lib.optionalString (attrs ? key-threshold) "key-threshold: ${attrs.key-threshold}"}
156+ EOF
157+
158+ export SSL_CERT_FILE=${pkgs.evalPackages.cacert}/etc/ssl/certs/ca-bundle.crt
159+ mkdir -p $out/.cabal/packages/${name}
160+ HOME=$out cabal new-update ${name}
161+ '';
162+ # Output of hackage-to-nix
163+ hackage = import (
164+ pkgs.evalPackages.runCommandLocal ("hackage-to-nix-" + name) {
165+ nativeBuildInputs = [ cabal-install pkgs.evalPackages.curl nix-tools ];
166+ LOCALE_ARCHIVE = pkgs.lib.optionalString (pkgs.evalPackages.stdenv.buildPlatform.libc == "glibc") "${pkgs.evalPackages.glibcLocales}/lib/locale/locale-archive";
167+ LANG = "en_US.UTF-8";
168+ } ''
169+ mkdir -p $out
170+ hackage-to-nix $out ${home}/.cabal/packages/${name}/01-index.tar ${attrs.url}
171+ '');
172+ # Tarball info in the same format as `extra-hackage-tarballs` passed in to cabalProject
173+ tarball = {
174+ ${name} = home + "/.cabal/packages/${name}/01-index.tar.gz";
175+ };
176+ # Replacement `repository` block using `file:` uri and the remaining text (text that was not part of the attribute block).
177+ updatedText = ''
178+ repository ${name}
179+ url: file:${home + "/.cabal/packages/${name}"}
180+ secure: True
181+ root-keys:
182+ key-threshold: 0
183+ '' + pkgs.lib.strings.concatStringsSep "\n" x.snd;
184+ };
185+
186+ parseRepositories = cabalProjectFileName: sha256map: cabal-install: nix-tools: projectFile:
187+ let
188+ # This will leave the name of repository in the first line of each block
189+ blocks = pkgs.lib.splitString "\nrepository " ("\n" + projectFile);
190+ initialText = pkgs.lib.lists.take 1 blocks;
191+ repoBlocks = builtins.map (parseRepositoryBlock cabalProjectFileName sha256map cabal-install nix-tools) (pkgs.lib.lists.drop 1 blocks);
192+ in {
193+ extra-hackages = pkgs.lib.lists.map (block: block.hackage) repoBlocks;
194+ tarballs = pkgs.lib.lists.foldl' (x: block: x // block.tarball) {} repoBlocks;
195+ updatedText = pkgs.lib.strings.concatStringsSep "\n" (
196+ initialText
197+ ++ (builtins.map (x: x.updatedText) repoBlocks));
198+ };
199+
102200in {
103− inherit parseIndexState parseBlockLines parseBlock;
201+ inherit parseIndexState parseSourceRepositoryPackages parseRepositories
202+ # These are only exposed for tests
203+ parseSourceRepositoryPackageBlock parseRepositoryBlock;
104204}