{-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE CPP #-} module Main (main) where import Control.Applicative.Combinators import qualified Control.Applicative.Combinators.NonEmpty as CNE import Control.Monad import qualified Data.List as L import Data.Monoid (Endo(..)) import qualified Options.Applicative as Opt import qualified Options.Applicative.Help as Opt import qualified Prettyprinter as PP import qualified Text.Parsec as P import qualified Text.Parsec.String as P import qualified Distribution.Verbosity as V import Status.Types (StatusDirection(..)) import Hackport.Env import Hackport.Command.List import Hackport.Command.MakeEbuild import Hackport.Command.Update import Hackport.Command.Status import Hackport.Command.Merge import Hackport.Completion import Data.Version (showVersion) import Distribution.Pretty (prettyShow) import Distribution.Simple.Utils (cabalVersion) import qualified Paths_cabal_install import qualified Paths_hackport main :: IO () main = join $ Opt.customExecParser prefs globalParser where prefs :: Opt.ParserPrefs prefs = Opt.prefs $ Opt.showHelpOnEmpty <> Opt.showHelpOnError <> Opt.noBacktrack <> Opt.helpLongEquals ----------------------------------------------------------------------- -- Global ----------------------------------------------------------------------- globalParser :: Opt.ParserInfo (IO ()) globalParser = Opt.info (Opt.helper <*> parser) infoMod where infoMod :: Opt.InfoMod (IO ()) infoMod = Opt.progDesc $ "HackPort is an .ebuild generator from .cabal files " ++ "with hackage index support" parser :: Opt.Parser (IO ()) parser = versionParser <|> numericVersionParser <|> mainParser where versionParser :: Opt.Parser (IO ()) versionParser = Opt.flag' printVersion $ Opt.short 'V' <> Opt.long "version" <> Opt.help "Print version information" where printVersion = putStrLn $ "hackport version " ++ showVersion Paths_hackport.version ++ "\nusing cabal-install " ++ showVersion Paths_cabal_install.version ++ " and the Cabal library version " ++ prettyShow cabalVersion numericVersionParser :: Opt.Parser (IO ()) numericVersionParser = Opt.flag' printNumericVersion $ Opt.long "numeric-version" <> Opt.help "Print just the version number" where printNumericVersion = putStrLn $ showVersion Paths_hackport.version mainParser :: Opt.Parser (IO ()) mainParser = do v <- silentFlag <|> verbosityOption <|> (`appEndo` V.normal) . mconcat <$> many verbosityFlag op <- optional $ Opt.strOption $ Opt.short 'p' <> Opt.long "overlay-path" <> Opt.metavar "PATH" <> helpMulti [ "Override search path list where .hackport/ lives" , "(default list: ['.', paludis-ovls or emerge-ovls])" ] pp <- optional $ Opt.strOption $ Opt.long "portage-path" <> Opt.metavar "PATH" <> Opt.help "Override path to your portage tree" mode <- Opt.hsubparser $ subcmd "list" listAction listParser <> subcmd "make-ebuild" makeEbuildAction makeEbuildParser <> subcmd "update" updateAction updateParser <> subcmd "status" statusAction statusParser <> subcmd "merge" mergeAction mergeParser pure $ mode GlobalEnv { globalVerbosity = v , globalPathToOverlay = op , globalPathToPortage = pp } where subcmd :: String -> Env env () -> Opt.ParserInfo env -> Opt.Mod Opt.CommandFields (GlobalEnv -> IO ()) subcmd name env = Opt.command name . fmap (runEnv env) silentFlag :: Opt.Parser V.Verbosity silentFlag = Opt.flag' V.silent $ Opt.long "silent" <> Opt.help "Use the \"silent\" verbosity level" verbosityString :: P.Parser V.Verbosity verbosityString = choice [ V.silent <$ (P.try (P.string "0") <|> P.string "silent") , V.normal <$ (P.try (P.string "1") <|> P.string "normal") , V.verbose <$ (P.try (P.string "2") <|> P.string "verbose") , V.deafening <$ (P.try (P.string "3") <|> P.string "deafening") ] <* P.eof verbosityFlag :: Opt.Parser (Endo V.Verbosity) verbosityFlag = Opt.flag' (Endo V.moreVerbose) $ Opt.short 'v' <> Opt.help "Increase verbosity level" verbosityOption :: Opt.Parser V.Verbosity verbosityOption = Opt.option readm $ Opt.long "verbosity" <> helpMulti [ "Set verbosity level " , "(0-3) or (" ++ L.intercalate "," (show . fst <$> verbs) ++ ")" ] where readm :: Opt.ReadM V.Verbosity readm = Opt.eitherReader $ either (Left . err) Right . P.runParser verbosityString () "command line option" err :: P.ParseError -> String err _ = show $ PP.vsep $ PP.pretty "Takes a number (0-3) or one of the following values:" : ((\(k,v) -> PP.hsep [PP.pretty (show k), PP.pretty "=", PP.pretty v]) <$> verbs ) verbs = [ ("silent", "No output") , ("normal", "Default verbosity") , ("verbose", "Increased verbosity") , ("deafening", "Maximum verbosity") ] ----------------------------------------------------------------------- -- List ----------------------------------------------------------------------- listParser :: Opt.ParserInfo ListEnv listParser = Opt.info parser infoMod where parser :: Opt.Parser ListEnv parser = do ps <- many $ Opt.strArgument $ Opt.metavar "PACKAGE" pure $ ListEnv ps infoMod :: Opt.InfoMod ListEnv infoMod = Opt.progDesc "List package versions matching pattern" ----------------------------------------------------------------------- -- Make Ebuild ----------------------------------------------------------------------- makeEbuildParser :: Opt.ParserInfo MakeEbuildEnv makeEbuildParser = Opt.info parser infoMod where parser :: Opt.Parser MakeEbuildEnv parser = do cat <- Opt.strArgument $ Opt.metavar "" <> Opt.completer categoryCompleter cabalFiles <- CNE.some $ Opt.strArgument $ Opt.metavar "[EBUILD FILE]" <> Opt.completer (Opt.bashCompleter "file") flags <- optional cabalFlagsParser notOnHackage <- Opt.switch $ Opt.long "not-on-hackage" <> Opt.help "Skip writing the hackage remote-id to metadata.xml" pure $ MakeEbuildEnv cat cabalFiles flags (not notOnHackage) infoMod :: Opt.InfoMod MakeEbuildEnv infoMod = Opt.progDesc "Make an ebuild from a .cabal file" ----------------------------------------------------------------------- -- Update ----------------------------------------------------------------------- updateParser :: Opt.ParserInfo () updateParser = Opt.info parser infoMod where parser :: Opt.Parser () parser = pure () infoMod :: Opt.InfoMod () infoMod = Opt.progDesc "Update the local package database" ----------------------------------------------------------------------- -- Status ----------------------------------------------------------------------- statusParser :: Opt.ParserInfo StatusEnv statusParser = Opt.info parser infoMod where parser :: Opt.Parser StatusEnv parser = do dir <- choice [ Opt.flag' OverlayToPortage $ Opt.long "to-portage" <> Opt.help "Print only packages likely to be interesting to move to the portage tree." , Opt.flag' HackageToOverlay $ Opt.long "from-hackage" <> Opt.help "Print only packages likely to be interesting to move from hackage tree." , pure PortagePlusOverlay ] pkgs <- many $ Opt.strArgument $ Opt.metavar "PACKAGE" pure StatusEnv { statusDirection = dir , statusPackages = pkgs } infoMod :: Opt.InfoMod StatusEnv infoMod = Opt.progDescDoc $ docMulti Opt.sep [ "Show up-to-date status against other repos" , "(hackage, ::gentoo)" ] ----------------------------------------------------------------------- -- Merge ----------------------------------------------------------------------- mergeParser :: Opt.ParserInfo MergeEnv mergeParser = Opt.info parser infoMod where parser :: Opt.Parser MergeEnv parser = do flags <- optional $ cabalFlagsParser pkg <- Opt.strArgument $ Opt.metavar "PACKAGE" <> Opt.completer cabalPackageCompleter pure $ MergeEnv flags pkg infoMod :: Opt.InfoMod MergeEnv infoMod = Opt.progDesc "Make an ebuild out of hackage package" ----------------------------------------------------------------------- -- Misc ----------------------------------------------------------------------- helpMulti :: [String] -> Opt.Mod f a helpMulti = Opt.helpDoc . docMulti Opt.sep docMulti :: ([Opt.Doc] -> Opt.Doc) -> [String] -> Maybe Opt.Doc docMulti f = Opt.unChunk . fmap f . traverse Opt.paragraph cabalFlagsParser :: Opt.Parser String cabalFlagsParser = Opt.strOption $ Opt.short 'f' <> Opt.long "flags" <> Opt.metavar "cabal_flags" <> helpMulti [ "Set cabal flags to certain state. Example:" , "--flags=-all_extensions" ]