{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
module Distribution.Simple.UHC (
configure, getInstalledPackages,
buildLib, buildExe, installLib, registerPackage, inplacePackageDbPath
) where
import Prelude ()
import Distribution.Compat.Prelude
import Distribution.InstalledPackageInfo
import Distribution.Package hiding (installedUnitId)
import Distribution.PackageDescription
import Distribution.Simple.BuildPaths
import Distribution.Simple.Compiler as C
import Distribution.Simple.LocalBuildInfo
import Distribution.Simple.PackageIndex
import Distribution.Simple.Program
import Distribution.Simple.Utils
import Distribution.Pretty
import Distribution.Parsec
import Distribution.Types.MungedPackageId
import Distribution.Verbosity
import Distribution.Version
import Distribution.System
import Language.Haskell.Extension
import qualified Data.Map as Map ( empty )
import System.Directory
import System.FilePath
configure :: Verbosity -> Maybe FilePath -> Maybe FilePath
-> ProgramDb -> IO (Compiler, Maybe Platform, ProgramDb)
configure verbosity hcPath _hcPkgPath progdb = do
(_uhcProg, uhcVersion, progdb') <-
requireProgramVersion verbosity uhcProgram
(orLaterVersion (mkVersion [1,0,2]))
(userMaybeSpecifyPath "uhc" hcPath progdb)
let comp = Compiler {
compilerId = CompilerId UHC uhcVersion,
compilerAbiTag = C.NoAbiTag,
compilerCompat = [],
compilerLanguages = uhcLanguages,
compilerExtensions = uhcLanguageExtensions,
compilerProperties = Map.empty
}
compPlatform = Nothing
return (comp, compPlatform, progdb')
uhcLanguages :: [(Language, C.Flag)]
uhcLanguages = [(Haskell98, "")]
uhcLanguageExtensions :: [(Extension, Maybe C.Flag)]
uhcLanguageExtensions =
let doFlag (f, (enable, disable)) = [(EnableExtension f, enable),
(DisableExtension f, disable)]
alwaysOn = (Nothing, Nothing)
in concatMap doFlag
[(CPP, (Just "--cpp", Nothing)),
(PolymorphicComponents, alwaysOn),
(ExistentialQuantification, alwaysOn),
(ForeignFunctionInterface, alwaysOn),
(UndecidableInstances, alwaysOn),
(MultiParamTypeClasses, alwaysOn),
(Rank2Types, alwaysOn),
(PatternSignatures, alwaysOn),
(EmptyDataDecls, alwaysOn),
(ImplicitPrelude, (Nothing, Just "--no-prelude")),
(TypeOperators, alwaysOn),
(OverlappingInstances, alwaysOn),
(FlexibleInstances, alwaysOn)]
getInstalledPackages :: Verbosity -> Compiler -> PackageDBStack -> ProgramDb
-> IO InstalledPackageIndex
getInstalledPackages verbosity comp packagedbs progdb = do
let compilerid = compilerId comp
systemPkgDir <- getGlobalPackageDir verbosity progdb
userPkgDir <- getUserPackageDir
let pkgDirs = nub (concatMap (packageDbPaths userPkgDir systemPkgDir) packagedbs)
pkgs <- liftM (map addBuiltinVersions . concat) $
traverse (\ d -> getDirectoryContents d >>= filterM (isPkgDir (prettyShow compilerid) d))
pkgDirs
let iPkgs =
map mkInstalledPackageInfo $
concatMap parsePackage $
pkgs
return (fromList iPkgs)
getGlobalPackageDir :: Verbosity -> ProgramDb -> IO FilePath
getGlobalPackageDir verbosity progdb = do
output <- getDbProgramOutput verbosity
uhcProgram progdb ["--meta-pkgdir-system"]
let pkgdir = trimEnd output
return pkgdir
where
trimEnd = reverse . dropWhile isSpace . reverse
getUserPackageDir :: NoCallStackIO FilePath
getUserPackageDir = do
homeDir <- getHomeDirectory
return $ homeDir </> ".cabal" </> "lib"
packageDbPaths :: FilePath -> FilePath -> PackageDB -> [FilePath]
packageDbPaths user system db =
case db of
GlobalPackageDB -> [ system ]
UserPackageDB -> [ user ]
SpecificPackageDB path -> [ path ]
addBuiltinVersions :: String -> String
addBuiltinVersions xs = xs
installedPkgConfig :: String
installedPkgConfig = "installed-pkg-config"
isPkgDir :: String -> String -> String -> NoCallStackIO Bool
isPkgDir _ _ ('.' : _) = return False
isPkgDir c dir xs = do
let candidate = dir </> uhcPackageDir xs c
doesFileExist (candidate </> installedPkgConfig)
parsePackage :: String -> [PackageId]
parsePackage = toList . simpleParsec
mkInstalledPackageInfo :: PackageId -> InstalledPackageInfo
mkInstalledPackageInfo p = emptyInstalledPackageInfo
{ installedUnitId = mkLegacyUnitId p,
sourcePackageId = p }
buildLib :: Verbosity -> PackageDescription -> LocalBuildInfo
-> Library -> ComponentLocalBuildInfo -> IO ()
buildLib verbosity pkg_descr lbi lib clbi = do
systemPkgDir <- getGlobalPackageDir verbosity (withPrograms lbi)
userPkgDir <- getUserPackageDir
let runUhcProg = runDbProgram verbosity uhcProgram (withPrograms lbi)
let uhcArgs =
["--pkg-build=" ++ prettyShow (packageId pkg_descr)]
++ constructUHCCmdLine userPkgDir systemPkgDir
lbi (libBuildInfo lib) clbi
(buildDir lbi) verbosity
++ map (map (\ c -> if c == '.' then pathSeparator else c))
(map prettyShow (allLibModules lib clbi))
runUhcProg uhcArgs
return ()
buildExe :: Verbosity -> PackageDescription -> LocalBuildInfo
-> Executable -> ComponentLocalBuildInfo -> IO ()
buildExe verbosity _pkg_descr lbi exe clbi = do
systemPkgDir <- getGlobalPackageDir verbosity (withPrograms lbi)
userPkgDir <- getUserPackageDir
let runUhcProg = runDbProgram verbosity uhcProgram (withPrograms lbi)
let uhcArgs =
constructUHCCmdLine userPkgDir systemPkgDir
lbi (buildInfo exe) clbi
(buildDir lbi) verbosity
++ ["--output", buildDir lbi </> prettyShow (exeName exe)]
++ [modulePath exe]
runUhcProg uhcArgs
constructUHCCmdLine :: FilePath -> FilePath
-> LocalBuildInfo -> BuildInfo -> ComponentLocalBuildInfo
-> FilePath -> Verbosity -> [String]
constructUHCCmdLine user system lbi bi clbi odir verbosity =
(if verbosity >= deafening then ["-v4"]
else if verbosity >= normal then []
else ["-v0"])
++ hcOptions UHC bi
++ languageToFlags (compiler lbi) (defaultLanguage bi)
++ extensionsToFlags (compiler lbi) (usedExtensions bi)
++ ["--hide-all-packages"]
++ uhcPackageDbOptions user system (withPackageDB lbi)
++ ["--package=uhcbase"]
++ ["--package=" ++ prettyShow (mungedName pkgid) | (_, pkgid) <- componentPackageDeps clbi ]
++ ["-i" ++ odir]
++ ["-i" ++ l | l <- nub (hsSourceDirs bi)]
++ ["-i" ++ autogenComponentModulesDir lbi clbi]
++ ["-i" ++ autogenPackageModulesDir lbi]
++ ["--optP=" ++ opt | opt <- cppOptions bi]
++ ["--odir=" ++ odir]
++ (case withOptimization lbi of
NoOptimisation -> ["-O0"]
NormalOptimisation -> ["-O1"]
MaximumOptimisation -> ["-O2"])
uhcPackageDbOptions :: FilePath -> FilePath -> PackageDBStack -> [String]
uhcPackageDbOptions user system db = map (\ x -> "--pkg-searchpath=" ++ x)
(concatMap (packageDbPaths user system) db)
installLib :: Verbosity -> LocalBuildInfo
-> FilePath -> FilePath -> FilePath
-> PackageDescription -> Library -> ComponentLocalBuildInfo -> IO ()
installLib verbosity _lbi targetDir _dynlibTargetDir builtDir pkg _library _clbi = do
installDirectoryContents verbosity (builtDir </> prettyShow (packageId pkg)) targetDir
uhcTarget, uhcTargetVariant :: String
uhcTarget = "bc"
uhcTargetVariant = "plain"
uhcPackageDir :: String -> String -> FilePath
uhcPackageSubDir :: String -> FilePath
uhcPackageDir pkgid compilerid = pkgid </> uhcPackageSubDir compilerid
uhcPackageSubDir compilerid = compilerid </> uhcTarget </> uhcTargetVariant
registerPackage
:: Verbosity
-> Compiler
-> ProgramDb
-> PackageDBStack
-> InstalledPackageInfo
-> IO ()
registerPackage verbosity comp progdb packageDbs installedPkgInfo = do
dbdir <- case registrationPackageDB packageDbs of
GlobalPackageDB -> getGlobalPackageDir verbosity progdb
UserPackageDB -> getUserPackageDir
SpecificPackageDB dir -> return dir
let pkgdir = dbdir </> uhcPackageDir (prettyShow pkgid) (prettyShow compilerid)
createDirectoryIfMissingVerbose verbosity True pkgdir
writeUTF8File (pkgdir </> installedPkgConfig)
(showInstalledPackageInfo installedPkgInfo)
where
pkgid = sourcePackageId installedPkgInfo
compilerid = compilerId comp
inplacePackageDbPath :: LocalBuildInfo -> FilePath
inplacePackageDbPath lbi = buildDir lbi