Skip to content

Commit 626c204

Browse files
committed
builder: Add haddock and hoogle to components.library.doc
1 parent 70f01c2 commit 626c204

File tree

11 files changed

+265
-12
lines changed

11 files changed

+265
-12
lines changed

builder/comp-builder.nix

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
, preBuild ? null, postBuild ? null
1717
, preCheck ? null, postCheck ? null
1818
, preInstall ? null, postInstall ? null
19+
, preHaddock ? null, postHaddock ? null
1920
, shellHook ? null
2021

2122
, doCheck ? component.doCheck || haskellLib.isTest componentId
@@ -25,6 +26,11 @@
2526

2627
, static ? stdenv.hostPlatform.isMusl
2728
, deadCodeElimination ? true
29+
30+
# Options for Haddock generation
31+
, doHaddock ? component.doHaddock # Enable haddock and hoogle generation
32+
, doHoogle ? true # Also build a hoogle index
33+
, hyperlinkSource ? true # Link documentation to the source code
2834
}:
2935

3036
let
@@ -157,6 +163,8 @@ let
157163
"--with-ld=${stdenv.cc.bintools.targetPrefix}ld"
158164
"--with-ar=${stdenv.cc.bintools.targetPrefix}ar"
159165
"--with-strip=${stdenv.cc.bintools.targetPrefix}strip"
166+
# HADDOCK
167+
"--docdir=${docdir "$doc"}"
160168
# other flags
161169
"--enable-executable-stripping"
162170
"--enable-library-stripping"
@@ -178,6 +186,9 @@ let
178186
inherit package configFiles;
179187
};
180188

189+
# the target dir for haddock documentation
190+
docdir = docoutput: docoutput + "/share/doc/" + componentId.cname;
191+
181192
in stdenv.mkDerivation ({
182193
name = fullName;
183194

@@ -188,6 +199,12 @@ in stdenv.mkDerivation ({
188199
config = component;
189200
inherit configFiles;
190201
env = shellWrappers;
202+
203+
# Given the haskell package, returns
204+
# the directory containing the haddock documentation.
205+
# `null' if no haddock documentation was built.
206+
# TODO: fetch the self from the fixpoint instead
207+
haddockDir = self: if doHaddock then "${docdir self.doc}/html" else null;
191208
};
192209

193210
meta = {
@@ -212,13 +229,17 @@ in stdenv.mkDerivation ({
212229
++ component.pkgconfig;
213230

214231
nativeBuildInputs =
215-
[ghc]
232+
[ghc buildPackages.removeReferencesTo]
216233
++ lib.optional (component.pkgconfig != []) pkgconfig
217234
++ executableToolDepends;
218235

219236
SETUP_HS = setup + /bin/Setup;
220237

238+
outputs = ["out" ] ++ (lib.optional (haskellLib.isLibrary componentId) "doc");
239+
221240
# Phases
241+
preInstallPhases = ["haddockPhase"];
242+
222243
prePatch = lib.optionalString (cabalFile != null) ''
223244
cat ${cabalFile} > ${package.identifier.name}.cabal
224245
'';
@@ -246,6 +267,35 @@ in stdenv.mkDerivation ({
246267
runHook postCheck
247268
'';
248269

270+
haddockPhase = ''
271+
runHook preHaddock
272+
${lib.optionalString (doHaddock && (haskellLib.isLibrary componentId)) ''
273+
docdir="${docdir "$doc"}"
274+
mkdir -p "$docdir"
275+
276+
$SETUP_HS haddock \
277+
"--html" \
278+
${lib.optionalString doHoogle "--hoogle"} \
279+
${lib.optionalString hyperlinkSource "--hyperlink-source"} \
280+
${lib.concatStringsSep " " component.setupHaddockFlags} \
281+
|| true # some packages don't have haddock documentation
282+
283+
html="dist/doc/html/${componentId.cname}"
284+
285+
if [ -d "$html" ]; then
286+
# Ensure that libraries are not pulled into the docs closure.
287+
# As an example, the prettified source code of a
288+
# Paths_package module will contain store paths of the library package.
289+
for x in "$html/src/"*.html; do
290+
remove-references-to -t $out $x
291+
done
292+
293+
cp -R "$html" "$docdir"/html
294+
fi
295+
''}
296+
runHook postHaddock
297+
'';
298+
249299
# Note: Cabal does *not* copy test executables during the `install` phase.
250300
installPhase = ''
251301
runHook preInstall
@@ -274,15 +324,10 @@ in stdenv.mkDerivation ({
274324
}
275325
# patches can (if they like) depend on the version and revision of the package.
276326
// lib.optionalAttrs (patches != []) { patches = map (p: if builtins.isFunction p then p { inherit (package.identifier) version; inherit revision; } else p) patches; }
277-
// lib.optionalAttrs (preUnpack != "") { inherit preUnpack; }
278-
// lib.optionalAttrs (postUnpack != "") { inherit postUnpack; }
279-
// lib.optionalAttrs (preConfigure != "") { inherit preConfigure; }
280-
// lib.optionalAttrs (postConfigure != "") { inherit postConfigure; }
281-
// lib.optionalAttrs (preBuild != "") { inherit preBuild; }
282-
// lib.optionalAttrs (postBuild != "") { inherit postBuild; }
283-
// lib.optionalAttrs (preCheck != "") { inherit preCheck; }
284-
// lib.optionalAttrs (postCheck != "") { inherit postCheck; }
285-
// lib.optionalAttrs (preInstall != "") { inherit preInstall; }
286-
// lib.optionalAttrs (postInstall != "") { inherit postInstall; }
327+
// haskellLib.optionalHooks {
328+
inherit preUnpack postUnpack preConfigure postConfigure
329+
preBuild postBuild preCheck postCheck
330+
preInstall postInstall preHaddock postHaddock;
331+
}
287332
// lib.optionalAttrs (stdenv.buildPlatform.libc == "glibc"){ LOCALE_ARCHIVE = "${buildPackages.glibcLocales}/lib/locale/locale-archive"; }
288333
)

builder/default.nix

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
, postCheck
2222
, preInstall
2323
, postInstall
24+
, preHaddock
25+
, postHaddock
2426

2527
, shellHook
2628

@@ -73,7 +75,9 @@ let
7375

7476
buildComp = componentId: component: comp-builder {
7577
inherit componentId component package name src flags setup cabalFile patches revision
76-
preUnpack postUnpack preConfigure postConfigure preBuild postBuild preCheck postCheck preInstall postInstall
78+
preUnpack postUnpack preConfigure postConfigure
79+
preBuild postBuild preCheck postCheck
80+
preInstall postInstall preHaddock postHaddock
7781
shellHook
7882
;
7983
};

lib/default.nix

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ with haskellLib;
6161
if componentId.ctype == "all" then ""
6262
else "${componentId.ctype}:${componentId.cname}";
6363

64+
# Remove null or empty values from an attrset.
65+
optionalHooks = lib.filterAttrs (_: hook: hook != null && hook != "");
66+
6467
# Avoid pkgs.callPackage for now. It does a lot of nonsense with OOP
6568
# style programming that we should avoid until we know we want it.
6669

modules/package.nix

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ in {
135135
type = listOfFilteringNulls str;
136136
default = config.setupInstallFlags;
137137
};
138+
setupHaddockFlags = mkOption {
139+
type = listOfFilteringNulls str;
140+
default = config.setupHaddockFlags;
141+
};
138142
doExactConfig = mkOption {
139143
type = bool;
140144
default = config.doExactConfig;
@@ -147,6 +151,11 @@ in {
147151
type = bool;
148152
default = config.doCrossCheck;
149153
};
154+
doHaddock = mkOption {
155+
description = "Enable building of the Haddock documentation from the annotated Haskell source code.";
156+
type = bool;
157+
default = config.doHaddock;
158+
};
150159
};
151160
};
152161
in {
@@ -225,6 +234,10 @@ in {
225234
type = listOfFilteringNulls str;
226235
default = [];
227236
};
237+
setupHaddockFlags = mkOption {
238+
type = listOfFilteringNulls str;
239+
default = [];
240+
};
228241
preUnpack = mkOption {
229242
type = nullOr lines;
230243
default = null;
@@ -265,6 +278,14 @@ in {
265278
type = nullOr string;
266279
default = null;
267280
};
281+
preHaddock = mkOption {
282+
type = nullOr string;
283+
default = null;
284+
};
285+
postHaddock = mkOption {
286+
type = nullOr string;
287+
default = null;
288+
};
268289
shellHook = mkOption {
269290
type = nullOr string;
270291
default = null;
@@ -282,6 +303,11 @@ in {
282303
type = bool;
283304
default = false;
284305
};
306+
doHaddock = mkOption {
307+
description = "Enable building of the Haddock documentation from the annotated Haskell source code.";
308+
type = bool;
309+
default = true;
310+
};
285311
};
286312

287313
# This has one quirk. Manually setting options on the all component

test/builder-haddock/Setup.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import Distribution.Simple
2+
main = defaultMain

test/builder-haddock/TestHaddock.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- | Haddock test stuff
2+
module TestHaddock (hello) where
3+
4+
-- | Standard hello text.
5+
hello :: String
6+
hello = "Hello, world!"

test/builder-haddock/default.nix

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
{ pkgs
2+
, haskell
3+
, stdenv
4+
}:
5+
6+
with stdenv.lib;
7+
8+
let
9+
pkgSet = haskell.mkPkgSet {
10+
inherit pkgs;
11+
# generated with:
12+
# cabal new-build
13+
# plan-to-nix dist-newstyle/cache/plan.json > plan.nix
14+
# cabal-to-nix test-haddock.cabal > test-haddock.nix
15+
pkg-def = import ./plan.nix;
16+
pkg-def-overlays = [
17+
{ test-haddock = ./test-haddock.nix; }
18+
];
19+
modules = [
20+
# overrides to fix the build
21+
{
22+
packages.transformers-compat.components.library.doExactConfig = true;
23+
}
24+
25+
{
26+
# Add a hook to the haddock phase
27+
packages.test-haddock.postHaddock = ''
28+
echo "==="
29+
echo "This is the postHaddock hook. The files are:"
30+
find .
31+
echo "==="
32+
'';
33+
34+
# Check that the package option works
35+
packages.stm.doHaddock = false;
36+
}
37+
];
38+
};
39+
40+
packages = pkgSet.config.hsPkgs;
41+
42+
in
43+
stdenv.mkDerivation {
44+
name = "builder-haddock-test";
45+
46+
buildCommand = let
47+
inherit (packages.test-haddock.components) library;
48+
noDocLibrary = packages.stm.components.library;
49+
in ''
50+
########################################################################
51+
# test haddock
52+
53+
doc="${toString library.doc}"
54+
docDir="${toString (library.haddockDir library)}"
55+
56+
# exeDoc="$ disabled {toString packages.test-haddock.components.exes.test-haddock.doc}"
57+
# printf "checking that executable output does not have docs ... " >& 2
58+
# echo $exeDoc
59+
# test "$exeDoc" = ""
60+
61+
printf "checking that documentation directory was built... " >& 2
62+
echo "$doc" >& 2
63+
test -n "$doc"
64+
65+
printf "checking that documentation was generated... " >& 2
66+
grep hello "$docDir/TestHaddock.html" > /dev/null
67+
echo yes >& 2
68+
69+
printf "checking for hoogle index of package... " >& 2
70+
grep hello "$docDir/test-haddock.txt" > /dev/null
71+
echo yes >& 2
72+
73+
printf "checking for absence of documentation in another package... " >& 2
74+
if [ -d "${toString (noDocLibrary.haddockDir noDocLibrary)}" ]; then
75+
echo "it exists - FAIL" >& 2
76+
else
77+
echo PASS >& 2
78+
fi
79+
80+
printf "checking for absence of library package store paths in docs... " >& 2
81+
if grep -R ${library} "$docDir" > /dev/null; then
82+
echo "Found ${library} - FAIL" >& 2
83+
exit 1
84+
else
85+
echo "PASS" >& 2
86+
fi
87+
88+
touch $out
89+
'';
90+
91+
meta.platforms = platforms.all;
92+
} // { inherit packages pkgSet; }

test/builder-haddock/plan.nix

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
hackage:
2+
{
3+
packages = {
4+
"ghc-prim".revision = hackage."ghc-prim"."0.5.2.0".revisions.default;
5+
"stm".revision = hackage."stm"."2.4.5.1".revisions.default;
6+
"rts".revision = hackage."rts"."1.0".revisions.default;
7+
"base".revision = hackage."base"."4.11.1.0".revisions.default;
8+
"array".revision = hackage."array"."0.5.2.0".revisions.default;
9+
"integer-gmp".revision = hackage."integer-gmp"."1.0.2.0".revisions.default;
10+
};
11+
compiler = {
12+
version = "8.4.4";
13+
nix-name = "ghc844";
14+
packages = {
15+
"ghc-prim" = "0.5.2.0";
16+
"stm" = "2.4.5.1";
17+
"rts" = "1.0";
18+
"base" = "4.11.1.0";
19+
"array" = "0.5.2.0";
20+
"integer-gmp" = "1.0.2.0";
21+
};
22+
};
23+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
cabal-version: 2.2
2+
name: test-haddock
3+
version: 0.1.0.0
4+
license: NONE
5+
author: Rodney Lorrimar
6+
maintainer: [email protected]
7+
8+
library
9+
exposed-modules: TestHaddock
10+
other-modules: Paths_test_haddock
11+
-- other-extensions:
12+
build-depends: base ^>=4.11.1.0
13+
, stm
14+
-- hs-source-dirs:
15+
default-language: Haskell2010

test/builder-haddock/test-haddock.nix

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{ system
2+
, compiler
3+
, flags
4+
, pkgs
5+
, hsPkgs
6+
, pkgconfPkgs
7+
, ... }:
8+
{
9+
flags = {};
10+
package = {
11+
specVersion = "2.2";
12+
identifier = {
13+
name = "test-haddock";
14+
version = "0.1.0.0";
15+
};
16+
license = "NONE";
17+
copyright = "";
18+
maintainer = "[email protected]";
19+
author = "Rodney Lorrimar";
20+
homepage = "";
21+
url = "";
22+
synopsis = "";
23+
description = "";
24+
buildType = "Simple";
25+
};
26+
components = {
27+
"library" = {
28+
depends = [
29+
(hsPkgs.base)
30+
(hsPkgs.stm)
31+
];
32+
};
33+
};
34+
} // rec {
35+
src = pkgs.lib.mkDefault ./.;
36+
}

0 commit comments

Comments
 (0)