How to make a Haskell cabal project with library+executables that still run with runhaskell/ghci?

后端 未结 2 1428
别跟我提以往
别跟我提以往 2020-12-07 11:04

If you declare a library + executable sections in a cabal file while avoiding double compilation of the library by putting the library into a hs-source-dirs dir

相关标签:
2条回答
  • 2020-12-07 11:36

    You can use cabal repl to start ghci with the configuration from the cabal file and cabal run to compile and run the executables. Unlike runhaskell and ghci, using cabal repl and cabal run also picks up dependencies from cabal sandboxes correctly.

    0 讨论(0)
  • 2020-12-07 11:37

    Let's assume you have a mylib library, and mylib-commandline and mylib-server executables.

    You use hs-source-dirs for the library and each executable so that each has their own project root, avoiding double compilation:

    mylib/                      # Project root
      mylib.cabal
      src/                      # Root for the library
      tests/
      mylib-commandline/        # Root for the command line utility + helper modules
      mylib-server/             # Root for the web service + helper modules
    

    Full directory layout:

    mylib/                      # Project root
      mylib.cabal
      src/                      # Root for the library
        Web/
          Mylib.hs              # Main library module
          Mylib/
            ModuleA             # Mylib.ModuleA
            ModuleB             # Mylib.ModuleB
      tests/
        ...
      mylib-commandline/        # Root for the command line utility
        Main.hs                 # "module Main where" stub with "main = Web.Mylib.Commandline.Main.main"
        Web/
          Mylib/
            Commandline/
              Main.hs           # CLI entry point
              Arguments.hs      # Programm command line arguments parser
      mylib-server/             # Root for the web service
        Server.hs               # "module Main where" stub with "main = Web.Mylib.Server.Main.main"
        Web/
          Mylib/
            Server/
              Main.hs           # Server entry point
              Arguments.hs      # Server command line arguments parser
    

    The stub-like entry point file mylib-commandline/Main.hs looks like this:

    module Main where
    
    import qualified Web.Mylib.Server.Main as MylibServer
    
    main :: IO ()
    main = MylibServer.main
    

    You need them because an executable must start on a module simply called Main.

    Your mylib.cabal looks like this:

    library
      hs-source-dirs:   src
      exposed-modules:
        Web.Mylib
        Web.Mylib.ModuleA
        Web.Mylib.ModuleB
      build-depends:
          base >= 4 && <= 5
        , [other dependencies of the library]
    
    executable mylib-commandline
      hs-source-dirs:   mylib-commandline
      main-is:          Main.hs
      other-modules:
        Web.Mylib.Commandline.Main
        Web.Mylib.Commandline.Arguments
      build-depends:
          base >= 4 && <= 5
        , mylib
        , [other depencencies for the CLI]
    
    executable mylib-server
      hs-source-dirs:   mylib-server
      main-is:          Server.hs
      other-modules:
        Web.Mylib.Server.Main
      build-depends:
          base >= 4 && <= 5
        , mylib
        , warp >= X.X
        , [other dependencies for the server]
    

    cabal build will build the library and the two executables without double compilation of the library, because each is in their own hs-source-dirs and the executables depend on the library.

    You can still run the executables with runghc from your project root, using the -i switch to tell where it shall look for modules (using : as separator):

    runhaskell -isrc:mylib-commandline mylib-commandline/Main.hs
    
    runhaskell -isrc:mylib-server mylib-server/Server.hs
    

    This way, you can have a clean layout, executables with helper modules, and everything still works with runhaskell/runghc and ghci. To avoid typing this flag repeatedly, you can add something similar to

    :set -isrc:mylib-commandline:mylib-server
    

    to your .ghci file.


    Note that sometimes should split your code into separate packages, e.g. mylib, mylib-commandline and mylib-server.

    0 讨论(0)
提交回复
热议问题