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
directory, you cannot usually run your project with ghci
and runhaskell
anymore, especially if the executables have helper modules themselves.
What is a recommended project layout that
runhaskell
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
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With