- -- -f flags on the command line add to the database stack, unless any
- -- of them are present in the stack already.
- let flag_stack = filter (`notElem` env_stack)
- [ f | FlagConfig f <- reverse flags ] ++ env_stack
-
- -- Now we have the full stack of databases. Next, if the current
- -- command is a "modify" type command, then we truncate the stack
- -- so that the topmost element is the database being modified.
- final_stack <-
- if not modify
- then return flag_stack
- else let
- go (FlagUser : fs) = modifying user_conf
- go (FlagGlobal : fs) = modifying virt_global_conf
- go (FlagConfig f : fs) = modifying f
- go (_ : fs) = go fs
- go [] = modifying virt_global_conf
-
- modifying f
- | f `elem` flag_stack = return (dropWhile (/= f) flag_stack)
- | otherwise = die ("requesting modification of database:\n\t" ++ f ++ "\n\twhich is not in the database stack.")
- in
- go flags
-
- -- we create the user database iff (a) we're modifying, and (b) the
- -- user asked to use it by giving the --user flag.
- when (not user_exists && user_conf `elem` final_stack) $ do
- putStrLn ("Creating user package database in " ++ user_conf)
- createDirectoryIfMissing True archdir
- writeFile user_conf emptyPackageConfig
-
- db_stack <- mapM readParseDatabase final_stack
- return db_stack
-
-readParseDatabase :: PackageDBName -> IO (PackageDBName,PackageDB)
-readParseDatabase filename = do
- str <- readFile filename
- let packages = read str
- Exception.evaluate packages
- `Exception.catch` \_ ->
- die (filename ++ ": parse error in package config file")
- return (filename,packages)
-
-emptyPackageConfig :: String
-emptyPackageConfig = "[]"
+ let db_flags = [ f | Just f <- map is_db_flag my_flags ]
+ where is_db_flag FlagUser
+ | Just (user_conf, _user_exists) <- mb_user_conf
+ = Just user_conf
+ is_db_flag FlagGlobal = Just virt_global_conf
+ is_db_flag (FlagConfig f) = Just f
+ is_db_flag _ = Nothing
+
+ let flag_db_names | null db_flags = env_stack
+ | otherwise = reverse (nub db_flags)
+
+ -- For a "modify" command, treat all the databases as
+ -- a stack, where we are modifying the top one, but it
+ -- can refer to packages in databases further down the
+ -- stack.
+
+ -- -f flags on the command line add to the database
+ -- stack, unless any of them are present in the stack
+ -- already.
+ let final_stack = filter (`notElem` env_stack)
+ [ f | FlagConfig f <- reverse my_flags ]
+ ++ env_stack
+
+ -- the database we actually modify is the one mentioned
+ -- rightmost on the command-line.
+ let to_modify
+ | not modify = Nothing
+ | null db_flags = Just virt_global_conf
+ | otherwise = Just (last db_flags)
+
+ db_stack <- mapM (readParseDatabase verbosity mb_user_conf use_cache) final_stack
+
+ let flag_db_stack = [ db | db_name <- flag_db_names,
+ db <- db_stack, location db == db_name ]
+
+ return (db_stack, to_modify, flag_db_stack)
+
+
+lookForPackageDBIn :: FilePath -> IO (Maybe FilePath)
+lookForPackageDBIn dir = do
+ let path_dir = dir </> "package.conf.d"
+ exists_dir <- doesDirectoryExist path_dir
+ if exists_dir then return (Just path_dir) else do
+ let path_file = dir </> "package.conf"
+ exists_file <- doesFileExist path_file
+ if exists_file then return (Just path_file) else return Nothing
+
+readParseDatabase :: Verbosity
+ -> Maybe (FilePath,Bool)
+ -> Bool -- use cache
+ -> FilePath
+ -> IO PackageDB
+
+readParseDatabase verbosity mb_user_conf use_cache path
+ -- the user database (only) is allowed to be non-existent
+ | Just (user_conf,False) <- mb_user_conf, path == user_conf
+ = return PackageDB { location = path, packages = [] }
+ | otherwise
+ = do e <- try $ getDirectoryContents path
+ case e of
+ Left _ -> do
+ pkgs <- parseMultiPackageConf verbosity path
+ return PackageDB{ location = path, packages = pkgs }
+ Right fs
+ | not use_cache -> ignore_cache
+ | otherwise -> do
+ let cache = path </> cachefilename
+ tdir <- getModificationTime path
+ e_tcache <- try $ getModificationTime cache
+ case e_tcache of
+ Left ex -> do
+ when (verbosity > Normal) $
+ putStrLn ("warning: cannot read cache file " ++ cache ++ ": " ++ show ex)
+ ignore_cache
+ Right tcache
+ | tcache >= tdir -> do
+ when (verbosity > Normal) $
+ putStrLn ("using cache: " ++ cache)
+ pkgs <- myReadBinPackageDB cache
+ let pkgs' = map convertPackageInfoIn pkgs
+ return PackageDB { location = path, packages = pkgs' }
+ | otherwise -> do
+ when (verbosity >= Normal) $ do
+ putStrLn ("WARNING: cache is out of date: " ++ cache)
+ putStrLn " use 'ghc-pkg recache' to fix."
+ ignore_cache
+ where
+ ignore_cache = do
+ let confs = filter (".conf" `isSuffixOf`) fs
+ pkgs <- mapM (parseSingletonPackageConf verbosity) $
+ map (path </>) confs
+ return PackageDB { location = path, packages = pkgs }
+
+-- read the package.cache file strictly, to work around a problem with
+-- bytestring 0.9.0.x (fixed in 0.9.1.x) where the file wasn't closed
+-- after it has been completely read, leading to a sharing violation
+-- later.
+myReadBinPackageDB :: FilePath -> IO [InstalledPackageInfoString]
+myReadBinPackageDB filepath = do
+ h <- openBinaryFile filepath ReadMode
+ sz <- hFileSize h
+ b <- B.hGet h (fromIntegral sz)
+ hClose h
+ return $ Bin.runGet Bin.get b
+
+parseMultiPackageConf :: Verbosity -> FilePath -> IO [InstalledPackageInfo]
+parseMultiPackageConf verbosity file = do
+ when (verbosity > Normal) $ putStrLn ("reading package database: " ++ file)
+ str <- readUTF8File file
+ let pkgs = map convertPackageInfoIn $ read str
+ Exception.evaluate pkgs
+ `catchError` \e->
+ die ("error while parsing " ++ file ++ ": " ++ show e)
+
+parseSingletonPackageConf :: Verbosity -> FilePath -> IO InstalledPackageInfo
+parseSingletonPackageConf verbosity file = do
+ when (verbosity > Normal) $ putStrLn ("reading package config: " ++ file)
+ readUTF8File file >>= parsePackageInfo
+
+cachefilename :: FilePath
+cachefilename = "package.cache"
+
+-- -----------------------------------------------------------------------------
+-- Creating a new package DB
+
+initPackageDB :: FilePath -> Verbosity -> [Flag] -> IO ()
+initPackageDB filename verbosity _flags = do
+ let eexist = die ("cannot create: " ++ filename ++ " already exists")
+ b1 <- doesFileExist filename
+ when b1 eexist
+ b2 <- doesDirectoryExist filename
+ when b2 eexist
+ changeDB verbosity [] PackageDB{ location = filename, packages = [] }