{-# 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 "<provider,args>"
<> value (CabalSandbox Nothing)
<> help "a ghc package db provider: cabal|ghc|dir\n")
<*>
strOption (
long "output"
<> short 'o'
<> metavar "<dir>"
<> 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 "<file.cabal>"
<> 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)