Skip to content

Commit 1b2eae0

Browse files
committed
Fix cabal-doctest support
1 parent cb20a5a commit 1b2eae0

File tree

6 files changed

+153
-138
lines changed

6 files changed

+153
-138
lines changed

builder/comp-builder.nix

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,14 @@ let self =
2323
, preInstall ? component.preInstall , postInstall ? component.postInstall
2424
, preHaddock ? component.preHaddock , postHaddock ? component.postHaddock
2525
, shellHook ? ""
26-
, isDoctest ? component.isDoctest
26+
, configureAllComponents ? component.configureAllComponents ||
27+
# When set, configure all the components in the package
28+
# (not just the one we are building).
29+
# Enable for tests in packages that use cabal-doctest.
30+
( haskellLib.isTest componentId &&
31+
lib.any (x: x.identifier.name or "" == "cabal-doctest") package.setup-depends
32+
)
33+
, allComponent # Used when `configureAllComponents` is set to get a suitable configuration.
2734

2835
, build-tools ? component.build-tools
2936
, pkgconfig ? component.pkgconfig
@@ -107,8 +114,12 @@ let
107114
needsProfiling = enableExecutableProfiling || enableLibraryProfiling;
108115

109116
configFiles = makeConfigFiles {
117+
component =
118+
if configureAllComponents
119+
then allComponent
120+
else component;
110121
inherit (package) identifier;
111-
inherit component fullName flags needsProfiling;
122+
inherit fullName flags needsProfiling;
112123
};
113124

114125
enableFeature = enable: feature:
@@ -118,8 +129,13 @@ let
118129

119130
finalConfigureFlags = lib.concatStringsSep " " (
120131
[ "--prefix=$out"
121-
"${haskellLib.componentTarget componentId}"
122-
"$(cat ${configFiles}/configure-flags)"
132+
] ++ (
133+
# If configureAllComponents is set we should not specify the component
134+
# and Setup will attempt to configure them all.
135+
if configureAllComponents
136+
then ["--enable-tests" "--enable-benchmarks"]
137+
else ["${haskellLib.componentTarget componentId}"]
138+
) ++ [ "$(cat ${configFiles}/configure-flags)"
123139
] ++ commonConfigureFlags);
124140

125141
# From nixpkgs 20.09, the pkg-config exe has a prefix matching the ghc one

builder/hspkg-builder.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ let
114114
};
115115

116116
in rec {
117-
components = haskellLib.applyComponents (buildComp pkg.components.all) pkg;
117+
components = haskellLib.applyComponents (buildComp pkg.allComponent) pkg;
118118
checks = pkgs.recurseIntoAttrs (builtins.mapAttrs
119119
(_: d: haskellLib.check d)
120120
(lib.filterAttrs (_: d: d.config.doCheck) components.tests));

modules/package.nix

Lines changed: 119 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,86 @@ with types;
2121

2222
# Work around issue that can cause _lots_ of files to be copied into the store.
2323
# See https://github.com/NixOS/nixpkgs/pull/64691
24-
let path = types.path // { check = x: types.path.check (x.origSrc or x); };
24+
let
25+
path = types.path // { check = x: types.path.check (x.origSrc or x); };
26+
27+
componentType = submodule {
28+
# add the shared componentOptions
29+
options = (packageOptions config) // {
30+
buildable = mkOption {
31+
type = bool;
32+
default = true;
33+
};
34+
depends = mkOption {
35+
type = listOfFilteringNulls unspecified;
36+
default = [];
37+
};
38+
libs = mkOption {
39+
type = listOfFilteringNulls (nullOr package);
40+
default = [];
41+
};
42+
frameworks = mkOption {
43+
type = listOfFilteringNulls package;
44+
default = [];
45+
};
46+
pkgconfig = mkOption {
47+
type = listOf (listOfFilteringNulls package);
48+
default = [];
49+
};
50+
build-tools = mkOption {
51+
type = listOfFilteringNulls unspecified;
52+
default = [];
53+
};
54+
modules = mkOption {
55+
type = listOfFilteringNulls unspecified;
56+
default = [];
57+
};
58+
asmSources = mkOption {
59+
type = listOfFilteringNulls unspecified;
60+
default = [];
61+
};
62+
cmmSources = mkOption {
63+
type = listOfFilteringNulls unspecified;
64+
default = [];
65+
};
66+
cSources = mkOption {
67+
type = listOfFilteringNulls unspecified;
68+
default = [];
69+
};
70+
cxxSources = mkOption {
71+
type = listOfFilteringNulls unspecified;
72+
default = [];
73+
};
74+
jsSources = mkOption {
75+
type = listOfFilteringNulls unspecified;
76+
default = [];
77+
};
78+
hsSourceDirs = mkOption {
79+
type = listOfFilteringNulls unspecified;
80+
default = ["."];
81+
};
82+
includeDirs = mkOption {
83+
type = listOfFilteringNulls unspecified;
84+
default = [];
85+
};
86+
includes = mkOption {
87+
type = listOfFilteringNulls unspecified;
88+
default = [];
89+
};
90+
mainPath = mkOption {
91+
type = listOfFilteringNulls unspecified;
92+
default = [];
93+
};
94+
extraSrcFiles = mkOption {
95+
type = listOfFilteringNulls unspecified;
96+
default = [];
97+
};
98+
platforms = mkOption {
99+
type = nullOr (listOfFilteringNulls unspecified);
100+
default = null;
101+
};
102+
};
103+
};
25104

26105
in {
27106
# This is how the Nix expressions generated by *-to-nix receive
@@ -143,85 +222,7 @@ in {
143222
};
144223
};
145224

146-
components = let
147-
componentType = submodule {
148-
# add the shared componentOptions
149-
options = (packageOptions config) // {
150-
buildable = mkOption {
151-
type = bool;
152-
default = true;
153-
};
154-
depends = mkOption {
155-
type = listOfFilteringNulls unspecified;
156-
default = [];
157-
};
158-
libs = mkOption {
159-
type = listOfFilteringNulls (nullOr package);
160-
default = [];
161-
};
162-
frameworks = mkOption {
163-
type = listOfFilteringNulls package;
164-
default = [];
165-
};
166-
pkgconfig = mkOption {
167-
type = listOf (listOfFilteringNulls package);
168-
default = [];
169-
};
170-
build-tools = mkOption {
171-
type = listOfFilteringNulls unspecified;
172-
default = [];
173-
};
174-
modules = mkOption {
175-
type = listOfFilteringNulls unspecified;
176-
default = [];
177-
};
178-
asmSources = mkOption {
179-
type = listOfFilteringNulls unspecified;
180-
default = [];
181-
};
182-
cmmSources = mkOption {
183-
type = listOfFilteringNulls unspecified;
184-
default = [];
185-
};
186-
cSources = mkOption {
187-
type = listOfFilteringNulls unspecified;
188-
default = [];
189-
};
190-
cxxSources = mkOption {
191-
type = listOfFilteringNulls unspecified;
192-
default = [];
193-
};
194-
jsSources = mkOption {
195-
type = listOfFilteringNulls unspecified;
196-
default = [];
197-
};
198-
hsSourceDirs = mkOption {
199-
type = listOfFilteringNulls unspecified;
200-
default = ["."];
201-
};
202-
includeDirs = mkOption {
203-
type = listOfFilteringNulls unspecified;
204-
default = [];
205-
};
206-
includes = mkOption {
207-
type = listOfFilteringNulls unspecified;
208-
default = [];
209-
};
210-
mainPath = mkOption {
211-
type = listOfFilteringNulls unspecified;
212-
default = [];
213-
};
214-
extraSrcFiles = mkOption {
215-
type = listOfFilteringNulls unspecified;
216-
default = [];
217-
};
218-
platforms = mkOption {
219-
type = nullOr (listOfFilteringNulls unspecified);
220-
default = null;
221-
};
222-
};
223-
};
224-
in {
225+
components = {
225226
setup = mkOption {
226227
type = nullOr componentType;
227228
default = {
@@ -300,5 +301,43 @@ in {
300301
type = listOf (either unspecified path);
301302
default = [];
302303
};
304+
# This used to be `components.all` but it has been added back as `allComponent` to
305+
# to avoid confusion. It is not mapped by `builder/hspkg-builder.nix` to anything
306+
# you can build. Instead it is used internally when `configureAllComponents`
307+
# is set or for tests whe on `cabal-doctest` is in the `setup-depends` of the package.
308+
allComponent = mkOption {
309+
type = componentType;
310+
apply = all: all // {
311+
# TODO: Should this check for the entire component
312+
# definition to match, rather than just the identifier?
313+
depends = builtins.filter (p: p.identifier != config.package.identifier) all.depends;
314+
};
315+
description = "The merged dependencies of all other components";
316+
};
303317
};
318+
319+
# This has one quirk. Manually setting options on the all component
320+
# will be considered a conflict. This is almost always fine; most
321+
# settings should be modified in either the package options, or an
322+
# individual component's options. When this isn't sufficient,
323+
# mkForce is a reasonable workaround.
324+
#
325+
# An alternative solution to mkForce for many of the options where
326+
# this is relevant would be to switch from the bool type to
327+
# something like an anyBool type, which would merge definitions by
328+
# returning true if any is true.
329+
config.allComponent =
330+
let allComps = haskellLib.getAllComponents config;
331+
in lib.mkMerge (
332+
builtins.map (c:
333+
# Exclude attributes that are likely to have conflicting definitions
334+
# (a common use case for `all` is in `shellFor` and it only has an
335+
# install phase).
336+
builtins.removeAttrs c ["preCheck" "postCheck" "keepSource"]
337+
) allComps
338+
) // {
339+
# If any one of the components needs us to keep the source
340+
# then keep it for the `all` component
341+
keepSource = lib.foldl' (x: comp: x || comp.keepSource) false allComps;
342+
};
304343
}

modules/plan.nix

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ let
138138
type = bool;
139139
default = (def.enableShared or true);
140140
};
141-
isDoctest = mkOption {
141+
configureAllComponents = mkOption {
142+
description = "If set all the components in the package are configured (useful for cabal-doctest).";
142143
type = bool;
143144
default = false;
144145
};

test/cabal-doctests/default.nix

Lines changed: 10 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,23 @@
11
# Test a package set
2-
{ stdenv, util, cabalProject', haskellLib, gmp6, zlib, recurseIntoAttrs, runCommand }:
2+
{ stdenv, util, cabalProject', haskellLib, gmp6, zlib, recurseIntoAttrs, runCommand, testSrc, compiler-nix-name }:
33

44
with stdenv.lib;
55

66
let
77
project =
88
cabalProject' {
9-
src = haskellLib.cleanGit { src = ../..; subDir = "test/cabal-doctests"; };
10-
index-state = "2020-01-26T00:00:00Z";
11-
modules = [
12-
{
13-
# This is needed for the doctest library to be built. Without it, you
14-
# get an error like the following:
15-
#
16-
# Configuring library for doctest-0.16.2..
17-
# Error:
18-
# The following packages are broken because other packages they
19-
# depend on are missing. These broken packages must be rebuilt
20-
# before they can be used.
21-
# - installed package ghc-8.6.5 is broken due to missing package
22-
# terminfo-0.4.1.2
23-
nonReinstallablePkgs =
24-
[ "rts" "ghc-heap" "ghc-prim" "integer-gmp" "integer-simple" "base"
25-
"deepseq" "array" "ghc-boot-th" "pretty" "template-haskell"
26-
"ghc-boot"
27-
"ghc" "Cabal" "Win32" "array" "binary" "bytestring" "containers"
28-
"directory" "filepath" "ghc-boot" "ghc-compact" "ghc-prim"
29-
"ghci" "haskeline"
30-
"hpc"
31-
"mtl" "parsec" "process" "text" "time" "transformers"
32-
"unix" "xhtml"
33-
"stm" "terminfo"
34-
];
35-
36-
packages.cabal-doctests-test.components.tests.doctests.isDoctest = true;
37-
}
38-
];
9+
inherit compiler-nix-name;
10+
src = testSrc "cabal-doctests";
11+
index-state = "2021-01-11T00:00:00Z";
3912
};
4013

4114
packages = project.hsPkgs;
4215

43-
in
44-
45-
# Making this work for cross compilers will be difficult. Skipping for now.
46-
recurseIntoAttrs (if stdenv.buildPlatform != stdenv.hostPlatform
47-
then
48-
let skip = runCommand "skipping-test-cabal-doctests" {} ''
49-
echo "Skipping cabal-doctests test on cross compilers (does not work yet)" >& 2
50-
touch $out
51-
'';
52-
in {
53-
ifdInputs = { plan-nix = skip; };
54-
env = skip;
55-
run = skip;
56-
}
57-
else {
16+
in recurseIntoAttrs ({
5817
ifdInputs = {
5918
inherit (project) plan-nix;
6019
};
6120

62-
# Used for testing externally with nix-shell (../tests.sh).
63-
# This just adds cabal-install to the existing shells.
64-
# test-shell = util.addCabalInstall packages.cabal-doctests-test.components.all;
65-
6621
run = stdenv.mkDerivation {
6722
name = "cabal-doctests-test";
6823

@@ -75,7 +30,11 @@ recurseIntoAttrs (if stdenv.buildPlatform != stdenv.hostPlatform
7530
touch $out
7631
'';
7732

78-
meta.platforms = platforms.all;
33+
meta = {
34+
platforms = platforms.all;
35+
# Making this work for cross compilers will be difficult.
36+
disabled = stdenv.buildPlatform != stdenv.hostPlatform;
37+
};
7938

8039
passthru = {
8140
# Used for debugging with nix repl

test/default.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ let
179179
ghc-options-stack = callTest ./ghc-options/stack.nix {};
180180
exe-only = callTest ./exe-only { inherit util compiler-nix-name; };
181181
stack-source-repo = callTest ./stack-source-repo {};
182-
cabal-doctests = callTest ./cabal-doctests {};
182+
cabal-doctests = callTest ./cabal-doctests { inherit util compiler-nix-name; };
183183
extra-hackage = callTest ./extra-hackage { inherit compiler-nix-name; };
184184
compiler-nix-name = callTest ./compiler-nix-name {};
185185
hls-cabal = callTest ./haskell-language-server/cabal.nix { inherit compiler-nix-name; };

0 commit comments

Comments
 (0)