cabal-doctest
A Setup.hs helper for running doctests
https://github.com/ulidtko/cabal-doctest
| LTS Haskell 24.16: | 1.0.11 |
| Stackage Nightly 2025-10-23: | 1.0.11 |
| Latest on Hackage: | 1.0.11 |
cabal-doctest-1.0.11@sha256:86c3f54d41e1693a6b01d4b2294ce2fb0838544e1e17b3a7ae6afea6628f3d82,2047Module documentation for 1.0.11
- Distribution
- Distribution.Extra
cabal-doctest
A Setup.hs helper for running doctests.
Why this exists
Doctesting is a nifty technique that stimulates 3 good things to happen:
- library documentation gains runnable code examples that are also tested;
- library test suite gains documented usage examples as “tests for free”;
- get both of the above for the price of one.
That’s what the doctest tool does — not this package! — just for clarity.
Off the shelf, doctest doesn’t require any package management mumbo-jumbo:
you just run it on a source file with haddocks with doctests.
Issues come in when library authors and maintainers wish to integrate doctests
into CI pipelines. When doctests start to require dependencies or non-default
compiler flags: that’s when it gets hairy. There, if you want stack test and/or
cabal test to run doctests too with minimal shenanigans, then read on.
Among different available approaches, this package cabal-doctest helps with
one, which is known as custom setup, build-type: Custom more precisely.
You should stick to the default build-type: Simple, unless you know what
you’re doing.
In a nutshell, this custom Setup.hs shim generates a module Build_doctests
that allows your doctest driver test-suite to look like this:
module Main where
import Build_doctests (flags, pkgs, module_sources)
import Test.Doctest (doctest)
main :: IO ()
main = doctest (flags ++ pkgs ++ module_sources)
More detailed examples below.
Regardless of the name, this also works with Stack.
For old versions of stack, cabal-install, GHC, see caveats below.
Simple example
Follow simple example for the common case of a single-library .cabal package with doctests.
To recap the example’s code:
-
specify
build-type: Customin your.cabalfile; -
declare dependencies of
Setup.hs:custom-setup setup-depends: base >= 4 && <5, cabal-doctest >= 1 && <1.1See Notes below for a caveat with cabal-install < 2.4.
-
Populate
Setup.hslike so:module Main where import Distribution.Extra.Doctest (defaultMainWithDoctests) main :: IO () main = defaultMainWithDoctests "doctests"Assuming your test-suite is called
doctests, thisSetupwill generate aBuild_doctestsmodule during package build. If your test-suite goes by namefoo,defaultMainWithDoctests "foo"creates aBuild_foomodule. -
Use the generated module in a testsuite, simply like so:
module Main where import Build_doctests (flags, pkgs, module_sources) import Data.Foldable (traverse_) import System.Environment (unsetEnv) import Test.DocTest (doctest) main :: IO () main = do traverse_ putStrLn args -- optionally print arguments unsetEnv "GHC_ENVIRONMENT" -- see 'Notes'; you may not need this doctest args where args = flags ++ pkgs ++ module_sources
Ultimately, cabal test or stack test should run the doctests of your package.
Example with multiple cabal components
cabal-doctest also supports more exotic use cases where a .cabal file
contains more components with doctests than just the main library, including:
- doctests in executables,
- doctests in internal libraries (if using
Cabal-2.0or later).
Unlike the simple example shown above, these examples involve named
components. You don’t need to change the Setup.hs script to support
this use case. However, in this scenario Build_doctests will generate extra
copies of the flags, pkgs, and module_sources values for each additional
named component.
The simplest approach is to use x-doctest-components field in .cabal:
x-doctest-components: lib lib:internal exe:example
In that case, the test driver is generally:
module Main where
import Build_doctests (Component (..), components)
import Data.Foldable (for_)
import System.Environment (unsetEnv)
import Test.DocTest (doctest)
main :: IO ()
main = for_ components $ \(Component name flags pkgs sources) -> do
print name
putStrLn "----------------------------------------"
let args = flags ++ pkgs ++ sources
for_ args putStrLn
unsetEnv "GHC_ENVIRONMENT"
doctest args
There is also a more explicit approach: if you have an executable named foo, then
Build_doctest will contain flags_exe_foo, pkgs_exe_foo, and module_sources_exe_foo.
If the name has hyphens in it (e.g., my-exe), cabal-doctest will convert them to
underscores (e.g., you’d get flags_my_exe, pkgs_my_exe, module_sources_my_exe).
Internal library bar values will have a _lib_bar suffix.
An example testsuite driver for this use case might look like this:
module Main where
import Build_doctests
(flags, pkgs, module_sources,
flags_exe_my_exe, pkgs_exe_my_exe, module_sources_exe_my_exe)
import Data.Foldable (traverse_)
import System.Environment (unsetEnv)
import Test.DocTest
main :: IO ()
main = do
unsetEnv "GHC_ENVRIONMENT"
-- doctests for library
traverse_ putStrLn libArgs
doctest libArgs
-- doctests for executable
traverse_ putStrLn exeArgs
doctest exeArgs
where
libArgs = flags ++ pkgs ++ module_sources
exeArgs = flags_exe_my_exe ++ pkgs_exe_my_exe ++ module_sources_exe_my_exe
See the multiple-components-example.
Additional configuration
The cabal-doctest based Setup.hs supports a few extensions fields
in pkg.cabal files to customize the doctest runner behavior, without
customizing the default doctest.hs.
test-suite doctests:
if impl(ghc >= 8.0)
x-doctest-options: -fdiagnostics-color=never
x-doctest-source-dirs: test
x-doctest-modules: Servant.Utils.LinksSpec
x-doctest-optionsAdditional arguments passed intodoctestcommand.x-doctest-modulesAdditional modules todoctest. May be useful if you have doctests in tests or executables (i.e not the default library component).x-doctest-src-dirsAdditional source directories to look for the modules.
Notes
-
If support for cabal-install < 2.4 is required, you’ll have to add
Cabaltosetup-depends; see issue haskell/cabal#4288. -
Some versions of
Cabal(for instance, 2.0) can choose to build a package’sdoctesttest suite before the library. However, in order forcabal-doctestto work correctly, the library must be built first, asdoctestrelies on the presence of generated files that are only created when the library is built. See #19.A hacky workaround for this problem is to depend on the library itself in a
docteststest suite. See simple-example.cabal for a demonstration. (This assumes that the test suite has the ability to read build artifacts from the library, a separate build component. In practice, this assumption holds, which is why this library works at all.) -
custom-setupsection is supported starting fromcabal-install-1.24. For oldercabal-install'syou have to install custom setup dependencies manually. -
stackrespectscustom-setupstarting from version 1.3.3. Before that you have to useexplicit-setup-depssetting in yourstack.yaml; stack#2094. -
With base < 4.7 (GHC < 7.8, pre-2014),
System.Environment.unsetEnvfunction will need to be imported frombase-compatlibrary. It is already in transitive dependencies ofdoctest. Simply declare the dependency uponbase-compat, and thenimport System.Environment.Compat (unsetEnv)if you need that old GHC. -
You can use
x-doctest-optionsfield intest-suite docteststo pass additional flags to thedoctest. -
For
build-type: Configurepackages, you can usedefaultMainAutoconfWithDoctestsfunction to make customSetup.hsscript. -
If you use the default
.inhs-source-dirs, then runningdoctestsmight fail with weird errors (ambiguous module errors). Workaround is to move sources undersrc/or some non-top-level directory. -
The
extensions:field isn’t supported. Upgrade your.cabalfile to use at leastcabal-version: >= 1.10and usedefault-extensionsorother-extensions. -
If you use QuickCheck properties (
prop>) in your doctests, thetest-suite doctestshould depend onQuickCheckandtemplate-haskell. This is a little HACK: These dependencies aren’t needed to build thedocteststest-suite executable. However, as we letCabalresolve dependencies, we can pass the resolved (and installed!) package identifiers to to thedoctestcommand. This way,QuickCheckandtemplate-haskellare available todoctest, otherwise you’ll get errors like:Variable not in scope: mkName :: [Char] -> template-haskell-2.11.1.0:Language.Haskell.TH.Syntax.Nameor
Variable not in scope: polyQuickCheck :: Language.Haskell.TH.Syntax.Name -> Language.Haskell.TH.Lib.ExpQ -
From version 2, Stack sets the
GHC_ENVIRONMENTvariable, and GHC (as invoked bydoctest) will pick that up. This is undesirable:cabal-doctestpasses all the necessary information on the command line already, and can lead to ambiguous module errors as GHC will load the environment in addition to whatcabal-doctestinstructs it to.Hence,
cabal-doctesttells GHC to ignore package environments altogether on the command line. However, this is only possible since GHC 8.2. If you are usingcabal-doctestwith Stack 2 and GHC 8.0 or earlier and seeing ambiguous module errors or other mysterious failures, try manually unsettingGHC_ENVIRONMENTbefore invokingdoctest. -
If you are on Nix.
doctestwill not pick up your version of GHC if you don’t point it towards it, and therefore will result in “cannot satisfy -package-id” errors. You will need to setNIX_GHCandNIX_GHC_LIBDIRwithin your environment in order for doctest to pick up your GHC. Put the following inshell.nixand runnix-shell.# shell.nix { pkgs ? import <nixpkgs> {} }: let myHaskell = (pkgs.haskellPackages.ghcWithHoogle (p: with p; [ # Put your dependencies here containers hslogger ])); in pkgs.mkShell { name = "myPackage"; # These environment variables are important. Without these, # doctest doesn't pick up nix's version of ghc, and will fail # claiming it can't find your dependencies shellHook = '' export NIX_GHC=${myHaskell}/bin/ghc export NIX_GHC_LIBDIR=${myHaskell}/lib/ghc-8.10.7 ''; buildInputs = with pkgs; [ myHaskell ]; }
Copyright
Copyright 2017 Oleg Grenrus.
With contributions from:
- Ryan Scott
- Andreas Abel
- Max Ulidtko
Available under the BSD 3-clause license.
Changes
1.0.11 – 2024-11-22
- Support Cabal 3.14.0.0. cabal-doctest#85.
- Motivate the package in README cabal-doctest#43.
- Fix
stack testof examples, add CI integration.
1.0.10 – 2024-06-26
- Maintainership hand-over. See cabal-doctest#79.
- Support GHC 9.4, 9.6, 9.8, 9.10.
- Drop support & CI for GHC < 8.0.
1.0.9 – 2021-11-07
- Support
GHC-9.2,base-4.16, andCabal-3.6(thanks Alistair Burrowes).
1.0.8 – 2019-10-02
- Pass
-package-env=-when compiler supports it. - Amend examples to
unsetEnv "GHC_ENVIRONMENT".
1.0.7 – 2019-08-26
- Make
Distribution.Extra.Doctest-Wall-clean. - Support
GHC-8.8,base-4.13, andCabal-3.0.
1.0.6 – 2018-01-28
- Hook
haddockbuild too. Fixes issue whenhaddockfails, asBuild_doctestsisn’t generated.
1.0.5 – 2018-01-26
- Add a hack so
Build_doctestsmodule is automatically added to toother-modulesandautogen-moduleswhen compiled with Cabal-2.0. Thanks to that, we don’t get warnings because of-Wmissing-home-modules.
1.0.4 – 2017-12-05
- Add support for doctests in executables and (with
Cabal-2.0or later) internal libraries. Refer to theREADMEfor more details.
1.0.3 – 2017-11-02
- Add an explicit
Preludeimport toBuild_doctests.
1.0.2 – 2017-05-16
- Add
defaultMainAutoconfWithDoctestsandaddDoctestsUserHook. - Add support for
.hscand other preprocessed files (#8). - Add support for
x-doctest-source-dirsandx-doctest-modules.
1.0.1 – 2017-05-05
- Add support for
x-doctest-optionscabal-file field. - Proper support for
GHC-8.2.1andCabal-2.0.0.0. - Add support to
default-extensionsin library.
1 – 2017-01-31
- First version. Released on an unsuspecting world.