packages feed

hix-0.9.0: lib/Hix/Managed/Cabal/Init.hs

{-# language CPP #-}

module Hix.Managed.Cabal.Init where

import Distribution.Client.Config (defaultCacheDir)
import Distribution.Client.GlobalFlags (GlobalFlags (..), defaultGlobalFlags)
import Distribution.Client.NixStyleOptions (NixStyleFlags (..), defaultNixStyleFlags)
import Distribution.Client.ProjectFlags (flagIgnoreProject)
import Distribution.Client.Types (RemoteRepo (..), RepoName (RepoName))
import Distribution.Simple.Program (defaultProgramDb)
import Distribution.Simple.Setup (ConfigFlags (..), defaultConfigFlags, maybeToFlag, toFlag)
import Distribution.Utils.NubList (toNubList)
import Exon (exon)
import Path (reldir, relfile, toFilePath, (</>))

import Hix.Data.Error (ErrorMessage (Fatal))
import qualified Hix.Managed.Cabal.Data.Config
import Hix.Managed.Cabal.Data.Config (GhcPath (GhcPath), SolveConfig)
import Hix.Managed.Cabal.Data.HackageLocation (HackageLocation (..), hackageTlsBool)
import Hix.Managed.Cabal.Data.HackageRepo (HackageName (..), HackageRepo (..))
import Hix.Managed.Cabal.HackageLocation (hackageLocationUri)
import Hix.Managed.Cabal.Repo (ensureHackageIndex)
import Hix.Monad (M, tryIOMWith)

#if MIN_VERSION_Cabal(3,14,0)
import Distribution.Simple.Setup (CommonSetupFlags (..))
#endif

data SolveFlags =
  SolveFlags {
    global :: GlobalFlags,
    main :: NixStyleFlags ()
  }

emptySolveFlags :: SolveFlags
emptySolveFlags =
  SolveFlags {
    global = defaultGlobalFlags {globalConfigFile = toFlag "/dev/null"},
    main = defaultNixStyleFlags ()
  }

remoteRepo :: HackageRepo -> RemoteRepo
remoteRepo HackageRepo {name, location, secure, keys} =
  RemoteRepo {
    remoteRepoName = RepoName (toString @Text (coerce name)),
    remoteRepoURI = hackageLocationUri location,
    remoteRepoSecure = secure,
    remoteRepoRootKeys = foldMap (fmap toString . toList) keys,
    remoteRepoKeyThreshold = 0,
    remoteRepoShouldTryHttps = hackageTlsBool location.tls
  }

globalFlags :: NonEmpty RemoteRepo -> FilePath -> GlobalFlags
globalFlags repos cacheDir =
  defaultGlobalFlags {
    -- Cabal *always* reads ~/.cabal/config if no file is specified, and crashes if the file doesn't exist
    globalConfigFile = toFlag "/dev/null",
    globalCacheDir = toFlag cacheDir,
    globalRemoteRepos = toNubList (toList repos)
  }

badCacheDir :: Text -> ErrorMessage
badCacheDir err = Fatal [exon|Cannot access Cabal cache dir: #{err}|]

mainFlags ::
  SolveConfig ->
  NixStyleFlags ()
mainFlags conf =
  basic {
    configFlags,
    projectFlags = basic.projectFlags {flagIgnoreProject = toFlag True}
  }
  where
    basic = defaultNixStyleFlags ()


#if MIN_VERSION_Cabal(3,14,0)
    configFlags =
      configDef {
        configHcPath = pathFlag [relfile|ghc|],
        configHcPkg = pathFlag [relfile|ghc-pkg|],
        configCommonFlags = configDef.configCommonFlags {
          setupVerbosity = toFlag conf.verbosity
        }
      }

    configDef = defaultConfigFlags defaultProgramDb
#else
    configFlags =
      (defaultConfigFlags defaultProgramDb) {
        configHcPath = pathFlag [relfile|ghc|],
        configHcPkg = pathFlag [relfile|ghc-pkg|],
        configVerbosity = toFlag conf.verbosity
      }
#endif

    pathFlag exe = maybeToFlag (ghcPath exe <$> conf.ghc)

    ghcPath exe (GhcPath dir) = toFilePath (dir </> [reldir|bin|] </> exe)

solveFlags ::
  NonEmpty RemoteRepo ->
  SolveConfig ->
  M SolveFlags
solveFlags repos conf = do
  cacheDir <- tryIOMWith badCacheDir defaultCacheDir
  let global = globalFlags repos cacheDir
  let main = mainFlags conf
  pure SolveFlags {global, main}

initialize ::
  SolveConfig ->
  M SolveFlags
initialize conf = do
  flags <- solveFlags (remoteRepo <$> conf.hackageRepos) conf
  ensureHackageIndex conf flags.global flags.main
  pure flags