hsdev-0.3.4.0: src/HsDev/PackageDb.hs
{-# LANGUAGE OverloadedStrings, CPP #-}
module HsDev.PackageDb (
module HsDev.PackageDb.Types,
packageDbPath, readPackageDb
) where
import Control.Lens
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.Maybe (listToMaybe)
import Data.Text (pack, unpack)
import qualified Data.Text.Encoding as E (encodeUtf8)
import Data.Traversable
import Distribution.InstalledPackageInfo
import Distribution.Package
import Distribution.Text (display)
import System.FilePath
import GHC.Paths
import HsDev.PackageDb.Types
import HsDev.Error
import HsDev.Symbols.Location
import HsDev.Tools.Base
import HsDev.Util (directoryContents, readFileUtf8)
import System.Directory.Paths
-- | Get path to package-db
packageDbPath :: PackageDb -> IO Path
packageDbPath GlobalDb = do
out <- fmap lines $ runTool_ ghc_pkg ["list", "--global"]
case out of
(fpath:_) -> return $ fromFilePath $ normalise fpath
[] -> hsdevError $ ToolError ghc_pkg "empty output, expecting path to global package-db"
packageDbPath UserDb = do
out <- fmap lines $ runTool_ ghc_pkg ["list", "--user"]
case out of
(fpath:_) -> return $ fromFilePath $ normalise fpath
-- Bailing on the user package db if there isn't one doesn't seem quite correct. 'stack' and 'cabal'
-- can report no path...
-- [] -> hsdevError $ ToolError ghc_pkg "empty output, expecting path to user package db"
-- Report an empty path instead.
[] -> return $ fromFilePath ""
packageDbPath (PackageDb fpath) = return fpath
-- | Read package-db conf files
readPackageDb :: PackageDb -> IO (Map ModulePackage [ModuleLocation])
readPackageDb pdb = do
p <- packageDbPath pdb
mlibdir <- fmap (listToMaybe . lines) $ runTool_ ghc ["--print-libdir"]
confs <- fmap (filter isConf) $ directoryContents (p ^. path)
fmap M.unions $ forM confs $ \conf -> do
cts <- readFileUtf8 conf
case parseResult (parseInstalledPackageInfo (unpackCts cts)) of
Left _ -> return M.empty -- FIXME: Should log as warning
Right (_, res) -> mapM (mapM canonicalize) $
over (each . each . moduleInstallDirs . each) (subst mlibdir) $ listMods res
where
#if MIN_VERSION_Cabal(3,2,0)
unpackCts = E.encodeUtf8
#else
unpackCts = unpack
#endif
#if MIN_VERSION_Cabal(3,0,0)
parseResult = id
#else
parseResult (ParseFailed e) = Left [show e]
parseResult (ParseOk ws res) = Right (map show ws, res)
#endif
isConf f = takeExtension f == ".conf"
listMods pinfo = M.singleton pname pmods where
pname = ModulePackage
(pack . display . pkgName $ sourcePackageId pinfo)
(pack . display . pkgVersion $ sourcePackageId pinfo)
pmods = [InstalledModule (map fromFilePath $ libraryDirs pinfo) pname nm exposed' | (nm, exposed') <- names]
names = zip (map (pack . display) (exposedModules pinfo)) (repeat True) ++ zip (map (pack . display) (hiddenModules pinfo)) (repeat False)
subst Nothing f = f
subst (Just libdir') f = case splitPaths f of
("$topdir":rest) -> joinPaths (fromFilePath libdir' : rest)
_ -> f