on iko/ghcjs-patch-master help sign in
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 https://github.com/input-output-hk/haskell.nix/pull/427#issuecomment-605761780. * 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 <moritz.angermann@gmail.com> Co-authored-by: Hamish Mackenzie <Hamish.Mackenzie@iohk.io> Co-authored-by: Hamish Mackenzie <Hamish.K.Mackenzie@gmail.com>

commit6c627b429b99cc4753cff96365daec1dade675dc authorDennis Gosnell <cdep.illabout@gmail.com> dateSat 20 Mar 2021, 10:25 (6 years ago) parentd655dbf
11 files changed+298−95
builder/comp-builder.nix+51−11 builder/hspkg-builder.nix+3−3 lib/check.nix+1−1 modules/package.nix+119−80 modules/plan.nix+5 test/cabal-doctests/Setup.hs+6 test/cabal-doctests/cabal-doctests-test.cabal+31 test/cabal-doctests/default.nix+47 test/cabal-doctests/doctests/Doctests.hs+16 test/cabal-doctests/src/Lib.hs+18 test/default.nix+1
builder/comp-builder.nix+51−11
@@ -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
7987let
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
@@ -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
116116in 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
@@ -10,7 +10,7 @@ let
1010in 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
@@ -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
26105in {
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
@@ -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.hsnew file+6
@@ -0,0 +1,6 @@
1+module Main where
2+
3+import Distribution.Extra.Doctest (defaultMainWithDoctests)
4+
5+main :: IO ()
6+main = defaultMainWithDoctests "doctests"
test/cabal-doctests/cabal-doctests-test.cabalnew file+31
@@ -0,0 +1,31 @@
1+cabal-version: 1.12
2+
3+name: cabal-doctests-test
4+version: 0.1.0.0
5+build-type: Custom
6+
7+custom-setup
8+ setup-depends:
9+ base >= 4 && <5,
10+ Cabal,
11+ cabal-doctest >= 1 && <1.1
12+
13+library
14+ hs-source-dirs: src
15+ exposed-modules: Lib
16+ other-modules: Paths_cabal_doctests_test
17+ build-depends:
18+ base >=4.7 && <5,
19+ aeson
20+ default-language: Haskell2010
21+
22+test-suite doctests
23+ hs-source-dirs: doctests
24+ x-doctest-options: --no-magic
25+ type: exitcode-stdio-1.0
26+ main-is: Doctests.hs
27+ ghc-options: -threaded -rtsopts -with-rtsopts=-N
28+ build-depends:
29+ base >=4.7 && <5
30+ , doctest
31+ default-language: Haskell2010
test/cabal-doctests/default.nixnew file+47
@@ -0,0 +1,47 @@
1+# Test a package set
2+{ stdenv, util, cabalProject', haskellLib, gmp6, zlib, recurseIntoAttrs, runCommand, testSrc, compiler-nix-name }:
3+
4+with stdenv.lib;
5+
6+let
7+ project =
8+ cabalProject' {
9+ inherit compiler-nix-name;
10+ src = testSrc "cabal-doctests";
11+ index-state = "2021-01-11T00:00:00Z";
12+ };
13+
14+ packages = project.hsPkgs;
15+
16+ meta = {
17+ platforms = platforms.all;
18+ # Making this work for cross compilers will be difficult.
19+ disabled = stdenv.buildPlatform != stdenv.hostPlatform;
20+ };
21+
22+in recurseIntoAttrs ({
23+ ifdInputs = {
24+ plan-nix = addMetaAttrs meta project.plan-nix;
25+ };
26+ run = stdenv.mkDerivation {
27+ name = "cabal-doctests-test";
28+
29+ buildCommand = ''
30+ printf "Checking that doctest tests have run ... " >& 2
31+ cat ${packages.cabal-doctests-test.checks.doctests}/test-stdout >& 2
32+
33+ touch $out
34+ '';
35+
36+ meta = {
37+ platforms = platforms.all;
38+ # Making cabal-doctest work for cross compilers will be difficult.
39+ disabled = stdenv.buildPlatform != stdenv.hostPlatform;
40+ };
41+
42+ passthru = {
43+ # Used for debugging with nix repl
44+ inherit project packages;
45+ };
46+ };
47+})
test/cabal-doctests/doctests/Doctests.hsnew file+16
@@ -0,0 +1,16 @@
1+module Main where
2+
3+import Build_doctests (flags, pkgs, module_sources)
4+import Data.Foldable (traverse_)
5+import System.Environment (unsetEnv)
6+import Test.DocTest (doctest)
7+
8+main :: IO ()
9+main = do
10+ putStrLn ""
11+ traverse_ putStrLn args -- optionally print arguments
12+ unsetEnv "GHC_ENVIRONMENT" -- see 'Notes'; you may not need this
13+ doctest args
14+ where
15+ args :: [String]
16+ args = flags ++ pkgs ++ module_sources -- ++ ["-v"]
test/cabal-doctests/src/Lib.hsnew file+18
@@ -0,0 +1,18 @@
1+module Lib
2+ ( someFunc
3+ ) where
4+
5+import Data.Aeson (encode)
6+import Paths_cabal_doctests_test (version)
7+
8+-- |
9+-- >>> 1 + 1
10+-- 2
11+--
12+-- >>> version
13+-- Version {versionBranch = [0,1,0,0], versionTags = []}
14+--
15+-- >>> encode (Just 1 :: Maybe Int)
16+-- "1"
17+someFunc :: IO ()
18+someFunc = putStrLn "someFunc"
test/default.nix+1
@@ -179,6 +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 { inherit util compiler-nix-name; };
182183 extra-hackage = callTest ./extra-hackage { inherit compiler-nix-name; };
183184 hls-cabal = callTest ./haskell-language-server/cabal.nix { inherit compiler-nix-name; };
184185 hls-stack = callTest ./haskell-language-server/stack.nix { inherit compiler-nix-name; };