Skip to content

Commit 9840a0b

Browse files
authored
Merge pull request input-output-hk#92 from input-output-hk/hkm/download-fix
Avoid multiple downloads in stack-to-nix
2 parents 85be772 + 1452512 commit 9840a0b

File tree

10 files changed

+246
-68
lines changed

10 files changed

+246
-68
lines changed

nix-tools/.buildkite/build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ cabal new-configure
1717

1818
echo
1919
echo "+++ Run stable version of plan-to-nix"
20-
nix build '(let haskellNix = import (builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz") {}; in (import haskellNix.sources.nixpkgs-default haskellNix.nixpkgsArgs).haskell-nix.nix-tools)' -o nt
20+
nix build '(let haskellNix = import (builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz") {}; in (import haskellNix.sources.nixpkgs haskellNix.nixpkgsArgs).haskell-nix.nix-tools.ghc883)' -o nt
2121
./nt/bin/plan-to-nix --output .buildkite/nix1 --plan-json dist-newstyle/cache/plan.json
2222

2323
# Replace currently broken plan-to-nix output

nix-tools/.buildkite/fixed.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{ haskellNix ? import (builtins.fetchTarball https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {}
2-
, nixpkgs ? haskellNix.sources.nixpkgs-default }:
2+
, nixpkgs ? haskellNix.sources.nixpkgs }:
33

44
let
55
pkgs = import nixpkgs haskellNix.nixpkgsArgs;

nix-tools/cabal2nix/Main.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ main = getArgs >>= \case
4444
[url,hash] | "http" `isPrefixOf` url ->
4545
let subdir = "." in
4646
fetch (\dir -> cabalFromPath url hash subdir $ dir </> subdir)
47-
(Source url mempty UnknownHash subdir) >>= \case
47+
(Source url mempty UnknownHash) >>= \case
4848
(Just (DerivationSource{..}, genBindings)) -> genBindings derivHash
4949
_ -> return ()
5050
[path,file] -> doesDirectoryExist file >>= \case

nix-tools/default.nix

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,21 @@
1-
{ haskellNixSrc ? builtins.fetchTarball {
2-
url = "https://github.com/input-output-hk/haskell.nix/archive/61b1c8a06c74a83c0d2dc7d937d8daa6b32b2a2f.tar.gz";
3-
sha256 = "1vi8is7h85sb8acymjcnkjm39fp5pal2wq9p7zdv5cmillzs2sza";
4-
}
5-
, nixpkgs ? (import haskellNixSrc {}).sources.nixpkgs-default
6-
, pkgs ? import nixpkgs (import haskellNixSrc {}).nixpkgsArgs
7-
, haskellCompiler ? "ghc883"
1+
{ sourcesOverride ? {}
2+
, sources ? (import ./nix/sources.nix {}) // sourcesOverride
3+
, pkgs ? (import sources."haskell.nix" {}).pkgs
4+
, compiler-nix-name ? "ghc883"
85
}:
96
let
107
project = pkgs.haskell-nix.cabalProject {
8+
inherit compiler-nix-name;
119
src = pkgs.haskell-nix.haskellLib.cleanGit { src = ./.; name = "nix-tools"; };
12-
ghc = pkgs.haskell-nix.compiler.${haskellCompiler};
13-
modules = [{
14-
nonReinstallablePkgs= [ "rts" "ghc-heap" "ghc-prim" "integer-gmp" "integer-simple" "base"
15-
"deepseq" "array" "ghc-boot-th" "pretty" "template-haskell"
16-
# ghcjs custom packages
17-
"ghcjs-prim" "ghcjs-th"
18-
"ghc-boot"
19-
"ghc" "Win32" "array" "binary" "bytestring" "containers"
20-
"directory" "filepath" "ghc-boot" "ghc-compact" "ghc-prim"
21-
# "ghci" "haskeline"
22-
"hpc"
23-
"mtl" "parsec" "process" "text" "time" "transformers"
24-
"unix" "xhtml"
25-
# "stm" "terminfo"
26-
];
27-
}];
10+
modules = [{ reinstallableLibGhc = true; }];
2811
};
2912
in
3013
project // {
3114
shell = project.shellFor {
3215
tools = { cabal = "3.2.0.0"; };
16+
buildInputs = [
17+
pkgs.nix-prefetch-git
18+
];
3319
};
3420
}
3521

nix-tools/lib/Distribution/Nixpkgs/Fetch.hs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ data Source = Source
3131
, sourceRevision :: String -- ^ Revision to use. For protocols where this doesn't make sense (such as HTTP), this
3232
-- should be the empty string.
3333
, sourceHash :: Hash -- ^ The expected hash of the source, if available.
34-
, sourceCabalDir :: String -- ^ Directory where Cabal file is found.
3534
} deriving (Show, Eq, Ord, Generic)
3635

3736
instance NFData Source
@@ -69,7 +68,7 @@ instance FromJSON DerivationSource where
6968
parseJSON _ = error "invalid DerivationSource"
7069

7170
fromDerivationSource :: DerivationSource -> Source
72-
fromDerivationSource DerivationSource{..} = Source derivUrl derivRevision (Certain derivHash) "."
71+
fromDerivationSource DerivationSource{..} = Source derivUrl derivRevision (Certain derivHash)
7372

7473
-- | Fetch a source, trying any of the various nix-prefetch-* scripts.
7574
fetch :: forall a. (String -> MaybeT IO a) -- ^ This function is passed the output path name as an argument.
@@ -105,7 +104,7 @@ fetch f = runMaybeT . fetchers where
105104
localArchive :: FilePath -> MaybeT IO (DerivationSource, a)
106105
localArchive path = do
107106
absolutePath <- liftIO $ canonicalizePath path
108-
unpacked <- snd <$> fetchWith (False, "url", ["--unpack"]) (Source ("file://" ++ absolutePath) "" UnknownHash ".")
107+
unpacked <- snd <$> fetchWith (False, "url", ["--unpack"]) (Source ("file://" ++ absolutePath) "" UnknownHash)
109108
process (DerivationSource "" absolutePath "" "", unpacked)
110109

111110
process :: (DerivationSource, FilePath) -> MaybeT IO (DerivationSource, a)

nix-tools/lib/Stack2nix.hs

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ import Data.String (fromString)
1111

1212
import Control.Monad.Trans.Maybe
1313
import Control.Monad.IO.Class (liftIO)
14-
import Control.Monad (unless, forM)
14+
import Control.Monad (unless, forM, forM_)
1515
import Extra (unlessM)
1616

1717
import qualified Data.Map as M (fromListWith, toList)
1818
import System.FilePath ((<.>), (</>), takeDirectory, dropFileName)
19-
import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getCurrentDirectory)
19+
import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist)
2020
import System.IO (IOMode(..), openFile, hClose)
2121
import Data.Yaml (decodeFileEither)
2222

@@ -62,7 +62,7 @@ stackexpr args =
6262
=<< resolveSnapshot (argStackYaml args) value
6363

6464
stack2nix :: Args -> Stack -> IO NExpr
65-
stack2nix args stack@(Stack resolver compiler pkgs pkgFlags ghcOpts) =
65+
stack2nix args (Stack resolver compiler pkgs pkgFlags ghcOpts) =
6666
do let extraDeps = extraDeps2nix pkgs
6767
flags = flags2nix pkgFlags
6868
ghcOptions = ghcOptions2nix ghcOpts
@@ -147,7 +147,6 @@ writeDoc file doc =
147147
-- makeRelativeToCurrentDirectory
148148
packages2nix :: Args -> [Dependency] -> IO [(T.Text, Binding NExpr)]
149149
packages2nix args pkgs =
150-
do cwd <- getCurrentDirectory
151150
fmap concat . forM pkgs $ \case
152151
(LocalPath folder) ->
153152
do cabalFiles <- findCabalFiles (argHpackUse args) (dropFileName (argStackYaml args) </> folder)
@@ -161,42 +160,64 @@ packages2nix args pkgs =
161160
prettyNix <$> cabal2nix True (argDetailLevel args) src cabalFile
162161
return (fromString pkg, fromString pkg $= mkPath False nix)
163162
(DVCS (Git url rev) _ subdirs) ->
164-
fmap concat . forM subdirs $ \subdir ->
165-
do cacheHits <- liftIO $ cacheHits (argCacheFile args) url rev subdir
166-
case cacheHits of
167-
[] -> do
168-
fetch (\dir -> cabalFromPath url rev subdir $ dir </> subdir)
169-
(Source url rev UnknownHash subdir) >>= \case
170-
(Just (DerivationSource{..}, genBindings)) -> genBindings derivHash
171-
_ -> return []
172-
hits ->
173-
forM hits $ \( pkg, nix ) -> do
174-
return (fromString pkg, fromString pkg $= mkPath False nix)
163+
do hits <- forM subdirs $ \subdir -> liftIO $ cacheHits (argCacheFile args) url rev subdir
164+
let generateBindings =
165+
fetch (cabalFromPath url rev subdirs)
166+
(Source url rev UnknownHash) >>= \case
167+
(Just (DerivationSource{..}, genBindings)) -> genBindings derivHash
168+
_ -> return []
169+
if any null hits
170+
then
171+
-- If any of the subdirs were missing we need to fetch the files and
172+
-- generate the bindings.
173+
generateBindings
174+
else do
175+
let allHits = concat hits
176+
(and <$> forM allHits (\( _, nix ) -> doesFileExist (argOutputDir args </> nix))) >>= \case
177+
False ->
178+
-- One or more of the generated binding files are missing
179+
generateBindings
180+
True ->
181+
-- If the subdirs are all in the cache then the bindings should already be
182+
-- generated too.
183+
forM allHits $ \( pkg, nix ) ->
184+
return (fromString pkg, fromString pkg $= mkPath False nix)
175185
_ -> return []
176186
where relPath = shortRelativePath (argOutputDir args) (dropFileName (argStackYaml args))
177187
cabalFromPath
178-
:: String -- URL
179-
-> String -- Revision
180-
-> FilePath -- Subdir
181-
-> FilePath -- Local Directory
188+
:: String -- URL
189+
-> String -- Revision
190+
-> [FilePath] -- Subdirs
191+
-> FilePath -- Local Directory
182192
-> MaybeT IO (String -> IO [(T.Text, Binding NExpr)])
183-
cabalFromPath url rev subdir path = do
184-
d <- liftIO $ doesDirectoryExist path
185-
unless d $ fail ("not a directory: " ++ path)
186-
cabalFiles <- liftIO $ findCabalFiles (argHpackUse args) path
187-
return $ \sha256 ->
193+
cabalFromPath url rev subdirs dir = do
194+
-- Check that all the subdirs exist if not this
195+
-- fail the MaybeT so that the next fetcher will be tried
196+
forM_ subdirs $ \subdir -> do
197+
let path = dir </> subdir
198+
d <- liftIO $ doesDirectoryExist path
199+
unless d $ fail ("not a directory: " ++ path)
200+
-- If we got this far we are confident we have downloaded
201+
-- with the right fetcher. Return an action that will
202+
-- be used to generate the bindings.
203+
return $ \sha256 -> fmap concat . forM subdirs $ \subdir -> do
204+
let path = dir </> subdir
205+
cabalFiles <- liftIO $ findCabalFiles (argHpackUse args) path
188206
forM cabalFiles $ \cabalFile -> do
189-
let pkg = cabalFilePkgName cabalFile
190-
nix = pkg <.> "nix"
191-
nixFile = argOutputDir args </> nix
192-
subdir' = if subdir == "." then Nothing
193-
else Just subdir
194-
src = Just $ C2N.Git url rev (Just sha256) subdir'
195-
createDirectoryIfMissing True (takeDirectory nixFile)
196-
writeDoc nixFile =<<
197-
prettyNix <$> cabal2nix True (argDetailLevel args) src cabalFile
198-
liftIO $ appendCache (argCacheFile args) url rev subdir sha256 pkg nix
199-
return (fromString pkg, fromString pkg $= mkPath False nix)
207+
let pkg = cabalFilePkgName cabalFile
208+
nix = pkg <.> "nix"
209+
nixFile = argOutputDir args </> nix
210+
subdir' = if subdir == "." then Nothing
211+
else Just subdir
212+
src = Just $ C2N.Git url rev (Just sha256) subdir'
213+
createDirectoryIfMissing True (takeDirectory nixFile)
214+
writeDoc nixFile =<<
215+
prettyNix <$> cabal2nix True (argDetailLevel args) src cabalFile
216+
-- Only update the cache if there is not already a record
217+
cacheHits (argCacheFile args) url rev subdir >>= \case
218+
[hit] | hit == (pkg, nix) -> return ()
219+
_ -> appendCache (argCacheFile args) url rev subdir sha256 pkg nix
220+
return (fromString pkg, fromString pkg $= mkPath False nix)
200221

201222
defaultNixContents :: String
202223
defaultNixContents = unlines

nix-tools/nix/sources.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"haskell.nix": {
3+
"branch": "master",
4+
"description": "Alternative Haskell Infrastructure for Nixpkgs",
5+
"homepage": "https://input-output-hk.github.io/haskell.nix",
6+
"owner": "input-output-hk",
7+
"repo": "haskell.nix",
8+
"rev": "aa85608fe978276c0a9dcde6c31e61bf1829c2f9",
9+
"sha256": "1yhds5s73myvb1aw6df5b2ka62kz6hzkfqdx3wgq68j5159xr0j5",
10+
"type": "tarball",
11+
"url": "https://github.com/input-output-hk/haskell.nix/archive/aa85608fe978276c0a9dcde6c31e61bf1829c2f9.tar.gz",
12+
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
13+
},
14+
"niv": {
15+
"branch": "master",
16+
"description": "Easy dependency management for Nix projects",
17+
"homepage": "https://github.com/nmattia/niv",
18+
"owner": "nmattia",
19+
"repo": "niv",
20+
"rev": "f73bf8d584148677b01859677a63191c31911eae",
21+
"sha256": "0jlmrx633jvqrqlyhlzpvdrnim128gc81q5psz2lpp2af8p8q9qs",
22+
"type": "tarball",
23+
"url": "https://github.com/nmattia/niv/archive/f73bf8d584148677b01859677a63191c31911eae.tar.gz",
24+
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
25+
},
26+
"nixpkgs": {
27+
"branch": "nixos-19.09",
28+
"description": "A read-only mirror of NixOS/nixpkgs tracking the released channels. Send issues and PRs to",
29+
"homepage": "https://github.com/NixOS/nixpkgs",
30+
"owner": "NixOS",
31+
"repo": "nixpkgs-channels",
32+
"rev": "289466dd6a11c65a7de4a954d6ebf66c1ad07652",
33+
"sha256": "0r5ja052s86fr54fm1zlhld3fwawz2w1d1gd6vbvpjrpjfyajibn",
34+
"type": "tarball",
35+
"url": "https://github.com/NixOS/nixpkgs-channels/archive/289466dd6a11c65a7de4a954d6ebf66c1ad07652.tar.gz",
36+
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
37+
}
38+
}

nix-tools/nix/sources.nix

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# This file has been generated by Niv.
2+
3+
let
4+
5+
#
6+
# The fetchers. fetch_<type> fetches specs of type <type>.
7+
#
8+
9+
fetch_file = pkgs: spec:
10+
if spec.builtin or true then
11+
builtins_fetchurl { inherit (spec) url sha256; }
12+
else
13+
pkgs.fetchurl { inherit (spec) url sha256; };
14+
15+
fetch_tarball = pkgs: spec:
16+
if spec.builtin or true then
17+
builtins_fetchTarball { inherit (spec) url sha256; }
18+
else
19+
pkgs.fetchzip { inherit (spec) url sha256; };
20+
21+
fetch_git = spec:
22+
builtins.fetchGit { url = spec.repo; inherit (spec) rev ref; };
23+
24+
fetch_builtin-tarball = spec:
25+
builtins.trace
26+
''
27+
WARNING:
28+
The niv type "builtin-tarball" will soon be deprecated. You should
29+
instead use `builtin = true`.
30+
31+
$ niv modify <package> -a type=tarball -a builtin=true
32+
''
33+
builtins_fetchTarball { inherit (spec) url sha256; };
34+
35+
fetch_builtin-url = spec:
36+
builtins.trace
37+
''
38+
WARNING:
39+
The niv type "builtin-url" will soon be deprecated. You should
40+
instead use `builtin = true`.
41+
42+
$ niv modify <package> -a type=file -a builtin=true
43+
''
44+
(builtins_fetchurl { inherit (spec) url sha256; });
45+
46+
#
47+
# Various helpers
48+
#
49+
50+
# The set of packages used when specs are fetched using non-builtins.
51+
mkPkgs = sources:
52+
let
53+
sourcesNixpkgs =
54+
import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) {};
55+
hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
56+
hasThisAsNixpkgsPath = <nixpkgs> == ./.;
57+
in
58+
if builtins.hasAttr "nixpkgs" sources
59+
then sourcesNixpkgs
60+
else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
61+
import <nixpkgs> {}
62+
else
63+
abort
64+
''
65+
Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
66+
add a package called "nixpkgs" to your sources.json.
67+
'';
68+
69+
# The actual fetching function.
70+
fetch = pkgs: name: spec:
71+
72+
if ! builtins.hasAttr "type" spec then
73+
abort "ERROR: niv spec ${name} does not have a 'type' attribute"
74+
else if spec.type == "file" then fetch_file pkgs spec
75+
else if spec.type == "tarball" then fetch_tarball pkgs spec
76+
else if spec.type == "git" then fetch_git spec
77+
else if spec.type == "builtin-tarball" then fetch_builtin-tarball spec
78+
else if spec.type == "builtin-url" then fetch_builtin-url spec
79+
else
80+
abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
81+
82+
# Ports of functions for older nix versions
83+
84+
# a Nix version of mapAttrs if the built-in doesn't exist
85+
mapAttrs = builtins.mapAttrs or (
86+
f: set: with builtins;
87+
listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
88+
);
89+
90+
# fetchTarball version that is compatible between all the versions of Nix
91+
builtins_fetchTarball = { url, sha256 }@attrs:
92+
let
93+
inherit (builtins) lessThan nixVersion fetchTarball;
94+
in
95+
if lessThan nixVersion "1.12" then
96+
fetchTarball { inherit url; }
97+
else
98+
fetchTarball attrs;
99+
100+
# fetchurl version that is compatible between all the versions of Nix
101+
builtins_fetchurl = { url, sha256 }@attrs:
102+
let
103+
inherit (builtins) lessThan nixVersion fetchurl;
104+
in
105+
if lessThan nixVersion "1.12" then
106+
fetchurl { inherit url; }
107+
else
108+
fetchurl attrs;
109+
110+
# Create the final "sources" from the config
111+
mkSources = config:
112+
mapAttrs (
113+
name: spec:
114+
if builtins.hasAttr "outPath" spec
115+
then abort
116+
"The values in sources.json should not have an 'outPath' attribute"
117+
else
118+
spec // { outPath = fetch config.pkgs name spec; }
119+
) config.sources;
120+
121+
# The "config" used by the fetchers
122+
mkConfig =
123+
{ sourcesFile ? ./sources.json
124+
, sources ? builtins.fromJSON (builtins.readFile sourcesFile)
125+
, pkgs ? mkPkgs sources
126+
}: rec {
127+
# The sources, i.e. the attribute set of spec name to spec
128+
inherit sources;
129+
130+
# The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
131+
inherit pkgs;
132+
};
133+
in
134+
mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); }

0 commit comments

Comments
 (0)