Skip to content

Commit abca1ae

Browse files
committed
Add sha256 to source-repository-packages for hydra
This allows source-repository-package to be downloaded and replaced in with `package: /nix/store/...` by callCabalPackageToNix so that `cabal` and `plan-to-nix` do not need to download them.
1 parent 4c5cf05 commit abca1ae

File tree

6 files changed

+184
-7
lines changed

6 files changed

+184
-7
lines changed

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

Lines changed: 111 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{ dotCabal, pkgs, runCommand, nix-tools, cabal-install, ghc, hpack, symlinkJoin, cacert, index-state-hashes }:
22
let defaultGhc = ghc;
33
defaultCabalInstall = cabal-install;
4-
in { index-state, index-sha256 ? index-state-hashes.${index-state} or null, src, ghc ? defaultGhc, cabal-install ? defaultCabalInstall }:
4+
in { index-state, index-sha256 ? index-state-hashes.${index-state} or null, src, ghc ? defaultGhc,
5+
cabal-install ? defaultCabalInstall, cabalProject ? null }:
56

67
# better error message than just assert failed.
78
assert (if index-sha256 == null then throw "provided sha256 for index-state ${index-state} is null!" else true);
@@ -13,18 +14,111 @@ let
1314
maybeCleanedSource =
1415
if pkgs.lib.canCleanSource src
1516
then pkgs.lib.cleanSourceWith {
16-
src = builtins.trace "src = ${src};" src;
17+
inherit src;
1718
filter = path: type:
1819
type == "directory" ||
1920
pkgs.lib.any (i: (pkgs.lib.hasSuffix i path)) [ ".project" ".cabal" "package.yaml" ]; }
20-
else src;
21+
else src;
22+
23+
# Using origSrc bypasses any cleanSourceWith so that it will work when
24+
# access to the store is restricted. If origSrc was already in the store
25+
# you can pass the project in as a string.
26+
rawCabalProject =
27+
let origSrcDir = maybeCleanedSource.origSrc or maybeCleanedSource;
28+
in if cabalProject != null
29+
then cabalProject
30+
else
31+
if ((builtins.readDir origSrcDir)."cabal.project" or "") == "regular"
32+
then builtins.readFile (origSrcDir + "/cabal.project")
33+
else null;
34+
35+
span = pred: list:
36+
let n = pkgs.lib.lists.foldr (x: acc: if pred x then acc + 1 else 0) 0 list;
37+
in { fst = pkgs.lib.lists.take n list; snd = pkgs.lib.lists.drop n list; };
38+
39+
# Parse lines of a source-repository-package block
40+
parseBlockLines = blockLines: builtins.listToAttrs (builtins.concatMap (s:
41+
let pair = builtins.match " *([^:]*): *(.*)" s;
42+
in pkgs.lib.optional (pair != null) (pkgs.lib.attrsets.nameValuePair
43+
(builtins.head pair)
44+
(builtins.elemAt pair 1))) blockLines);
45+
46+
fetchRepo = repo: (pkgs.fetchgit {
47+
url = repo.location;
48+
rev = repo.tag;
49+
sha256 = repo."--sha256";
50+
}) + (if repo.subdir or "" == "" then "" else "/" + repo.subdir);
51+
52+
# Parse a source-repository-package and fetch it if it containts
53+
# a line of the form
54+
# --shar256: <<SHA256>>
55+
parseBlock = block:
56+
let
57+
x = span (pkgs.lib.strings.hasPrefix " ") (pkgs.lib.splitString "\n" block);
58+
attrs = parseBlockLines x.fst;
59+
in
60+
if attrs."--sha256" or "" == ""
61+
then {
62+
sourceRepo = [];
63+
otherText = "\nsource-repository-package\n" + block;
64+
}
65+
else {
66+
sourceRepo = [ (fetchRepo attrs) ];
67+
otherText = pkgs.lib.strings.concatStringsSep "\n" x.snd;
68+
};
69+
70+
# Deal with source-repository-packages in a way that will work on
71+
# hydra build agents (as long as a sha256 is included).
72+
# Replace source-repository-package blocks that have a sha256 with
73+
# packages: block containing nix sotre paths of the fetched repos.
74+
replaceSoureRepos = projectFile:
75+
let
76+
blocks = pkgs.lib.splitString "\nsource-repository-package\n" projectFile;
77+
initialText = pkgs.lib.lists.take 1 blocks;
78+
repoBlocks = builtins.map parseBlock (pkgs.lib.lists.drop 1 blocks);
79+
sourceRepos = pkgs.lib.lists.concatMap (x: x.sourceRepo) repoBlocks;
80+
otherText = pkgs.writeText "cabal.project" (pkgs.lib.strings.concatStringsSep "\n" (
81+
initialText
82+
++ (builtins.map (x: x.otherText) repoBlocks)));
83+
in {
84+
inherit sourceRepos;
85+
makeFixedProjectFile = ''
86+
cp -f ${otherText} ./cabal.project
87+
chmod +w -R ./cabal.project
88+
echo "packages:" >> ./cabal.project
89+
mkdir -p ./.source-repository-packages
90+
'' +
91+
( pkgs.lib.strings.concatStrings (
92+
pkgs.lib.lists.zipListsWith (n: f: ''
93+
mkdir -p ./.source-repository-packages/${builtins.toString n}
94+
rsync -a --prune-empty-dirs \
95+
--include '*/' --include '*.cabal' --include 'package.yaml' \
96+
--exclude '*' \
97+
"${f}/" "./.source-repository-packages/${builtins.toString n}/"
98+
echo " ./.source-repository-packages/${builtins.toString n}" >> ./cabal.project
99+
'')
100+
(pkgs.lib.lists.range 0 ((builtins.length fixedProject.sourceRepos) - 1))
101+
sourceRepos
102+
)
103+
);
104+
};
105+
106+
fixedProject =
107+
if rawCabalProject == null
108+
then {
109+
sourceRepos = [];
110+
makeFixedProjectFile = "";
111+
}
112+
else replaceSoureRepos rawCabalProject;
113+
21114
plan = runCommand "plan-to-nix-pkgs" {
22115
nativeBuildInputs = [ nix-tools ghc hpack cabal-install pkgs.rsync pkgs.git ];
23-
} ''
116+
} (''
24117
tmp=$(mktemp -d)
25118
cd $tmp
26119
cp -r ${maybeCleanedSource}/* .
27120
chmod +w -R .
121+
${fixedProject.makeFixedProjectFile}
28122
# warning: this may not generate the proper cabal file.
29123
# hpack allows globbing, and turns that into module lists
30124
# without the source available (we cleaneSourceWith'd it),
@@ -61,7 +155,7 @@ let
61155
62156
# move pkgs.nix to default.nix ensure we can just nix `import` the result.
63157
mv $out/pkgs.nix $out/default.nix
64-
'';
158+
'');
65159
in
66160
# TODO: We really want this (symlinks) instead of copying the source over each and
67161
# every time. However this will not work with sandboxed builds. They won't
@@ -79,13 +173,23 @@ in
79173
# # todo: should we clean `src` to drop any .git, .nix, ... other irelevant files?
80174
# buildInputs = [ plan src ];
81175
# }
82-
runCommand "plan-to-nix-pkgs-with-src" { nativeBuildInputs = [ pkgs.rsync ]; } ''
176+
runCommand "plan-to-nix-pkgs-with-src" { nativeBuildInputs = [ pkgs.rsync ]; } (''
83177
mkdir $out
84178
# todo: should we clean `src` to drop any .git, .nix, ... other irelevant files?
85179
rsync -a "${src}/" "$out/"
86180
rsync -a ${plan}/ $out/
181+
'' +
182+
( pkgs.lib.strings.concatStrings (
183+
pkgs.lib.lists.zipListsWith (n: f: ''
184+
mkdir -p $out/.source-repository-packages/${builtins.toString n}
185+
rsync -a "${f}/" "$out/.source-repository-packages/${builtins.toString n}/"
186+
'')
187+
(pkgs.lib.lists.range 0 ((builtins.length fixedProject.sourceRepos) - 1))
188+
fixedProject.sourceRepos
189+
)
190+
) + ''
87191
# Rsync will have made $out read only and that can cause problems when
88192
# nix sandboxing is enabled (since it can prevent nix from moving the directory
89193
# out of the chroot sandbox).
90194
chmod +w $out
91-
''
195+
'')

test/cabal-source-repo/Main.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module Main where
2+
3+
main :: IO ()
4+
main = putStrLn "Hello, Haskell!"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
packages:
2+
./
3+
4+
source-repository-package
5+
type: git
6+
location: https://github.com/input-output-hk/haskell.nix.git
7+
tag: bc01ebc05a8105035c9449943046b46c8364b932
8+
subdir: test/cabal-simple
9+
--sha256: 003lm3pm024vhbfmii7xcdd9v2rczpflxf7gdl2pyxia7p014i8z

test/cabal-source-repo/default.nix

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{ stdenv, mkCabalProjectPkgSet, callCabalProjectToNix }:
2+
3+
with stdenv.lib;
4+
5+
let
6+
pkgSet = mkCabalProjectPkgSet {
7+
plan-pkgs = import (callCabalProjectToNix {
8+
index-state = "2019-04-30T00:00:00Z";
9+
# reuse the cabal-simple test project
10+
src = ./.;
11+
});
12+
};
13+
packages = pkgSet.config.hsPkgs;
14+
in
15+
stdenv.mkDerivation {
16+
name = "call-cabal-project-to-nix-test";
17+
18+
buildCommand = ''
19+
exe="${packages.use-cabal-simple.components.exes.use-cabal-simple}/bin/use-cabal-simple"
20+
21+
printf "checking whether executable runs... " >& 2
22+
$exe
23+
24+
touch $out
25+
'';
26+
27+
meta.platforms = platforms.all;
28+
29+
passthru = {
30+
# Attributes used for debugging with nix repl
31+
inherit pkgSet packages;
32+
};
33+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
cabal-version: >=1.10
2+
-- Initial package description 'cabal-simple.cabal' generated by 'cabal
3+
-- init'. For further documentation, see
4+
-- http://haskell.org/cabal/users-guide/
5+
6+
name: use-cabal-simple
7+
version: 0.1.0.0
8+
-- synopsis:
9+
-- description:
10+
-- bug-reports:
11+
license: PublicDomain
12+
author: Rodney Lorrimar
13+
maintainer: rodney.lorrimar@iohk.io
14+
-- category:
15+
build-type: Simple
16+
17+
executable use-cabal-simple
18+
main-is: Main.hs
19+
-- other-modules:
20+
-- other-extensions:
21+
build-depends: base
22+
, cabal-simple
23+
, extra
24+
, optparse-applicative
25+
-- hs-source-dirs:
26+
default-language: Haskell2010

test/default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ in pkgs.recurseIntoAttrs {
2020
shell-for = haskell.callPackage ./shell-for {};
2121
callStackToNix = haskell.callPackage ./call-stack-to-nix {};
2222
callCabalProjectToNix = haskell.callPackage ./call-cabal-project-to-nix {};
23+
cabal-source-repo = haskell.callPackage ./cabal-source-repo {};
2324

2425
# Run unit tests with: nix-instantiate --eval --strict -A unit.tests
2526
# An empty list means success.

0 commit comments

Comments
 (0)