----------------------------------------------------------------------------- -- | -- Module : PackageConfig -- Copyright : Malcolm Wallace -- -- Maintainer : Malcolm Wallace -- Stability : Stable -- Portability : All -- -- Work out the import directories for a bunch of packages. -- -- * For ghc, we need to consult ghc-pkg for package import directories. -- -- * nhc98 <= 1.16 stores package imports under its default incdir. -- >= 1.17 stores package imports under $incdir/packages. ----------------------------------------------------------------------------- module PackageConfig ( packageDirs ) where import Config import Compiler import Platform (unsafePerformIO,escape) import RunAndReadStdout (runAndReadStdout,basename,dirname) import Directory (doesDirectoryExist) import IO (hPutStrLn, stderr) import List (partition,intersperse,isPrefixOf) import Char (isDigit) import Monad (when,foldM) -- Missing from List library elemBy :: (a -> a -> Bool) -> a -> [a] -> Bool elemBy eq _ [] = False elemBy eq x (y:ys) = x `eq` y || elemBy eq x ys -- | Work out the import directories for a bunch of packages. packageDirs :: CompilerConfig -- ^ Which compiler, where it's located, etc. -> [String] -- ^ The packages -> [FilePath] -- For ghc, we need to consult ghc-pkg for package import directories. packageDirs config@(CompilerConfig{ compilerStyle=Ghc , compilerPath=ghc }) packages = let ghcsym = (read . take 3 . filter isDigit . (++"00") . compilerVersion) config in if ghcsym < (500::Int) then [] else unsafePerformIO $ do pkgcfg <- runAndReadStdout (ghc++" --print-libdir") let libdir = escape pkgcfg incdir1 = libdir++"/imports" ok <- doesDirectoryExist incdir1 if ok then do let ghcpkg = matching ghc (ghcPkg ghc (compilerVersion config)) -- ghcpkg <- runAndReadStdout -- ("echo `" ++ ghc ++ " --print-libdir`/bin/ghc-pkg") -- pkgs <- runAndReadStdout (ghcpkg++" --list-packages") pkgs <- runAndReadStdout (ghcpkg++" -l") let (ok,bad) = partition (\p-> elemBy versionMatch p (deComma pkgs)) packages when (not (null bad)) (hPutStrLn stderr ("\nWarning: package(s) " ++concat (intersperse ", " bad) ++" not available (according to ghc-pkg)")) idirs <- mapM (\p-> runAndReadStdout (ghcpkg++" --show-package="++p ++" --field=import_dirs")) ok return (pkgDirs libdir idirs) else do ioError (userError ("Can't find ghc packages at "++incdir1)) where pkgDirs libdir dirs = map (\dir-> if "$libdir" `isPrefixOf` dir then libdir++drop 7 dir else if "[\"" `isPrefixOf` dir then let d = drop 2 (init (init dir)) in if "$topdir" `isPrefixOf` d then libdir++drop 7 d else d else dir) (concatMap words dirs) deComma pkgs = map (\p-> if last p==',' then init p else p) (words pkgs) matching path cmd = if '/' `elem` path then dirname path++"/"++cmd else cmd ghcPkg ghc ver = if '-' `elem` basename ghc then "ghc-pkg-"++ver else "ghc-pkg" p `versionMatch` q = case extractPkgVersion p of (p,"") -> p == fst (extractPkgVersion q) _ -> p == q extractPkgVersion :: String -> (String,String) extractPkgVersion p = let p' = case p of ('(':ps)-> init ps; _-> p; (suf,pref) = span (/='-') (reverse p') in case pref of "" -> (p',""); _-> (reverse (tail pref), reverse suf) -- nhc98 <= 1.16 stores package imports under its default incdir. -- >= 1.17 stores package imports under $incdir/packages. packageDirs config@(CompilerConfig{ compilerStyle=Nhc98 , includePaths=[incdir] }) packages = let (pkgdir,base) | compilerVersion config <= "v1.16" = (incdir, []) | otherwise = (incdir++"/packages", [pkgdir++"/base"]) in unsafePerformIO $ do ok <- doesDirectoryExist pkgdir if ok then do (good,bad) <- foldM (\(g,b) d-> do let dir = pkgdir++"/"++d ok <- doesDirectoryExist dir return (if ok then (dir:g, b) else (g, d:b))) (base,[]) packages when (not (null bad)) (hPutStrLn stderr ("\nWarning: package(s) " ++concat (intersperse ", " bad) ++" not available in "++pkgdir)) return good else ioError (userError ("Can't find nhc98 packages at "++pkgdir)) -- No other compiler supports packages. packageDirs config packages = []