Skip to content

Commit a4f25c3

Browse files
Try #1370:
2 parents c2677de + bcf1154 commit a4f25c3

File tree

15 files changed

+227
-31
lines changed

15 files changed

+227
-31
lines changed

lib/cabal-project-parser.nix

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ 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: lookupSha256: repo: {
7878
url = repo.location;
7979
ref = repo.tag;
8080
sha256 = repo."--sha256" or (lookupSha256 repo);
@@ -95,10 +95,90 @@ let
9595
otherText = "\nsource-repository-package\n" + block;
9696
}
9797
else {
98-
sourceRepo = extractRepoData cabalProjectFileName lookupSha256 attrs;
98+
sourceRepo = extractSourceRepoPackageData cabalProjectFileName lookupSha256 attrs;
9999
otherText = pkgs.lib.strings.concatStringsSep "\n" x.snd;
100100
};
101101

102+
parseSourceRepositoryPackages = cabalProjectFileName: lookupSha256: source-repo-override: projectFile:
103+
let
104+
blocks = pkgs.lib.splitString "\nsource-repository-package\n" ("\n" + projectFile);
105+
initialText = pkgs.lib.lists.take 1 blocks;
106+
repoBlocks = builtins.map (parseBlock cabalProjectFileName lookupSha256) (pkgs.lib.lists.drop 1 blocks);
107+
overrideSourceRepo = sourceRepo: (source-repo-override.${sourceRepo.url} or (pkgs.lib.id)) sourceRepo;
108+
in {
109+
sourceRepos = pkgs.lib.lists.map (block: overrideSourceRepo block.sourceRepo) repoBlocks;
110+
otherText = pkgs.lib.strings.concatStringsSep "\n" (
111+
initialText
112+
++ (builtins.map (x: x.otherText) repoBlocks));
113+
};
114+
115+
# Parse a repository
116+
parseRepositoryBlock = cabalProjectFileName: lookupSha256: cabal-install: nix-tools: block:
117+
let
118+
lines = pkgs.lib.splitString "\n" block;
119+
x = span (pkgs.lib.strings.hasPrefix " ") (__tail lines);
120+
attrs = parseBlockLines x.fst;
121+
in rec {
122+
name = __head lines;
123+
repo = attrs;
124+
home =
125+
pkgs.evalPackages.runCommandLocal name {
126+
nativeBuildInputs = [ cabal-install pkgs.evalPackages.curl nix-tools ];
127+
LOCALE_ARCHIVE = pkgs.lib.optionalString (pkgs.evalPackages.stdenv.buildPlatform.libc == "glibc") "${pkgs.evalPackages.glibcLocales}/lib/locale/locale-archive";
128+
LANG = "en_US.UTF-8";
129+
outputHashMode = "recursive";
130+
outputHashAlgo = "sha256";
131+
outputHash = attrs."--sha256" or (lookupSha256 attrs);
132+
} ''
133+
mkdir -p $out/.cabal
134+
cat <<EOF > $out/.cabal/config
135+
repository ${name}
136+
url: ${attrs.url}
137+
${pkgs.lib.optionalString (attrs ? secure) "secure: ${attrs.secure}"}
138+
${pkgs.lib.optionalString (attrs ? root-keys) "root-keys: ${attrs.root-keys}"}
139+
${pkgs.lib.optionalString (attrs ? key-threshold) "key-threshold: ${attrs.key-threshold}"}
140+
EOF
141+
142+
export SSL_CERT_FILE=${pkgs.evalPackages.cacert}/etc/ssl/certs/ca-bundle.crt
143+
mkdir -p $out/.cabal/packages/${name}
144+
HOME=$out cabal new-update ${name}
145+
ls -ld $out/.cabal
146+
'';
147+
hackage = import (
148+
pkgs.evalPackages.runCommandLocal ("hackageg-to-nix-" + name) {
149+
nativeBuildInputs = [ cabal-install pkgs.evalPackages.curl nix-tools ];
150+
LOCALE_ARCHIVE = pkgs.lib.optionalString (pkgs.evalPackages.stdenv.buildPlatform.libc == "glibc") "${pkgs.evalPackages.glibcLocales}/lib/locale/locale-archive";
151+
LANG = "en_US.UTF-8";
152+
} ''
153+
mkdir -p $out
154+
hackage-to-nix $out ${home}/.cabal/packages/${name}/01-index.tar ${attrs.url}
155+
'');
156+
tarball = {
157+
inherit name;
158+
index = home + "/.cabal/packages/${name}/01-index.tar.gz";
159+
};
160+
otherText = ''
161+
repository ${name}
162+
url: file:${home + "/.cabal/packages/${name}"}
163+
secure: True
164+
root-keys:
165+
key-threshold: 0
166+
'' + pkgs.lib.strings.concatStringsSep "\n" x.snd;
167+
};
168+
169+
parseRepositories = cabalProjectFileName: lookupSha256: cabal-install: nix-tools: projectFile:
170+
let
171+
blocks = pkgs.lib.splitString "\nrepository " ("\n" + projectFile);
172+
initialText = pkgs.lib.lists.take 1 blocks;
173+
repoBlocks = builtins.map (parseRepositoryBlock cabalProjectFileName lookupSha256 cabal-install nix-tools) (pkgs.lib.lists.drop 1 blocks);
174+
in {
175+
extra-hackages = pkgs.lib.lists.map (block: block.hackage) repoBlocks;
176+
tarballs = pkgs.lib.lists.map (block: block.tarball) repoBlocks;
177+
otherText = pkgs.lib.strings.concatStringsSep "\n" (
178+
initialText
179+
++ (builtins.map (x: x.otherText) repoBlocks));
180+
};
181+
102182
in {
103-
inherit parseIndexState parseBlockLines parseBlock;
183+
inherit parseIndexState parseSourceRepositoryPackages parseRepositories;
104184
}

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

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ let
192192

193193
replaceSourceRepos = projectFile:
194194
let
195-
fetchRepo = fetchgit: repoData:
195+
fetchPackageRepo = fetchgit: repoData:
196196
let
197197
fetched =
198198
if repoData.sha256 != null
@@ -221,14 +221,13 @@ let
221221
tag = "minimal";
222222
};
223223

224-
blocks = pkgs.lib.splitString "\nsource-repository-package\n" ("\n" + projectFile);
225-
initialText = pkgs.lib.lists.take 1 blocks;
226-
repoBlocks = builtins.map (pkgs.haskell-nix.haskellLib.parseBlock cabalProjectFileName lookupSha256) (pkgs.lib.lists.drop 1 blocks);
227-
overrideSourceRepo = sourceRepo: (source-repo-override.${sourceRepo.url} or (pkgs.lib.id)) sourceRepo;
228-
sourceRepoData = pkgs.lib.lists.map (x: overrideSourceRepo x.sourceRepo) repoBlocks;
229-
otherText = pkgs.evalPackages.writeText "cabal.project" (pkgs.lib.strings.concatStringsSep "\n" (
230-
initialText
231-
++ (builtins.map (x: x.otherText) repoBlocks)));
224+
# Parse the `source-repository-package` blocks
225+
sourceRepoPackageResult = pkgs.haskell-nix.haskellLib.parseSourceRepositoryPackages
226+
cabalProjectFileName lookupSha256 source-repo-override projectFile;
227+
228+
# Parse the `repository` blocks
229+
repoResult = pkgs.haskell-nix.haskellLib.parseRepositories
230+
cabalProjectFileName lookupSha256 cabal-install nix-tools sourceRepoPackageResult.otherText;
232231

233232
# we need the repository content twice:
234233
# * at eval time (below to build the fixed project file)
@@ -239,12 +238,13 @@ let
239238
# on the target system would use, so that the derivation is unaffected
240239
# and, say, a linux release build job can identify the derivation
241240
# as built by a darwin builder, and fetch it from a cache
242-
sourceReposEval = builtins.map (fetchRepo pkgs.evalPackages.fetchgit) sourceRepoData;
243-
sourceReposBuild = builtins.map (x: (fetchRepo pkgs.fetchgit x).fetched) sourceRepoData;
241+
sourceReposEval = builtins.map (fetchPackageRepo pkgs.evalPackages.fetchgit) sourceRepoPackageResult.sourceRepos;
242+
sourceReposBuild = builtins.map (x: (fetchPackageRepo pkgs.fetchgit x).fetched) sourceRepoPackageResult.sourceRepos;
244243
in {
245244
sourceRepos = sourceReposBuild;
245+
inherit (repoResult) tarballs extra-hackages;
246246
makeFixedProjectFile = ''
247-
cp -f ${otherText} ./cabal.project
247+
cp -f ${pkgs.evalPackages.writeText "cabal.project" repoResult.otherText} ./cabal.project
248248
'' +
249249
pkgs.lib.optionalString (builtins.length sourceReposEval != 0) (''
250250
chmod +w -R ./cabal.project
@@ -279,7 +279,7 @@ let
279279

280280
fixedProject =
281281
if rawCabalProject == null
282-
then { sourceRepos = []; makeFixedProjectFile = ""; replaceLocations = ""; }
282+
then { sourceRepos = []; tarballs = []; extra-hackages = []; makeFixedProjectFile = ""; replaceLocations = ""; }
283283
else replaceSourceRepos rawCabalProject;
284284

285285
# The use of the actual GHC can cause significant problems:
@@ -524,7 +524,11 @@ let
524524
# some packages that will be excluded by `index-state-found`
525525
# which is used by cabal (cached-index-state >= index-state-found).
526526
dotCabal {
527-
inherit cabal-install nix-tools extra-hackage-tarballs;
527+
inherit cabal-install nix-tools;
528+
extra-hackage-tarballs =
529+
if __length extra-hackage-tarballs != 0
530+
then extra-hackage-tarballs
531+
else fixedProject.tarballs;
528532
index-state = cached-index-state;
529533
sha256 = index-sha256-found;
530534
}
@@ -610,5 +614,5 @@ in {
610614
projectNix = plan-nix;
611615
index-state = index-state-found;
612616
inherit src;
613-
inherit (fixedProject) sourceRepos;
617+
inherit (fixedProject) sourceRepos extra-hackages;
614618
}

lib/default.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ in {
275275

276276
inherit (import ./cabal-project-parser.nix {
277277
inherit pkgs;
278-
}) parseIndexState parseBlock;
278+
}) parseIndexState parseSourceRepositoryPackages parseRepositories;
279279

280280

281281
cabalToNixpkgsLicense = import ./spdx/cabal.nix pkgs;

mk-local-hackage-repo/default.nix

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@
1717
pkgs:
1818
{ name, index }:
1919

20-
pkgs.evalPackages.runCommandLocal "hackage-repo-${name}" { nativeBuildInputs = [ pkgs.evalPackages.nix ]; } ''
20+
pkgs.evalPackages.runCommandLocal "hackage-repo-${name}" {} ''
2121
mkdir -p $out
2222
export expires="4000-01-01T00:00:00Z"
2323
2424
ln -sf ${index} $out/01-index.tar.gz
25-
export index_md5=$(nix-hash --flat --type md5 ${index})
26-
export index_sha256=$(nix-hash --flat --type sha256 ${index})
25+
export index_md5=$(md5sum ${index} | awk '{ print $1 }')
26+
export index_sha256=$(sha256sum ${index} | awk '{ print $1 }')
2727
${
2828
# When possible check the hash we calculate here against the `outputHash`
2929
# of the index derivation (when the `extra-hackages` feature is used the index
@@ -37,18 +37,18 @@ ${
3737
export index_length=$(stat --printf="%s" ${index})
3838
3939
substituteAll ${./root.json} $out/root.json
40-
export root_md5=$(nix-hash --flat --type md5 $out/root.json)
41-
export root_sha256=$(nix-hash --flat --type sha256 $out/root.json)
40+
export root_md5=$(md5sum $out/root.json | awk '{ print $1 }')
41+
export root_sha256=$(sha256sum $out/root.json | awk '{ print $1 }')
4242
export root_length=$(stat --printf="%s" $out/root.json)
4343
4444
substituteAll ${./mirrors.json} $out/mirrors.json
45-
export mirrors_md5=$(nix-hash --flat --type md5 $out/mirrors.json)
46-
export mirrors_sha256=$(nix-hash --flat --type sha256 $out/mirrors.json)
45+
export mirrors_md5=$(md5sum $out/mirrors.json | awk '{ print $1 }')
46+
export mirrors_sha256=$(sha256sum $out/mirrors.json | awk '{ print $1 }')
4747
export mirrors_length=$(stat --printf="%s" $out/mirrors.json)
4848
4949
substituteAll ${./snapshot.json} $out/snapshot.json
50-
export snapshot_md5=$(nix-hash --flat --type md5 $out/snapshot.json)
51-
export snapshot_sha256=$(nix-hash --flat --type sha256 $out/snapshot.json)
50+
export snapshot_md5=$(md5sum $out/snapshot.json | awk '{ print $1 }')
51+
export snapshot_sha256=$(sha256sum $out/snapshot.json | awk '{ print $1 }')
5252
export snapshot_length=$(stat --printf="%s" $out/snapshot.json)
5353
5454
substituteAll ${./timestamp.json} $out/timestamp.json

overlays/haskell.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ final: prev: {
515515
++ final.lib.optional (args.ghcOverride != null || args.ghc != null)
516516
{ ghc.package = if args.ghcOverride != null then args.ghcOverride else args.ghc; }
517517
++ [ { compiler.nix-name = final.lib.mkForce args.compiler-nix-name; } ];
518-
extra-hackages = args.extra-hackages or [];
518+
extra-hackages = args.extra-hackages or [] ++ callProjectResults.extra-hackages;
519519
};
520520

521521
project = addProjectAndPackageAttrs rec {

test/default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ let
186186
stack-source-repo = callTest ./stack-source-repo { inherit compiler-nix-name; };
187187
cabal-doctests = callTest ./cabal-doctests { inherit util compiler-nix-name; };
188188
extra-hackage = callTest ./extra-hackage { inherit compiler-nix-name; };
189+
ghcjs-overlay = callTest ./ghcjs-overlay { inherit compiler-nix-name; };
189190
hls-cabal = callTest ./haskell-language-server/cabal.nix { inherit compiler-nix-name; };
190191
hls-stack = callTest ./haskell-language-server/stack.nix { inherit compiler-nix-name; };
191192
cabal-hpack = callTest ./cabal-hpack { inherit util compiler-nix-name; };
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
packages: *.cabal
22

3-
repository local
4-
url: http://127.0.0.1:7777
3+
-- Including `repository` here now actually causes haskell.nix to try
4+
-- to download the repository. Luckily this test still works with this
5+
-- commented out.
6+
-- See the ghcjs-overlays test for an example of how to use `repository` blocks.
7+
-- repository local
8+
-- url: http://127.0.0.1:7777

test/ghcjs-overlay/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Revision history for external-package-user
2+
3+
## 0.1.0.0 -- YYYY-mm-dd
4+
5+
* First version. Released on an unsuspecting world.

test/ghcjs-overlay/Main.hs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module Main where
2+
3+
import qualified MyLib (someFunc)
4+
5+
main :: IO ()
6+
main = do
7+
putStrLn "Hello, Haskell!"
8+
MyLib.someFunc

test/ghcjs-overlay/MyLib.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module MyLib (someFunc) where
2+
3+
someFunc :: IO ()
4+
someFunc = putStrLn "someFunc"

test/ghcjs-overlay/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Tests for extra Hackage functionality
2+
3+
This directory contains two packages, `external-package-demo` and `external-package-user`, the
4+
second one depends on the first one. Both packages were created with `cabal init`.
5+
6+
`external-package-demo` was uploaded to local Hackage at `localhost` and `01-index.tar.gz` from that
7+
Hackage was downloaded to this directory. Then the index file was processed with `hackage-to-nix`,
8+
the result is in `hackage/` directory.
9+
10+
The tests check that `cabalProject'` is able to construct plan with dependencies from extra Hackage
11+
and then build the package itself.

test/ghcjs-overlay/Setup.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import Distribution.Simple
2+
main = defaultMain

test/ghcjs-overlay/cabal.project

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
packages: *.cabal
2+
3+
repository ghcjs-overlay
4+
url: https://raw.githubusercontent.com/input-output-hk/hackage-overlay-ghcjs/bfc363b9f879c360e0a0460ec0c18ec87222ec32
5+
secure: True
6+
root-keys:
7+
key-threshold: 0
8+
--sha256: sha256-g9xGgJqYmiczjxjQ5JOiK5KUUps+9+nlNGI/0SpSOpg=
9+

test/ghcjs-overlay/default.nix

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{ stdenv, lib, cabalProject', haskellLib, recurseIntoAttrs, testSrc, compiler-nix-name }:
2+
3+
with lib;
4+
5+
let
6+
project = cabalProject' {
7+
src = testSrc "ghcjs-overlay";
8+
inherit compiler-nix-name;
9+
};
10+
packages = project.hsPkgs;
11+
12+
in recurseIntoAttrs {
13+
ifdInputs = {
14+
inherit (project) plan-nix;
15+
};
16+
run = stdenv.mkDerivation {
17+
name = "ghcjs-overlay-test";
18+
19+
buildCommand = ''
20+
exe="${packages.ghcjs-overlay-test.components.exes.ghcjs-overlay-test.exePath}"
21+
size=$(command stat --format '%s' "$exe")
22+
printf "size of executable $exe is $size. \n" >& 2
23+
# fixme: run on target platform when cross-compiled
24+
printf "checking whether executable runs... " >& 2
25+
cat ${haskellLib.check packages.ghcjs-overlay-test.components.exes.ghcjs-overlay-test}/test-stdout
26+
'' + (if stdenv.hostPlatform.isMusl
27+
then ''
28+
printf "checking that executable is statically linked... " >& 2
29+
(ldd $exe 2>&1 || true) | grep -i "not a"
30+
''
31+
else
32+
# Skip this on aarch as we do not have an `ldd` tool
33+
optionalString (!stdenv.hostPlatform.isAarch32 && !stdenv.hostPlatform.isAarch64) (''
34+
printf "checking that executable is dynamically linked to system libraries... " >& 2
35+
'' + optionalString stdenv.isLinux ''
36+
ldd $exe | grep libpthread
37+
'' + optionalString stdenv.isDarwin ''
38+
otool -L $exe |grep .dylib
39+
'')) + ''
40+
touch $out
41+
'';
42+
meta.platforms = platforms.all;
43+
passthru = {
44+
inherit project;
45+
};
46+
};
47+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
cabal-version: 2.4
2+
name: ghcjs-overlay-test
3+
version: 0.1.0.0
4+
synopsis: User package
5+
description: Uses ghcjs-overlay to get patched double-conversion
6+
license: BSD-3-Clause
7+
author: Hamish Mackenzie
8+
maintainer: Hamish.K.Mackenzie@gmail.com
9+
extra-source-files: CHANGELOG.md
10+
11+
library
12+
exposed-modules: MyLib
13+
build-depends: base < 5,
14+
double-conversion ==2.0.2.0
15+
default-language: Haskell2010
16+
17+
executable ghcjs-overlay-test
18+
main-is: Main.hs
19+
other-modules: MyLib
20+
build-depends: base, ghcjs-overlay-test
21+
default-language: Haskell2010

0 commit comments

Comments
 (0)