How to reduce duplication in the build-depends fields of a .cabal file?

前端 未结 5 1954
深忆病人
深忆病人 2020-12-04 15:33

Here\'s a .cabal file:

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

Executable myprogram
  HS-s         


        
相关标签:
5条回答
  • 2020-12-04 15:43

    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
    
    0 讨论(0)
  • 2020-12-04 15:47

    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.

    0 讨论(0)
  • 2020-12-04 15:52

    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.

    0 讨论(0)
  • 2020-12-04 15:56

    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.

    0 讨论(0)
  • 2020-12-04 15:57

    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.*
    
    0 讨论(0)
提交回复
热议问题