-- | -- Module : Distribution.Package.Debian.Bundled -- Copyright : David Fox 2008 -- -- Maintainer : David Fox -- Stability : alpha -- Portability : portable -- -- Determine whether a specific version of a Haskell package is -- bundled with into this particular version of the given compiler. -- This software may be used and distributed according to the terms of -- the GNU General Public License, incorporated herein by reference. module Distribution.Package.Debian.Bundled ( Bundled , bundledWith , isBundled , isLibrary -- , builtIns -- , ghc6BuiltIns , PackageType(..) , debianName , versionSplits , ghcBuiltIns ) where import qualified Data.ByteString.Char8 as B import Data.Char (toLower) import Data.Function (on) import Data.List (find, isPrefixOf, sortBy) import qualified Data.Map as Map import Data.Maybe (maybeToList, fromJust) import Data.Version (Version(..)) import Debian.Control(Control'(Control), fieldValue, parseControlFromFile) import Debian.Relation.ByteString() import Debian.Relation(Relation(Rel),parseRelations) import Distribution.InstalledPackageInfo(libraryDirs, sourcePackageId) import Distribution.Simple.Compiler (Compiler(..), CompilerId(..), CompilerFlavor(..), PackageDB(GlobalPackageDB), compilerFlavor) import Distribution.Simple.Configure (getInstalledPackages) -- import Distribution.Simple.GHC (getInstalledPackages) import Distribution.Simple.PackageIndex (PackageIndex, SearchResult(None, Unambiguous), allPackages, searchByName) import Distribution.Simple.Program (configureAllKnownPrograms, defaultProgramConfiguration) import Distribution.Package (PackageIdentifier(..), PackageName(..), Dependency(..)) import Distribution.Verbosity(normal) import Distribution.Version (withinRange, VersionRange(..)) --import System.Unix.Chroot (fchroot) --import System.Unix.Process (lazyProcess, collectStdout) --import Data.ByteString.Lazy.Char8 (empty, unpack) import Text.ParserCombinators.Parsec(ParseError) import Text.Regex.TDFA ((=~)) -- | List the packages bundled with this version of the given -- compiler. If the answer is not known, return the empty list. bundledWith :: [(CompilerFlavor, Version, [PackageIdentifier])] -> Compiler -> Maybe [PackageIdentifier] bundledWith builtIns c = let cv = (compilerFlavor c, (\ (CompilerId _ v) -> v) $ compilerId c) in thd `fmap` find (\(n,v,_) -> (n,v) == cv) builtIns where thd (_,_,x) = x -- | Determine whether a specific version of a Haskell package is -- bundled with into this particular version of the given compiler. isBundled :: [(CompilerFlavor, Version, [PackageIdentifier])] -> Compiler -> Dependency -> Bool isBundled builtIns c (Dependency pkg version) = let cv = (compilerFlavor c, (\ (CompilerId _ v) -> v) (compilerId c)) in case find (\(n, k, _) -> (n,k) == cv) builtIns of Nothing -> False (Just (_, _, cb)) -> any checkVersion $ pkgVersion `fmap` filter ((== pkg) . pkgName) cb where checkVersion = flip withinRange version type Bundled = (CompilerFlavor, Version, [PackageIdentifier]) -- |Return a list of built in packages for the compiler in an environment. -- ghcBuiltIns :: FilePath -> IO [PackageIdentifier] -- ghcBuiltIns root = -- fchroot root (lazyProcess "ghc-pkg" ["list", "--simple-output"] Nothing Nothing empty) >>= -- return . map parsePackageIdentifier . words . unpack . fst . collectStdout -- where -- parsePackageIdentifier s = -- let (v', n') = break (== '-') (reverse s) -- (v, n) = (reverse (tail n'), reverse v') in -- PackageIdentifier (PackageName n) (Version (map read (filter (/= ".") (groupBy (\ a b -> (a == '.') == (b == '.')) v))) []) ghcBuiltIns :: Compiler -> Bundled ghcBuiltIns (Compiler {compilerId = CompilerId GHC compilerVersion}) = case Map.lookup compilerVersion (Map.fromList [ (Version [7,2,1] [], (GHC, Version [7,2,1] [], ghc721BuiltIns)) , (Version [7,0,4] [], (GHC, Version [7,0,4] [], ghc701BuiltIns)) , (Version [7,0,3] [], (GHC, Version [7,0,3] [], ghc701BuiltIns)) , (Version [7,0,1] [], (GHC, Version [7,0,1] [], ghc701BuiltIns)) , (Version [6,8,3] [], (GHC, Version [6,8,3] [], ghc683BuiltIns)) , (Version [6,8,2] [], (GHC, Version [6,8,2] [], ghc682BuiltIns)) , (Version [6,8,1] [], (GHC, Version [6,8,1] [], ghc681BuiltIns)) , (Version [6,6,1] [], (GHC, Version [6,6,1] [], ghc661BuiltIns)) , (Version [6,6] [], (GHC, Version [6,6] [], ghc66BuiltIns)) ]) of Nothing -> error $ "cabal-debian: No bundled package list for ghc " ++ show compilerVersion Just x -> x _builtIns :: Compiler -> IO [Bundled] _builtIns compiler = do ghc6 <- fmap maybeToList $ ghc6BuiltIns compiler return $ ghc6 ++ [ (GHC, Version [7,2,1] [], ghc721BuiltIns) , (GHC, Version [7,0,3] [], ghc701BuiltIns) , (GHC, Version [7,0,1] [], ghc701BuiltIns) , (GHC, Version [6,8,3] [], ghc683BuiltIns) , (GHC, Version [6,8,2] [], ghc682BuiltIns) , (GHC, Version [6,8,1] [], ghc681BuiltIns) , (GHC, Version [6,6,1] [], ghc661BuiltIns) , (GHC, Version [6,6] [], ghc66BuiltIns) ] _ghc6BuiltIns :: Compiler -> IO (Maybe (CompilerFlavor, Version, [PackageIdentifier])) _ghc6BuiltIns compiler@(Compiler {compilerId = CompilerId GHC compilerVersion}) = #ifdef CABAL19 do installedPackages <- _getInstalledPackageIndex compiler ghc6Files <- fmap lines $ readFile "/var/lib/dpkg/info/ghc.list" let ghcProvides = filter (\package -> any (\dir -> elem dir ghc6Files) (libraryDirs package)) (allPackages installedPackages) return (Just (GHC, compilerVersion, map sourcePackageId ghcProvides)) #else do mInstalledPackages <- getInstalledPackageIndex compiler case mInstalledPackages of Nothing -> error "Could not find the installed package database." (Just installedPackages) -> do ghc6Files <- fmap lines $ readFile "/var/lib/dpkg/info/ghc.list" let ghcProvides = filter (\package -> any (\dir -> elem dir ghc6Files) (libraryDirs package)) (allPackages installedPackages) return (Just (GHC, compilerVersion, map sourcePackageId ghcProvides)) #endif ghc6BuiltIns _ = return Nothing _ghc6BuiltIns' :: Compiler -> IO (Maybe (CompilerFlavor, Version, [PackageIdentifier])) _ghc6BuiltIns' compiler@(Compiler {compilerId = CompilerId GHC compilerVersion}) = do eDebs <- _ghc6Provides case eDebs of Left e -> error e Right debNames -> #ifdef CABAL19 do installedPackages <- _getInstalledPackageIndex compiler let packages = concatMap (\n -> fromRight $ _installedVersions (fromRight $ _extractBaseName n) installedPackages) debNames return $ Just (GHC, compilerVersion, packages) #else do mInstalledPackages <- getInstalledPackageIndex compiler case mInstalledPackages of Nothing -> error "Could not find the installed package database." (Just installedPackages) -> let packages = concatMap (\n -> fromRight $ installedVersions (fromRight $ _extractBaseName n) installedPackages) debNames in return $ Just (GHC, compilerVersion, packages) #endif where fromRight (Right r) = r fromRight (Left e) = error e _ghc6BuiltIns' (Compiler {}) = return Nothing _ghc6Provides :: IO (Either String [String]) _ghc6Provides = do eC <- parseControlFromFile "/var/lib/dpkg/status" :: IO (Either ParseError (Control' B.ByteString)) case eC of Left e -> return $ Left (show e) Right (Control c) -> case find (\p -> fieldValue "Package" p == Just (B.pack "ghc")) c of Nothing -> return $ Left "You do not seem to have ghc installed." (Just p) -> case fieldValue "Provides" p of Nothing -> return $ Left "Your ghc package does not seem to Provide anything." (Just p) -> case parseRelations p of (Left e) -> return (Left (show e)) (Right relations) -> return $ Right $ filter (isPrefixOf "libghc-") $ map (\ (Rel pkgName _ _) -> pkgName) (concat relations) _extractBaseName :: String -> Either String String _extractBaseName name = let (_,_,_,subs) = (name =~ "^libghc-(.*)-.*$") :: (String, String, String, [String]) in case subs of [base] -> Right base _ -> Left ("When attempt to extract the base name of " ++ name ++ " I found the following matches: " ++ show subs) --getInstalledPackageIndex :: Compiler -> IO (Maybe PackageIndex) _getInstalledPackageIndex compiler = do pc <- configureAllKnownPrograms normal defaultProgramConfiguration getInstalledPackages normal compiler [GlobalPackageDB] pc _installedVersions :: String -> PackageIndex -> Either String [PackageIdentifier] _installedVersions name packageIndex = case searchByName packageIndex name of None -> Left $ "The package " ++ name ++ " does not seem to be installed." Unambiguous pkgs -> case sortBy (compare `on` (pkgVersion . sourcePackageId)) pkgs of [] -> Left $ "Odd. searchByName returned an empty Unambiguous match for " ++ name ps -> Right (map sourcePackageId ps) v :: String -> [Int] -> PackageIdentifier v n x = PackageIdentifier (PackageName n) (Version x []) ghc721BuiltIns :: [PackageIdentifier] ghc721BuiltIns = [ v "Cabal" [1,12,0], v "array" [0,3,0,3], v "base" [4,4,0,0], v "bin-package-db" [0,0,0,0], v "binary" [0,5,0,2], v "bytestring" [0,9,2,0], v "containers" [0,4,1,0], v "directory" [1,1,0,1], v "extensible-exceptions" [0,1,1,3], v "filepath" [1,2,0,1], v "ghc" [7,2,1], -- ghc-binary renamed to binary v "ghc-prim" [0,2,0,0], v "haskell2010" [1,1,0,0], v "haskell98" [2,0,0,0], v "hoopl" [3,8,7,1], -- new v "hpc" [0,5,1,0], v "integer-gmp" [0,3,0,0], v "old-locale" [1,0,0,3], v "old-time" [1,0,0,7], v "pretty" [1,1,0,0], v "process" [1,1,0,0], -- random removed v "rts" [1,0], v "template-haskell" [2,6,0,0], v "time" [1,2,0,5], v "unix" [2,5,0,0] ] ghc701BuiltIns :: [PackageIdentifier] ghc701BuiltIns = [ v "Cabal" [1,10,0,0], v "array" [0,3,0,2], v "base" [4,3,0,0], v "bin-package-db" [0,0,0,0], v "bytestring" [0,9,1,8], v "containers" [0,4,0,0], v "directory" [1,1,0,0], v "extensible-exceptions" [0,1,1,2], v "filepath" [1,2,0,0], v "ghc" [7,0,1], v "ghc-binary" [0,5,0,2], v "ghc-prim" [0,2,0,0], v "haskell2010" [1,0,0,0], v "haskell98" [1,1,0,0], v "hpc" [0,5,0,6], v "integer-gmp" [0,2,0,2], v "old-locale" [1,0,0,2], v "old-time" [1,0,0,6], v "pretty" [1,0,1,2], v "process" [1,0,1,4], v "random" [1,0,0,3], v "rts" [1,0], v "template-haskell" [2,5,0,0], v "time" [1,2,0,3], v "unix" [2,4,1,0] ] ghc683BuiltIns :: [PackageIdentifier] ghc683BuiltIns = ghc682BuiltIns ghc682BuiltIns :: [PackageIdentifier] ghc682BuiltIns = [ v "Cabal" [1,2,3,0], v "array" [0,1,0,0], v "base" [3,0,1,0], v "bytestring" [0,9,0,1], v "containers" [0,1,0,1], v "directory" [1,0,0,0], v "filepath" [1,1,0,0], v "ghc" [6,8,2,0], v "haskell98" [1,0,1,0], v "hpc" [0,5,0,0], v "old-locale" [1,0,0,0], v "old-time" [1,0,0,0], v "packedstring" [0,1,0,0], v "pretty" [1,0,0,0], v "process" [1,0,0,0], v "random" [1,0,0,0], v "readline" [1,0,1,0], v "template-haskell" [2,2,0,0], v "unix" [2,3,0,0] ] ghc681BuiltIns :: [PackageIdentifier] ghc681BuiltIns = [ v "base" [3,0,0,0], v "Cabal" [1,2,2,0], v "GLUT" [2,1,1,1], v "HGL" [3,2,0,0], v "HUnit" [1,2,0,0], v "OpenAL" [1,3,1,1], v "OpenGL" [2,2,1,1], v "QuickCheck" [1,1,0,0], v "X11" [1,2,3,1], v "array" [0,1,0,0], v "bytestring" [0,9,0,1], v "cgi" [3001,1,5,1], v "containers" [0,1,0,0], v "directory" [1,0,0,0], v "fgl" [5,4,1,1], v "filepatch" [1,1,0,0], v "ghc" [6,8,1,0], v "haskell-src" [1,0,1,1], v "haskell98" [1,0,1,0], v "hpc" [0,5,0,0], v "html" [1,0,1,1], v "mtl" [1,1,0,0], v "network" [2,1,0,0], v "old-locale" [1,0,0,0], v "old-time" [1,0,0,0], v "packedstring" [0,1,0,0], v "parallel" [1,0,0,0], v "parsec" [2,1,0,0], v "pretty" [1,0,0,0], v "process" [1,0,0,0], v "random" [1,0,0,0], v "readline" [1,0,1,0], v "regex-base" [0,72,0,1], v "regex-compat" [0,71,0,1], v "regex-posix" [0,72,0,1], v "stm" [2,1,1,0], v "template-haskell" [2,2,0,0], v "time" [1,1,2,0], v "unix" [2,2,0,0], v "xhtml" [3000,0,2,1] ] ghc661BuiltIns :: [PackageIdentifier] ghc661BuiltIns = [ v "base" [2,1,1], v "Cabal" [1,1,6,2], v "cgi" [3001,1,1], v "fgl" [5,4,1], v "filepath" [1,0], v "ghc" [6,6,1], v "GLUT" [2,1,1], v "haskell98" [1,0], v "haskell-src" [1,0,1], v "HGL" [3,1,1], v "html" [1,0,1], v "HUnit" [1,1,1], v "mtl" [1,0,1], v "network" [2,0,1], v "OpenAL" [1,3,1], v "OpenGL" [2,2,1], v "parsec" [2,0], v "QuickCheck" [1,0,1], v "readline" [1,0], v "regex-base" [0,72], v "regex-compat" [0,71], v "regex-posix" [0,71], v "rts" [1,0], v "stm" [2,0], v "template-haskell" [2,1], v "time" [1,1,1], v "unix" [2,1], v "X11" [1,2,1], v "xhtml" [3000,0,2] ] ghc66BuiltIns :: [PackageIdentifier] ghc66BuiltIns = [ v "base" [2,0], v "Cabal" [1,1,6], v "cgi" [2006,9,6], v "fgl" [5,2], v "ghc" [6,6], v "GLUT" [2,0], v "haskell98" [1,0], v "haskell-src" [1,0], v "HGL" [3,1], v "html" [1,0], v "HTTP" [2006,7,7], v "HUnit" [1,1], v "mtl" [1,0], v "network" [2,0], v "OpenAL" [1,3], v "OpenGL" [2,1], v "parsec" [2,0], v "QuickCheck" [1,0], v "readline" [1,0], v "regex-base" [0,71], v "regex-compat" [0,71], v "regex-posix" [0,71], v "rts" [1,0], v "stm" [2,0], v "template-haskell" [2,0], v "time" [1,0], v "unix" [1,0], v "X11" [1,1], v "xhtml" [2006,9,13] ] -- |Some dependencies are libraries, some are executables. isLibrary :: Compiler -> Dependency -> Bool isLibrary _ (Dependency (PackageName "happy") _ ) = False isLibrary _ _ = True data PackageType = Source | Development | Profiling | Documentation | Utilities | Extra deriving (Eq, Show) {- debianName :: PackageType -> PackageName -> Maybe Version -> String debianName typ (PackageName name) version = prefix typ ++ foldr f def versionSplits ++ suffix typ where f (PackageName n, v, ltName, geName) debName = if n == name -- For unversioned dependencies (when version is Nothing), -- use the name corresponding to the newest version number. then if maybe True (>= v) version then geName else ltName else debName -- No splits apply, make the name safe for Debian def = map fixChar name -} debianName :: PackageType -> PackageName -> VersionRange -> String debianName typ (PackageName name) version = prefix typ ++ foldr f def versionSplits ++ suffix typ where f (PackageName n, v, ltName, geName) debName = if n == name -- For unversioned dependencies (when version is Nothing), -- use the name corresponding to the newest version number. then case version of AnyVersion -> geName ThisVersion v' | v' >= v -> geName ThisVersion _ -> ltName LaterVersion v' -> geName EarlierVersion v' | v' >= v -> ltName EarlierVersion v' -> geName _ -> error $ "debianName - unexpected version range: " ++ show version else debName -- make the name safe for Debian def = case name of -- The debian package haskell-src-exts should have been -- called haskell-haskell-src-exts. Consequently, the -- binary packages are missing a "haskell-" prefix, so we -- need to generated dependencies to match. "haskell-src-exts" -> "src-exts" _ -> map fixChar name versionSplits = [(PackageName "parsec", Version [3] [], "parsec2", "parsec3"), (PackageName "QuickCheck", Version [2] [], "quickcheck1", "quickcheck2")] suffix Source = "" suffix Documentation = "-doc" suffix Development = "-dev" suffix Profiling = "-prof" suffix Utilities = "-utils" suffix Extra = "" prefix Source = "haskell-" prefix Documentation = "libghc-" prefix Development = "libghc-" prefix Profiling = "libghc-" prefix Utilities = "haskell-" prefix Extra = "" fixChar :: Char -> Char fixChar '_' = '-' fixChar c = toLower c