Как создать Cabal-проект на Haskell с библиотекой + исполняемыми файлами, которые по-прежнему работают с runhaskell/ghci?
Если вы объявите библиотеку + исполняемые разделы в файле cabal, избегая двойной компиляции библиотеки, поместив библиотеку в hs-source-dirs
каталог, вы обычно не можете запустить свой проект с ghci
а также runhaskell
больше, особенно если исполняемые файлы сами имеют вспомогательные модули.
Каков рекомендуемый макет проекта, который
- строит только то, что нужно один раз
- позволяет использовать
runhaskell
- имеет чистую структуру без взломов?
2 ответа
Давайте предположим, что у вас есть mylib
библиотека и mylib-commandline
а также mylib-server
исполняемые файлы.
Ты используешь hs-source-dirs
для библиотеки и каждого исполняемого файла, так что каждый имеет свой собственный корень проекта, избегая двойной компиляции:
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
Полный макет каталога:
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
Файл точки входа в виде заглушки mylib-commandline/Main.hs
выглядит так:
module Main where
import qualified Web.Mylib.Server.Main as MylibServer
main :: IO ()
main = MylibServer.main
Вы нуждаетесь в них, потому что executable
должен начинаться с модуля, называемого просто Main
,
Ваш mylib.cabal
выглядит так:
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
создаст библиотеку и два исполняемых файла без двойной компиляции библиотеки, потому что каждый из них по-своему hs-source-dirs
и исполняемые файлы зависят от библиотеки.
Вы все еще можете запустить исполняемые файлы с runghc
из корня вашего проекта, используя -i
переключатель, чтобы сказать, где он будет искать модули (используя :
в качестве разделителя):
runhaskell -isrc:mylib-commandline mylib-commandline/Main.hs
runhaskell -isrc:mylib-server mylib-server/Server.hs
Таким образом, вы можете иметь чистый макет, исполняемые файлы с вспомогательными модулями, и все по-прежнему работает с runhaskell
/runghc
а также ghci
, Чтобы не вводить этот флаг несколько раз, вы можете добавить что-то похожее на
:set -isrc:mylib-commandline:mylib-server
на ваш .ghci
файл.
Обратите внимание, что иногда следует разбить ваш код на отдельные пакеты, например mylib
, mylib-commandline
а также mylib-server
,
Ты можешь использовать cabal repl
запустить ghci с конфигурацией из файла cabal и cabal run
скомпилировать и запустить исполняемые файлы. В отличие от runhaskell
а также ghci
, с помощью cabal repl
а также cabal run
также правильно подбирает зависимости из песочницы клики.