Code Monkey home page Code Monkey logo

cabal-doctest's Introduction

cabal-doctest

Hackage Haskell-CI

A Setup.hs helper for running doctests.

Simple example

Follow simple example for the common case of a single-library .cabal package with doctests.

To recap the example's code:

  1. specify build-type: Custom in your .cabal file;

  2. declare dependencies of Setup.hs:

    custom-setup
     setup-depends:
       base >= 4 && <5,
       cabal-doctest >= 1 && <1.1
    

    See Notes below for a caveat with cabal-install < 2.4.

  3. Populate Setup.hs like so:

    module Main where
    
    import Distribution.Extra.Doctest (defaultMainWithDoctests)
    
    main :: IO ()
    main = defaultMainWithDoctests "doctests"

    Assuming your test-suite is called doctests, this Setup will generate a Build_doctests module during package build. If your test-suite goes by name foo, defaultMainWithDoctests "foo" creates a Build_foo module.

  4. 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.0 or 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 customise the doctest runner behaviour, without customising 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-options Additional arguments passed into doctest command.
  • x-doctest-modules Additional modules to doctest. May be useful if you have doctest in test or executables (i.e not default library component).
  • x-doctest-src-dirs Additional source directories to look for the modules.

Notes

  • If support for cabal-install < 2.4 is required, you'll have to add Cabal to setup-depends; see issue haskell/cabal#4288.

  • Some versions of Cabal (for instance, 2.0) can choose to build a package's doctest test suite before the library. However, in order for cabal-doctest to work correctly, the library must be built first, as doctest relies 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 doctests test 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-setup section is supported starting from cabal-install-1.24. For older cabal-install's you have to install custom setup dependencies manually.

  • stack respects custom-setup starting from version 1.3.3. Before that you have to use explicit-setup-deps setting in your stack.yaml; stack#2094.

  • With base < 4.7 (GHC < 7.8, pre-2014), System.Environment.unsetEnv function will need to be imported from base-compat library. It is already in transitive dependencies of doctest. Simply declare the dependency upon base-compat, and then import System.Environment.Compat (unsetEnv) if you need that old GHC.

  • You can use x-doctest-options field in test-suite doctests to pass additional flags to the doctest.

  • For build-type: Configure packages, you can use defaultMainAutoconfWithDoctests function to make custom Setup.hs script.

  • If you use the default . in hs-source-dirs, then running doctests might fail with weird errors (ambiguous module errors). Workaround is to move sources under src/ or some non-top-level directory.

  • The extensions: field isn't supported. Upgrade your .cabal file to use at least cabal-version: >= 1.10 and use default-extensions or other-extensions.

  • If you use QuickCheck properties (prop>) in your doctests, the test-suite doctest should depend on QuickCheck and template-haskell. This is a little HACK: These dependencies aren't needed to build the doctests test-suite executable. However, as we let Cabal resolve dependencies, we can pass the resolved (and installed!) package identifiers to to the doctest command. This way, QuickCheck and template-haskell are available to doctest, otherwise you'll get errors like:

      Variable not in scope:
        mkName
          :: [Char]
             -> template-haskell-2.11.1.0:Language.Haskell.TH.Syntax.Name
    

    or

      Variable not in scope:
        polyQuickCheck
          :: Language.Haskell.TH.Syntax.Name -> Language.Haskell.TH.Lib.ExpQ
    
  • From version 2, Stack sets the GHC_ENVIRONMENT variable, and GHC (as invoked by doctest) will pick that up. This is undesirable: cabal-doctest passes 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 what cabal-doctest instructs it to.

    Hence, cabal-doctest tells GHC to ignore package environments altogether on the command line. However, this is only possible since GHC 8.2. If you are using cabal-doctest with Stack 2 and GHC 8.0 or earlier and seeing ambiguous module errors or other mysterious failures, try manually unsetting GHC_ENVIRONMENT before invoking doctest.

  • If you are on Nix. doctest will 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 set NIX_GHC and NIX_GHC_LIBDIR within your environment in order for doctest to pick up your GHC. Put the following in shell.nix and run nix-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.

cabal-doctest's People

Contributors

alistairb avatar andreasabel avatar christiaanb avatar cocreature avatar felixonmars avatar hazelfire avatar nikita-volkov avatar phadej avatar quasicomputational avatar ryanglscott avatar ulidtko avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

cabal-doctest's Issues

Build error with GHC-8.8.1

I get the following error when trying to build with 8.8.1:

Preprocessing library for cabal-doctest-1.0.6..
Building library for cabal-doctest-1.0.6..
[1 of 1] Compiling Distribution.Extra.Doctest
                     
/tmp/stack3057/cabal-doctest-1.0.6/src/Distribution/Extra/Doctest.hs:434:60: error:
    • Couldn't match type ‘Distribution.Types.LibraryName.LibraryName’
                     with ‘Maybe
                             Distribution.Types.UnqualComponentName.UnqualComponentName’
      Expected type: Library
                     -> Maybe Distribution.Types.UnqualComponentName.UnqualComponentName
        Actual type: Library
                     -> Distribution.Types.LibraryName.LibraryName
    • In the second argument of ‘(.)’, namely ‘libName’
      In the second argument of ‘(.)’, namely
        ‘fmap unUnqualComponentName . libName’
      In the expression: NameLib . fmap unUnqualComponentName . libName
    |                
434 |     mbLibraryName = NameLib . fmap unUnqualComponentName . libName
    |                                                            ^^^^^^^
  

I think the types exported from cabal might have changed with the new compiler.

Doesn't build with Cabal HEAD (2.1)

I am working on a project that requires features from the latest HEAD version of Cabal (see haskell/cabal#4799). Something in my dependency tree pulled in cabal-doctest which caused my build to fail with this error:

Error: While constructing the build plan, the following exceptions were encountered:

In the dependencies for cabal-doctest-1.0.2:
    Cabal-2.1.0.0 must match >=1.10 && <2.1 (latest applicable is 2.0.0.2)
needed due to example-0.0.0 -> cabal-doctest-1.0.2

Plan construction failed.

I initially thought this was a problem with distributive (see ekmett/distributive#37) but was sent here instead.

I tried to fix this by bumping the bounds (--allow-newer), but the build failed:

    /private/tmp/cabal-doctest/src/Distribution/Extra/Doctest.hs:227:7: error:
        • Couldn't match expected type ‘IO ()’
                      with actual type ‘String -> IO ()’
        • In a stmt of a 'do' block:
            rewriteFile (testAutogenDir </> "Build_doctests.hs")
            $ unlines
                ["module Build_doctests where", "", "import Prelude", "", ....]
          In the second argument of ‘($)’, namely
            ‘do { let testBI = testBuildInfo suite;
                  let additionalFlags
                        = maybe ... words
                          $ lookup "x-doctest-options" $ customFieldsBI testBI;
                  let additionalModules
                        = maybe ... words
                          $ lookup "x-doctest-modules" $ customFieldsBI testBI;
                  let additionalDirs'
                        = maybe ... words
                          $ lookup "x-doctest-source-dirs" $ customFieldsBI testBI;
                  .... }’
          In the expression:
            when (testName suite == fromString testSuiteName)
            $ do { let testBI = testBuildInfo suite;
                   let additionalFlags
                         = maybe ... words
                           $ lookup "x-doctest-options" $ customFieldsBI testBI;
                   let additionalModules
                         = maybe ... words
                           $ lookup "x-doctest-modules" $ customFieldsBI testBI;
                   let additionalDirs'
                         = maybe ... words
                           $ lookup "x-doctest-source-dirs" $ customFieldsBI testBI;
                   .... }
    
    /private/tmp/cabal-doctest/src/Distribution/Extra/Doctest.hs:227:20: error:
        • Couldn't match type ‘[Char]’
                         with ‘Distribution.Verbosity.Verbosity’
          Expected type: Distribution.Verbosity.Verbosity
            Actual type: FilePath
        • In the first argument of ‘rewriteFile’, namely
            ‘(testAutogenDir </> "Build_doctests.hs")’
          In the expression:
            rewriteFile (testAutogenDir </> "Build_doctests.hs")
          In a stmt of a 'do' block:
            rewriteFile (testAutogenDir </> "Build_doctests.hs")
            $ unlines
                ["module Build_doctests where", "", "import Prelude", "", ....]

Deprecation plan

The library mentions the following plan for deprecation

There is an issue in the Cabal issue tracker about adding cabal doctest command. After that command is implemented, this library will be deprecated.

But

  • That issue has already been closed (although there is only cabal v1-doctest)
  • This library is useful independently of doctest. It's probably easy to say so in retrospect, but I think it should have been given a different name. I like it as a maintained replacement of https://hackage.haskell.org/package/cabal-toolkit.

I use this to initialise the GHC API so that I can compile Haskell code to Core. I used cabal-toolkit before, but that didn't work well with doctest. Hmm. Maybe I should use hie-bios instead? 🤔
Edit: No, I don't think that would work. There's no way I can make sure that the GHC versions match, which is trivial with cabal-doctest and cabal-toolkit.

Regardless, I think it would be good to update that note.

Failure with GHC 8.8 / Cabal 2.5

This is with Cabal 2.5 / GHC 8.8:

[1 of 1] Compiling Distribution.Extra.Doctest ( src/Distribution/Extra/Doctest.hs, dist/build/D

src/Distribution/Extra/Doctest.hs:437:60: error:
    ���<80>��� Couldn't match type ���<80><98>Distribution.Types.LibraryName.LibraryName���<80><99>
                     with ���<80><98>Maybe
                             Distribution.Types.UnqualComponentName.UnqualComponentName���<80>
      Expected type: Library
                     -> Maybe Distribution.Types.UnqualComponentName.UnqualComponentName
        Actual type: Library
                     -> Distribution.Types.LibraryName.LibraryName
    ���<80>��� In the second argument of ���<80><98>(.)���<80><99>, namely ���<80><98>libName���<80><99>
      In the second argument of ���<80><98>(.)���<80><99>, namely
        ���<80><98>fmap unUnqualComponentName . libName���<80><99>
      In the expression: NameLib . fmap unUnqualComponentName . libName
    |
437 |     mbLibraryName = NameLib . fmap unUnqualComponentName . libName
    |                                                            ^^^^^^^

(Sorry for the botched Unicode, tty copy-paste can sometimes do this..)

Build_doctests isn't generated unless Cabal file defines a library

I tried for quite a while now to run a doctest suite with cabal-doctest in a package that defines an executable, but no library, and all attempts invariably failed because the required Build_doctests.hs file wasn't generated. Then I added an empty Library stanza to my Cabal file, and immediately everything worked! I'm not sure why things work that way, but it doesn't seem right. Is that a bug, maybe?

cabal install <packages> fails with "Could not find module ‘Distribution.Extra.Doctest’"

Describe the bug
Cabal fails to install packages with ghc error.

To Reproduce
Steps to reproduce the behavior:

NOTE: This also fails on cabal new-install snap snap-templates

$ cabal install snap snap-templates
...
...
...
cabal: Failed to build distributive-0.6.1 (which is required by snap-1.1.2.0).
The failure occurred during the configure step. The exception was:
dieVerbatim: user error (cabal: '/home/drew/.ghcup/bin/ghc' exited with an
error:

/tmp/cabal-install.-25560/dist-newstyle/tmp/src-25562/distributive-0.6.1/dist/setup/setup.hs:12:1:
error:
Could not find module ‘Distribution.Extra.Doctest’
There are files missing in the ‘cabal-doctest-1.0.8’ package,
try running 'ghc-pkg check'.
Use -v to see a list of the files searched for.
|
12 | import Distribution.Extra.Doctest ( defaultMainWithDoctests )
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)

drew@pizza ~/code $ ghc-pkg -v0 check
drew@pizza ~/code $ ghc-pkg -v check
GHC package manager version 8.6.5
Timestamp 2020-02-01 19:08:08.967753776 UTC for /home/drew/.ghcup/ghc/8.6.5/lib/ghc-8.6.5/package.conf.d/package.cache
using cache: /home/drew/.ghcup/ghc/8.6.5/lib/ghc-8.6.5/package.conf.d/package.cache
db stack: ["/home/drew/.ghc/x86_64-linux-8.6.5/package.conf.d","/home/drew/.ghcup/ghc/8.6.5/lib/ghc-8.6.5/package.conf.d"]
flag db stack: ["/home/drew/.ghc/x86_64-linux-8.6.5/package.conf.d","/home/drew/.ghcup/ghc/8.6.5/lib/ghc-8.6.5/package.conf.d"]

System information

  • Operating system: Arch Linux kernel version 5.4
$ uname -a
Linux pizza 5.4.15-arch1-1 #1 SMP PREEMPT Sun, 26 Jan 2020 09:48:50 +0000 x86_64 GNU/Linux
  • cabal, ghc versions
$ cabal --version
cabal-install version 3.0.0.0
compiled using version 3.0.0.0 of the Cabal library

$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.6.5

Additional context
On arch linux, there is a linkage issue - so my ~/.cabal/config has the following added to it:

library-vanilla: False
shared: True
executable-dynamic: True
 ghc-options:
   -dynamic

Dyplicate symbol definition failure

When running doctest in the manner described in the readme (a custom setup etc) I get to the point of actually running the test:

cabal test --builddir=build <component>:doctests

This fails on my OS-X machine but works on the Linux based CI systems:

GHC runtime linker: fatal error: I found a duplicate definition for symbol                                    [161/779]
   __ZNK17double_conversion6VectorIcEixEi
whilst processing object file
   /Users/tommd/.cabal/store/ghc-8.10.1/dbl-cnvrsn-2.0.2.0-d1095495/lib/libHSdbl-cnvrsn-2.0.2.0-d1095495.a
The symbol was previously defined in
   /Users/tommd/.cabal/store/ghc-8.10.1/dbl-cnvrsn-2.0.2.0-d1095495/lib/libHSdbl-cnvrsn-2.0.2.0-d1095495.a(hs-double-co
nversion.o)
This could be caused by:
   * Loading two different object files which export the same symbol
   * Specifying the same object file twice on the GHCi command line
   * An incorrect `package.conf' entry, causing some object to be
     loaded twice.
doctests:
lookupSymbol failed in relocateSection (relocate external)
/Users/tommd/.cabal/store/ghc-8.10.1/dbl-cnvrsn-2.0.2.0-d1095495/lib/libHSdbl-cnvrsn-2.0.2.0-d1095495.a: unknown symbol
 `__ZN17double_conversion6StrtofENS_6VectorIKcEEi'
GHC runtime linker: fatal error: I found a duplicate definition for symbol
   __ZN17double_conversion7BitCastIdyEET_RKT0_
whilst processing object file
   /Users/tommd/.cabal/store/ghc-8.10.1/dbl-cnvrsn-2.0.2.0-d1095495/lib/libHSdbl-cnvrsn-2.0.2.0-d1095495.a
The symbol was previously defined in                                                                                      /Users/tommd/.cabal/store/ghc-8.10.1/dbl-cnvrsn-2.0.2.0-d1095495/lib/libHSdbl-cnvrsn-2.0.2.0-d1095495.a(double-conve
rsion.o)
This could be caused by:
   * Loading two different object files which export the same symbol
   * Specifying the same object file twice on the GHCi command line
   * An incorrect `package.conf' entry, causing some object to be
     loaded twice.

Is this a known issue? Should I reroute to GHC's issue tracker?

My last attempt to make a minimal example failed, but I can retry.

Support for mixins

I'm trying to get doctest+cabal-doctest to work on a project, but I can't get it to play ball with mixins.

Namely, I'm using a custom prelude and using mixins: base hiding (Prelude) to hide the default one. But it seems doctest doesn't seem to recognize this mixin and says there are two preludes in scope.

I've created a test project here, so we can reproduce this issue: https://github.com/dcastro/doctest-mixins
stack build works fine, but stack test fails.

$ stack build doctest-mixins:lib doctest-mixins:test:doctests --no-run-tests
doctest-mixins> configure (lib + test)
Configuring doctest-mixins-0.1.0.0...
doctest-mixins> build (lib + test)
Preprocessing library for doctest-mixins-0.1.0.0..
Building library for doctest-mixins-0.1.0.0..
Preprocessing test suite 'doctests' for doctest-mixins-0.1.0.0..
Building test suite 'doctests' for doctest-mixins-0.1.0.0..
[1 of 2] Compiling Build_doctests
Linking .stack-work/dist/x86_64-linux-tinfo6/Cabal-3.0.1.0/build/doctests/doctests ...
            
Warning: The following modules should be added to exposed-modules or other-modules in /home/dc/dev/other/doctest-mixins/doctest-mixins.cabal:
             - In test:doctests:
                 Build_doctests
             - In test:doctests:
                 Build_doctests
         
         Missing modules in the cabal file are likely to cause undefined reference errors from the linker, along with other problems.
doctest-mixins> copy/register
Installing library in /home/dc/dev/other/doctest-mixins/.stack-work/install/x86_64-linux-tinfo6/f379cbdb645e2f7b975c4e9af6625e7b004143a74d5f8f815a31249737060a3e/8.8.3/lib/x86_64-linux-ghc-8.8.3/doctest-mixins-0.1.0.0-J9NorAghXcQDDEeMJ3tfFn
Registering library for doctest-mixins-0.1.0.0..
doctest-mixins> Test running disabled by --no-run-tests flag.
Completed 2 action(s).
$ stack test doctest-mixins:test:doctests

doctest-mixins> test (suite: doctests)
            
Progress 1/2: doctest-mixins-i
-i/home/dc/dev/other/doctest-mixins/.stack-work/dist/x86_64-linux-tinfo6/Cabal-3.0.1.0/build/autogen
-i/home/dc/dev/other/doctest-mixins/.stack-work/dist/x86_64-linux-tinfo6/Cabal-3.0.1.0/build
-i/home/dc/dev/other/doctest-mixins/src
-package-env=-
-hide-all-packages
-no-user-package-db
-package-db=/home/dc/.stack/snapshots/x86_64-linux-tinfo6/f379cbdb645e2f7b975c4e9af6625e7b004143a74d5f8f815a31249737060a3e/8.8.3/pkgdb
-package-db=/home/dc/dev/other/doctest-mixins/.stack-work/install/x86_64-linux-tinfo6/f379cbdb645e2f7b975c4e9af6625e7b004143a74d5f8f815a31249737060a3e/8.8.3/pkgdb
-package-db=.stack-work/dist/x86_64-linux-tinfo6/Cabal-3.0.1.0/package.conf.inplace
-optP-include
-optP.stack-work/dist/x86_64-linux-tinfo6/Cabal-3.0.1.0/build/autogen/cabal_macros.h
-package-id=base-4.13.0.0
-package-id=morley-prelude-0.3.0-emFluUQAgu2cOHiMlTd8V
-package-id=doctest-0.16.3-AoCHnIRCAFH3tdcWc14oMy
-package=doctest-mixins-0.1.0.0
Main
Paths_doctest_mixins

/home/dc/dev/other/doctest-mixins/src/Main.hs:1:8: error:
    Ambiguous module name ‘Prelude’:
      it was found in multiple packages:
      base-4.13.0.0 morley-prelude-0.3.0
  |
1 | module Main where
  |        ^^^^
                            
doctest-mixins> Test suite doctests failed
Completed 2 action(s).      
Test suite failure for package doctest-mixins-0.1.0.0
    doctests:  exited with: ExitFailure 1
Logs printed to console

I've first reported this issue in sol/doctest#289. It was suggested that maybe cabal-doctest would be the ideal place to solve this issue.

If it helps, I've also uploaded the output of cabal build lib:doctest-mixins -v3 in a gist.

Do doctests test suites always need to depend on the library?

It seems that doctests test suites might always have an implicit dependency on the library being tested. I noticed this when doing the following:

$ cabal get parsers-0.12.5
Unpacking to parsers-0.12.5/
$ cd parsers-0.12.5/
$ cabal configure --enable-tests
Resolving dependencies...
[1 of 1] Compiling Main             ( dist/setup/setup.hs, dist/setup/Main.o )
Linking ./dist/setup/setup ...
Configuring parsers-0.12.5...
$ cabal test doctests
Preprocessing test suite 'doctests' for parsers-0.12.5..
Building test suite 'doctests' for parsers-0.12.5..

<no location info>: warning: [-Wmissing-home-modules]
    These modules are needed for compilation but not listed in your .cabal file's other-modules: Build_doctests
[1 of 2] Compiling Build_doctests   ( dist/build/doctests/autogen/Build_doctests.hs, dist/build/doctests/doctests-tmp/Build_doctests.o )
[2 of 2] Compiling Main             ( tests/doctests.hs, dist/build/doctests/doctests-tmp/Main.o )

<no location info>: warning: [-Wmissing-home-modules]
    These modules are needed for compilation but not listed in your .cabal file's other-modules: Build_doctests
Linking dist/build/doctests/doctests ...
Running 1 test suites...
Test suite doctests: RUNNING...

<command-line>:7:0: error:
     fatal error: dist/build/autogen/cabal_macros.h: No such file or directory
compilation terminated.
-i
-i/home/rgscott/Documents/Hacking/Haskell/parsers-0.12.5/dist/build/autogen
-i/home/rgscott/Documents/Hacking/Haskell/parsers-0.12.5/dist/build
-i/home/rgscott/Documents/Hacking/Haskell/parsers-0.12.5/src
-hide-all-packages
-package-db=dist/package.conf.inplace
-optP-include
-optPdist/build/autogen/cabal_macros.h
-package-id=base-4.10.0.0
-package-id=base-orphans-0.6-9iPDLrUqQ5yHb9fb9OUZ9L
-package-id=charset-0.3.7.1-GkbAILH2Vj9HXMMGXPSeNg
-package-id=containers-0.5.10.2
-package-id=parsec-3.1.11-DPgnR92AWEaFOaixmwipet
-package-id=attoparsec-0.13.1.0-2xtipSJ3oFDBfLwedqccX
-package-id=text-1.2.2.2-EGUst8sqNAZCw1xLPcmcMH
-package-id=transformers-0.5.2.0
-package-id=mtl-2.2.1-19EL8AGBsN3DnnOhrC9xY3
-package-id=scientific-0.3.5.1-L0qYzdp0wpz8rO8gircNSR
-package-id=unordered-containers-0.2.8.0-HVcKYx0GQoVIIbKuoCcJbx
-package-id=bytestring-0.10.8.2
-package-id=directory-1.3.0.2
-package-id=doctest-0.12.0-JcuLvnckae744xvqjXdMWF
-package-id=filepath-1.4.1.2
-package-id=QuickCheck-2.10.0.1-DTIBC3CyU6p3h4xSnOjkg1
-package-id=quickcheck-instances-0.3.16-A15GUudjPiB6fV2AyfxwM5
Text.Parser.Char
Text.Parser.Combinators
Text.Parser.LookAhead
Text.Parser.Permutation
Text.Parser.Expression
Text.Parser.Token
Text.Parser.Token.Style
Text.Parser.Token.Highlight
doctests: `gcc' failed in phase `C pre-processor'. (Exit code: 1)
Test suite doctests: FAIL
Test suite logged to: dist/test/parsers-0.12.5-doctests.log
0 of 1 test suites (0 of 1 test cases) passed.

The culprit appears to be that the doctests test suite in parsers lacks an explicit dependency on the parsers library. If I add that, then cabal test doctests works as expected.

Currently, we advise users in the documentation to depend on the library in a doctests test suite only in the event that one uses cbits, but perhaps we should always be advising this?

Configuring a Setup.hs script without cabal-doctest installed

This isn't an issue when using cabal-doctest so much as it is an issue when not using cabal-doctest. Or, to put it it more clearly, we have several backwards-compatible Setup scripts which guard against the presence of cabal-doctest like so:

#if MIN_VERSION_cabal_doctest(1,0,0)
-- This should be the code you use with Cabal-1.24 or later
import Distribution.Extra.Doctest ( defaultMainWithDoctests )
#else
-- Backported cabal-doctest code
-- In theory, this code should only ever be reachable with old versions of Cabal
#endif

main :: IO ()
main = defaultMainWithDoctests "doctests"

As I put in the comments above, there's a bit of an assumption that when you have a recent version of Cabal installed, you always go down one code path, and when you have an older Cabal, you go down the other, legacy code path. When installing a package that uses this sort of script, this assumption should always hold, since your build tool will make sure you've installed cabal-doctest if you require it (i.e., if you're using a recent Cabal).

But ekmett/lens#721 revealed that this assumption doesn't always hold when you simply configure a package. Here's a simple way to illustrate the problem:

$ /opt/cabal/2.0/bin/cabal get distributive
Unpacking to distributive-0.5.2/
$ cd distributive-0.5.2/
$ /opt/cabal/2.0/bin/cabal sandbox init
$ /opt/cabal/2.0/bin/cabal configure -w /opt/ghc/8.2.1/bin/ghc
Resolving dependencies...
Warning: solver failed to find a solution:
Could not resolve dependencies:
trying: distributive-0.5.2:+tagged
unknown package: tagged (dependency of distributive-0.5.2:+tagged)
After searching the rest of the dependency tree exhaustively, these were the
goals I've had most trouble fulfilling: distributive, distributive-0.5.2:flag,
taggedTrying configure anyway.
[1 of 1] Compiling Main             ( dist/setup/setup.hs, dist/setup/Main.o )

dist/setup/setup.hs:103:7: error:
    • Couldn't match expected type ‘IO ()’
                  with actual type ‘String -> IO ()’
    • In a stmt of a 'do' block:
        rewriteFile (testAutogenDir </> "Build_doctests.hs")
          $ unlines
              ["module Build_doctests where", "", "pkgs :: [String]",
               "pkgs = " ++ (show $ formatDeps $ testDeps libcfg suitecfg), ....]
      In the second argument of ‘($)’, namely
        ‘do let testAutogenDir = autogenComponentModulesDir lbi suitecfg
            createDirectoryIfMissingVerbose verbosity True testAutogenDir
            rewriteFile (testAutogenDir </> "Build_doctests.hs")
              $ unlines ["module Build_doctests where", "", ....]’
      In the expression:
        when (testName suite == fromString testsuiteName)
          $ do let testAutogenDir = autogenComponentModulesDir lbi suitecfg
               createDirectoryIfMissingVerbose verbosity True testAutogenDir
               rewriteFile (testAutogenDir </> "Build_doctests.hs")
                 $ unlines ["module Build_doctests where", "", ....]
    |
103 |       rewriteFile (testAutogenDir </> "Build_doctests.hs") $ unlines
    |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

dist/setup/setup.hs:103:20: error:
    • Couldn't match type ‘[Char]’
                     with ‘Distribution.Verbosity.Verbosity’
      Expected type: Distribution.Verbosity.Verbosity
        Actual type: FilePath
    • In the first argument of ‘rewriteFile’, namely
        ‘(testAutogenDir </> "Build_doctests.hs")’
      In the expression:
        rewriteFile (testAutogenDir </> "Build_doctests.hs")
      In a stmt of a 'do' block:
        rewriteFile (testAutogenDir </> "Build_doctests.hs")
          $ unlines
              ["module Build_doctests where", "", "pkgs :: [String]",
               "pkgs = " ++ (show $ formatDeps $ testDeps libcfg suitecfg), ....]
    |
103 |       rewriteFile (testAutogenDir </> "Build_doctests.hs") $ unlines
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

dist/setup/setup.hs:168:18: error:
    • Couldn't match type ‘Distribution.Types.MungedPackageId.MungedPackageId’
                     with ‘Distribution.Types.PackageId.PackageIdentifier’
      Expected type: [(UnitId, PackageId)]
        Actual type: [(UnitId,
                       Distribution.Types.MungedPackageId.MungedPackageId)]
    • In the expression:
        nub $ componentPackageDeps xs ++ componentPackageDeps ys
      In an equation for ‘testDeps’:
          testDeps xs ys
            = nub $ componentPackageDeps xs ++ componentPackageDeps ys
    |
168 | testDeps xs ys = nub $ componentPackageDeps xs ++ componentPackageDeps ys
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Ack! Things went wrong here because we have a recent Cabal installed, but since we were in a fresh sandbox, we also didn't have cabal-doctest installed. Therefore we went down the wrong (legacy) codepath, and unsurprisingly, the more recent Cabal API doesn't work against the legacy code.

My question is: is there anything we can do here to lessen the surprise for users who might run into this situation? Obviously, we can't keep updating the legacy code in these Setup scripts forever to support new versions of Cabal—after all, we created cabal-doctest in the first place to avoid having to do that! Perhaps we should emit a large, scary GHC warning whenever you go down the legacy codepath with a recent Cabal? Or is there a more graceful way to handle this?

Add constraint `Cabal >=1.24` to example in README

@phadej, thanks for this tool, it solved my problem to communicate the preprocessor(happy/alex)-generated files to my doctests-main in a robust way.

In the light of your explanation at haskell-CI/haskell-ci#81 (comment)

custom-setup section is supported with Cabal >= 1.24

it may make sense to include the constraint Cabal >=1.24 in your simple example and in the README.
At least this constraint make my build succeed:

I can prepare a PR.

Could not find module ‘Distribution.Extra.Doctest’ --

If I want to compile for example comonad or parsers I get the following errors:

comonad-5.0.2-9P9ZbrB2lYo8asSaS7XXK9 failed during the configure step. The
exception was:
dieVerbatim: user error (cabal: '/usr/bin/ghc' exited with an error:

/tmp/cabal-tmp-6837/comonad-5.0.2/dist/setup/setup.hs:12:1: error:
Could not find module ‘Distribution.Extra.Doctest’
There are files missing in the ‘cabal-doctest-1.0.2’ package,
try running 'ghc-pkg check'.
Use -v to see a list of the files searched for.
|
12 | import Distribution.Extra.Doctest ( defaultMainWithDoctests )
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)

I'm using manjaro-linux,

My versions are:
cabal-install version 2.0.0.0
compiled using version 2.0.0.2 of the Cabal library
The Glorious Glasgow Haskell Compilation System, version 8.2.1

Due to this error many packages that Idris is using are not buildable.

I tried to use the patch from issue #19, but it did not work, still the same error.

Build failure with GHC 9.2 alpha-2

I tried GHC 9.2 alpha-2 on cabal-doctest. Building fails with:

$ cabal install cabal-doctest --allow-newer
Warning: Unknown/unsupported 'ghc' version detected (Cabal 3.5.0.0 supports
'ghc' version < 9.1): /usr/local/bin/ghc is version 9.2.0.20210422
Warning: Unknown/unsupported 'ghc' version detected (Cabal 3.5.0.0 supports
'ghc' version < 9.1): /usr/local/bin/ghc is version 9.2.0.20210422
Resolving dependencies...
Build profile: -w ghc-9.2.0.20210422 -O1
In order, the following will be built (use -v for more details):
 - cabal-doctest-1.0.8 (lib) (requires build)
Configuring library for cabal-doctest-1.0.8..
Preprocessing library for cabal-doctest-1.0.8..
Building library for cabal-doctest-1.0.8..
[1 of 1] Compiling Distribution.Extra.Doctest ( src/Distribution/Extra/Doctest.hs, dist/build/Distribution/Extra/Doctest.o, dist/build/Distribution/Extra/Doctest.dyn_o )

src/Distribution/Extra/Doctest.hs:329:18: error:
    • Couldn't match type: Distribution.Utils.Path.SymbolicPath
                             Distribution.Utils.Path.PackageDir
                             Distribution.Utils.Path.SourceDir
                     with: [Char]
      Expected: [FilePath]
        Actual: [Distribution.Utils.Path.SymbolicPath
                   Distribution.Utils.Path.PackageDir
                   Distribution.Utils.Path.SourceDir]
    • In the second argument of ‘(:)’, namely ‘hsSourceDirs compBI’
      In the second argument of ‘(:)’, namely
        ‘(distPref ++ "/build") : hsSourceDirs compBI’
      In the second argument of ‘($)’, namely
        ‘compAutogenDir : (distPref ++ "/build") : hsSourceDirs compBI’
    |
329 |                : hsSourceDirs compBI
    |                  ^^^^^^^^^^^^^^^^^^^
cabal: Failed to build cabal-doctest-1.0.8.

Mention possible need to put library itself in test-suite's build-depends to avoid a bug?

I tripped over this when upgrading bits to use cabal-doctest. If I uses the following build-depends in the doctests test-suite:

    build-depends:
      base,
      doctest >= 0.11.1 && < 0.12

Then cabal test fails!

$ cabal configure --enable-tests
Resolving dependencies...
[1 of 1] Compiling Main             ( dist/setup/setup.hs, dist/setup/Main.o )
Linking ./dist/setup/setup ...
Configuring bits-0.5...
$ cabal test
Preprocessing test suite 'doctests' for bits-0.5...
[1 of 2] Compiling Build_doctests   ( dist/build/autogen/Build_doctests.hs, dist/build/doctests/doctests-tmp/Build_doctests.o )
[2 of 2] Compiling Main             ( tests/doctests.hs, dist/build/doctests/doctests-tmp/Main.o )
Linking dist/build/doctests/doctests ...
Running 1 test suites...
Test suite doctests: RUNNING...
### Failure in /home/rgscott/Documents/Hacking/Haskell/bits/src/Data/Bits/Coded.hs:37: expression `runPutL . runEncode $ encode (Unary 1) >> flush'
expected: "\128"
 but got: 
          ByteCodeLink: can't find label
          During interactive linking, GHCi couldn't find the following symbol:
            debruijn_lsb64
          This may be due to you not asking GHCi to load extra object files,
          archives or DLLs needed by your current session.  Restart GHCi, specifying
          the missing library using the -L/path/to/object/dir and -lmissinglibname
          flags, or simply by naming the relevant files on the GHCi command line.
          Alternatively, this link failure might indicate a bug in GHCi.
          If you suspect the latter, please send a bug report to:
            [email protected]
          
Examples: 2  Tried: 1  Errors: 0  Failures: 1
-i/home/rgscott/Documents/Hacking/Haskell/bits/dist/build/autogen
-i/home/rgscott/Documents/Hacking/Haskell/bits/src
-hide-all-packages
-package-db=dist/package.conf.inplace
-optP-include
-optPdist/build/autogen/cabal_macros.h
-package-id=base-4.9.1.0
-package-id=bytes-0.15.2-D9QILzOcmbYG6aOpzK9Wp5
-package-id=mtl-2.2.1-BLKBelFsPB3BoFeSWSOYj6
-package-id=transformers-0.5.2.0
-package-id=doctest-0.11.1-A85NJCxz1CxFJY7u28Ku7R
Data.Bits.Coding
Data.Bits.Coded
Data.Bits.Extras
Test suite doctests: FAIL
Test suite logged to: dist/test/bits-0.5-doctests.log
0 of 1 test suites (0 of 1 test cases) passed.

That's because cabal hadn't compiled debruijn.c (which defines debruijn_lsb64) into an object file! It turns out that bytes (another library like bits which has a C dependency) doesn't suffer from this issue because the doctests test-suite adds a dependency on the bytes library. Or, to use the earlier example of bits, if you change the build-depends in the doctests test-suite to this:

    build-depends:
      base,
      bits,
      doctest >= 0.11.1 && < 0.12

Then cabal test works without issue, because the library dependency forces cabal to build the library (and therefore debruijn.c) beforehand:

rgscott@gearloose:~/.../Hacking/Haskell/bits$ cabal configure --enable-tests
Resolving dependencies...
[1 of 1] Compiling Main             ( dist/setup/setup.hs, dist/setup/Main.o )
Linking ./dist/setup/setup ...
Configuring bits-0.5...
rgscott@gearloose:~/.../Hacking/Haskell/bits$ cabal test
Preprocessing library bits-0.5...
[1 of 3] Compiling Data.Bits.Extras ( src/Data/Bits/Extras.hs, dist/build/Data/Bits/Extras.o )
[2 of 3] Compiling Data.Bits.Coding ( src/Data/Bits/Coding.hs, dist/build/Data/Bits/Coding.o )
[3 of 3] Compiling Data.Bits.Coded  ( src/Data/Bits/Coded.hs, dist/build/Data/Bits/Coded.o )
Preprocessing test suite 'doctests' for bits-0.5...
[1 of 2] Compiling Build_doctests   ( dist/build/autogen/Build_doctests.hs, dist/build/doctests/doctests-tmp/Build_doctests.o )
[2 of 2] Compiling Main             ( tests/doctests.hs, dist/build/doctests/doctests-tmp/Main.o )
Linking dist/build/doctests/doctests ...
Running 1 test suites...
Test suite doctests: RUNNING...
Test suite doctests: PASS
Test suite logged to: dist/test/bits-0.5-doctests.log
1 of 1 test suites (1 of 1 test cases) passed.

Perhaps we should mention this gotcha in the cabal-doctest README?

running cabal-doctest on code that does FFI

I'm trying to run cabal-doctest on a package that contains code that does FFI, but I am getting an error about an Illegal foreign declaration:

$ stack test mypackage:mypackage-doctests

mypackage-0.1.0.0: test (suite: mypackage-doctests)

-i
-i/home/user/git/mypackage/.stack-work/dist/x86_64-linux-nopie/Cabal-2.0.1.0/build/autogen
-i/home/user/git/mypackage/.stack-work/dist/x86_64-linux-nopie/Cabal-2.0.1.0/build
-i/home/user/git/mypackage/src
-I/home/user/git/mypackage/include
-hide-all-packages
-no-user-package-db
-package-db=/home/user/.stack/snapshots/x86_64-linux-nopie/lts-10.1/8.2.2/pkgdb
-package-db=/home/user/git/mypackage/.stack-work/install/x86_64-linux-nopie/lts-10.1/8.2.2/pkgdb
-package-db=.stack-work/dist/x86_64-linux-nopie/Cabal-2.0.1.0/package.conf.inplace
-optP-include
-optP.stack-work/dist/x86_64-linux-nopie/Cabal-2.0.1.0/build/autogen/cabal_macros.h
-optP-std=c++11
-optP-g
-XCPP
-XDeriveDataTypeable
-XEmptyDataDecls
-XFlexibleContexts
-XFlexibleInstances
-XGADTs
-XGeneralizedNewtypeDeriving
-XMultiParamTypeClasses
-XNoImplicitPrelude
-XNoMonomorphismRestriction
-XOverloadedStrings
-XQuasiQuotes
-XRecordWildCards
-XStrict
-XStrictData
-XTemplateHaskell
-XTupleSections
-XTypeFamilies
-XViewPatterns
-package-id=base-4.10.1.0
-package-id=bytestring-0.10.8.2
-package-id=text-1.2.2.2-9VTsh6V7U7hpagw2HDvpZ
-package-id=yaml-0.8.25-4pReAuIriLM49ZzXYo2cQB
-package-id=directory-1.3.0.2
-package-id=aeson-1.2.3.0-ugexjDh4BlCBSU1ypSCIP
-package-id=monad-logger-0.3.26-HML9yY1QV17FFM52i6WgV1
-package-id=transformers-0.5.2.0
-package-id=unordered-containers-0.2.8.0-6Q8cKU0tfULGVDjEZYkMDG
-package-id=containers-0.5.10.2
-package-id=vector-0.12.0.1-IUGn3M9mkBh8CyXcBnfTR4
-package-id=multimap-1.2.1-3GJfs4zkCkW9B3Fr8In11Z
-package-id=time-1.8.0.2
-package-id=stm-2.4.4.1-6AExGOUG8NB2Rzejnay0ww
-package-id=mtl-2.2.1-DscMMmDQUE6GBfOSl4qMUH
-package-id=async-2.1.1.1-H3j65XcXMHtBvmNwGCQ80G
-package-id=machinecell-3.3.2-FExSUJRpVnSzomdqOFNnQ
-package-id=repa-3.4.1.3-GEnIKEZpCV61MI3ixuOgDr
-package-id=base64-bytestring-1.0.0.1-8MlzMz2YH3lCqJ4GOwL1Be
-package-id=JuicyPixels-3.2.9.1-BVWqbGS41WB1algPpFp0MX
-package-id=JuicyPixels-repa-0.7.1.0-HdCUFuhlE2KLkfygLx9oIP
-package-id=protocol-buffers-2.4.6-47f03hB2WvJAHf1FpvcHy
-package-id=split-0.2.3.2-7cayvoeRj5XIrIBUB58mMy
-package-id=binary-0.8.5.1
-package-id=binary-orphans-0.1.8.0-8PLkSxRGG1kBUzNCD3xKty
-package-id=random-1.1-LLUGZ7T9DqQ5vN0Jbcd0We
-package-id=random-shuffle-0.0.4-KDdg8pdjcYGELQehGYXr0X
-package-id=network-2.6.3.2-Elf6Dxkfz0iKjb1zv5eBTP
-package-id=network-ip-0.3.0.2-B85Xtbe1mSN1xAVqRKIk2F
-package-id=MissingH-1.4.0.1-ImyRV7T8Qr2ErDeNfuBTMk
-package-id=fft-0.1.8.6-HQVRSUFjKmx2zA2dGyOfvA
-package-id=carray-0.1.6.8-HwQa2zABqEdJhijAqYXaoj
-package-id=csv-conduit-0.6.7-1fdundjwG291W8v1e6PJ81
-package-id=classy-prelude-1.3.1-ANeqbljMhXs5dVxxtSFDQi
-package-id=doctest-0.13.0-FhL1bQZiTYWBiul8DyT6Aa
-package=mypackage-0.1.0.0
-package-id=QuickCheck-2.10.1-Ewacc04OJICEFWthkI3IgS
-package-id=template-haskell-2.12.0.0
....
MyModule
....


/home/user/git/mypackage/src/MyModule.hs:44:1: error:
    • Illegal foreign declaration: requires unregisterised, llvm (-fllvm) or native code generation (-fasm)
    • When checking declaration:
        foreign export ccall "my_func" my_func
          :: IO Bool
   |
44 | foreign export ccall my_func :: IO Bool
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Examples: 0  Tried: 0  Errors: 0  Failures: 0

mypackage-0.1.0.0: Test suite mypackage-doctests passed

Here's the mypackage-doctests stanza out of the cabal file:

test-suite mypackage-doctests
    type:                exitcode-stdio-1.0
    main-is:             doctests.hs
    hs-source-dirs:      test
    build-depends:       base
                       , doctest >= 0.11.3 && <0.14
                       , mypackage
                       -- QuickCheck and template-haskell are used when
                       -- actually running the the doctest exectuable.  They
                       -- must not be removed.
                       , QuickCheck ==2.10.*
                       , template-haskell

    ghc-options:         -Wall -threaded
    default-language:    Haskell2010

The DocTest.hs file is identical to the one defined here. The Setup.hs file in the repository is identical to the one defined here.


Is it possible to run doctests on code that does FFI?

If not, is there an easy way to tell cabal-doctest that I don't actually need that file tested, but please test the other ones?

How to handle code involving unboxed tuples

When testing the structs library with cabal-doctest, I ran into this error:

Test suite doctests: RUNNING...
Error: bytecode compiler can't handle unboxed tuples.
  Possibly due to foreign import/export decls in source.
  Workaround: use -fobject-code, or compile this module to .o separately.

Or, if you want a more minimal example than structs, you can just use this simple file that uses UnboxedTuples:

{-# LANGUAGE UnboxedTuples #-}
module Foo where

-- | A simple test involving tuples.
-- >>> foo
-- 3
foo :: Int
foo = let (# x, y #) = (# 1, 2 #) in x + y
$ doctest Foo.hs
Error: bytecode compiler can't handle unboxed tuples and sums.
  Possibly due to foreign import/export decls in source.
  Workaround: use -fobject-code, or compile this module to .o separately.
### Failure in Foo.hs:5: expression `foo'
expected: "3"
 but got: ""
          "\ESC[0m\ESC[;1m<interactive>:23:1: \ESC[0m\ESC[;1m\ESC[31merror:\ESC[0m\ESC[;1m Variable not in scope: foo\ESC[0m\ESC[0m"
          "\ESC[0m\ESC[0m\ESC[0m\ESC[0m"
Examples: 1  Tried: 1  Errors: 0  Failures: 1

A workaround is to pass -fobject-code to doctest, and indeed, I applied this fix manually to doctests.hs in structs to work around the issue in that library. But this feels somewhat unsatisfactory, since now I have to deviate from the template which works for 99% of libraries.

My question is: is there a way cabal-doctest could detect the use of UnboxedTuples automatically and bring in -fobject-code when needed? Or, if this isn't a good idea, should we instead include a section in the README warning about the perils of UnboxedTuples?

Dropping maintenance, takeovers welcome

Dear cabal-doctest users,
I am dropping my dependency on cabal-doctest and thus have no material interest in maintaining it.
So I am happy for a new maintainer to take over.
Express your interest in the comments below, maybe with a short motivating statement.

Consider generating `Build_*` modules for more than just test-suite targets

I'm hijacking the purpose of cabal-doctest somewhat by using it to initialise a GHC API session to compile stuff to Core (on which then I run a custom strictness analysis). I've been using cabal-toolkit before, but I realised I need cabal-doctest for my doctests anyway, and using it for my test suite to initialise the GHC API seems to work quite well. The only problem is that I can't use it to compile my benchmarks, because it's not a test-suite.

As I said in #59, this is probably not how you imagined cabal-doctest to be used, but it seems like a waste of maintenance effort to just target doctesting in particular. So maybe just put the Build_* module in global-autogen?

Give example of testsuite driver file

This repo leaves out a description of (arguably) the most important part: how to actually use the generated build module in a testsuite! I don't think this needs to be much, just a simple description in a README that shows this code:

module Main where

import Build_doctests (flags, pkgs, module_sources)
import Data.Foldable (traverse_)
import Test.DocTest

main :: IO ()
main = do
    traverse_ putStrLn args
    doctest args
  where
    args = flags ++ pkgs ++ module_sources

"Attempting to use module .. which is not loaded" error

I am trying to add doctest support for the linear-base library, which uses GHC 9.

The usual cabal-doctests setup return a bunch of errors along the lines of:

/home/utdemir/workspace/github.com/tweag/linear-base/src/Foreign/Marshal/Pure.hs:22: failure in expression `import Data.Unrestricted.Linear'
expected: 
 but got: 
          ^
          <interactive>:1:1: error:
              attempting to use module ‘main:Data.Unrestricted.Linear’ (/home/utdemir/workspace/github.com/tweag/linear-base/src/Data/Unrestricted/Linear.hs) which is not loaded

However, a command like: cabal exec -- doctest -package linear-base src/**/*.hs succeeds.

I found out that the issue happens because of the -isrc/ flag introduced by cabal-doctest. I think the reason is GHCi trying to use the modules introduced by -isrc/, rather than the library in package-db, which are not loaded yet.

Here is a slightly modified version of flags ++ pkgs ++ module_sources returned by cabal-doctests:

[ "-i",
  "-i/home/utdemir/workspace/github.com/tweag/linear-base/dist-newstyle/build/x86_64-linux/ghc-9.0.1/linear-base-0.1.1/build/autogen",
  "-i/home/utdemir/workspace/github.com/tweag/linear-base/dist-newstyle/build/x86_64-linux/ghc-9.0.1/linear-base-0.1.1/build",
  -- "-i/home/utdemir/workspace/github.com/tweag/linear-base/src",
  "-package-env=-",
  "-hide-all-packages",
  "-no-user-package-db",
  "-package-db=/home/utdemir/.cabal/store/ghc-9.0.1/package.db",
  "-package-db=/home/utdemir/workspace/github.com/tweag/linear-base/dist-newstyle/packagedb/ghc-9.0.1",
  "-package-db=/home/utdemir/workspace/github.com/tweag/linear-base/dist-newstyle/build/x86_64-linux/ghc-9.0.1/linear-base-0.1.1/package.conf.inplace",
  "-optP-include",
  "-optP/home/utdemir/workspace/github.com/tweag/linear-base/dist-newstyle/build/x86_64-linux/ghc-9.0.1/linear-base-0.1.1/build/autogen/cabal_macros.h",
  "-package-id=base-4.15.0.0",
  "-package-id=containers-0.6.4.1",
  "-package-id=ghc-prim-0.7.0",
  "-package-id=hashable-1.3.1.0-a92c75af1fea2a65b2365eb935f4a4802dac5057158f909650e1011dc3a01a23",
  "-package-id=primitive-0.7.1.0-f003beddf0fc9d04aaf34af7ddb2d21771469f6e4225ce5a793266b89bfbb923",
  "-package-id=storable-tuple-0.0.3.3-7f76b1fd43fb107740fd5c1db4bf4e5a25de9b66b13ca4f53845700662363649",
  "-package-id=text-1.2.4.1",
  "-package-id=transformers-0.5.6.2",
  "-package-id=vector-0.12.3.0-8cc976946fcdbc43a65d82e2ca0ef40a7bb90b17e6cc65c288a8b694f5ac3127",
  "-package-id=doctest-0.18.1-ba4233742e9e8602bcf50dd58a39029934715a541fa504ea2e97f9de2502fb2a",
  "-package=linear-base",
  "src/Foreign/Marshal/Pure.hs"
  -- other modules omitted
]

The above list of arguments passes the tests, when the -i command including the source directory is commented out. When it is uncommented, the doctests fail with the above error. Also keep in mind that I have to change the list to use the module path instead of the name (src/Foreign/Marshal/Pure.hs instead of Foreign.Marshall.Pure).

To reproduce the issue (apologies that I couldn't find a smaller example):

$ # obtain GHC 9.0.1 and cabal-install 3.4.
$ git clone https://github.com/tweag/linear-base; cd linear-base; git checkout 9b477e4
$ cabal test doctests

Relevant linear-base issue: tweag/linear-base#317

cabal-doctest support for .hsc files?

I encountered an interesting problem recently: cabal-doctest doesn't seem to mix well with hsc2hs. In particular, if you have a library with any .hsc file (regardless of whether it actually contains any doctests), trying to run doctest on that library via cabal-doctest will always fail with an error like this:

$ cabal test
Preprocessing test suite 'doctests' for distributive-0.5.2...
Preprocessing library distributive-0.5.2...
Preprocessing test suite 'spec' for distributive-0.5.2...
Running 2 test suites...
Test suite doctests: RUNNING...

<no location info>: error:
    module ‘Data.Distributive’ is a package module
-i/home/rgscott/Documents/Hacking/Haskell/distributive-0.5.2/dist/build/autogen
-i/home/rgscott/Documents/Hacking/Haskell/distributive-0.5.2/src
-hide-all-packages
-package-db=dist/package.conf.inplace
-optP-include
-optPdist/build/autogen/cabal_macros.h
-package-id=base-4.9.1.0
-package-id=base-orphans-0.5.4-ABoxiBf7nXc7Qqh66CgYc9
-package-id=tagged-0.8.5-jDBtbBndklGIlXZjVMhpH
-package-id=transformers-0.5.2.0
-package-id=transformers-compat-0.5.1.4-84sV5mkFftgD9qwogvuEDr
-package-id=doctest-0.11.1-Iq0kyguMBZj6Gqp1YR6VOk
Data.Distributive
Data.Distributive.Generic
Test suite doctests: FAIL
Test suite logged to: dist/test/distributive-0.5.2-doctests.log
Test suite spec: RUNNING...
Test suite spec: PASS
Test suite logged to: dist/test/distributive-0.5.2-spec.log
1 of 2 test suites (1 of 2 test cases) passed.

(In that example, I just renamed Data/Distributive.hs to Data/Distributive.hsc from the distributive library for simplicity's sake.)

But it's a bit unfair to put the blame entirely on cabal-doctest here, as really the error comes from doctest itself. If you have any file with an .hsc extension, then directly invoking doctest on it also brings about the error:

$ touch Foo.hsc
$ doctest Foo

<no location info>: error: module ‘Foo’ is a package module

Perhaps this isn't too surprising when you think about it, since we need hsc2hs to preprocess the .hsc file before doctest can operate on the resulting .hs file. One might argue that it should be doctest's job to call hsc2hs if it sees a .hsc file... but should doctest also have knowledge about .chs files (for c2hs) as well? What about other preprocessors? The more one thinks about it, the uglier it sounds to actually implement at the doctest level.

So my thought is: what if we trained cabal-doctest to do this? After all, Cabal knows about hsc2hs, c2hs, etc., so it is in a much better position to deal with this than doctest alone. Of course, actually implementing will assuredly be trickier than I think it will.

Alternatively, we could just wait until cabal doctest is a reality.

Either way, we should think of some way to work around this, as not being able to use hsc2hs and doctest together is pretty annoying.

Make a release

We are prevented from using cabal-doctest because our test suite uses NoImplicitPrelude

Getting can't find soure for Build_doctests error.

Hi,

I followed the doc with the code in Setup.hs taken from the readme. But I am still getting the following error:

setup: can't find source for Build_doctests in test,
.stack-work/dist/x86_64-linux-nix/Cabal-2.4.0.1/build/doctests/autogen,
.stack-work/dist/x86_64-linux-nix/Cabal-2.4.0.1/build/global-autogen

I am using latest stack with Cabal-2.4.0 and cabal-doctests is defined. This is running on nixos 18.03 and ghc-8.6.*. Is there any step missing or any test I could do to see what is missing? Thanks for your help.

Work better with Haskell.nix?

Hi!

I tried to integrate two things new to me recently, Cabal-doctest and Haskell.nix. As you might expect, Doctests are already pretty hacky when it comes to Cabal integration. It only gets trickier when you throw in Nix. Actually, the default way of building Haskell projects with Nix (Nixpkgs) didn't give me a problem with Doctests, even with Cabal-doctest. But Haskell.nix, specifically, causes me issues.

I have a couple ideas on how we might improve the state of things.

Background on Nix and Haskell.nix

I don't know much you already know about Nix and Haskell.nix, but I figured it would be good to spell out some relevant context, even if for others finding this ticket.

In Nix, everything is cached at the level of a Nix derivation. A derivation sets up a very hygenic/controlled environment, with all of the files/folders/permissions/envvars explicitly specified. Then the crank is turned, and specific outputs are cached as the output of the derivation. These can then be used to set up the environment for subsequent derivations. This is important, because it means that transient build artifacts from a previous derivation are not just lying around for the next derivation. They have to be explicitly copied over.

What Haskell.nix does that breaks the normal Cabal-doctest workflow is have a separate derivation for each component in a Cabal project. So the libraries, executables, and test suites each get their own derivation. Furthermore, there is even a separate derivation to run a test that is different from the one that builds the test code. This is kind of nice because it gives developers more control. A developer can make sure all these tests build without actually running the tests. But then, because in Nix everything is cached at the derivation-level, we can run the tests later and use the same artifacts we cached previously. Stack/Cabal users take this for granted, but in Nix we have to make Nix expressions to regain this flexibility, which is one of the reasons the Haskell.nix project exists as a alternative to the default way of building things with Nix (Nixpkgs).

What's going wrong with Haskell.nix + Cabal-doctest

Components are seen only one at a time

The code for Cabal-doctest assumes that most of the flags we need are derived from the library and executable components. But in a Haskell.nix derivation for a test component, there are no library or executable components. They have already been built and made available in the package database provided to the derivation's Cabal run. However, the source code of these components can be made available (obviously needed for the authored doctests themselves).

Right now, Cabal-doctest when running in a Haskell.nix derivation won't output any flags, packages, or module sources. Because these flags are output only for libraries and executables, which are not available when compiling the test component. What would be useful is to get the at least the packages flags (possibly the rest too) from the test component. The code I need to load up in my doctests have already been shuttled into a package database. And I can access this package database for normal dependencies from my doctest. This does mean to list dependencies I need for doctests on my test component, not just the dependencies needed for the doctest fixture. But that seems like an okay compromise to me.

Some references to missing artifacts

All the references to build/ and dist/, but also Build_doctests aren't useful for a Haskell.nix derivation. All of this stuff is in the package database.

Possible solutions (not mutually exclusive)

1. At least output flags for the test component

I made a patch of cabal-doctest that does this (not cleaned up for a PR yet, and not sure if you want to go this direction anyway), and it makes this following Build_doctests.hs file:

module Build_doctests where

import Prelude

data Name = NameLib (Maybe String) | NameExe String deriving (Eq, Show)
data Component = Component Name [String] [String] [String] deriving (Eq, Show)

pkgs_test_doctests :: [String]
pkgs_test_doctests = ["-package-id=base-4.12.0.0","-package-id=base-compat-0.11.0-BTru338SRTmL3dK24Mui3j","-package-id=doctest-0.16.2-6z3pUedJOHdCkF4jrPAXAU","-package=exceptions-checked-0.0.1"]

flags_test_doctests :: [String]
flags_test_doctests = ["-i","-i/build/exceptions-checked/dist/build/doctests/autogen","-i/build/exceptions-checked/dist/build","-i/build/exceptions-checked/test","-i/build/exceptions-checked/test","-package-env=-","-hide-all-packages","-clear-package-db","-package-db=/nix/store/ryxw8lj748jbmvriz3w8k4vw7brnann4-exceptions-checked-0.0.1-test-doctests-config/package.conf.d","-package-db=dist/package.conf.inplace","-optP-include","-optPdist/build/doctests/autogen/cabal_macros.h"]

module_sources_test_doctests :: [String]
module_sources_test_doctests = ["Build_doctests", "Doctest.Checked.Catch","Doctest.Checked.Throw"]

-- [NameLib Nothing]
components :: [Component]
components = []

2. Eliding problematic missing references

Although there's a few references to artifacts that are missing:

  • -i/build/exceptions-checked/dist/build/doctests/autogen
  • -i/build/exceptions-checked/dist/build
  • -i/build/exceptions-checked/test
  • -i/build/exceptions-checked/test
  • -package-db=dist/package.conf.inplace
  • -optPdist/build/doctests/autogen/cabal_macros.h
  • Build_doctests

The only ones that really cause my Haskell.nix build trouble are the two bolded above. The Build_doctests file should be elided from module_sources_test_* always, I think. If we did nothing with the missing dist/package.conf.inplace I can always do a ghc-pkg init dist/package.conf.inplace in my Haskell.nix configuration. It's a small one-liner.

We could possibly make an option to elide the "inplace" package flag. But we'd need this flag for non-Haskell.nix builds. I'd much more prefer to have a static Cabal configuration and source code that works with all builds systems. Small tweaks seem better on the Nix side than that Cabal side.

3. Nothing

I list this for completeness. I kind of understand the argument that Cabal-doctest is complicated enough as it stands. Is Haskell.nix breaking an invariant expected of Cabal?

I'm probably going to point some Haskell.nix developers at this ticket. Maybe there's something about the Haskell.nix implementation that can be changed to allows all the components to show up in the LocalBuildInfo when Setup.hs runs.

Doesn't build with recent ghc-8.2

I've using GHC that I built from the ghc-8.2 branch (version is reported as 8.2.0.20170430) and cabal-doctest fails to build with:

src/Distribution/Extra/Doctest.hs:249:18: error:
    • Couldn't match type ‘Distribution.Types.MungedPackageId.MungedPackageId’
                     with ‘Distribution.Types.PackageId.PackageIdentifier’
      Expected type: [(UnitId, PackageId)]
        Actual type: [(UnitId,
                       Distribution.Types.MungedPackageId.MungedPackageId)]
    • In the expression:
        nub $ componentPackageDeps xs ++ componentPackageDeps ys
      In an equation for ‘testDeps’:
          testDeps xs ys
            = nub $ componentPackageDeps xs ++ componentPackageDeps ys
    |
249 | testDeps xs ys = nub $ componentPackageDeps xs ++ componentPackageDeps ys
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Thought you'd like to know.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.