-#ifdef mingw32_TARGET_OS
-unDosifyPath xs = subst '\\' '/' xs
-#endif
-
-runit clis = do
-#ifndef mingw32_TARGET_OS
- conf_file <-
- case [ f | Config f <- clis ] of
- [] -> die "missing -f option, location of package.conf unknown"
- [f] -> return f
- _ -> die (usageInfo usageHeader flags)
-#else
- h <- getModuleHandle Nothing
- n <- getModuleFileName h
- let conf_file = reverse (tail (dropWhile (not . isSlash) (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 all registered packages, both global and user (unless either\n" ++
+ " --global or --user is specified), and both hidden and exposed.\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
+ dbs <- getPkgDatabases cli
+ db_stack <- mapM readParseDatabase dbs
+ let
+ force = FlagForce `elem` cli
+ auto_ghci_libs = FlagAutoGHCiLibs `elem` cli
+ --
+ -- first, parse the command
+ case nonopts of
+ ["register", filename] ->
+ registerPackage filename [] db_stack auto_ghci_libs False force
+ ["update", filename] ->
+ registerPackage filename [] db_stack auto_ghci_libs True force
+ ["unregister", pkgid_str] -> do
+ pkgid <- readPkgId pkgid_str
+ unregisterPackage pkgid db_stack
+ ["expose", pkgid_str] -> do
+ pkgid <- readPkgId pkgid_str
+ exposePackage pkgid db_stack
+ ["hide", pkgid_str] -> do
+ pkgid <- readPkgId pkgid_str
+ hidePackage pkgid db_stack
+ ["list"] -> do
+ listPackages db_stack
+ ["describe", pkgid_str] -> do
+ pkgid <- readPkgId pkgid_str
+ describePackage db_stack pkgid
+ ["field", pkgid_str, field] -> do
+ pkgid <- readPkgId pkgid_str
+ describeField db_stack 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"
+
+-- -----------------------------------------------------------------------------
+-- 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.
+
+-- The output of this function is the list of databases to act upon, with
+-- the "topmost" overlapped database last. The commands which operate on a
+-- single database will use the last one. Commands which operate on multiple
+-- databases will interpret the databases as overlapping.
+getPkgDatabases :: [Flag] -> IO [PackageDBName]
+getPkgDatabases 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
+ databases = foldl addDB [global_conf] flags
+
+ -- implement the following rules:
+ -- global database is the default
+ -- --user means overlap with the user database
+ -- --global means reset to just the global database
+ -- -f <file> means overlap with <file>
+ addDB dbs FlagUser = user_conf : dbs
+ addDB dbs FlagGlobal = [global_conf]
+ addDB dbs (FlagConfig f) = f : dbs
+ addDB dbs _ = dbs
+
+ return databases
+
+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?
+ -> PackageDBStack
+ -> Bool -- auto_ghci_libs
+ -> Bool -- update
+ -> Bool -- force
+ -> IO ()
+registerPackage input defines db_stack auto_ghci_libs update force = do
+ 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)
+
+-- Used for converting versionless package names to new
+-- PackageIdentifiers. "Version [] []" is special: it means "no
+-- version" or "any version"
+pkgNameToId :: String -> PackageIdentifier
+pkgNameToId name = PackageIdentifier name (Version [] [])
+
+-- -----------------------------------------------------------------------------
+-- Exposing, Hiding, Unregistering are all similar
+
+exposePackage :: PackageIdentifier -> PackageDBStack -> IO ()
+exposePackage = modifyPackage (\p -> [p{exposed=True}])
+
+hidePackage :: PackageIdentifier -> PackageDBStack -> IO ()
+hidePackage = modifyPackage (\p -> [p{exposed=False}])
+
+unregisterPackage :: PackageIdentifier -> PackageDBStack -> IO ()
+unregisterPackage = modifyPackage (\p -> [])
+
+modifyPackage
+ :: (InstalledPackageInfo -> [InstalledPackageInfo])
+ -> PackageIdentifier
+ -> PackageDBStack
+ -> IO ()
+modifyPackage _ _ [] = error "modifyPackage"
+modifyPackage fn pkgid ((db_name, pkgs) : _) = do
+ checkConfigAccess db_name
+ p <- findPackage [(db_name,pkgs)] pkgid
+ let pid = package p
+ savePackageConfig db_name
+ let new_config = concat (map modify pkgs)
+ modify pkg
+ | package pkg == pid = fn pkg
+ | otherwise = [pkg]
+ maybeRestoreOldConfig db_name $
+ writeNewConfig db_name new_config
+
+-- -----------------------------------------------------------------------------
+-- Listing packages
+
+listPackages :: PackageDBStack -> IO ()
+listPackages db_confs = do
+ mapM_ show_pkgconf (reverse db_confs)
+ 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 :: PackageDBStack -> PackageIdentifier -> IO ()
+describePackage db_stack pkgid = do
+ p <- findPackage db_stack pkgid
+ putStrLn (showInstalledPackageInfo p)
+
+findPackage :: PackageDBStack -> PackageIdentifier -> IO InstalledPackageInfo
+findPackage db_stack pkgid
+ = case [ p | p <- all_pkgs, pkgid `matches` p ] of
+ [] -> die ("cannot find package " ++ showPackageId pkgid)
+ [p] -> return p
+ ps -> die ("package " ++ showPackageId pkgid ++
+ " matches multiple packages: " ++
+ concat (intersperse ", " (
+ map (showPackageId.package) ps)))
+ where
+ all_pkgs = concat (map snd db_stack)
+
+matches :: PackageIdentifier -> InstalledPackageInfo -> Bool
+pid `matches` p =
+ pid == package p ||
+ not (realVersion pid) && pkgName pid == pkgName (package p)
+
+-- -----------------------------------------------------------------------------
+-- Field
+
+describeField :: PackageDBStack -> PackageIdentifier -> String -> IO ()
+describeField db_stack pkgid field = do
+ case toField field of
+ Nothing -> die ("unknown field: " ++ field)
+ Just fn -> do
+ p <- findPackage db_stack pkgid
+ putStrLn (fn p)
+
+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 . extraCcOpts
+toField "extra_ld_opts" = Just $ strList . extraLdOpts
+toField "framework_dirs" = Just $ strList . frameworkDirs
+toField "extra_frameworks"= Just $ strList . extraFrameworks
+toField s = showInstalledPackageInfoField s
+
+strList :: [String] -> String
+strList = show
+
+-- -----------------------------------------------------------------------------
+-- Manipulating package.conf files