Skip to content

Commit 6c627b4

Browse files
cdepillaboutangermanhamishmack
authored
support cabal-doctest (#427)
* Add test. * wip * Fix merge regression * Combine drv, drv.source and drv.dist for doctest * Skip cabal-doctests test when cross compiling * Add the --no-magic flag to the .cabal file for the cabal-doctests test. This appears to be necessary on OSX. The --no-magic flag stops the doctest executable from expanding path arguments, trying to locate the package db, etc. This shouldn't be necessary with cabal-doctest, since all the necessary options to pass to doctest are computed when running the Setup.hs script. See #427 (comment). * Fix cabal-doctest support * Skip cabal-doctest test plan cross compiling * More fixes for cabal-doctest * Skip cabal-doctest tests when cross compiling Co-authored-by: Moritz Angermann <[email protected]> Co-authored-by: Hamish Mackenzie <[email protected]> Co-authored-by: Hamish Mackenzie <[email protected]>
1 parent d655dbf commit 6c627b4

File tree

11 files changed

+298
-95
lines changed

11 files changed

+298
-95
lines changed

builder/comp-builder.nix

+51-11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ let self =
2323
, preInstall ? component.preInstall , postInstall ? component.postInstall
2424
, preHaddock ? component.preHaddock , postHaddock ? component.postHaddock
2525
, shellHook ? ""
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.
2634

2735
, build-tools ? component.build-tools
2836
, pkgconfig ? component.pkgconfig
@@ -47,7 +55,7 @@ let self =
4755
, doHoogle ? component.doHoogle # Also build a hoogle index
4856
, hyperlinkSource ? component.doHyperlinkSource # Link documentation to the source code
4957
, quickjump ? component.doQuickjump # Generate an index for interactive documentation navigation
50-
, keepSource ? component.keepSource # Build from `source` output in the store then delete `dist`
58+
, keepSource ? component.keepSource || configureAllComponents # Build from `source` output in the store then delete `dist`
5159
, setupHaddockFlags ? component.setupHaddockFlags
5260

5361
# Profiling
@@ -77,6 +85,11 @@ let self =
7785
}@drvArgs:
7886

7987
let
88+
componentForSetup =
89+
if configureAllComponents
90+
then allComponent
91+
else component;
92+
8093
# Ignore attempts to include DWARF info when it is not possible
8194
enableDWARF = drvArgs.enableDWARF or false
8295
&& stdenv.hostPlatform.isLinux
@@ -96,7 +109,7 @@ let
96109
# is the sub directory in that root path that contains the package.
97110
# `cleanSrc.subDir` is used in `prePatch` and `lib/cover.nix`.
98111
cleanSrc = haskellLib.rootAndSubDir (if canCleanSource
99-
then haskellLib.cleanCabalComponent package component "${componentId.ctype}-${componentId.cname}" src
112+
then haskellLib.cleanCabalComponent package componentForSetup "${componentId.ctype}-${componentId.cname}" src
100113
else
101114
# We can clean out the siblings though to at least avoid changes to other packages
102115
# from triggering a rebuild of this one.
@@ -118,8 +131,9 @@ let
118131
needsProfiling = enableExecutableProfiling || enableLibraryProfiling;
119132

120133
configFiles = makeConfigFiles {
134+
component = componentForSetup;
121135
inherit (package) identifier;
122-
inherit component fullName flags needsProfiling enableDWARF;
136+
inherit fullName flags needsProfiling enableDWARF;
123137
};
124138

125139
enableFeature = enable: feature:
@@ -129,8 +143,13 @@ let
129143

130144
finalConfigureFlags = lib.concatStringsSep " " (
131145
[ "--prefix=$out"
132-
"${haskellLib.componentTarget componentId}"
133-
"$(cat ${configFiles}/configure-flags)"
146+
] ++ (
147+
# If configureAllComponents is set we should not specify the component
148+
# and Setup will attempt to configure them all.
149+
if configureAllComponents
150+
then ["--enable-tests" "--enable-benchmarks"]
151+
else ["${haskellLib.componentTarget componentId}"]
152+
) ++ [ "$(cat ${configFiles}/configure-flags)"
134153
] ++ commonConfigureFlags);
135154

136155
# From nixpkgs 20.09, the pkg-config exe has a prefix matching the ghc one
@@ -328,7 +347,7 @@ let
328347
++ (lib.optional enableSeparateDataOutput "data")
329348
++ (lib.optional keepSource "source");
330349

331-
configurePhase =
350+
prePatch =
332351
# emcc is very slow if it cannot cache stuff in $HOME
333352
(lib.optionalString (stdenv.hostPlatform.isGhcjs) ''
334353
export HOME=$(mktemp -d)
@@ -340,7 +359,9 @@ let
340359
cp -r . $source
341360
cd $source
342361
chmod -R +w .
343-
'') + ''
362+
'') + commonAttrs.prePatch;
363+
364+
configurePhase = ''
344365
runHook preConfigure
345366
echo Configure flags:
346367
printf "%q " ${finalConfigureFlags}
@@ -368,7 +389,11 @@ let
368389
target-pkg-and-db = "${ghc.targetPrefix}ghc-pkg -v0 --package-db $out/package.conf.d";
369390
in ''
370391
runHook preInstall
371-
$SETUP_HS copy ${lib.concatStringsSep " " setupInstallFlags}
392+
$SETUP_HS copy ${lib.concatStringsSep " " (
393+
setupInstallFlags
394+
++ lib.optional configureAllComponents
395+
(haskellLib.componentTarget componentId)
396+
)}
372397
${lib.optionalString (haskellLib.isLibrary componentId) ''
373398
$SETUP_HS register --gen-pkg-config=${name}.conf
374399
${ghc.targetPrefix}ghc-pkg -v0 init $out/package.conf.d
@@ -455,9 +480,24 @@ let
455480
'')
456481
}
457482
runHook postInstall
458-
'' + (lib.optionalString keepSource ''
459-
rm -rf dist
460-
'') + (lib.optionalString (haskellLib.isTest componentId) ''
483+
'' + (
484+
# Keep just the autogen files and package.conf.inplace package
485+
# DB (probably empty unless this is a library component).
486+
# We also have to remove any refernces to $out to avoid
487+
# circular references.
488+
if configureAllComponents
489+
then ''
490+
mv dist dist-tmp-dir
491+
mkdir -p dist/build
492+
mv dist-tmp-dir/build/${componentId.cname}/autogen dist/build/
493+
mv dist-tmp-dir/package.conf.inplace dist/
494+
remove-references-to -t $out dist/build/autogen/*
495+
rm -rf dist-tmp-dir
496+
''
497+
else lib.optionalString keepSource ''
498+
rm -rf dist
499+
''
500+
) + (lib.optionalString (haskellLib.isTest componentId) ''
461501
echo The test ${package.identifier.name}.components.tests.${componentId.cname} was built. To run the test build ${package.identifier.name}.checks.${componentId.cname}.
462502
'');
463503

builder/hspkg-builder.nix

+3-3
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,14 @@ let
107107
inherit (pkg) preUnpack postUnpack;
108108
};
109109

110-
buildComp = componentId: component: comp-builder {
111-
inherit componentId component package name src flags setup cabalFile cabal-generator patches revision
110+
buildComp = allComponent: componentId: component: comp-builder {
111+
inherit allComponent componentId component package name src flags setup cabalFile cabal-generator patches revision
112112
shellHook
113113
;
114114
};
115115

116116
in rec {
117-
components = haskellLib.applyComponents buildComp 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));

lib/check.nix

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ let
1010
in stdenv.mkDerivation ({
1111
name = (drv.name + "-check");
1212

13-
# Useing `srcOnly` (rather than getting the `src` via a `drv.passthru`)
13+
# Using `srcOnly` (rather than getting the `src` via a `drv.passthru`)
1414
# should correctly apply the patches from `drv` (if any).
1515
src = drv.source or (srcOnly drv);
1616

modules/package.nix

+119-80
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
@@ -138,85 +217,7 @@ in {
138217
};
139218
};
140219

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

modules/plan.nix

+5
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ let
138138
type = bool;
139139
default = (def.enableShared or true);
140140
};
141+
configureAllComponents = mkOption {
142+
description = "If set all the components in the package are configured (useful for cabal-doctest).";
143+
type = bool;
144+
default = false;
145+
};
141146
shellHook = mkOption {
142147
description = "Hook to run when entering a shell";
143148
type = unspecified; # Can be either a string or a function

test/cabal-doctests/Setup.hs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Main where
2+
3+
import Distribution.Extra.Doctest (defaultMainWithDoctests)
4+
5+
main :: IO ()
6+
main = defaultMainWithDoctests "doctests"

0 commit comments

Comments
 (0)