module Distribution.Simple.Program.HcPkg (
HcPkgInfo(..),
init,
invoke,
register,
reregister,
registerMultiInstance,
unregister,
recache,
expose,
hide,
dump,
describe,
list,
initInvocation,
registerInvocation,
reregisterInvocation,
registerMultiInstanceInvocation,
unregisterInvocation,
recacheInvocation,
exposeInvocation,
hideInvocation,
dumpInvocation,
describeInvocation,
listInvocation,
) where
import Distribution.Package hiding (installedUnitId)
import Distribution.InstalledPackageInfo
import Distribution.ParseUtils
import Distribution.Simple.Compiler
import Distribution.Simple.Program.Types
import Distribution.Simple.Program.Run
import Distribution.Text
import Distribution.Simple.Utils
import Distribution.Verbosity
import Distribution.Compat.Exception
import Prelude hiding (init)
import Data.Char
( isSpace )
import Data.List
( stripPrefix )
import System.FilePath as FilePath
( (</>), (<.>)
, splitPath, splitDirectories, joinPath, isPathSeparator )
import qualified System.FilePath.Posix as FilePath.Posix
data HcPkgInfo = HcPkgInfo
{ hcPkgProgram :: ConfiguredProgram
, noPkgDbStack :: Bool
, noVerboseFlag :: Bool
, flagPackageConf :: Bool
, supportsDirDbs :: Bool
, requiresDirDbs :: Bool
, nativeMultiInstance :: Bool
, recacheMultiInstance :: Bool
}
init :: HcPkgInfo -> Verbosity -> Bool -> FilePath -> IO ()
init hpi verbosity preferCompat path
| not (supportsDirDbs hpi)
|| (not (requiresDirDbs hpi) && preferCompat)
= writeFile path "[]"
| otherwise
= runProgramInvocation verbosity (initInvocation hpi verbosity path)
invoke :: HcPkgInfo -> Verbosity -> PackageDBStack -> [String] -> IO ()
invoke hpi verbosity dbStack extraArgs =
runProgramInvocation verbosity invocation
where
args = packageDbStackOpts hpi dbStack ++ extraArgs
invocation = programInvocation (hcPkgProgram hpi) args
register :: HcPkgInfo -> Verbosity -> PackageDBStack
-> Either FilePath
InstalledPackageInfo
-> IO ()
register hpi verbosity packagedb pkgFile =
runProgramInvocation verbosity
(registerInvocation hpi verbosity packagedb pkgFile)
reregister :: HcPkgInfo -> Verbosity -> PackageDBStack
-> Either FilePath
InstalledPackageInfo
-> IO ()
reregister hpi verbosity packagedb pkgFile =
runProgramInvocation verbosity
(reregisterInvocation hpi verbosity packagedb pkgFile)
registerMultiInstance :: HcPkgInfo -> Verbosity
-> PackageDBStack
-> InstalledPackageInfo
-> IO ()
registerMultiInstance hpi verbosity packagedbs pkgInfo
| nativeMultiInstance hpi
= runProgramInvocation verbosity
(registerMultiInstanceInvocation hpi verbosity packagedbs (Right pkgInfo))
| recacheMultiInstance hpi
= do let pkgdb = last packagedbs
writeRegistrationFileDirectly hpi pkgdb pkgInfo
recache hpi verbosity pkgdb
| otherwise
= die $ "HcPkg.registerMultiInstance: the compiler does not support "
++ "registering multiple instances of packages."
writeRegistrationFileDirectly :: HcPkgInfo
-> PackageDB
-> InstalledPackageInfo
-> IO ()
writeRegistrationFileDirectly hpi (SpecificPackageDB dir) pkgInfo
| supportsDirDbs hpi
= do let pkgfile = dir </> display (installedUnitId pkgInfo) <.> "conf"
writeUTF8File pkgfile (showInstalledPackageInfo pkgInfo)
| otherwise
= die $ "HcPkg.writeRegistrationFileDirectly: compiler does not support dir style package dbs"
writeRegistrationFileDirectly _ _ _ =
die $ "HcPkg.writeRegistrationFileDirectly: only supports SpecificPackageDB for now"
unregister :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId -> IO ()
unregister hpi verbosity packagedb pkgid =
runProgramInvocation verbosity
(unregisterInvocation hpi verbosity packagedb pkgid)
recache :: HcPkgInfo -> Verbosity -> PackageDB -> IO ()
recache hpi verbosity packagedb =
runProgramInvocation verbosity
(recacheInvocation hpi verbosity packagedb)
expose :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId -> IO ()
expose hpi verbosity packagedb pkgid =
runProgramInvocation verbosity
(exposeInvocation hpi verbosity packagedb pkgid)
describe :: HcPkgInfo -> Verbosity -> PackageDBStack -> PackageId -> IO [InstalledPackageInfo]
describe hpi verbosity packagedb pid = do
output <- getProgramInvocationOutput verbosity
(describeInvocation hpi verbosity packagedb pid)
`catchIO` \_ -> return ""
case parsePackages output of
Left ok -> return ok
_ -> die $ "failed to parse output of '"
++ programId (hcPkgProgram hpi) ++ " describe " ++ display pid ++ "'"
hide :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId -> IO ()
hide hpi verbosity packagedb pkgid =
runProgramInvocation verbosity
(hideInvocation hpi verbosity packagedb pkgid)
dump :: HcPkgInfo -> Verbosity -> PackageDB -> IO [InstalledPackageInfo]
dump hpi verbosity packagedb = do
output <- getProgramInvocationOutput verbosity
(dumpInvocation hpi verbosity packagedb)
`catchIO` \_ -> die $ programId (hcPkgProgram hpi) ++ " dump failed"
case parsePackages output of
Left ok -> return ok
_ -> die $ "failed to parse output of '"
++ programId (hcPkgProgram hpi) ++ " dump'"
parsePackages :: String -> Either [InstalledPackageInfo] [PError]
parsePackages str =
let parsed = map parseInstalledPackageInfo' (splitPkgs str)
in case [ msg | ParseFailed msg <- parsed ] of
[] -> Left [ setUnitId
. maybe id mungePackagePaths (pkgRoot pkg)
$ pkg
| ParseOk _ pkg <- parsed ]
msgs -> Right msgs
where
parseInstalledPackageInfo' =
parseFieldsFlat fieldsInstalledPackageInfo emptyInstalledPackageInfo
splitPkgs :: String -> [String]
splitPkgs = checkEmpty . map unlines . splitWith ("---" ==) . lines
where
checkEmpty [s] | all isSpace s = []
checkEmpty ss = ss
splitWith :: (a -> Bool) -> [a] -> [[a]]
splitWith p xs = ys : case zs of
[] -> []
_:ws -> splitWith p ws
where (ys,zs) = break p xs
mungePackagePaths :: FilePath -> InstalledPackageInfo -> InstalledPackageInfo
mungePackagePaths pkgroot pkginfo =
pkginfo {
importDirs = mungePaths (importDirs pkginfo),
includeDirs = mungePaths (includeDirs pkginfo),
libraryDirs = mungePaths (libraryDirs pkginfo),
frameworkDirs = mungePaths (frameworkDirs pkginfo),
haddockInterfaces = mungePaths (haddockInterfaces pkginfo),
haddockHTMLs = mungeUrls (haddockHTMLs pkginfo)
}
where
mungePaths = map mungePath
mungeUrls = map mungeUrl
mungePath p = case stripVarPrefix "${pkgroot}" p of
Just p' -> pkgroot </> p'
Nothing -> p
mungeUrl p = case stripVarPrefix "${pkgrooturl}" p of
Just p' -> toUrlPath pkgroot p'
Nothing -> p
toUrlPath r p = "file:///"
++ FilePath.Posix.joinPath (r : FilePath.splitDirectories p)
stripVarPrefix var p =
case splitPath p of
(root:path') -> case stripPrefix var root of
Just [sep] | isPathSeparator sep -> Just (joinPath path')
_ -> Nothing
_ -> Nothing
setUnitId :: InstalledPackageInfo -> InstalledPackageInfo
setUnitId pkginfo@InstalledPackageInfo {
installedUnitId = SimpleUnitId (ComponentId ""),
sourcePackageId = pkgid
}
= pkginfo {
installedUnitId = mkLegacyUnitId pkgid
}
setUnitId pkginfo = pkginfo
list :: HcPkgInfo -> Verbosity -> PackageDB
-> IO [PackageId]
list hpi verbosity packagedb = do
output <- getProgramInvocationOutput verbosity
(listInvocation hpi verbosity packagedb)
`catchIO` \_ -> die $ programId (hcPkgProgram hpi) ++ " list failed"
case parsePackageIds output of
Just ok -> return ok
_ -> die $ "failed to parse output of '"
++ programId (hcPkgProgram hpi) ++ " list'"
where
parsePackageIds = sequence . map simpleParse . words
initInvocation :: HcPkgInfo -> Verbosity -> FilePath -> ProgramInvocation
initInvocation hpi verbosity path =
programInvocation (hcPkgProgram hpi) args
where
args = ["init", path]
++ verbosityOpts hpi verbosity
registerInvocation, reregisterInvocation, registerMultiInstanceInvocation
:: HcPkgInfo -> Verbosity -> PackageDBStack
-> Either FilePath InstalledPackageInfo
-> ProgramInvocation
registerInvocation = registerInvocation' "register" False
reregisterInvocation = registerInvocation' "update" False
registerMultiInstanceInvocation = registerInvocation' "update" True
registerInvocation' :: String -> Bool
-> HcPkgInfo -> Verbosity -> PackageDBStack
-> Either FilePath InstalledPackageInfo
-> ProgramInvocation
registerInvocation' cmdname multiInstance hpi
verbosity packagedbs pkgFileOrInfo =
case pkgFileOrInfo of
Left pkgFile ->
programInvocation (hcPkgProgram hpi) (args pkgFile)
Right pkgInfo ->
(programInvocation (hcPkgProgram hpi) (args "-")) {
progInvokeInput = Just (showInstalledPackageInfo pkgInfo),
progInvokeInputEncoding = IOEncodingUTF8
}
where
args file = [cmdname, file]
++ (if noPkgDbStack hpi
then [packageDbOpts hpi (last packagedbs)]
else packageDbStackOpts hpi packagedbs)
++ [ "--enable-multi-instance" | multiInstance ]
++ verbosityOpts hpi verbosity
unregisterInvocation :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId
-> ProgramInvocation
unregisterInvocation hpi verbosity packagedb pkgid =
programInvocation (hcPkgProgram hpi) $
["unregister", packageDbOpts hpi packagedb, display pkgid]
++ verbosityOpts hpi verbosity
recacheInvocation :: HcPkgInfo -> Verbosity -> PackageDB
-> ProgramInvocation
recacheInvocation hpi verbosity packagedb =
programInvocation (hcPkgProgram hpi) $
["recache", packageDbOpts hpi packagedb]
++ verbosityOpts hpi verbosity
exposeInvocation :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId
-> ProgramInvocation
exposeInvocation hpi verbosity packagedb pkgid =
programInvocation (hcPkgProgram hpi) $
["expose", packageDbOpts hpi packagedb, display pkgid]
++ verbosityOpts hpi verbosity
describeInvocation :: HcPkgInfo -> Verbosity -> PackageDBStack -> PackageId
-> ProgramInvocation
describeInvocation hpi verbosity packagedbs pkgid =
programInvocation (hcPkgProgram hpi) $
["describe", display pkgid]
++ (if noPkgDbStack hpi
then [packageDbOpts hpi (last packagedbs)]
else packageDbStackOpts hpi packagedbs)
++ verbosityOpts hpi verbosity
hideInvocation :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId
-> ProgramInvocation
hideInvocation hpi verbosity packagedb pkgid =
programInvocation (hcPkgProgram hpi) $
["hide", packageDbOpts hpi packagedb, display pkgid]
++ verbosityOpts hpi verbosity
dumpInvocation :: HcPkgInfo -> Verbosity -> PackageDB -> ProgramInvocation
dumpInvocation hpi _verbosity packagedb =
(programInvocation (hcPkgProgram hpi) args) {
progInvokeOutputEncoding = IOEncodingUTF8
}
where
args = ["dump", packageDbOpts hpi packagedb]
++ verbosityOpts hpi silent
listInvocation :: HcPkgInfo -> Verbosity -> PackageDB -> ProgramInvocation
listInvocation hpi _verbosity packagedb =
(programInvocation (hcPkgProgram hpi) args) {
progInvokeOutputEncoding = IOEncodingUTF8
}
where
args = ["list", "--simple-output", packageDbOpts hpi packagedb]
++ verbosityOpts hpi silent
packageDbStackOpts :: HcPkgInfo -> PackageDBStack -> [String]
packageDbStackOpts hpi dbstack = case dbstack of
(GlobalPackageDB:UserPackageDB:dbs) -> "--global"
: "--user"
: map specific dbs
(GlobalPackageDB:dbs) -> "--global"
: ("--no-user-" ++ packageDbFlag hpi)
: map specific dbs
_ -> ierror
where
specific (SpecificPackageDB db) = "--" ++ packageDbFlag hpi ++ "=" ++ db
specific _ = ierror
ierror :: a
ierror = error ("internal error: unexpected package db stack: " ++ show dbstack)
packageDbFlag :: HcPkgInfo -> String
packageDbFlag hpi
| flagPackageConf hpi
= "package-conf"
| otherwise
= "package-db"
packageDbOpts :: HcPkgInfo -> PackageDB -> String
packageDbOpts _ GlobalPackageDB = "--global"
packageDbOpts _ UserPackageDB = "--user"
packageDbOpts hpi (SpecificPackageDB db) = "--" ++ packageDbFlag hpi ++ "=" ++ db
verbosityOpts :: HcPkgInfo -> Verbosity -> [String]
verbosityOpts hpi v
| noVerboseFlag hpi
= []
| v >= deafening = ["-v2"]
| v == silent = ["-v0"]
| otherwise = []