packages feed

cabal-dev-0.7.2: src/Distribution/Dev/Sandbox.hs

{-# LANGUAGE CPP, GADTs, EmptyDataDecls #-}
module Distribution.Dev.Sandbox
    ( KnownVersion
    , PackageDbType(..)
    , Sandbox
    , UnknownVersion
    , cabalConf
    , getVersion
    , indexTar
    , indexTarBase
    , localRepoPath
    , newSandbox
    , pkgConf
    , resolveSandbox
    , sandbox
    , setVersion
    )
where

import Control.Monad             ( unless )
import Data.Version              ( Version, showVersion )
import Distribution.Simple.Utils ( debug )
import Distribution.Verbosity    ( Verbosity )
import System.Directory          ( createDirectoryIfMissing
                                 , doesFileExist, copyFile )
import System.FilePath           ( (</>) )

#ifdef mingw32_HOST_OS
import System.IO ( hPutStrLn, stderr )
import System.Win32.Types ( getLastError )
#endif

import qualified Distribution.Dev.Flags as F
    ( getVerbosity, Config, getSandbox, sandboxSpecified )
import Distribution.Dev.Utilities ( ensureAbsolute )

import Paths_cabal_dev ( getDataFileName )

-- A sandbox directory that we may or may not know what kind of
-- package format it uses
data UnknownVersion
data KnownVersion

data Sandbox a where
    UnknownVersion :: FilePath -> Sandbox UnknownVersion
    KnownVersion :: FilePath -> PackageDbType -> Version -> Sandbox KnownVersion

data PackageDbType = GHC_6_8_Db FilePath | GHC_6_10_Db | GHC_6_12_Db

-- NOTE: GHC < 6.12: compilation warnings about non-exhaustive pattern
-- matches are spurious (we'd get a type error if we tried to make
-- them complete!)
setVersion :: Sandbox UnknownVersion -> PackageDbType -> Version -> Sandbox KnownVersion
setVersion (UnknownVersion p) v ty = KnownVersion p v ty

getVersion :: Sandbox KnownVersion -> PackageDbType
getVersion (KnownVersion _ db _) = db

sandbox :: Sandbox a -> FilePath
sandbox (UnknownVersion p) = p
sandbox (KnownVersion p _ _) = p

sPath :: FilePath -> Sandbox a -> FilePath
sPath p s = sandbox s </> p

localRepoPath :: Sandbox a -> FilePath
localRepoPath = sPath "packages"

pkgConf :: Sandbox KnownVersion -> FilePath
pkgConf s@(KnownVersion _ _ v) = sPath packageDbName s
    where
      packageDbName = "packages-" ++ showVersion v ++ ".conf"

cabalConf :: Sandbox a -> FilePath
cabalConf = sPath "cabal.config"

newSandbox :: Verbosity -> FilePath -> IO (Sandbox UnknownVersion)
newSandbox v relSandboxDir = do
  debug v $ "Using " ++ relSandboxDir ++ " as the relative cabal-dev sandbox"
  sandboxDir <- ensureAbsolute relSandboxDir
  debug v $ "Using " ++ sandboxDir ++ " as the cabal-dev sandbox"
  vista32Workaround_createDirectoryIfMissing True sandboxDir
  let sb = UnknownVersion sandboxDir
  debug v $ "Creating local repo " ++ localRepoPath sb
  vista32Workaround_createDirectoryIfMissing True $ localRepoPath sb
  extant <- doesFileExist (indexTar sb)
  unless extant $ do
    emptyIdxFile <- getDataFileName $ "admin" </> indexTarBase
    copyFile emptyIdxFile (indexTar sb)
  return sb

-- | Ugly hack to try and get around a bug with
-- createDirectoryIfMissing that only occurs on 32-bit windows.
vista32Workaround_createDirectoryIfMissing :: Bool -> FilePath -> IO ()
vista32Workaround_createDirectoryIfMissing b fp =
#ifdef mingw32_HOST_OS
  createDirectoryIfMissing b fp `catch` \e -> do
    erCode <- getLastError
    case erCode of
      1006 -> hPutStrLn stderr "Directory already exists--error swallowed"
      _    -> ioError e
#else
  createDirectoryIfMissing b fp
#endif

resolveSandbox :: F.Config -> IO (Sandbox UnknownVersion)
resolveSandbox cfg = do
  let v = F.getVerbosity cfg
      relSandbox = F.getSandbox cfg
  unless (F.sandboxSpecified cfg) $
         debug v $ "No sandbox specified. Using " ++ relSandbox
  newSandbox v relSandbox

-- |The name of the cabal-install package index
indexTarBase :: FilePath
indexTarBase = "00-index.tar"

indexTar :: Sandbox a -> FilePath
indexTar sb = localRepoPath sb </> indexTarBase