summoner-2.2.0.0: src/Summoner/Template/Cabal.hs
{- |
Module : Summoner.Template.Cabal
Copyright : (c) 2017-2026 Kowainik
SPDX-License-Identifier : MPL-2.0
Maintainer : Kowainik <xrom.xkov@gmail.com>
Stability : Stable
Portability : Portable
@.cabal@ file template.
-}
module Summoner.Template.Cabal
( cabalFile
) where
import Colourista (indent)
import Summoner.CustomPrelude (CustomPrelude (..))
import Summoner.Default (defaultCabalVersion)
import Summoner.GhcVer (cabalBaseVersions, showGhcVer)
import Summoner.License (LicenseName (..))
import Summoner.Settings (Settings (..))
import Summoner.Text (intercalateMap, packageToModule)
import Summoner.Tree (TreeFs (..))
import qualified Data.Text as T
-- | Creates a `.cabal` file from the given 'Settings'.
cabalFile :: Settings -> TreeFs
cabalFile Settings{..} = File (toString settingsRepo ++ ".cabal") cabalFileContent
where
cabalFileContent :: Text
cabalFileContent = T.concat
[ cabalHeader
, memptyIfFalse settingsGitHub sourceRepository
, commonStanza
, memptyIfFalse settingsIsLib libraryStanza
, memptyIfFalse settingsIsExe executableStanza
, memptyIfFalse settingsTest testSuiteStanza
, memptyIfFalse settingsBench benchmarkStanza
]
cabalHeader :: Text
cabalHeader = unlines $
[ "cabal-version: " <> defaultCabalVersion
, "name: " <> settingsRepo
, "version: 0.0.0.0"
, "synopsis: " <> settingsDescription
, "description:"
] <> fullDescription <>
[ "homepage: " <> githubUrl | settingsGitHub ] ++
[ "bug-reports: " <> githubBugReports | settingsGitHub ] ++
( "license: " <> licenseName) :
[ "license-file: LICENSE" | settingsLicenseName /= NONE] ++
[ "author: " <> settingsFullName
, "maintainer: " <> settingsFullName <> " <" <> settingsEmail <> ">"
, "copyright: " <> settingsYear <> " " <> settingsFullName ] ++
[ "category: " <> settingsCategories | "" /= settingsCategories ] ++
[ "build-type: Simple"
, "extra-doc-files: README.md"
, " CHANGELOG.md"
, "tested-with: " <> testedGhcs
]
fullDescription :: [Text]
fullDescription =
[ " " <> addDot settingsDescription
, " See " <> readme <> " for more details."
]
where
readme :: Text
readme = if settingsGitHub
then "[README.md](" <> githubUrl <> "#" <> settingsRepo <> ")"
else "README.md"
addDot :: Text -> Text
addDot "" = ""
addDot txt = case T.last txt of
'.' -> txt
_otherChar -> txt <> "."
githubUrl, githubBugReports :: Text
githubUrl = "https://github.com/" <> settingsOwner <> "/" <> settingsRepo
githubBugReports = githubUrl <> "/issues"
licenseName, libModuleName :: Text
licenseName = show settingsLicenseName
libModuleName = packageToModule settingsRepo
testedGhcs :: Text
testedGhcs = intercalateMap
("\n" <> T.replicate 21 " ")
(mappend "GHC == " . showGhcVer)
settingsTestedVersions
sourceRepository :: Text
sourceRepository = unlines
[ ""
, "source-repository head"
, " type: git"
, " location: " <> githubUrl <> ".git"
]
commonStanza :: Text
commonStanza = unlines $
[ ""
, "common common-options"
, " build-depends: base " <> baseBounds
]
<> customPrelude
<> ghcOptions
<>
( ""
: " default-language: Haskell2010"
: defaultExtensions
)
baseBounds :: Text
baseBounds = cabalBaseVersions settingsTestedVersions
ghcOptions :: [Text]
ghcOptions = "" : case settingsGhcOptions of
[] -> defaultGhcOptions
x:xs -> " ghc-options: " <> x : map (indent 23 <>) xs
defaultGhcOptions :: [Text]
defaultGhcOptions =
[ " ghc-options: -Wall"
, " -Wcompat"
, " -Widentities"
, " -Wincomplete-uni-patterns"
, " -Wincomplete-record-updates"
, " -Wredundant-constraints"
, " -Wnoncanonical-monad-instances"
, " if impl(ghc >= 8.2)"
, " ghc-options: -fhide-source-paths"
, " if impl(ghc >= 8.4)"
, " ghc-options: -Wmissing-export-lists"
, " -Wpartial-fields"
, " if impl(ghc >= 8.8)"
, " ghc-options: -Wmissing-deriving-strategies"
, " -fwrite-ide-info"
, " -hiedir=.hie"
, " if impl(ghc >= 8.10)"
, " ghc-options: -Wunused-packages"
, " if impl(ghc >= 9.0)"
, " ghc-options: -Winvalid-haddock"
, " if impl(ghc >= 9.2)"
, " ghc-options: -Wredundant-bang-patterns"
, " -Woperator-whitespace"
, " if impl(ghc >= 9.4 && < 9.10)"
, " ghc-options: -Wforall-identifier"
, " if impl(ghc >= 9.4)"
, " ghc-options: -Wredundant-strictness-flags"
, " if impl(ghc >= 9.8)"
, " ghc-options: -Wterm-variable-capture"
, " -Winconsistent-flags"
, " if impl(ghc >= 9.10)"
, " ghc-options: -Wincomplete-record-selectors"
, " -Wdeprecated-type-abstractions"
, " -Wdata-kinds-tc"
, " -Wdefaulted-exception-context"
, " if impl(ghc >= 9.12)"
, " ghc-options: -Wview-pattern-signatures"
]
libraryStanza :: Text
libraryStanza = unlines
[ ""
, "library"
, " import: common-options"
, " hs-source-dirs: src"
, " exposed-modules: " <> libModuleName
]
executableStanza :: Text
executableStanza = unlines $
[ ""
, "executable " <> settingsRepo
, " import: common-options"
, " hs-source-dirs: app"
, " main-is: Main.hs"
]
<> buildDepends
<> rtsOptions
testSuiteStanza :: Text
testSuiteStanza = unlines $
[ ""
, "test-suite " <> settingsRepo <> "-test"
, " import: common-options"
, " type: exitcode-stdio-1.0"
, " hs-source-dirs: test"
, " main-is: Spec.hs"
]
<> buildDepends
<> rtsOptions
benchmarkStanza :: Text
benchmarkStanza = unlines $
[ ""
, "benchmark " <> settingsRepo <> "-benchmark"
, " import: common-options"
, " type: exitcode-stdio-1.0"
, " hs-source-dirs: benchmark"
, " main-is: Main.hs"
]
<> buildDepends
<> rtsOptions
-- @build-depends@ for the repo, only if the library is on.
buildDepends :: [Text]
buildDepends = memptyIfFalse settingsIsLib
[" build-depends: " <> settingsRepo]
rtsOptions :: [Text]
rtsOptions =
[ " ghc-options: -threaded"
, " -rtsopts"
, " -with-rtsopts=-N"
]
customPrelude :: [Text]
customPrelude = case settingsPrelude of
Nothing -> []
Just CustomPrelude{..} ->
[ " , " <> cpPackage
, ""
, " mixins: base hiding (Prelude)"
, " , " <> cpPackage <> " (" <> cpModule <> " as Prelude)"
]
defaultExtensions :: [Text]
defaultExtensions = case settingsExtensions of
[] -> []
x:xs ->
" default-extensions: " <> x
: map (indent 23 <>) xs