{-# LANGUAGE TupleSections #-} module Options where import Control.Applicative import Control.Monad.M import qualified Data.List as L import Data.Monoid import qualified Distribution.Package as C import qualified Distribution.Version as CV import Distribution.Text import Options.Applicative.Types (readerAsk) import Options.Applicative.Builder import Options.Applicative.Common import Options.Cabal import Options.CabalConstraints (toConstraints, none, CabalConstraints) import Options.DbProvider import PackageId import Pipes import qualified Data.Set as S data Options = Options { dbprovider :: DbProvider, outputDir :: FilePath, quiet :: Bool, cabal :: Maybe FilePath, cabalConstraints :: CabalConstraints, packages :: [C.PackageId] } deriving Show packageReadM :: Text a => ReadM a packageReadM = do s <- readerAsk maybe (readerError $ "failed parsing packages:\n " ++ s) return -- Qualified, as simpleParse is a little obscure. (Distribution.Text.simpleParse s) parser :: Parser Options parser = Options <$> option toProvider (long "dbprovider" <> short 'p' <> metavar "" <> value (CabalSandbox Nothing) <> help "a ghc package db provider: cabal|ghc|dir\n") <*> strOption ( long "output" <> short 'o' <> metavar "" <> value "./docsets" <> help "the directory to write created docsets to") <*> switch (long "quiet" <> short 'q' <> help "set to quiet output") <*> option (Just <$> readerAsk) (long "cabal" <> short 'c' <> metavar "" <> value Nothing <> help "the cabal file to retrieve package dependencies from") <*> option toConstraints (long "cabal-constraints" <> short 'r' <> value none <> metavar "executable=name, .." <> help "limit package results from a cabal file source, see documentation") <*> many ( argument packageReadM (metavar "packages" <> help "a list of packages to specifically build, e.g. either-1.0.1 text" )) -- | Given two lists of package satisfying strings, -- return a list that is non-duplicate, the most versioned of the two. -- e.g. if 'either-4.1.0' is in one, and 'either' is the other, -- the versioned is chosen. If both are versioned, both appear in -- the final result. reduce :: [C.PackageId] -> [C.PackageId] reduce = fromAsc . L.sort where -- Here we exploit the fact that you only need to examine the next member of -- an ascending list to determine a version fromAsc :: [C.PackageId] -> [C.PackageId] fromAsc [] = [] fromAsc ([p]) = [p] fromAsc (p:nxt:rest) | p == nxt = -- duplicate fromAsc (nxt:rest) | unversioned p == unversioned nxt = if unversioned p == p then fromAsc $ nxt:rest else -- both are versioned by list ordering p : nxt : fromAsc rest | otherwise = -- they're different packages p : fromAsc ( nxt : rest ) versionless :: String -> C.PackageId versionless n = C.PackageIdentifier (C.PackageName n) $ CV.Version [] [] prod_Packages :: Options -> ProducerM (S.Set String) () prod_Packages options = do cabal_dependencies <- lift $ case cabal options of Nothing -> return [] Just fp -> (S.toList . S.map versionless) <$> readPackages fp (cabalConstraints options) yield . S.fromList . map (show . disp) $ reduce (packages options ++ cabal_dependencies)