Skip to content

Commit f3148a5

Browse files
authored
Fix the way ExeDependency is handled (input-output-hk#101)
We should look up hsPkgs.${pkg-name}.components.exe.${component-name} instead of including the package as a dependency. Adding `hsPkgs.${pkg-name}` a tool it is not clear which executable in the package `haskell.nix` should choose. Haskell.nix did not deal with this well and in fact it could lead to `nix-shell` crashing. For instance using `shellFor` to make a shell for building `haskell-language-server` with `cabal build` crashed as a dependency on `ghcide` the executable (by the `ghcide-bench` executable) caused infinite recursion. Allowing `ghcide-bench` to correctly depend just on `components.exes.ghcide` fixes this.
1 parent 9a47eb9 commit f3148a5

File tree

6 files changed

+121
-66
lines changed

6 files changed

+121
-66
lines changed

cabal.project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
index-state: 2020-05-03T00:00:00Z
1+
index-state: 2021-02-08T00:00:00Z
22

33
packages: .
44

default.nix

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
{ sourcesOverride ? {}
2-
, sources ? (import ./nix/sources.nix {}) // sourcesOverride
3-
, pkgs ? (import sources."haskell.nix" {}).pkgs
4-
, compiler-nix-name ? "ghc883"
1+
{ compiler-nix-name ? "ghc8104"
52
}:
63
let
4+
sources = import ./nix/sources.nix {};
5+
haskellNix = import sources."haskell.nix" {};
6+
pkgs = import haskellNix.sources.nixpkgs-unstable haskellNix.nixpkgsArgs;
77
project = pkgs.haskell-nix.cabalProject {
88
inherit compiler-nix-name;
99
src = pkgs.haskell-nix.haskellLib.cleanGit { src = ./.; name = "nix-tools"; };
10-
modules = [{ reinstallableLibGhc = true; }];
1110
};
1211
in
1312
project // {
1413
shell = project.shellFor {
15-
tools = { cabal = "3.2.0.0"; };
14+
tools = { cabal = "latest"; };
1615
buildInputs = [
1716
pkgs.nix-prefetch-git
1817
];

lib/Cabal2Nix.hs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,14 @@ errorHandler = "errorHandler"
5555
pkgconfPkgs = "pkgconfPkgs"
5656
flags = "flags"
5757

58-
buildDepError, sysDepError, pkgConfDepError, exeDepError, legacyExeDepError, buildToolDepError :: Text
58+
buildDepError, sysDepError, pkgConfDepError, exeDepError, legacyExeDepError, buildToolDepError, setupDepError :: Text
5959
buildDepError = "buildDepError"
6060
sysDepError = "sysDepError"
6161
pkgConfDepError = "pkgConfDepError"
6262
exeDepError = "exeDepError"
6363
legacyExeDepError = "legacyExeDepError"
6464
buildToolDepError = "buildToolDepError"
65+
setupDepError = "setupDepError"
6566

6667
($//?) :: NExpr -> Maybe NExpr -> NExpr
6768
lhs $//? (Just e) = lhs $// e
@@ -198,7 +199,7 @@ toNixPackageDescription isLocal detailLevel pd = mkNonRecSet $
198199
] ++
199200
[ "isLocal" $= mkBool True | isLocal
200201
] ++
201-
[ "setup-depends" $= toNix (BuildToolDependency . depPkgName <$> deps) | Just deps <- [setupDepends <$> setupBuildInfo pd ]] ++
202+
[ "setup-depends" $= toNix (SetupDependency . depPkgName <$> deps) | Just deps <- [setupDepends <$> setupBuildInfo pd ]] ++
202203
if detailLevel == MinimalDetails
203204
then []
204205
else
@@ -241,7 +242,8 @@ mkPrivateHackageUrl hackageUrl pi' =
241242
pkgNameVersion = unPackageName (pkgName pi') <> "-" <> show (pretty (pkgVersion pi'))
242243

243244
newtype SysDependency = SysDependency { unSysDependency :: String } deriving (Show, Eq, Ord)
244-
newtype BuildToolDependency = BuildToolDependency { unBuildToolDependency :: PackageName } deriving (Show, Eq, Ord)
245+
newtype SetupDependency = SetupDependency { unSetupDependency :: PackageName } deriving (Show, Eq, Ord)
246+
data BuildToolDependency = BuildToolDependency PackageName UnqualComponentName deriving (Show, Eq, Ord)
245247

246248
mkSysDep :: String -> SysDependency
247249
mkSysDep = SysDependency
@@ -278,11 +280,11 @@ toNixGenericPackageDescription isLocal detailLevel gpd = mkNonRecSet
278280
[ "mainPath" $= toNix p | Just p <- [shakeTree . fmap (maybeToList . getMainPath) $ comp] ])
279281
where name = fromString $ unUnqualComponentName unQualName
280282
toolDeps = getToolDependencies (packageDescription gpd)
281-
toBuildToolDep (ExeDependency pkg _ _) = BuildToolDependency pkg
283+
toBuildToolDep (ExeDependency pkg c _) = BuildToolDependency pkg c
282284
getToolDependencies pkg bi =
283285
map toBuildToolDep (buildToolDepends bi)
284286
<> map (\led -> maybe (guess led) toBuildToolDep $ desugarBuildTool pkg led) (buildTools bi)
285-
guess (LegacyExeDependency n _) = BuildToolDependency (mkPackageName n)
287+
guess (LegacyExeDependency n _) = BuildToolDependency (mkPackageName n) (mkUnqualComponentName n)
286288
components = mkNonRecSet $
287289
[ component "library" lib | Just lib <- [condLibrary gpd] ] ++
288290
(bindTo "sublibs" . mkNonRecSet <$> filter (not . null) [ uncurry component <$> condSubLibraries gpd ]) ++
@@ -313,17 +315,31 @@ instance ToNixExpr ExeDependency where
313315
where
314316
pkg = fromString . show . pretty $ pkgName'
315317

316-
instance ToNixExpr BuildToolDependency where
317-
toNix (BuildToolDependency pkgName') =
318+
instance ToNixExpr SetupDependency where
319+
toNix (SetupDependency pkgName') =
318320
-- TODO once https://github.com/haskell-nix/hnix/issues/52
319321
-- is reolved use something like:
320322
-- [nix| hsPkgs.buildPackages.$((pkgName)) or pkgs.buildPackages.$((pkgName)) ]
321323
selectOr (mkSym hsPkgs) buildPackagesDotName
322-
(selectOr (mkSym pkgs) buildPackagesDotName (mkSym errorHandler @. buildToolDepError @@ mkStr pkg))
324+
(selectOr (mkSym pkgs) buildPackagesDotName (mkSym errorHandler @. setupDepError @@ mkStr pkg))
323325
where
324326
pkg = fromString . show . pretty $ pkgName'
325327
buildPackagesDotName = mkSelector "buildPackages" <> mkSelector pkg
326328

329+
instance ToNixExpr BuildToolDependency where
330+
toNix (BuildToolDependency pkgName' componentName') =
331+
selectOr (mkSym hsPkgs) (
332+
mkSelector "buildPackages"
333+
<> mkSelector pkg
334+
<> mkSelector "components"
335+
<> mkSelector "exes"
336+
<> mkSelector componentName)
337+
(selectOr (mkSym pkgs) (mkSelector "buildPackages" <> mkSelector componentName)
338+
(mkSym errorHandler @. buildToolDepError @@ mkStr (pkg <> ":" <> componentName)))
339+
where
340+
pkg = fromString . show . pretty $ pkgName'
341+
componentName = fromString . show . pretty $ componentName'
342+
327343
instance ToNixExpr LegacyExeDependency where
328344
toNix (LegacyExeDependency name _versionRange) = selectOr (mkSym hsPkgs) (mkSelector $ quoted pkg) (mkSym errorHandler @. legacyExeDepError @@ mkStr pkg)
329345
where

nix/sources.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
"homepage": "https://input-output-hk.github.io/haskell.nix",
66
"owner": "input-output-hk",
77
"repo": "haskell.nix",
8-
"rev": "aa85608fe978276c0a9dcde6c31e61bf1829c2f9",
9-
"sha256": "1yhds5s73myvb1aw6df5b2ka62kz6hzkfqdx3wgq68j5159xr0j5",
8+
"rev": "cd91692380339f1d8fae8a798e8cc32e2d5bc39d",
9+
"sha256": "0wm9ym7q2xyxkzw0n7r0z5783i2s1ibb0qld1skzp3dz37mjmcrn",
1010
"type": "tarball",
11-
"url": "https://github.com/input-output-hk/haskell.nix/archive/aa85608fe978276c0a9dcde6c31e61bf1829c2f9.tar.gz",
11+
"url": "https://github.com/input-output-hk/haskell.nix/archive/cd91692380339f1d8fae8a798e8cc32e2d5bc39d.tar.gz",
1212
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
1313
},
1414
"niv": {

nix/sources.nix

Lines changed: 86 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,52 +6,63 @@ let
66
# The fetchers. fetch_<type> fetches specs of type <type>.
77
#
88

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; };
9+
fetch_file = pkgs: name: spec:
10+
let
11+
name' = sanitizeName name + "-src";
12+
in
13+
if spec.builtin or true then
14+
builtins_fetchurl { inherit (spec) url sha256; name = name'; }
15+
else
16+
pkgs.fetchurl { inherit (spec) url sha256; name = name'; };
2017

21-
fetch_git = spec:
22-
builtins.fetchGit { url = spec.repo; inherit (spec) rev ref; };
18+
fetch_tarball = pkgs: name: spec:
19+
let
20+
name' = sanitizeName name + "-src";
21+
in
22+
if spec.builtin or true then
23+
builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
24+
else
25+
pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
2326

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`.
27+
fetch_git = name: spec:
28+
let
29+
ref =
30+
if spec ? ref then spec.ref else
31+
if spec ? branch then "refs/heads/${spec.branch}" else
32+
if spec ? tag then "refs/tags/${spec.tag}" else
33+
abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!";
34+
in
35+
builtins.fetchGit { url = spec.repo; inherit (spec) rev; inherit ref; };
3036

31-
$ niv modify <package> -a type=tarball -a builtin=true
32-
''
33-
builtins_fetchTarball { inherit (spec) url sha256; };
37+
fetch_local = spec: spec.path;
3438

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`.
39+
fetch_builtin-tarball = name: throw
40+
''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
41+
$ niv modify ${name} -a type=tarball -a builtin=true'';
4142

42-
$ niv modify <package> -a type=file -a builtin=true
43-
''
44-
(builtins_fetchurl { inherit (spec) url sha256; });
43+
fetch_builtin-url = name: throw
44+
''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
45+
$ niv modify ${name} -a type=file -a builtin=true'';
4546

4647
#
4748
# Various helpers
4849
#
4950

51+
# https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695
52+
sanitizeName = name:
53+
(
54+
concatMapStrings (s: if builtins.isList s then "-" else s)
55+
(
56+
builtins.split "[^[:alnum:]+._?=-]+"
57+
((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name)
58+
)
59+
);
60+
5061
# The set of packages used when specs are fetched using non-builtins.
51-
mkPkgs = sources:
62+
mkPkgs = sources: system:
5263
let
5364
sourcesNixpkgs =
54-
import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) {};
65+
import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; };
5566
hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
5667
hasThisAsNixpkgsPath = <nixpkgs> == ./.;
5768
in
@@ -71,14 +82,27 @@ let
7182

7283
if ! builtins.hasAttr "type" spec then
7384
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
85+
else if spec.type == "file" then fetch_file pkgs name spec
86+
else if spec.type == "tarball" then fetch_tarball pkgs name spec
87+
else if spec.type == "git" then fetch_git name spec
88+
else if spec.type == "local" then fetch_local spec
89+
else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
90+
else if spec.type == "builtin-url" then fetch_builtin-url name
7991
else
8092
abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
8193

94+
# If the environment variable NIV_OVERRIDE_${name} is set, then use
95+
# the path directly as opposed to the fetched source.
96+
replace = name: drv:
97+
let
98+
saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name;
99+
ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
100+
in
101+
if ersatz == "" then drv else
102+
# this turns the string into an actual Nix path (for both absolute and
103+
# relative paths)
104+
if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}";
105+
82106
# Ports of functions for older nix versions
83107

84108
# a Nix version of mapAttrs if the built-in doesn't exist
@@ -87,23 +111,37 @@ let
87111
listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
88112
);
89113

114+
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
115+
range = first: last: if first > last then [] else builtins.genList (n: first + n) (last - first + 1);
116+
117+
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
118+
stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
119+
120+
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
121+
stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
122+
concatMapStrings = f: list: concatStrings (map f list);
123+
concatStrings = builtins.concatStringsSep "";
124+
125+
# https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331
126+
optionalAttrs = cond: as: if cond then as else {};
127+
90128
# fetchTarball version that is compatible between all the versions of Nix
91-
builtins_fetchTarball = { url, sha256 }@attrs:
129+
builtins_fetchTarball = { url, name ? null, sha256 }@attrs:
92130
let
93131
inherit (builtins) lessThan nixVersion fetchTarball;
94132
in
95133
if lessThan nixVersion "1.12" then
96-
fetchTarball { inherit url; }
134+
fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
97135
else
98136
fetchTarball attrs;
99137

100138
# fetchurl version that is compatible between all the versions of Nix
101-
builtins_fetchurl = { url, sha256 }@attrs:
139+
builtins_fetchurl = { url, name ? null, sha256 }@attrs:
102140
let
103141
inherit (builtins) lessThan nixVersion fetchurl;
104142
in
105143
if lessThan nixVersion "1.12" then
106-
fetchurl { inherit url; }
144+
fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
107145
else
108146
fetchurl attrs;
109147

@@ -115,20 +153,22 @@ let
115153
then abort
116154
"The values in sources.json should not have an 'outPath' attribute"
117155
else
118-
spec // { outPath = fetch config.pkgs name spec; }
156+
spec // { outPath = replace name (fetch config.pkgs name spec); }
119157
) config.sources;
120158

121159
# The "config" used by the fetchers
122160
mkConfig =
123-
{ sourcesFile ? ./sources.json
124-
, sources ? builtins.fromJSON (builtins.readFile sourcesFile)
125-
, pkgs ? mkPkgs sources
161+
{ sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
162+
, sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile)
163+
, system ? builtins.currentSystem
164+
, pkgs ? mkPkgs sources system
126165
}: rec {
127166
# The sources, i.e. the attribute set of spec name to spec
128167
inherit sources;
129168

130169
# The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
131170
inherit pkgs;
132171
};
172+
133173
in
134174
mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); }

shell.nix

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
{ sourcesOverride ? {}, compiler-nix-name ? "ghc883" }:
2-
(import ./. { inherit sourcesOverride compiler-nix-name; }).shell
1+
{ compiler-nix-name ? "ghc8104" }:
2+
(import ./. { inherit compiler-nix-name; }).shell
33

0 commit comments

Comments
 (0)