-#ifdef mingw32_TARGET_OS
-subst a b ls = map (\ x -> if x == a then b else x) ls
-
-unDosifyPath xs = subst '\\' '/' xs
-#endif
-
-runit clis = do
- conf_file <-
- case [ f | Config f <- clis ] of
- fs@(_:_) -> return (last fs)
-#ifndef mingw32_TARGET_OS
- [] -> die "missing -f option, location of package.conf unknown"
-#else
- [] -> do h <- getModuleHandle Nothing
- n <- getModuleFileName h
- return (reverse (drop (length "/bin/ghc-pkg.exe") (reverse (unDosifyPath n))) ++ "/package.conf")
-#endif
-
- let toField "import_dirs" = return import_dirs
- toField "source_dirs" = return source_dirs
- toField "library_dirs" = return library_dirs
- toField "hs_libraries" = return hs_libraries
- toField "extra_libraries" = return extra_libraries
- toField "include_dirs" = return include_dirs
- toField "c_includes" = return c_includes
- toField "package_deps" = return package_deps
- toField "extra_ghc_opts" = return extra_ghc_opts
- toField "extra_cc_opts" = return extra_cc_opts
- toField "extra_ld_opts" = return extra_ld_opts
- toField s = die ("unknown field: `" ++ s ++ "'")
-
- fields <- mapM toField [ f | Field f <- clis ]
-
- s <- readFile conf_file
- let details = read s :: [PackageConfig]
- eval_catch details (\_ -> die "parse error in package config file")
-
- case [ c | c <- clis, not (isConfigOrField c) ] of
- [ List ] -> listPackages details
- [ Add ] -> addPackage details conf_file
- [ Remove p ] -> removePackage details conf_file p
- [ Show p ] -> showPackage details conf_file p fields
- _ -> die (usageInfo usageHeader flags)
-
-
-listPackages :: [PackageConfig] -> IO ()
-listPackages details = do
- hPutStr stdout (listPkgs details)
- hPutChar stdout '\n'
-
-showPackage :: [PackageConfig] -> FilePath -> String
- -> [PackageConfig->[String]] -> IO ()
-showPackage details pkgconf pkg_name fields =
- case [ p | p <- details, name p == pkg_name ] of
- [] -> die ("can't find package `" ++ pkg_name ++ "'")
- [pkg] | null fields -> hPutStrLn stdout (render (dumpPkgGuts pkg))
- | otherwise -> hPutStrLn stdout (render (vcat
- (map (vcat . map text) (map ($pkg) fields))))
- _ -> die "showPackage: internal error"
-
-addPackage :: [PackageConfig] -> FilePath -> IO ()
-addPackage details pkgconf = do
- checkConfigAccess pkgconf
- hPutStr stdout "Reading package info from stdin... "
- s <- getContents
- let new_pkg = read s :: PackageConfig
- eval_catch new_pkg (\_ -> die "parse error in package info")
- hPutStrLn stdout "done."
- if (name new_pkg `elem` map name details)
- then die ("package `" ++ name new_pkg ++ "' already installed")
- else do
- savePackageConfig pkgconf
- maybeRestoreOldConfig pkgconf $
- writeNewConfig pkgconf (details ++ [new_pkg])
-
-removePackage :: [PackageConfig] -> FilePath -> String -> IO ()
-removePackage details pkgconf pkg = do
- checkConfigAccess pkgconf
- if (pkg `notElem` map name details)
- then die ("package `" ++ pkg ++ "' not installed")
- else do
- savePackageConfig pkgconf
- maybeRestoreOldConfig pkgconf $
- writeNewConfig pkgconf (filter ((/= pkg) . name) details)
+ourCopyright :: String
+ourCopyright = "GHC package manager version " ++ version ++ "\n"
+
+usageHeader :: String -> String
+usageHeader prog = substProg prog $
+ "Usage:\n" ++
+ " $p register {filename | -}\n" ++
+ " Register the package using the specified installed package\n" ++
+ " description. The syntax for the latter is given in the $p\n" ++
+ " documentation.\n" ++
+ "\n" ++
+ " $p update {filename | -}\n" ++
+ " Register the package, overwriting any other package with the\n" ++
+ " same name.\n" ++
+ "\n" ++
+ " $p unregister {pkg-id}\n" ++
+ " Unregister the specified package.\n" ++
+ "\n" ++
+ " $p expose {pkg-id}\n" ++
+ " Expose the specified package.\n" ++
+ "\n" ++
+ " $p hide {pkg-id}\n" ++
+ " Hide the specified package.\n" ++
+ "\n" ++
+ " $p list\n" ++
+ " List registered packages in the global database, and also the" ++
+ " user database if --user is given.\n" ++
+ "\n" ++
+ " $p describe {pkg-id}\n" ++
+ " Give the registered description for the specified package. The\n" ++
+ " description is returned in precisely the syntax required by $p\n" ++
+ " register.\n" ++
+ "\n" ++
+ " $p field {pkg-id} {field}\n" ++
+ " Extract the specified field of the package description for the\n" ++
+ " specified package.\n" ++
+ "\n" ++
+ " The following optional flags are also accepted:\n"
+
+substProg :: String -> String -> String
+substProg _ [] = []
+substProg prog ('$':'p':xs) = prog ++ substProg prog xs
+substProg prog (c:xs) = c : substProg prog xs
+
+-- -----------------------------------------------------------------------------
+-- Do the business
+
+runit :: [Flag] -> [String] -> IO ()
+runit cli nonopts = do
+ prog <- getProgramName
+ let
+ force = FlagForce `elem` cli
+ auto_ghci_libs = FlagAutoGHCiLibs `elem` cli
+ --
+ -- first, parse the command
+ case nonopts of
+ ["register", filename] ->
+ registerPackage filename [] cli auto_ghci_libs False force
+ ["update", filename] ->
+ registerPackage filename [] cli auto_ghci_libs True force
+ ["unregister", pkgid_str] -> do
+ pkgid <- readGlobPkgId pkgid_str
+ unregisterPackage pkgid cli
+ ["expose", pkgid_str] -> do
+ pkgid <- readGlobPkgId pkgid_str
+ exposePackage pkgid cli
+ ["hide", pkgid_str] -> do
+ pkgid <- readGlobPkgId pkgid_str
+ hidePackage pkgid cli
+ ["list"] -> do
+ listPackages cli
+ ["describe", pkgid_str] -> do
+ pkgid <- readGlobPkgId pkgid_str
+ describePackage cli pkgid
+ ["field", pkgid_str, field] -> do
+ pkgid <- readGlobPkgId pkgid_str
+ describeField cli pkgid field
+ [] -> do
+ die ("missing command\n" ++
+ usageInfo (usageHeader prog) flags)
+ (_cmd:_) -> do
+ die ("command-line syntax error\n" ++
+ usageInfo (usageHeader prog) flags)
+
+parseCheck :: ReadP a a -> String -> String -> IO a
+parseCheck parser str what =
+ case [ x | (x,ys) <- readP_to_S parser str, all isSpace ys ] of
+ [x] -> return x
+ _ -> die ("cannot parse \'" ++ str ++ "\' as a " ++ what)
+
+readPkgId :: String -> IO PackageIdentifier
+readPkgId str = parseCheck parsePackageId str "package identifier"
+
+readGlobPkgId :: String -> IO PackageIdentifier
+readGlobPkgId str = parseCheck parseGlobPackageId str "package identifier"
+
+parseGlobPackageId :: ReadP r PackageIdentifier
+parseGlobPackageId =
+ parsePackageId
+ +++
+ (do n <- parsePackageName; string "-*"
+ return (PackageIdentifier{ pkgName = n, pkgVersion = globVersion }))
+
+-- globVersion means "all versions"
+globVersion = Version{ versionBranch=[], versionTags=["*"] }
+
+-- -----------------------------------------------------------------------------
+-- Package databases
+
+-- Some commands operate on a single database:
+-- register, unregister, expose, hide
+-- however these commands also check the union of the available databases
+-- in order to check consistency. For example, register will check that
+-- dependencies exist before registering a package.
+--
+-- Some commands operate on multiple databases, with overlapping semantics:
+-- list, describe, field
+
+type PackageDBName = FilePath
+type PackageDB = [InstalledPackageInfo]
+
+type PackageDBStack = [(PackageDBName,PackageDB)]
+ -- A stack of package databases. Convention: head is the topmost
+ -- in the stack. Earlier entries override later one.
+
+getPkgDatabases :: Bool -> [Flag] -> IO PackageDBStack
+getPkgDatabases modify flags = do
+ -- first we determine the location of the global package config. On Windows,
+ -- this is found relative to the ghc-pkg.exe binary, whereas on Unix the
+ -- location is passed to the binary using the --global-config flag by the
+ -- wrapper script.
+ let err_msg = "missing --global-conf option, location of global package.conf unknown\n"
+ global_conf <-
+ case [ f | FlagGlobalConfig f <- flags ] of
+ [] -> do mb_dir <- getExecDir "/bin/ghc-pkg.exe"
+ case mb_dir of
+ Nothing -> die err_msg
+ Just dir -> return (dir `joinFileName` "package.conf")
+ fs -> return (last fs)
+
+ -- get the location of the user package database, and create it if necessary
+ appdir <- getAppUserDataDirectory "ghc"
+
+ let
+ subdir = targetARCH ++ '-':targetOS ++ '-':version
+ archdir = appdir `joinFileName` subdir
+ user_conf = archdir `joinFileName` "package.conf"
+ b <- doesFileExist user_conf
+ when (not b) $ do
+ putStrLn ("Creating user package database in " ++ user_conf)
+ createDirectoryIfMissing True archdir
+ writeFile user_conf emptyPackageConfig
+
+ let
+ -- The semantics here are slightly strange. If we are
+ -- *modifying* the database, then the default is to modify
+ -- the global database by default, unless you say --user.
+ -- If we are not modifying (eg. list, describe etc.) then
+ -- the user database is included by default.
+ databases
+ | modify = foldl addDB [global_conf] flags
+ | not modify = foldl addDB [user_conf,global_conf] flags
+
+ -- implement the following rules:
+ -- --user means overlap with the user database
+ -- --global means reset to just the global database
+ -- -f <file> means overlap with <file>
+ addDB dbs FlagUser = if user_conf `elem` dbs
+ then dbs
+ else user_conf : dbs
+ addDB dbs FlagGlobal = [global_conf]
+ addDB dbs (FlagConfig f) = f : dbs
+ addDB dbs _ = dbs
+
+ db_stack <- mapM readParseDatabase databases
+ return db_stack
+
+readParseDatabase :: PackageDBName -> IO (PackageDBName,PackageDB)
+readParseDatabase filename = do
+ str <- readFile filename
+ let packages = read str
+ evaluate packages
+ `Exception.catch` \_ ->
+ die (filename ++ ": parse error in package config file")
+ return (filename,packages)
+
+emptyPackageConfig :: String
+emptyPackageConfig = "[]"
+
+-- -----------------------------------------------------------------------------
+-- Registering
+
+registerPackage :: FilePath
+ -> [(String,String)] -- defines, ToDo: maybe remove?
+ -> [Flag]
+ -> Bool -- auto_ghci_libs
+ -> Bool -- update
+ -> Bool -- force
+ -> IO ()
+registerPackage input defines flags auto_ghci_libs update force = do
+ db_stack <- getPkgDatabases True flags
+ let
+ db_to_operate_on = my_head "db" db_stack
+ db_filename = fst db_to_operate_on
+ --
+ checkConfigAccess db_filename
+
+ s <-
+ case input of
+ "-" -> do
+ putStr "Reading package info from stdin... "
+ getContents
+ f -> do
+ putStr ("Reading package info from " ++ show f ++ " ")
+ readFile f
+
+ pkg <- parsePackageInfo s defines force
+ putStrLn "done."
+
+ validatePackageConfig pkg db_stack auto_ghci_libs update force
+ new_details <- updatePackageDB db_stack (snd db_to_operate_on) pkg
+ savePackageConfig db_filename
+ maybeRestoreOldConfig db_filename $
+ writeNewConfig db_filename new_details
+
+parsePackageInfo
+ :: String
+ -> [(String,String)]
+ -> Bool
+ -> IO InstalledPackageInfo
+parsePackageInfo str defines force =
+ case parseInstalledPackageInfo str of
+ ParseOk ok -> return ok
+ ParseFailed err -> die (showError err)
+
+-- -----------------------------------------------------------------------------
+-- Exposing, Hiding, Unregistering are all similar
+
+exposePackage :: PackageIdentifier -> [Flag] -> IO ()
+exposePackage = modifyPackage (\p -> [p{exposed=True}])
+
+hidePackage :: PackageIdentifier -> [Flag] -> IO ()
+hidePackage = modifyPackage (\p -> [p{exposed=False}])
+
+unregisterPackage :: PackageIdentifier -> [Flag] -> IO ()
+unregisterPackage = modifyPackage (\p -> [])
+
+modifyPackage
+ :: (InstalledPackageInfo -> [InstalledPackageInfo])
+ -> PackageIdentifier
+ -> [Flag]
+ -> IO ()
+modifyPackage fn pkgid flags = do
+ db_stack <- getPkgDatabases True{-modify-} flags
+ let ((db_name, pkgs) : _) = db_stack
+ checkConfigAccess db_name
+ ps <- findPackages [(db_name,pkgs)] pkgid
+ let pids = map package ps
+ savePackageConfig db_name
+ let new_config = concat (map modify pkgs)
+ modify pkg
+ | package pkg `elem` pids = fn pkg
+ | otherwise = [pkg]
+ maybeRestoreOldConfig db_name $
+ writeNewConfig db_name new_config
+
+-- -----------------------------------------------------------------------------
+-- Listing packages
+
+listPackages :: [Flag] -> IO ()
+listPackages flags = do
+ db_stack <- getPkgDatabases False flags
+ mapM_ show_pkgconf (reverse db_stack)
+ where show_pkgconf (db_name,pkg_confs) =
+ hPutStrLn stdout (render $
+ text (db_name ++ ":") $$ nest 4 packages
+ )
+ where packages = fsep (punctuate comma (map pp_pkg pkg_confs))
+ pp_pkg p
+ | exposed p = doc
+ | otherwise = parens doc
+ where doc = text (showPackageId (package p))
+
+-- -----------------------------------------------------------------------------
+-- Describe
+
+describePackage :: [Flag] -> PackageIdentifier -> IO ()
+describePackage flags pkgid = do
+ db_stack <- getPkgDatabases False flags
+ ps <- findPackages db_stack pkgid
+ mapM_ (putStrLn . showInstalledPackageInfo) ps
+
+-- PackageId is can have globVersion for the version
+findPackages :: PackageDBStack -> PackageIdentifier -> IO [InstalledPackageInfo]
+findPackages db_stack pkgid
+ = case [ p | p <- all_pkgs, pkgid `matches` p ] of
+ [] -> die ("cannot find package " ++ showPackageId pkgid)
+ [p] -> return [p]
+ -- if the version is globVersion, then we are allowed to match
+ -- multiple packages. So eg. "Cabal-*" matches all Cabal packages,
+ -- but "Cabal" matches just one Cabal package - if there are more,
+ -- you get an error.
+ ps | pkgVersion pkgid == globVersion
+ -> return ps
+ | otherwise
+ -> die ("package " ++ showPackageId pkgid ++
+ " matches multiple packages: " ++
+ concat (intersperse ", " (
+ map (showPackageId.package) ps)))
+ where
+ pid `matches` pkg
+ = (pkgName pid == pkgName p)
+ && (pkgVersion pid == pkgVersion p || not (realVersion pid))
+ where p = package pkg
+
+ all_pkgs = concat (map snd db_stack)
+
+-- -----------------------------------------------------------------------------
+-- Field
+
+describeField :: [Flag] -> PackageIdentifier -> String -> IO ()
+describeField flags pkgid field = do
+ db_stack <- getPkgDatabases False flags
+ case toField field of
+ Nothing -> die ("unknown field: " ++ field)
+ Just fn -> do
+ ps <- findPackages db_stack pkgid
+ mapM_ (putStrLn.fn) ps
+
+toField :: String -> Maybe (InstalledPackageInfo -> String)
+-- backwards compatibility:
+toField "import_dirs" = Just $ strList . importDirs
+toField "source_dirs" = Just $ strList . importDirs
+toField "library_dirs" = Just $ strList . libraryDirs
+toField "hs_libraries" = Just $ strList . hsLibraries
+toField "extra_libraries" = Just $ strList . extraLibraries
+toField "include_dirs" = Just $ strList . includeDirs
+toField "c_includes" = Just $ strList . includes
+toField "package_deps" = Just $ strList . map showPackageId. depends
+toField "extra_cc_opts" = Just $ strList . ccOptions
+toField "extra_ld_opts" = Just $ strList . ldOptions
+toField "framework_dirs" = Just $ strList . frameworkDirs
+toField "extra_frameworks"= Just $ strList . frameworks
+toField s = showInstalledPackageInfoField s
+
+strList :: [String] -> String
+strList = show
+
+-- -----------------------------------------------------------------------------
+-- Manipulating package.conf files