module Distribution.Simple.UHC (
configure, getInstalledPackages,
buildLib, buildExe, installLib, registerPackage, inplacePackageDbPath
) where
import Distribution.Compat.ReadP
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.Text
import Distribution.Verbosity
import Distribution.Version
import Distribution.System
import Language.Haskell.Extension
import Control.Monad
import Data.List
import qualified Data.Map as M ( empty )
import System.Directory
import System.FilePath
configure :: Verbosity -> Maybe FilePath -> Maybe FilePath
-> ProgramConfiguration -> IO (Compiler, Maybe Platform, ProgramConfiguration)
configure verbosity hcPath _hcPkgPath conf = do
(_uhcProg, uhcVersion, conf') <-
requireProgramVersion verbosity uhcProgram
(orLaterVersion (Version [1,0,2] []))
(userMaybeSpecifyPath "uhc" hcPath conf)
let comp = Compiler {
compilerId = CompilerId UHC uhcVersion,
compilerAbiTag = C.NoAbiTag,
compilerCompat = [],
compilerLanguages = uhcLanguages,
compilerExtensions = uhcLanguageExtensions,
compilerProperties = M.empty
}
compPlatform = Nothing
return (comp, compPlatform, conf')
uhcLanguages :: [(Language, C.Flag)]
uhcLanguages = [(Haskell98, "")]
uhcLanguageExtensions :: [(Extension, C.Flag)]
uhcLanguageExtensions =
let doFlag (f, (enable, disable)) = [(EnableExtension f, enable),
(DisableExtension f, disable)]
alwaysOn = ("", "")
in concatMap doFlag
[(CPP, ("--cpp", "")),
(PolymorphicComponents, alwaysOn),
(ExistentialQuantification, alwaysOn),
(ForeignFunctionInterface, alwaysOn),
(UndecidableInstances, alwaysOn),
(MultiParamTypeClasses, alwaysOn),
(Rank2Types, alwaysOn),
(PatternSignatures, alwaysOn),
(EmptyDataDecls, alwaysOn),
(ImplicitPrelude, ("", "--no-prelude")),
(TypeOperators, alwaysOn),
(OverlappingInstances, alwaysOn),
(FlexibleInstances, alwaysOn)]
getInstalledPackages :: Verbosity -> Compiler -> PackageDBStack -> ProgramConfiguration
-> IO InstalledPackageIndex
getInstalledPackages verbosity comp packagedbs conf = do
let compilerid = compilerId comp
systemPkgDir <- getGlobalPackageDir verbosity conf
userPkgDir <- getUserPackageDir
let pkgDirs = nub (concatMap (packageDbPaths userPkgDir systemPkgDir) packagedbs)
pkgs <- liftM (map addBuiltinVersions . concat) $
mapM (\ d -> getDirectoryContents d >>= filterM (isPkgDir (display compilerid) d))
pkgDirs
let iPkgs =
map mkInstalledPackageInfo $
concatMap parsePackage $
pkgs
return (fromList iPkgs)
getGlobalPackageDir :: Verbosity -> ProgramConfiguration -> IO FilePath
getGlobalPackageDir verbosity conf = do
output <- rawSystemProgramStdoutConf verbosity
uhcProgram conf ["--meta-pkgdir-system"]
let [pkgdir] = lines output
return pkgdir
getUserPackageDir :: IO 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 -> IO Bool
isPkgDir _ _ ('.' : _) = return False
isPkgDir c dir xs = do
let candidate = dir </> uhcPackageDir xs c
doesFileExist (candidate </> installedPkgConfig)
parsePackage :: String -> [PackageId]
parsePackage x = map fst (filter (\ (_,y) -> null y) (readP_to_S parse x))
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 = rawSystemProgramConf verbosity uhcProgram (withPrograms lbi)
let uhcArgs =
["--pkg-build=" ++ display (packageId pkg_descr)]
++ constructUHCCmdLine userPkgDir systemPkgDir
lbi (libBuildInfo lib) clbi
(buildDir lbi) verbosity
++ map (map (\ c -> if c == '.' then pathSeparator else c))
(map display (libModules lib))
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 = rawSystemProgramConf verbosity uhcProgram (withPrograms lbi)
let uhcArgs =
constructUHCCmdLine userPkgDir systemPkgDir
lbi (buildInfo exe) clbi
(buildDir lbi) verbosity
++ ["--output", buildDir lbi </> 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=" ++ display (pkgName pkgid) | (_, pkgid) <- componentPackageDeps clbi ]
++ ["-i" ++ odir]
++ ["-i" ++ l | l <- nub (hsSourceDirs bi)]
++ ["-i" ++ autogenModulesDir 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 </> display (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
-> ProgramConfiguration
-> PackageDBStack
-> InstalledPackageInfo
-> IO ()
registerPackage verbosity comp progdb packageDbs installedPkgInfo = do
dbdir <- case last packageDbs of
GlobalPackageDB -> getGlobalPackageDir verbosity progdb
UserPackageDB -> getUserPackageDir
SpecificPackageDB dir -> return dir
let pkgdir = dbdir </> uhcPackageDir (display pkgid) (display compilerid)
createDirectoryIfMissingVerbose verbosity True pkgdir
writeUTF8File (pkgdir </> installedPkgConfig)
(showInstalledPackageInfo installedPkgInfo)
where
pkgid = sourcePackageId installedPkgInfo
compilerid = compilerId comp
inplacePackageDbPath :: LocalBuildInfo -> FilePath
inplacePackageDbPath lbi = buildDir lbi