haskell-updater-1.4.1.0: Distribution/Gentoo/CmdLine.hs
{- |
Module : Distribution.Gentoo.CmdLine.Types
Functions and logic for parsing command line options and converting them
into a valid internal representation of haskell-updater modes (see
@Distribution.Gentoo.Types.HUMode@).
-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
module Distribution.Gentoo.CmdLine
( parseArgs
, mkHUMode
, options
, argString
) where
import Control.Monad ((>=>))
import Data.Proxy
import System.Console.GetOpt
import Distribution.Gentoo.CmdLine.Types
import Distribution.Gentoo.PkgManager
import Distribution.Gentoo.PkgManager.Types
import Distribution.Gentoo.Types
import qualified Distribution.Gentoo.Types.HUMode as Mode
import Output
parseArgs :: PkgManager -> RawPMArgs -> Either String (CmdLineArgs, RawPMArgs)
parseArgs defPM args = case getOpt' Permute options args of
(_, _, _, errs@(_:_)) -> Left $ unwords $ "Errors in arguments:" : errs
(_, _, unk@(_:_), _) -> Left $ unwords $ "Unknown options:" : unk
(fs, raw, _, _) ->
(,raw) <$> foldr (>=>) pure fs (defCmdLineArgs defPM)
mkHUMode :: CmdLineArgs -> RawPMArgs -> Either String Mode.HUMode
mkHUMode cmdLine raw
| cmdLineHelp cmdLine = pure Mode.HelpMode
| cmdLineVersion cmdLine = pure Mode.VersionMode
| otherwise = do
let pkgMgr = cmdLinePkgManager cmdLine
mPkgMgr <- go pkgMgr
pure $ Mode.RunMode runModifier mPkgMgr
where
go :: PkgManager -> Either String Mode.PkgManager
go pkgMgr = case (pkgMgr, cmdLineMode cmdLine, cmdLineTarget cmdLine) of
(Portage, ReinstallAtomsMode, Right WorldTarget) -> pure
$ Mode.Portage $ Right $ Mode.ReinstallAtomsMode
$ Right $ Mode.WorldTarget
(Portage, ReinstallAtomsMode, Left targs) -> pure
$ Mode.Portage $ Right $ Mode.ReinstallAtomsMode
$ Right $ Mode.CustomTargets targs
(Portage, _, Right WorldTarget) -> Left
"\"world\" target is only valid with reinstall-atoms mode"
(Portage, _, Left _) -> Left
"custom targets are only valid with reinstall-atoms mode"
(Portage, ReinstallAtomsMode, Right targ) -> pure
$ Mode.Portage $ Right $ Mode.ReinstallAtomsMode
$ Left $ convTarget targ
(_, ReinstallAtomsMode, Right WorldTarget) -> Left
"\"world\" target is only valid with portage package manager"
(_, _, Right WorldTarget) -> Left $ unwords
[ "\"world\" target is only valid with reinstall-atoms mode and portage"
, "package manager"]
(_, _, Left _) -> Left $ unwords
[ "custom targets are only valid with reinstall-atoms mode and portage"
, "package manager"]
(_, ReinstallAtomsMode, _) -> Left
"reinstall-atoms mode is only valid with portage package manager"
(_, mode, Right targ) -> pure $ convPkgMgr pkgMgr mode targ
convPkgMgr :: PkgManager -> RunMode -> BuildTarget -> Mode.PkgManager
convPkgMgr Portage mode targ = Mode.Portage $ Left $ convMode mode targ
convPkgMgr Paludis mode targ = Mode.Paludis $ convMode mode targ
convPkgMgr PkgCore mode targ = Mode.PkgCore $ convMode mode targ
convPkgMgr (CustomPM pm) mode targ = Mode.CustomPM pm $ convMode mode targ
convPkgMgr _ _ _ = error "Undefined behavior in convPkgMgr"
convMode :: RunMode -> BuildTarget -> Mode.RunMode
convMode BasicMode targ = Mode.BasicMode (convTarget targ)
convMode ListMode targ = Mode.ListMode (convTarget targ)
convMode _ _ = error "Undefined behavior in convMode"
convTarget :: BuildTarget -> Mode.Target
convTarget OnlyInvalid = Mode.OnlyInvalid
convTarget AllInstalled = Mode.AllInstalled
convTarget _ = error "Undefined behavior in convTarget"
runModifier :: RunModifier
runModifier = RM
{ flags = (if cmdLinePretend cmdLine then (PretendBuild:) else id)
$ if cmdLineNoDeep cmdLine
then [UpdateAsNeeded]
else [UpdateDeep]
, withCmd = cmdLineAction cmdLine
, rawPMArgs = raw
, verbosity = cmdLineVerbosity cmdLine
}
options :: [OptDescr (CmdLineArgs -> Either String CmdLineArgs)]
options =
[ Option ['P'] ["package-manager"]
(ReqArg mkPM "PM")
$ "Use package manager PM, where PM can be one of:\n"
++ pmList ++ defPM
, Option ['C'] ["custom-pm"]
(ReqArg (\s c -> pure $ c { cmdLinePkgManager = CustomPM s }) "command")
$ "Use custom command as package manager;\n"
++ " ignores the --pretend and --no-deep flags."
, Option ['p'] ["pretend"]
(naUpdate $ \c -> c { cmdLinePretend = True } )
"Only pretend to build packages."
, Option [] ["no-deep"]
(naUpdate $ \c -> c { cmdLineNoDeep = True } )
"Don't pull deep dependencies (--deep with emerge)."
, Option ['V'] ["version"]
(naUpdate $ \c -> c { cmdLineVersion = True })
"Version information."
, Option [] ["action"]
(ReqArg (fromCmdline (\a c -> c { cmdLineAction = a })) "action")
(argHelp (Proxy @WithCmd))
, Option [] ["target"]
(ReqArg (fromCmdline (\a -> updateTarget (Right a))) "target")
(argHelp (Proxy @BuildTarget))
, Option ['c'] ["dep-check"]
(naUpdate $ updateTarget (Right OnlyInvalid))
$ "alias for --target=" ++ argString OnlyInvalid
-- deprecated alias for 'dep-check'
, Option ['u'] ["upgrade"]
(naUpdate $ updateTarget (Right OnlyInvalid))
$ "alias for --target=" ++ argString OnlyInvalid
, Option ['a'] ["all"]
(naUpdate $ updateTarget (Right AllInstalled))
$ "alias for --target=" ++ argString AllInstalled
, Option ['W'] ["world"]
(naUpdate $ \c -> updateTarget (Right WorldTarget) c
{ cmdLinePkgManager = Portage
, cmdLineMode = ReinstallAtomsMode
}
) $ "alias for --package-manager=portage"
++ " \\\n --target=" ++ argString WorldTarget
++ " \\\n --mode=" ++ argString ReinstallAtomsMode
, Option ['T'] ["custom-target"]
(ReqArg
(\s c -> pure $ updateTarget (Left s) c
{ cmdLinePkgManager = Portage
, cmdLineMode = ReinstallAtomsMode
}
)
"target")
$ "Use a custom target. May be given multiple times.\n"
++ " Enables portage PM and reinstall-targets mode.\n"
++ " Will override any non-custom targets."
, Option [] ["mode"]
(ReqArg (fromCmdline (\a c -> c { cmdLineMode = a })) "mode")
(argHelp (Proxy @RunMode))
, Option ['l'] ["list-only"]
(naUpdate $ \c -> c { cmdLineMode = ListMode })
$ "alias for --mode=" ++ argString ListMode
, Option ['R'] ["reinstall-atoms"]
(naUpdate $ \c -> c { cmdLineMode = ReinstallAtomsMode })
$ "alias for --mode=" ++ argString ReinstallAtomsMode
, Option ['q'] ["quiet"]
(naUpdate $ \c -> c { cmdLineVerbosity = Quiet })
"Print only fatal errors (to stderr)."
, Option ['v'] ["verbose"]
(naUpdate $ \c -> c { cmdLineVerbosity = Verbose })
"Be more elaborate (to stderr)."
, Option ['h', '?'] ["help"]
(naUpdate $ \c -> c { cmdLineHelp = True })
"Print this help message."
]
where
naUpdate f = NoArg (pure . f)
-- This touches some legacy code so we need a custom handler for it
mkPM :: String -> CmdLineArgs -> Either String CmdLineArgs
mkPM s c = case choosePM s of
InvalidPM pm -> Left $ "Unknown package manager: " ++ pm
Portage -> Right $ c { cmdLinePkgManager = Portage }
Paludis -> Right $ c { cmdLinePkgManager = Paludis }
PkgCore -> Right $ c { cmdLinePkgManager = PkgCore }
CustomPM _ -> error "Undefined behavior in mkPM"
pmList = unlines . map (" * " ++) $ definedPMs
defPM = "The last valid value of PM specified is chosen.\n\
\ The default package manager is: " ++ defaultPMName ++ ",\n\
\ which can be overriden with the \"PACKAGE_MANAGER\"\n\
\ environment variable."
-- Custom targets always override BuildTargets
-- New custom targets are appended to old custom targets
-- New BuildTargets override old BuildTargets
updateTarget :: Either String BuildTarget -> CmdLineArgs -> CmdLineArgs
updateTarget new old =
let newT = case (new, cmdLineTarget old) of
-- Override old BuildTargets with new BuildTargets
(Right t, Right _) -> Right t
-- Append new custom target
(Left s, Left ss) -> Left $ ss ++ [s]
-- Drop old BuildTargets for new custom target
(Left s, Right _) -> Left [s]
-- Drop new BuildTargets in favor of old custom targets
(Right _, Left ss) -> Left ss
in old { cmdLineTarget = newT }