59
votes

Here's a .cabal file:

Name:                myprogram
Version:             0.1
-- blah blah blah
Cabal-version:       >=1.9.2

Executable myprogram
  HS-source-dirs:       src
  Main-is:              Main.hs
  Build-depends:        attoparsec == 0.10.*,
                        base == 4.3.*,
                        -- long long list of packages

Test-Suite test
  HS-source-dirs:       test, src
  Type:                 exitcode-stdio-1.0
  Main-is:              Main.hs
  Build-depends:        attoparsec == 0.10.*,
                        base == 4.3.*,
                        -- long long list of packages
                        QuickCheck == 2.4.*

Is there any way I can replace the long list of build-depends packages for the test suite with "same as for the executable, plus QuickCheck"?

Edit: version information.

  • cabal-dev 0.9
  • cabal-install 0.10.2
  • Cabal library 1.10.2.0
  • GHC 7.0.4
  • Haskell Platform 2011.4.0.0
5
Your cabal file is structured in an unfoavourable way, leading to a lot of re-compilation and also introducing the dependency duplication you mention. You can make your tests/executables depend on your library. See stackoverflow.com/questions/12305970/…. Starting from there, you can move as much into the direction @Toxaris pointed out in his answer, if you like that.nh2
There are plans to add a common field to the .cabal format where e.g. shared build-depends can be specified.sjakobi

5 Answers

10
votes

Since version 2.2 Cabal supports common stanzas, to dedup build info fields: https://cabal.readthedocs.io/en/latest/developing-packages.html#common-stanzas

cabal-version:       2.2
name:                myprogram
version:             0.1
-- blah blah blah

common deps
  build-depends: base ^>= 4.11,
                 -- long long list of packages
  ghc-options: -Wall

library
  import: deps
  exposed-modules: Foo

test-suite tests
  import: deps
  type: exitcode-stdio-1.0
  main-is: Tests.hs
  build-depends: foo
33
votes

Is there any way I can replace the long list of build-depends packages for the test suite with "same as for the executable, plus QuickCheck"?

Not that I know of. However, there is a way to only mention the list of build-depends packages once, by structuring your project into three targets:

  1. a library that contains all your code, and needs the long build-depends list.
  2. an executable that consists of only one file, and depends on base and the library from above.
  3. a test-suite that depends on the library from above, and the testing packages you are using.

Maybe this approach is what indygemma's answer proposes, but the Cabal file proposed there will not quite achieve it, as Norman Ramsey points out in a comment. Here's the main points of what you need in a Cabal file. For a full example that works for me, you can look at this Cabal file.

name: my-program
version: ...

library
  hs-source-dirs: src-lib
  build-depends: base, containers, ...
  exposed-modules: My.Program.Main, ...

executable my-program
  hs-source-dirs: src-exec
  main-is: my-program.hs
  Build-depends: base, my-program

test-suite tests
  type: exitcode-stdio-1.0
  hs-source-dirs: src-test
  main-is: tests.hs
  other-modules: ...
  build-depends: base, my-program, test-framework, ...

Important points:

  • There are three separate source directories for the three targets. This is necessary to stop GHC from recompiling library files when building the other targets.

  • All of the application code is in the library. The executable is just a wrapper, like this:

    import My.Program.Main (realMain)
    main = realMain
    
  • The library exposes all modules that are necessary for testing.

The last point highlights the drawback of this approach: You end up having to expose internal modules. The main benefit of this approach is that you have less duplication in the Cabal file, and maybe more importantly, less duplication in the build process: The library code will be built only once, and then linked into both the executable and the test-suite.

4
votes

You could also consider using hpack instead of writing the .cabal file by hand:

In hpack's package.yaml format, you can specify a common dependencies field whose entries are added to every components' build-depends field when generating the .cabal file.

For example, see hpack's own package.yaml and the generated hpack.cabal.

To start using hpack with an existing package, you can use hpack-convert which will generate the package.yaml from an existing .cabal file.

To create a new package that uses hpack, you can use stack's simple-hpack template like so: stack new mypkg simple-hpack.

If you use stack for development, you don't have to call hpack manually to regenerate the .cabal file from an updated package.yaml – stack will do that automatically.

-1
votes

No easy way:

  • you can use m4 and specify your dependencies once, but then you will need to reprocess your Cabal file through m4 whenever you change it.

  • you can move the code you are testing out to a library, and then specify the library in your Build-depends for the test. That requires you to install a library even just to run the test.

  • You can just not put the test in the cabal file at all. Build it with ghc --make, which will pull in dependencies. But then you lose cabal integration.

-5
votes

There is an optional library section for .cabal files, which solves your problem.

name:              myprogram
version:           0.1
-- blah blah blah
cabal-version:     >=1.9.2

library
    build-depends: attoparsec == 0.10.*
                 , base == 4.3.*
                 -- long long list of packages

executable myprogram
    hs-source-dirs: src
    main-is:        Main.hs

test-suite test
    hs-source-dirs: test, src
    type:           exitcode-stdio-1.0
    main-is:        Main.hs
    build-depends:  QuickCheck == 2.4.*