staversion 0.1.2.0 → 0.1.3.0
raw patch · 20 files changed
+1139/−185 lines, 20 filesdep +megaparsecdep ~QuickCheckdep ~basedep ~directoryPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: megaparsec
Dependency ranges changed: QuickCheck, base, directory
API changes (from Hackage documentation)
- Staversion.Internal.BuildPlan: _setDisambiguator :: BuildPlanManager -> Maybe Disambiguator -> IO ()
- Staversion.Internal.BuildPlan: instance Data.Aeson.Types.FromJSON.FromJSON Staversion.Internal.BuildPlan.BuildPlan
- Staversion.Internal.BuildPlan: instance GHC.Base.Monoid Staversion.Internal.BuildPlan.BuildPlan
- Staversion.Internal.BuildPlan: loadBuildPlanYAML :: FilePath -> IO (Either ErrorMsg BuildPlan)
- Staversion.Internal.Query: Result :: PackageSource -> Query -> Either ErrorMsg ResultVersions -> Result
- Staversion.Internal.Query: [resultFor] :: Result -> Query
- Staversion.Internal.Query: [resultIn] :: Result -> PackageSource
- Staversion.Internal.Query: [resultVersions] :: Result -> Either ErrorMsg ResultVersions
- Staversion.Internal.Query: data Result
- Staversion.Internal.Query: data ResultVersions
- Staversion.Internal.Query: instance GHC.Classes.Eq Staversion.Internal.Query.Result
- Staversion.Internal.Query: instance GHC.Classes.Eq Staversion.Internal.Query.ResultVersions
- Staversion.Internal.Query: instance GHC.Show.Show Staversion.Internal.Query.Result
- Staversion.Internal.Query: instance GHC.Show.Show Staversion.Internal.Query.ResultVersions
- Staversion.Internal.Query: resultVersionsFromList :: [(PackageName, Maybe Version)] -> ResultVersions
- Staversion.Internal.Query: resultVersionsToList :: ResultVersions -> [(PackageName, Maybe Version)]
+ Staversion.Internal.BuildPlan: _setLTSDisambiguator :: BuildPlanManager -> Word -> Word -> IO ()
+ Staversion.Internal.BuildPlan: buildPlanSource :: BuildPlan -> PackageSource
+ Staversion.Internal.BuildPlan: class HasVersions t
+ Staversion.Internal.BuildPlan: data BuildPlanMap
+ Staversion.Internal.BuildPlan: instance Data.Aeson.Types.FromJSON.FromJSON Staversion.Internal.BuildPlan.BuildPlanMap
+ Staversion.Internal.BuildPlan: instance GHC.Base.Monoid Staversion.Internal.BuildPlan.BuildPlanMap
+ Staversion.Internal.BuildPlan: instance Staversion.Internal.BuildPlan.HasVersions Staversion.Internal.BuildPlan.BuildPlan
+ Staversion.Internal.BuildPlan: instance Staversion.Internal.BuildPlan.HasVersions Staversion.Internal.BuildPlan.BuildPlanMap
+ Staversion.Internal.BuildPlan: loadBuildPlanMapYAML :: FilePath -> IO (Either ErrorMsg BuildPlanMap)
+ Staversion.Internal.BuildPlan.Stackage: formatExactResolverString :: ExactResolver -> Resolver
+ Staversion.Internal.Cabal: BuildDepends :: Target -> [PackageName] -> BuildDepends
+ Staversion.Internal.Cabal: TargetBenchmark :: Text -> Target
+ Staversion.Internal.Cabal: TargetExecutable :: Text -> Target
+ Staversion.Internal.Cabal: TargetLibrary :: Target
+ Staversion.Internal.Cabal: TargetTestSuite :: Text -> Target
+ Staversion.Internal.Cabal: [depsPackages] :: BuildDepends -> [PackageName]
+ Staversion.Internal.Cabal: [depsTarget] :: BuildDepends -> Target
+ Staversion.Internal.Cabal: data BuildDepends
+ Staversion.Internal.Cabal: data Target
+ Staversion.Internal.Cabal: instance GHC.Classes.Eq Staversion.Internal.Cabal.BuildDepends
+ Staversion.Internal.Cabal: instance GHC.Classes.Eq Staversion.Internal.Cabal.Target
+ Staversion.Internal.Cabal: instance GHC.Classes.Ord Staversion.Internal.Cabal.BuildDepends
+ Staversion.Internal.Cabal: instance GHC.Classes.Ord Staversion.Internal.Cabal.Target
+ Staversion.Internal.Cabal: instance GHC.Show.Show Staversion.Internal.Cabal.BuildDepends
+ Staversion.Internal.Cabal: instance GHC.Show.Show Staversion.Internal.Cabal.Target
+ Staversion.Internal.Cabal: loadCabalFile :: FilePath -> IO (Either ErrorMsg [BuildDepends])
+ Staversion.Internal.Exec: _processCommandWithCustomBuildPlanManager :: (BuildPlanManager -> IO BuildPlanManager) -> Command -> IO [Result]
+ Staversion.Internal.Exec: instance GHC.Classes.Eq Staversion.Internal.Exec.ResolvedQuery
+ Staversion.Internal.Exec: instance GHC.Classes.Ord Staversion.Internal.Exec.ResolvedQuery
+ Staversion.Internal.Exec: instance GHC.Show.Show Staversion.Internal.Exec.ResolvedQuery
+ Staversion.Internal.Query: QueryCabalFile :: FilePath -> Query
+ Staversion.Internal.Query: parseQuery :: String -> Query
+ Staversion.Internal.Result: CabalResultBody :: FilePath -> Target -> [(PackageName, Maybe Version)] -> ResultBody
+ Staversion.Internal.Result: Result :: PackageSource -> Query -> Maybe PackageSource -> Either ErrorMsg ResultBody -> Result
+ Staversion.Internal.Result: SimpleResultBody :: PackageName -> (Maybe Version) -> ResultBody
+ Staversion.Internal.Result: [resultBody] :: Result -> Either ErrorMsg ResultBody
+ Staversion.Internal.Result: [resultFor] :: Result -> Query
+ Staversion.Internal.Result: [resultIn] :: Result -> PackageSource
+ Staversion.Internal.Result: [resultReallyIn] :: Result -> Maybe PackageSource
+ Staversion.Internal.Result: data Result
+ Staversion.Internal.Result: data ResultBody
+ Staversion.Internal.Result: instance GHC.Classes.Eq Staversion.Internal.Result.Result
+ Staversion.Internal.Result: instance GHC.Classes.Eq Staversion.Internal.Result.ResultBody
+ Staversion.Internal.Result: instance GHC.Show.Show Staversion.Internal.Result.Result
+ Staversion.Internal.Result: instance GHC.Show.Show Staversion.Internal.Result.ResultBody
- Staversion.Internal.BuildPlan: packageVersion :: BuildPlan -> PackageName -> Maybe Version
+ Staversion.Internal.BuildPlan: packageVersion :: HasVersions t => t -> PackageName -> Maybe Version
Files
- ChangeLog.md +6/−0
- README.md +34/−3
- src/Staversion/Internal/BuildPlan.hs +62/−28
- src/Staversion/Internal/BuildPlan/Stackage.hs +4/−0
- src/Staversion/Internal/Cabal.hs +137/−0
- src/Staversion/Internal/Command.hs +10/−5
- src/Staversion/Internal/Exec.hs +59/−21
- src/Staversion/Internal/Format.hs +62/−29
- src/Staversion/Internal/Query.hs +10/−25
- src/Staversion/Internal/Result.hs +28/−0
- staversion.cabal +13/−6
- test/NetworkTest.hs +58/−12
- test/Staversion/Internal/BuildPlanSpec.hs +17/−20
- test/Staversion/Internal/CabalSpec.hs +112/−0
- test/Staversion/Internal/ExecSpec.hs +71/−22
- test/Staversion/Internal/FormatSpec.hs +152/−10
- test/Staversion/Internal/TestUtil.hs +14/−4
- test/data/conduit.cabal_test +111/−0
- test/data/doctest.cabal_test +118/−0
- test/data/foobar.cabal_test +61/−0
ChangeLog.md view
@@ -1,5 +1,11 @@ # Revision history for staversion +## 0.1.3.0 -- 2016-12-29++* Now staversion shows the exact resolver for a partial resolver (e.g. "lts-4" -> "lts-4.2")+* Now staversion reads .cabal files, and uses their `build-depends` fields as query.+* Fix minor error in ordering the result.+ ## 0.1.2.0 -- 2016-11-10 * New option `--hackage`, which searches hackage for the latest version number.
README.md view
@@ -2,8 +2,14 @@  -staversion is a command-line tool to look for version numbers of Haskell packages. It mainly focuses on package versions in stackage, i.e. it answers to questions like "What version is the package X in stackage lts-Y.ZZ?" It aims to make it easier to write `build-depends` section in YOUR_PACKAGE.cabal.+staversion is a command-line tool to look for version numbers of Haskell packages. +See `--help` message for detailed usage.++## Package version in Stackage++staversion mainly focuses on package versions in stackage, i.e. it answers to questions like "What version is the package X in stackage lts-Y.ZZ?" It aims to make it easier to write `build-depends` section in YOUR_PACKAGE.cabal.+ $ staversion --resolver lts-4.2 conduit ------ lts-4.2 conduit ==1.2.6.1@@ -26,6 +32,8 @@ staversion first reads build plan YAML files that are stored locally in your computer, then it tries to fetch them over network. +## Package version in Hackage+ You can also look up the latest version numbers hosted on hackage. $ staversion --hackage conduit base@@ -33,12 +41,35 @@ conduit ==1.2.8, base ==4.9.0.0 -See `--help` message for detail.+## Package version for build-depends +You can also specify .cabal files in the query. In that case, staversion reads `build-depends` fields in all sections of those .cabal files, and shows versions of the dependency packages. + $ staversion --hackage staversion.cabal + ------ latest in hackage+ -- staversion.cabal - library+ base ==4.9.0.0,+ unordered-containers ==0.2.7.2,+ aeson ==1.0.2.1,+ text ==1.2.2.1,+ bytestring ==0.10.8.1,+ yaml ==0.8.21.1,+ filepath ==1.4.1.1,+ directory ==1.3.0.0,+ optparse-applicative ==0.13.0.0,+ containers ==0.5.9.1,+ http-client ==0.5.5,+ http-client-tls ==0.3.3,+ http-types ==0.9.1,+ transformers ==0.5.2.0,+ transformers-compat ==0.5.1.4,+ megaparsec ==5.1.2+ + (snip)++ ## TODO -- Read `build-depends` sections .cabal files for package name queries. - Show version number ranges supported by the given resolvers. - Cache build plans in some local storage (SQLite?)
src/Staversion/Internal/BuildPlan.hs view
@@ -4,18 +4,20 @@ -- Maintainer: Toshio Ito <debug.ito@gmail.com> -- -- __This is an internal module. End-users should not use it.__-{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE GeneralizedNewtypeDeriving, TupleSections #-} module Staversion.Internal.BuildPlan ( -- * Entry APIs+ HasVersions(..), BuildPlan,- packageVersion,+ buildPlanSource, BuildPlanManager, newBuildPlanManager, loadBuildPlan, -- * Low-level APIs- loadBuildPlanYAML,+ BuildPlanMap,+ loadBuildPlanMapYAML, -- * For tests- _setDisambiguator+ _setLTSDisambiguator ) where import Control.Applicative (empty, (<$>), (<*>))@@ -33,6 +35,7 @@ import Data.Text (Text, unpack) import Data.Traversable (Traversable(traverse)) import Data.Version (Version)+import Data.Word (Word) import qualified Data.Yaml as Yaml import System.FilePath ((</>), (<.>)) import qualified System.IO.Error as IOE@@ -54,18 +57,18 @@ ( Disambiguator, fetchDisambiguator, parseResolverString,- formatResolverString,- PartialResolver(..), ExactResolver,+ formatExactResolverString,+ PartialResolver(..), ExactResolver(..), fetchBuildPlanYAML ) import Staversion.Internal.BuildPlan.Version (unVersionJSON) -- | A data structure that keeps a map between package names and their -- versions.-newtype BuildPlan = BuildPlan (HM.HashMap PackageName Version) deriving (Monoid)+newtype BuildPlanMap = BuildPlanMap (HM.HashMap PackageName Version) deriving (Monoid) -instance FromJSON BuildPlan where- parseJSON (Object object) = (\p1 p2 -> BuildPlan $ p1 <> p2) <$> core_packages <*> other_packages where+instance FromJSON BuildPlanMap where+ parseJSON (Object object) = (\p1 p2 -> BuildPlanMap $ p1 <> p2) <$> core_packages <*> other_packages where core_packages = parseSysInfo =<< (object .: "system-info") parseSysInfo (Object o) = parseCorePackages =<< (o .: "core-packages") parseSysInfo _ = empty@@ -79,6 +82,22 @@ parsePackageObject _ = empty parseJSON _ = empty +-- | A 'BuildPlanMap' associated with its 'PackageSource'.+data BuildPlan = BuildPlan { buildPlanMap :: BuildPlanMap,+ buildPlanSource :: PackageSource+ }++-- | Types that have mapping between 'PackageName' and 'Version'.+class HasVersions t where+ packageVersion :: t -> PackageName -> Maybe Version++instance HasVersions BuildPlanMap where+ packageVersion (BuildPlanMap bp_map) name = HM.lookup name bp_map++instance HasVersions BuildPlan where+ packageVersion bp = packageVersion (buildPlanMap bp)++ -- | Stateful manager for 'BuildPlan's. data BuildPlanManager = BuildPlanManager { manBuildPlanDir :: FilePath,@@ -129,18 +148,22 @@ handler e = return $ Left (context ++ ": " ++ show e) loadBuildPlan :: BuildPlanManager- -> [PackageName] -- ^ package names whose versions the user is interested in.- -> PackageSource -> IO (Either ErrorMsg BuildPlan)+ -> [PackageName]+ -- ^ package names whose versions the user is interested in.+ -> PackageSource+ -> IO (Either ErrorMsg BuildPlan)+ -- ^ the second result is the real (disambiguated) PackageSource. loadBuildPlan man _ (SourceStackage resolver) = runExceptT impl where impl = loadBuildPlan_stackageLocalFile man resolver `loggedElse'` do e_resolver <- tryDisambiguate man =<< getPresolver- loadBuildPlan_stackageLocalFile man (formatResolverString $ PartialExact e_resolver) `loggedElse'` loadBuildPlan_stackageNetwork man e_resolver+ loadBuildPlan_stackageLocalFile man (formatExactResolverString e_resolver) `loggedElse'` loadBuildPlan_stackageNetwork man e_resolver getPresolver = maybeToLoadM ("Invalid resolver format for stackage.org: " ++ resolver) $ parseResolverString resolver loggedElse' = loggedElse $ manLogger man loadBuildPlan man names SourceHackage = runExceptT impl where impl = do http_man <- httpManagerM man- (mconcat . zipWith registeredVersionToBuildPlan names) <$> mapM (doFetch http_man) names+ build_plan_map <- (mconcat . zipWith registeredVersionToBuildPlanMap names) <$> mapM (doFetch http_man) names+ return $ BuildPlan { buildPlanMap = build_plan_map, buildPlanSource = SourceHackage } logDebug' msg = liftIO $ logDebug (manLogger man) msg doFetch http_man name = do logDebug' ("Ask hackage for the latest version of " ++ unpack name)@@ -151,7 +174,9 @@ yaml_file = manBuildPlanDir man </> resolver <.> "yaml" doLoad = do logDebug (manLogger man) ("Read " ++ yaml_file ++ " for build plan.")- loadBuildPlanYAML yaml_file+ e_build_plan_map <- loadBuildPlanMapYAML yaml_file+ return $ makeBuildPlan <$> e_build_plan_map+ makeBuildPlan bp_map = BuildPlan { buildPlanMap = bp_map, buildPlanSource = SourceStackage resolver } handleIOError :: IOException -> Maybe ErrorMsg handleIOError e | IOE.isDoesNotExistError e = Just $ makeErrorMsg e (yaml_file ++ " not found.") | IOE.isPermissionError e = Just $ makeErrorMsg e ("you cannot open " ++ yaml_file ++ ".")@@ -182,24 +207,33 @@ http_man <- httpManagerM man liftIO $ logDebug (manLogger man) ("Fetch build plan from network: resolver = " ++ show e_resolver) yaml_data <- httpExceptionToLoadM ("Downloading build plan failed: " ++ show e_resolver) $ liftIO $ fetchBuildPlanYAML http_man e_resolver- ExceptT $ return $ parseBuildPlanYAML $ BSL.toStrict yaml_data+ makeBuildPlan <$> (ExceptT $ return $ parseBuildPlanMapYAML $ BSL.toStrict yaml_data)+ where+ makeBuildPlan bp_map = BuildPlan { buildPlanMap = bp_map,+ buildPlanSource = SourceStackage $ formatExactResolverString e_resolver+ } -parseBuildPlanYAML :: BS.ByteString -> Either ErrorMsg BuildPlan-parseBuildPlanYAML = either (Left . toErrorMsg) Right . Yaml.decodeEither' where- toErrorMsg parse_exception = "Error while parsing BuildPlan YAML: " ++ show parse_exception+parseBuildPlanMapYAML :: BS.ByteString -> Either ErrorMsg BuildPlanMap+parseBuildPlanMapYAML = either (Left . toErrorMsg) Right . Yaml.decodeEither' where+ toErrorMsg parse_exception = "Error while parsing BuildPlanMap YAML: " ++ show parse_exception --- | Load a 'BuildPlan' from a file.-loadBuildPlanYAML :: FilePath -> IO (Either ErrorMsg BuildPlan)-loadBuildPlanYAML yaml_file = parseBuildPlanYAML <$> BS.readFile yaml_file where -- TODO: make it memory-efficient!+-- | Load a 'BuildPlanMap' from a file.+loadBuildPlanMapYAML :: FilePath -> IO (Either ErrorMsg BuildPlanMap)+loadBuildPlanMapYAML yaml_file = parseBuildPlanMapYAML <$> BS.readFile yaml_file where -- TODO: make it memory-efficient! -packageVersion :: BuildPlan -> PackageName -> Maybe Version-packageVersion (BuildPlan bp_map) name = HM.lookup name bp_map+registeredVersionToBuildPlanMap :: PackageName -> RegisteredVersions -> BuildPlanMap+registeredVersionToBuildPlanMap name rvers = BuildPlanMap $ HM.fromList $ pairs where+ pairs = case latestVersion rvers of+ Nothing -> []+ Just v -> [(name, v)] _setDisambiguator :: BuildPlanManager -> Maybe Disambiguator -> IO () _setDisambiguator bp_man = writeIORef (manDisambiguator bp_man) -registeredVersionToBuildPlan :: PackageName -> RegisteredVersions -> BuildPlan-registeredVersionToBuildPlan name rvers = BuildPlan $ HM.fromList $ pairs where- pairs = case latestVersion rvers of- Nothing -> []- Just v -> [(name, v)]+_setLTSDisambiguator :: BuildPlanManager+ -> Word -- ^ disambiguated LTS major version+ -> Word -- ^ disambiguated LTS minor version+ -> IO ()+_setLTSDisambiguator bp_man lts_major lts_minor = _setDisambiguator bp_man $ Just disam where+ disam PartialLTSLatest = Just $ ExactLTS lts_major lts_minor+ disam _ = Nothing
src/Staversion/Internal/BuildPlan/Stackage.hs view
@@ -13,6 +13,7 @@ PartialResolver(..), parseResolverString, formatResolverString,+ formatExactResolverString, Disambiguator, fetchDisambiguator, fetchBuildPlanYAML,@@ -84,6 +85,9 @@ PartialLTSLatest -> "lts" PartialLTSMajor major -> "lts-" ++ show major PartialNightlyLatest -> "nightly"++formatExactResolverString :: ExactResolver -> Resolver+formatExactResolverString er = formatResolverString $ PartialExact er type Disambiguator = PartialResolver -> Maybe ExactResolver
+ src/Staversion/Internal/Cabal.hs view
@@ -0,0 +1,137 @@+-- |+-- Module: Staversion.Internal.Cabal+-- Description: functions dealing with .cabal files.+-- Maintainer: Toshio Ito <debug.ito@gmail.com>+--+-- __This is an internal module. End-users should not use it.__+module Staversion.Internal.Cabal+ ( loadCabalFile,+ Target(..),+ BuildDepends(..)+ ) where++import Control.Applicative ((<*), (*>), (<|>), (<*>), many, some)+import Control.Monad (void, mzero, forM)+import Data.Bifunctor (first)+import Data.Char (isAlpha, isDigit, toLower, isSpace)+import Data.List (intercalate, nub)+import Data.Monoid (mconcat)+import Data.Text (pack, Text)+import qualified Data.Text as T+import qualified Data.Text.IO as TIO+import qualified Text.Megaparsec as P+import qualified Text.Megaparsec.Text as P++import Staversion.Internal.Query+ ( PackageName, ErrorMsg+ )++-- | Build target type.+data Target = TargetLibrary -- ^ the @library@ target.+ | TargetExecutable Text -- ^ the @executable NAME@ target.+ | TargetTestSuite Text -- ^ the @test-suite NAME@ target.+ | TargetBenchmark Text -- ^ the @benchmark NAME@ target.+ deriving (Show,Eq,Ord)++-- | A block of @build-depends:@.+data BuildDepends =+ BuildDepends { depsTarget :: Target,+ depsPackages :: [PackageName]+ } deriving (Show,Eq,Ord)++loadCabalFile :: FilePath -> IO (Either ErrorMsg [BuildDepends])+loadCabalFile cabal_filepath = first show <$> P.runParser (cabalParser <* P.eof) cabal_filepath <$> TIO.readFile cabal_filepath++isLineSpace :: Char -> Bool+isLineSpace ' ' = True+isLineSpace '\t' = True+isLineSpace _ = False++indent :: P.Parser Int+indent = length <$> (many $ P.satisfy isLineSpace)++finishLine :: P.Parser ()+finishLine = P.eof <|> void P.eol++emptyLine :: P.Parser ()+emptyLine = indent *> (P.try finishLine <|> comment_line) where+ comment_line = P.string "--" *> P.manyTill P.anyChar finishLine *> pure ()++blockHeadLine :: P.Parser Target+blockHeadLine = target <* trail <* finishLine where+ trail = indent+ target = target_lib <|> target_exe <|> target_test <|> target_bench+ target_lib = P.try (P.string' "library") *> pure TargetLibrary+ target_exe = TargetExecutable <$> targetNamed "executable"+ target_test = TargetTestSuite <$> targetNamed "test-suite"+ target_bench = TargetBenchmark <$> targetNamed "benchmark"+ targetNamed :: String -> P.Parser Text+ targetNamed target_type = P.try (P.string' target_type)+ *> (some $ P.satisfy isLineSpace)+ *> (fmap pack $ some $ P.satisfy (not . isSpace))++fieldStart :: Maybe String -- ^ expected field name. If Nothing, it just don't care.+ -> P.Parser (String, Int) -- ^ (lower-case field name, indent level)+fieldStart mexp_name = do+ level <- indent+ name <- nameParser <* indent <* P.char ':'+ return (map toLower name, level)+ where+ nameParser = case mexp_name of+ Nothing -> some $ P.satisfy $ \c -> not (isLineSpace c || c == ':')+ Just exp_name -> P.string' exp_name++fieldBlock :: P.Parser (String, Text) -- ^ (lower-case field name, block content)+fieldBlock = impl where+ impl = do+ _ <- many $ P.try conditionalLine+ (field_name, level) <- P.try $ fieldStart Nothing+ field_trail <- P.manyTill P.anyChar finishLine+ rest <- remainingLines level+ let text_block = T.intercalate "\n" $ map pack (field_trail : rest)+ return (field_name, text_block)+ remainingLines field_indent_level = reverse <$> go [] where+ go cur_lines = (P.eof *> pure cur_lines) <|> foundSomething cur_lines+ foundSomething cur_lines = do+ void $ many $ P.try emptyLine+ this_level <- P.lookAhead indent+ if this_level <= field_indent_level+ then pure cur_lines+ else do+ _ <- indent+ this_line <- P.manyTill P.anyChar finishLine+ go (this_line : cur_lines)++buildDependsLine :: P.Parser [PackageName]+buildDependsLine = P.space *> (pname `P.endBy` ignored) where+ pname = pack <$> (some $ P.satisfy allowedChar)+ allowedChar '-' = True+ allowedChar '_' = True+ allowedChar c = isAlpha c || isDigit c+ ignored = P.manyTill P.anyChar finishItem *> P.space+ finishItem = P.eof <|> (void $ P.char ',')++conditionalLine :: P.Parser ()+conditionalLine = void $ indent *> (term "if" <|> term "else") *> P.manyTill P.anyChar finishLine where+ term :: String -> P.Parser ()+ term t = P.try (P.string' t *> P.lookAhead P.space)++targetBlock :: P.Parser BuildDepends+targetBlock = do+ target <- P.try blockHeadLine+ _ <- many $ P.try emptyLine+ fields <- some fieldBlock+ let build_deps_blocks = map snd $ filter (("build-depends" ==) . fst) $ fields+ packages <- fmap (nub . concat) $ forM build_deps_blocks $ \block -> do+ either (fail . show) return $ P.runParser (buildDependsLine <* P.space <* P.eof) "build-depends" block+ return $ BuildDepends { depsTarget = target,+ depsPackages = packages+ }++cabalParser :: P.Parser [BuildDepends]+cabalParser = reverse <$> go [] where+ go cur_deps = targetBlockParsed cur_deps <|> (P.eof *> pure cur_deps) <|> ignoreLine cur_deps+ targetBlockParsed cur_deps = do+ new_dep <- targetBlock+ go (new_dep : cur_deps)+ ignoreLine cur_deps = P.manyTill P.anyChar finishLine *> go cur_deps
src/Staversion/Internal/Command.hs view
@@ -25,6 +25,7 @@ ( Resolver, PackageName, Query(..),+ parseQuery, PackageSource(..) ) @@ -81,11 +82,15 @@ Opt.short 'H', Opt.help "Search hackage.org for the latest version." ]- queries = some $ QueryName <$> package- package = fmap pack $ Opt.strArgument- $ mconcat [ Opt.help "Name of package whose version you want to check.",- Opt.metavar "PACKAGE_NAME"- ]+ queries = some $ parseQuery <$> (query_package <|> query_cabal)+ query_package = Opt.strArgument+ $ mconcat [ Opt.help "Name of package whose version you want to check.",+ Opt.metavar "PACKAGE_NAME"+ ]+ query_cabal = Opt.strArgument+ $ mconcat [ Opt.help "(EXPERIMENTAL) .cabal file name. It checks versions of packages in build-deps lists.",+ Opt.metavar "CABAL_FILEPATH"+ ] network = not <$> no_network no_network = Opt.switch $ mconcat [ Opt.long "no-network", Opt.help "Forbid network access."
src/Staversion/Internal/Exec.hs view
@@ -6,7 +6,9 @@ -- __This is an internal module. End-users should not use it.__ module Staversion.Internal.Exec ( main,- processCommand+ processCommand,+ -- * Only for testing+ _processCommandWithCustomBuildPlanManager ) where import Control.Applicative ((<$>))@@ -16,48 +18,84 @@ import qualified Data.Text.Lazy.IO as TLIO import Staversion.Internal.BuildPlan- ( BuildPlan, packageVersion, newBuildPlanManager, loadBuildPlan+ ( BuildPlan, packageVersion, buildPlanSource,+ newBuildPlanManager, loadBuildPlan,+ BuildPlanManager ) import Staversion.Internal.Command ( parseCommandArgs, Command(..) ) import Staversion.Internal.Format (formatResultsCabal)-import Staversion.Internal.Log (logDebug, logError)+import Staversion.Internal.Log (logDebug, logError, Logger) import Staversion.Internal.Query- ( Query(..), Result(..), PackageSource(..), PackageName,- resultVersionsFromList, ResultVersions,- ErrorMsg+ ( Query(..), PackageSource(..), PackageName, ErrorMsg )+import Staversion.Internal.Result (Result(..), ResultBody(..))+import Staversion.Internal.Cabal (BuildDepends(..), loadCabalFile) main :: IO () main = do comm <- parseCommandArgs (TLIO.putStr . formatResultsCabal) =<< (processCommand comm) +data ResolvedQuery = RQueryOne Query PackageName+ | RQueryCabal Query FilePath BuildDepends+ deriving (Show, Eq, Ord)+ processCommand :: Command -> IO [Result]-processCommand comm = impl where+processCommand = _processCommandWithCustomBuildPlanManager return++_processCommandWithCustomBuildPlanManager :: (BuildPlanManager -> IO BuildPlanManager) -> Command -> IO [Result]+_processCommandWithCustomBuildPlanManager customBPM comm = impl where impl = do- bp_man <- newBuildPlanManager (commBuildPlanDir comm) (commLogger comm) (commAllowNetwork comm)- fmap concat $ mapM (processQueriesIn bp_man) $ commSources comm+ bp_man <- customBPM =<< newBuildPlanManager (commBuildPlanDir comm) (commLogger comm) (commAllowNetwork comm)+ rqueries <- resolveQueries' logger $ commQueries comm+ fmap concat $ mapM (processQueriesIn bp_man rqueries) $ commSources comm logger = commLogger comm- processQueriesIn bp_man source = do- queried_names <- fmap (nub . concat) $ mapM getQueriedPackageNames $ commQueries comm+ processQueriesIn bp_man rqueries source = do+ let queried_names = nub $ concat $ map getQueriedPackageNames $ rqueries logDebug logger ("Retrieve package source " ++ show source) e_build_plan <- loadBuildPlan bp_man queried_names source logBuildPlanResult e_build_plan- return $ map (makeResult source e_build_plan) $ commQueries comm- makeResult source e_build_plan query = case e_build_plan of- Left error_msg -> Result { resultIn = source, resultFor = query, resultVersions = Left error_msg }- Right build_plan -> Result { resultIn = source, resultFor = query,- resultVersions = Right $ searchVersions build_plan query+ return $ map (makeResult source e_build_plan) $ rqueries+ makeResult source e_build_plan rquery = case e_build_plan of+ Left error_msg -> Result { resultIn = source, resultReallyIn = Nothing,+ resultFor = originalQuery rquery, resultBody = Left error_msg+ }+ Right build_plan -> Result { resultIn = source,+ resultReallyIn = if source == real_source then Nothing else Just real_source,+ resultFor = originalQuery rquery,+ resultBody = Right $ searchVersions build_plan rquery }+ where real_source = buildPlanSource build_plan logBuildPlanResult (Right _) = logDebug logger ("Successfully retrieved build plan.") logBuildPlanResult (Left error_msg) = logError logger ("Failed to load build plan: " ++ error_msg) -searchVersions :: BuildPlan -> Query -> ResultVersions-searchVersions build_plan (QueryName package_name) =- resultVersionsFromList [(package_name, packageVersion build_plan package_name)]+resolveQueries' :: Logger -> [Query] -> IO [ResolvedQuery]+resolveQueries' logger = fmap concat . mapM resolveQ where+ resolveQ query = reportAndFilterError =<< resolveQuery logger query+ reportAndFilterError (Left err) = logError logger err >> return []+ reportAndFilterError (Right ret) = return ret -getQueriedPackageNames :: Query -> IO [PackageName]-getQueriedPackageNames (QueryName n) = return [n]+resolveQuery :: Logger -> Query -> IO (Either ErrorMsg [ResolvedQuery])+resolveQuery _ q@(QueryName name) = return $ Right $ [RQueryOne q name]+resolveQuery logger q@(QueryCabalFile file) = do+ logDebug logger ("Load " ++ file ++ " for build-depends fields.")+ (fmap . fmap) processBuildDependsList $ loadCabalFile file+ where+ processBuildDependsList = map (RQueryCabal q file) . filter ((0 <) . length . depsPackages)++originalQuery :: ResolvedQuery -> Query+originalQuery (RQueryOne q _) = q+originalQuery (RQueryCabal q _ _) = q++searchVersions :: BuildPlan -> ResolvedQuery -> ResultBody+searchVersions build_plan (RQueryOne _ package_name) = SimpleResultBody package_name $ packageVersion build_plan package_name+searchVersions build_plan (RQueryCabal _ cabal_file build_deps) = CabalResultBody cabal_file target ret_list where+ target = depsTarget build_deps+ ret_list = map (\pname -> (pname, packageVersion build_plan pname)) $ depsPackages build_deps++getQueriedPackageNames :: ResolvedQuery -> [PackageName]+getQueriedPackageNames (RQueryOne _ n) = [n]+getQueriedPackageNames (RQueryCabal _ _ bd) = depsPackages bd
src/Staversion/Internal/Format.hs view
@@ -13,18 +13,19 @@ import Data.Monoid (mempty, mconcat, (<>)) import qualified Data.Text.Lazy as TL import Data.Text.Lazy.Builder (Builder, toLazyText, fromText, fromString)-import Data.Version (showVersion)+import Data.Version (showVersion, Version) import Staversion.Internal.Query- ( Result(..), Query(..),+ ( Query(..), sourceDesc,- ResultVersions,- resultVersionsToList+ PackageName )+import Staversion.Internal.Result (Result(..), ResultBody(..))+import Staversion.Internal.Cabal (Target(..)) -- | format 'Result's like it's in build-depends in .cabal files. formatResultsCabal :: [Result] -> TL.Text-formatResultsCabal = toLazyText . mconcat . map formatGroupedResultsCabal . groupAllPreservingOrderBy ((==) `on` resultIn)+formatResultsCabal = toLazyText . mconcat . map formatResultBlock . makeSourceBlocks groupAllPreservingOrderBy :: (a -> a -> Bool) -> [a] -> [[a]] groupAllPreservingOrderBy sameGroup = map snd . foldr f [] where@@ -32,31 +33,63 @@ update heads [] = (item, [item]) : heads update heads (cur@(cur_item, cur_list) : rest) = if sameGroup item cur_item- then heads ++ ( (cur_item, item : cur_list) : rest )+ then ((cur_item, item : cur_list) : heads) ++ rest else update (heads ++ [cur]) rest -formatGroupedResultsCabal :: [Result] -> Builder-formatGroupedResultsCabal [] = mempty-formatGroupedResultsCabal results@(head_ret : _) = header <> (concatLines $ single_result_output =<< results) where- header = "------ " <> (fromText $ sourceDesc $ resultIn head_ret) <> "\n"- single_result_output ret = case resultVersions ret of- Left _ -> [Left $ error_result ret]- Right versions -> formatVersionsCabal (resultFor ret) versions- error_result ret = case resultFor ret of- QueryName query_name -> "-- " <> fromText query_name <> " ERROR"- concatLines ebuilder_lines = (mconcat $ intersperse "\n" $ map (either id id) $ tailCommas ebuilder_lines) <> "\n\n"- tailCommas = fst . foldr f ([], False) where- -- flag: True if it has already encountered the last Right element in the list.- f eb (ret, flag) = let (next_e, next_flag) = getNext ret flag eb- in (next_e:ret, next_flag)- getNext [] flag e@(Left _) = (e, flag)- getNext _ flag (Left b) = (Left (b <> ","), flag)- getNext _ False e@(Right _) = (e, True)- getNext _ True (Right b) = (Right (b <> ","), True)+-- | 'Left' lines and 'Right' lines are handled differently by+-- 'formatResultBlock'. It puts commas at the right places assuming+-- 'Left' lines are commented out.+type ResultLine = Either Builder Builder -formatVersionsCabal :: Query -> ResultVersions -> [Either Builder Builder]-formatVersionsCabal (QueryName _) rvers = map format $ resultVersionsToList rvers where- format (name, mver) = case mver of- Nothing -> Left $ "-- " <> fromText name <> " N/A"- Just ver -> Right $ fromText name <> " ==" <> (fromString $ showVersion ver)+data ResultBlock = RBHead Builder [ResultBlock] -- ^ header and child blocks+ | RBLines [ResultLine] -- ^ a block, which consists of some lines. +makeSourceBlocks :: [Result] -> [ResultBlock]+makeSourceBlocks = map sourceBlock . groupAllPreservingOrderBy ((==) `on` resultIn) where+ sourceBlock [] = RBLines []+ sourceBlock results@(head_ret : _) = RBHead header $ makeQueryBlocks results where+ header = "------ " <> (fromText $ sourceDesc $ resultIn head_ret) <> header_real_source+ header_real_source = maybe "" fromText $ resultReallyIn head_ret >>= \real_source -> do+ return (" (" <> sourceDesc real_source <> ")")++makeQueryBlocks :: [Result] -> [ResultBlock]+makeQueryBlocks = uncurry prependLines . foldr f ([], []) where+ prependLines blocks [] = blocks+ prependLines blocks rlines = (RBLines rlines) : blocks+ f ret (blocks, rlines) = case (resultFor ret, resultBody ret) of+ (_, Right (SimpleResultBody name mver)) -> (blocks, (versionLine name mver) : rlines)+ (_, Right (CabalResultBody file target pairs)) -> (cabalFileSuccessBlock file target pairs : prependLines blocks rlines, [])+ ((QueryName name), Left _) -> (blocks, (packageErrorLine name) : rlines)+ ((QueryCabalFile file), Left _) -> (cabalFileErrorBlock file : prependLines blocks rlines, [])++versionLine :: PackageName -> Maybe Version -> ResultLine+versionLine name Nothing = Left $ "-- " <> fromText name <> " N/A"+versionLine name (Just ver) = Right $ fromText name <> " ==" <> (fromString $ showVersion ver)++packageErrorLine :: PackageName -> ResultLine+packageErrorLine name = Left $ "-- " <> fromText name <> " ERROR"++cabalFileErrorBlock :: FilePath -> ResultBlock+cabalFileErrorBlock file = RBLines [Left line] where+ line = "-- " <> fromString file <> " ERROR"++cabalFileSuccessBlock :: FilePath -> Target -> [(PackageName, Maybe Version)] -> ResultBlock+cabalFileSuccessBlock file target pairs = RBHead header [RBLines $ map (uncurry versionLine) pairs] where+ header = "-- " <> fromString file <> " - " <> target_text+ target_text = case target of+ TargetLibrary -> "library"+ TargetExecutable n -> "executable " <> fromText n+ TargetTestSuite n -> "test-suite " <> fromText n+ TargetBenchmark n -> "benchmark " <> fromText n++formatResultBlock :: ResultBlock -> Builder+formatResultBlock (RBHead header blocks) = header <> "\n" <> mconcat (map formatResultBlock blocks)+formatResultBlock (RBLines rlines) = (mconcat $ map ((<> "\n") . either id id) $ tailCommas rlines) <> "\n" where+ tailCommas = fst . foldr f ([], False)+ -- flag: True if it has already encountered the last Right element in the list.+ f eb (ret, flag) = let (next_e, next_flag) = getNext ret flag eb+ in (next_e:ret, next_flag)+ getNext [] flag e@(Left _) = (e, flag)+ getNext _ flag (Left b) = (Left (b <> ","), flag)+ getNext _ False e@(Right _) = (e, True)+ getNext _ True (Right b) = (Right (b <> ","), True)
src/Staversion/Internal/Query.hs view
@@ -8,18 +8,14 @@ ( PackageName, Resolver, PackageSource(..),+ sourceDesc, Query(..),- ErrorMsg,- Result(..),- ResultVersions,- resultVersionsFromList,- resultVersionsToList,- sourceDesc+ parseQuery,+ ErrorMsg ) where -import qualified Data.HashMap.Strict as HM+import Data.List (isSuffixOf) import Data.Text (Text, pack)-import Data.Version (Version) type PackageName = Text @@ -33,28 +29,17 @@ -- | Query for package version(s). data Query = QueryName PackageName+ | QueryCabalFile FilePath deriving (Show,Eq,Ord) type ErrorMsg = String --- | Result for a query.-data Result = Result { resultIn :: PackageSource,- resultFor :: Query,- resultVersions :: Either ErrorMsg ResultVersions- } deriving (Show,Eq)----- | The obtained version map.-newtype ResultVersions = ResultVersions (HM.HashMap PackageName (Maybe Version))- deriving (Show,Eq)--resultVersionsFromList :: [(PackageName, Maybe Version)] -> ResultVersions-resultVersionsFromList = ResultVersions . HM.fromList--resultVersionsToList :: ResultVersions -> [(PackageName, Maybe Version)]-resultVersionsToList (ResultVersions m) = HM.toList m- -- | description of a 'PackageSource'. sourceDesc :: PackageSource -> Text sourceDesc (SourceStackage r) = pack r sourceDesc SourceHackage = "latest in hackage"++parseQuery :: String -> Query+parseQuery s = if ".cabal" `isSuffixOf` s+ then QueryCabalFile s+ else QueryName $ pack s
+ src/Staversion/Internal/Result.hs view
@@ -0,0 +1,28 @@+-- |+-- Module: Staversion.Internal.Result+-- Description: Result data type and its utilities+-- Maintainer: Toshio Ito <debug.ito@gmail.com>+--+-- __This is an internal module. End-users should not use it.__+module Staversion.Internal.Result+ ( Result(..),+ ResultBody(..)+ ) where++import Data.Version (Version)+import Staversion.Internal.Query+ ( Query, PackageSource, ErrorMsg, PackageName+ )+import Staversion.Internal.Cabal (Target)++-- | Result for a query.+data Result = Result { resultIn :: PackageSource,+ resultFor :: Query,+ resultReallyIn :: Maybe PackageSource,+ -- ^ the true PackageSource resolved (or redirected) from 'resultIn', if any.+ resultBody :: Either ErrorMsg ResultBody+ } deriving (Show,Eq)++data ResultBody = SimpleResultBody PackageName (Maybe Version)+ | CabalResultBody FilePath Target [(PackageName, (Maybe Version))]+ deriving (Show,Eq)
staversion.cabal view
@@ -1,5 +1,5 @@ name: staversion-version: 0.1.2.0+version: 0.1.3.0 author: Toshio Ito <debug.ito@gmail.com> maintainer: Toshio Ito <debug.ito@gmail.com> license: BSD3@@ -16,7 +16,10 @@ test/data/lts-4.2.yaml, test/data/lts-2.22_conpact.yaml, test/data/snapshots.json,- test/data/aeson_preferred.json+ test/data/aeson_preferred.json,+ test/data/doctest.cabal_test,+ test/data/conduit.cabal_test,+ test/data/foobar.cabal_test homepage: https://github.com/debug-ito/staversion bug-reports: https://github.com/debug-ito/staversion/issues@@ -26,12 +29,14 @@ hs-source-dirs: src ghc-options: -Wall -fno-warn-unused-imports default-extensions: OverloadedStrings- other-extensions: CPP, DeriveDataTypeable, GeneralizedNewtypeDeriving+ other-extensions: CPP, DeriveDataTypeable, TupleSections exposed-modules: Staversion.Internal.BuildPlan, Staversion.Internal.BuildPlan.Stackage, Staversion.Internal.BuildPlan.Hackage, Staversion.Internal.BuildPlan.Version, Staversion.Internal.Query,+ Staversion.Internal.Result,+ Staversion.Internal.Cabal, Staversion.Internal.Command, Staversion.Internal.Log, Staversion.Internal.Exec,@@ -45,14 +50,15 @@ bytestring >=0.10.0 && <0.11, yaml >=0.8.3 && <0.9, filepath >=1.3.0 && <1.5,- directory >=1.2.0 && <1.3,+ directory >=1.2.0 && <1.4, optparse-applicative >=0.11.0 && <0.14, containers >=0.5.5 && <0.6, http-client >=0.4.18 && <0.6, http-client-tls >=0.2.2 && <0.4, http-types >=0.8.6 && <0.10, transformers >=0.3.0 && <0.6,- transformers-compat >=0.4.0 && <0.6+ transformers-compat >=0.4.0 && <0.6,+ megaparsec >=4.2.0 && <5.2 executable staversion default-language: Haskell2010@@ -78,10 +84,11 @@ Staversion.Internal.BuildPlan.VersionSpec, Staversion.Internal.ExecSpec, Staversion.Internal.FormatSpec,+ Staversion.Internal.CabalSpec, Staversion.Internal.TestUtil build-depends: base, staversion, text, filepath, bytestring, hspec >=2.1.7 && <2.4,- QuickCheck+ QuickCheck >=2.8.1 && <2.10 flag network-test description: Enable network tests.
test/NetworkTest.hs view
@@ -1,7 +1,9 @@ module Main (main,spec) where import Control.Applicative ((<$>))+import Control.Monad (forM_) import qualified Data.ByteString.Lazy as BSL+import Data.Word (Word) import Network.HTTP.Client (newManager, Manager) import Network.HTTP.Client.TLS (tlsManagerSettings) import Test.Hspec@@ -12,21 +14,23 @@ import Staversion.Internal.BuildPlan ( newBuildPlanManager, loadBuildPlan,- packageVersion+ packageVersion,+ buildPlanSource ) import Staversion.Internal.BuildPlan.Hackage (fetchPreferredVersions, latestVersion) import Staversion.Internal.BuildPlan.Stackage ( fetchDisambiguator, fetchBuildPlanYAML,- PartialResolver(..), ExactResolver(..)+ PartialResolver(..), ExactResolver(..),+ parseResolverString ) import Staversion.Internal.Command (Command(..)) import Staversion.Internal.Exec (processCommand) import Staversion.Internal.Log (defaultLogger, Logger(loggerThreshold)) import Staversion.Internal.Query- ( PackageSource(..), ErrorMsg, Query(..), Result(..),- resultVersionsToList+ ( PackageSource(..), ErrorMsg, Query(..) )+import Staversion.Internal.Result (Result(..), ResultBody(..)) main :: IO () main = hspec spec@@ -45,7 +49,10 @@ e_dis <- fetchDisambiguator man case e_dis of Left err -> expectationFailure ("should not be Left: " ++ err)- Right dis -> dis (PartialLTSMajor 2) `shouldBe` Just (ExactLTS 2 22)+ Right dis -> forM_ [(0,7), (1,15), (2,22), (3,22), (4,2), (5,18), (6,24), (7,8)] $ \(major, minor_min) -> do+ case dis (PartialLTSMajor major) of+ Just eresolver -> (PartialExact eresolver) `shouldBeAboveLTSMinorResolver` (major,minor_min)+ Nothing -> expectationFailure "Unexpected disambiguation error." describe "fetchBuildPlanYAML" $ do it "fetches a LTS build plan" $ \man -> do raw_yaml <- fetchBuildPlanYAML man (ExactLTS 2 22)@@ -66,19 +73,34 @@ isJustAnd :: Maybe a -> (a -> Bool) -> Bool isJustAnd m p = maybe False p m +shouldBeAboveLTSMinorResolver :: PartialResolver -> (Word,Word) -> IO ()+shouldBeAboveLTSMinorResolver (PartialExact (ExactLTS got_major got_minor)) (lts_major, lts_minor_min) = do+ got_major `shouldBe` lts_major+ got_minor `shouldSatisfy` (>= lts_minor_min)+shouldBeAboveLTSMinorResolver pr _ = expectationFailure ("Unexpected PartialResolver: " ++ show pr)++shouldBeAboveLTSMinor :: PackageSource -> (Word, Word) -> IO ()+shouldBeAboveLTSMinor (SourceStackage resolver) expected_lts =+ case parseResolverString resolver of+ Just presolver -> presolver `shouldBeAboveLTSMinorResolver` expected_lts+ ret_parse -> expectationFailure ("Unexpected parse result: " ++ show ret_parse)+shouldBeAboveLTSMinor source _ = expectationFailure ("Unexpected PackageSource: " ++ show source)+ spec_BuildPlan :: Spec spec_BuildPlan = describe "BuildPlan" $ do describe "loadBuildPlan from Stackage" $ do it "disambiguates LTS version and fetches a valid BuildPlan" $ do bp_man <- newBuildPlanManager "." quietLogger True bp <- expectRight "loadBuildPlan failed: " =<< loadBuildPlan bp_man [] (SourceStackage "lts-5")- packageVersion bp "base" `shouldBe` Just (ver [4,8,2,0])- packageVersion bp "bytestring" `shouldBe` Just (ver [0,10,6,0])- packageVersion bp "conduit" `shouldBe` Just (ver [1,2,6,6])+ buildPlanSource bp `shouldBeAboveLTSMinor` (5,18)+ packageVersion bp "base" `shouldSatisfy` (`isJustAnd` (>= ver [4,8,2,0]))+ packageVersion bp "bytestring" `shouldSatisfy` (`isJustAnd` (>= ver [0,10,6,0]))+ packageVersion bp "conduit" `shouldSatisfy` (`isJustAnd` (>= ver [1,2,6,6])) describe "loadBuildPlan from Hackage" $ do it "fetches BuildPlan for queried packages" $ do bp_man <- newBuildPlanManager "." quietLogger True bp <- expectRight "loadBuildPlan failed: " =<< loadBuildPlan bp_man ["base", "lens", "transformers"] SourceHackage+ buildPlanSource bp `shouldBe` SourceHackage packageVersion bp "base" `shouldSatisfy` (`isJustAnd` (>= ver [4,9,0,0])) packageVersion bp "lens" `shouldSatisfy` (`isJustAnd` (>= ver [4,15,1])) packageVersion bp "transformers" `shouldSatisfy` (`isJustAnd` (>= ver [0,5,2,0]))@@ -95,9 +117,28 @@ spec_Exec :: Spec spec_Exec = describe "Exec" $ describe "processCommand" $ do- specify "search hackage" $ do+ it "should fill resultReallyIn field if necesssary" $ do let comm = Command { commBuildPlanDir = ".", commLogger = quietLogger,+ commSources = [SourceStackage "lts-3"],+ commQueries = [QueryName "base"],+ commAllowNetwork = True+ }+ [ret] <- processCommand comm+ resultIn ret `shouldBe` SourceStackage "lts-3"+ resultFor ret `shouldBe` QueryName "base"+ case resultBody ret of+ Right (SimpleResultBody got_name (Just got_version)) -> do+ got_name `shouldBe` "base"+ got_version `shouldSatisfy` (>= ver [4,8,1,0])+ body -> expectationFailure ("Unexpected body: " ++ show body)+ case resultReallyIn ret of+ Just source -> source `shouldBeAboveLTSMinor` (3,22)+ ret_really_in -> expectationFailure ("Unexpected resultReallyIn: " ++ show ret_really_in)++ it "should search hackage" $ do+ let comm = Command { commBuildPlanDir = ".",+ commLogger = quietLogger, commSources = [SourceHackage], commQueries = [QueryName "base"], commAllowNetwork = True@@ -105,8 +146,13 @@ [ret] <- processCommand comm resultIn ret `shouldBe` SourceHackage resultFor ret `shouldBe` QueryName "base"- [(got_name, Just got_version)] <- resultVersionsToList <$> expectRight "processCommand error" (resultVersions ret)- got_name `shouldBe` "base"- got_version `shouldSatisfy` (>= ver [4,9,0,0]) + resultReallyIn ret `shouldBe` Nothing+ case resultBody ret of+ Right (SimpleResultBody got_name (Just got_version)) -> do+ got_name `shouldBe` "base"+ got_version `shouldSatisfy` (>= ver [4,9,0,0])+ body -> expectationFailure ("Unexpected body: " ++ show body)+ +
test/Staversion/Internal/BuildPlanSpec.hs view
@@ -2,23 +2,21 @@ import Data.Text (Text, pack) import Data.Version (Version(..))+import Data.Word (Word) import System.FilePath ((</>), (<.>)) import Test.Hspec import Staversion.Internal.Log (defaultLogger, loggerThreshold) import Staversion.Internal.Query (PackageName, PackageSource(..)) import Staversion.Internal.BuildPlan- ( BuildPlan, - loadBuildPlanYAML, + ( BuildPlanMap, + loadBuildPlanMapYAML, packageVersion, BuildPlanManager, newBuildPlanManager,- _setDisambiguator,- loadBuildPlan- )-import Staversion.Internal.BuildPlan.Stackage- ( Disambiguator,- PartialResolver(..), ExactResolver(..)+ _setLTSDisambiguator,+ loadBuildPlan,+ buildPlanSource ) main :: IO ()@@ -31,7 +29,7 @@ packageVersion_spec :: Spec packageVersion_spec = describe "packageVersion" $ do- forBuildPlan "conpact_build_plan" $ \loader -> do+ forBuildPlanMap "conpact_build_plan" $ \loader -> do specify "drawille -> 0.1.0.6" $ do loadVersion "drawille" loader `shouldReturn` Just (Version [0,1,0,6] []) specify "unknown -> Nothing" $ do@@ -41,27 +39,27 @@ specify "base -> 4.8.2.0" $ do loadVersion "base" loader `shouldReturn` Just (Version [4,8,2,0] []) - forBuildPlan "lts-4.2" $ \loader -> do+ forBuildPlanMap "lts-4.2" $ \loader -> do specify "conduit -> 1.2.6.1" $ do loadVersion "conduit" loader `shouldReturn` Just (Version [1,2,6,1] []) specify "transformers -> 0.4.2.0" $ do loadVersion "transformers" loader `shouldReturn` Just (Version [0,4,2,0] []) -forBuildPlan :: String -> (IO BuildPlan -> Spec) -> Spec-forBuildPlan build_plan_base testWith = describe build_plan_base (testWith loader) where- loader = either error return =<< loadBuildPlanYAML ("test" </> "data" </> build_plan_base <.> "yaml")+forBuildPlanMap :: String -> (IO BuildPlanMap -> Spec) -> Spec+forBuildPlanMap build_plan_base testWith = describe build_plan_base (testWith loader) where+ loader = either error return =<< loadBuildPlanMapYAML ("test" </> "data" </> build_plan_base <.> "yaml") -loadVersion :: PackageName -> IO BuildPlan -> IO (Maybe Version)+loadVersion :: PackageName -> IO BuildPlanMap -> IO (Maybe Version) loadVersion package_name loader = do plan <- loader return $ packageVersion plan package_name -mockBuildPlanManager :: Disambiguator -> IO BuildPlanManager-mockBuildPlanManager disam = do+mockBuildPlanManager :: Word -> Word -> IO BuildPlanManager+mockBuildPlanManager lts_major lts_minor = do bp_man <- newBuildPlanManager build_plan_dir logger False- _setDisambiguator bp_man (Just disam)+ _setLTSDisambiguator bp_man lts_major lts_minor return bp_man where build_plan_dir = "test" </> "data"@@ -70,8 +68,7 @@ loadBuildPlan_spec :: Spec loadBuildPlan_spec = describe "loadBuildPlan" $ do it "reads local file after disambiguation" $ do- let disam (PartialLTSLatest) = Just $ ExactLTS 4 2- disam _ = Nothing- bp_man <- mockBuildPlanManager disam+ bp_man <- mockBuildPlanManager 4 2 bp <- either (\e -> error ("Error: " ++ e)) return =<< loadBuildPlan bp_man [] (SourceStackage "lts") packageVersion bp "base" `shouldBe` (Just $ Version [4,8,2,0] [])+ buildPlanSource bp `shouldBe` SourceStackage "lts-4.2"
+ test/Staversion/Internal/CabalSpec.hs view
@@ -0,0 +1,112 @@+module Staversion.Internal.CabalSpec (main,spec) where++import System.FilePath ((</>), (<.>))+import Test.Hspec++import Staversion.Internal.Cabal (loadCabalFile, BuildDepends(..), Target(..))++main :: IO ()+main = hspec spec++shouldBeParsedTo :: FilePath -> [BuildDepends] -> IO ()+shouldBeParsedTo cabal_file expectation = do+ got_deps <- either error return =<< loadCabalFile ("test" </> "data" </> cabal_file)+ got_deps `shouldBe` expectation++spec :: Spec+spec = describe "loadCabalFile" $ do+ it "should load library, executable and test-suite targets" $ do+ "doctest.cabal_test" `shouldBeParsedTo`+ [ BuildDepends { depsTarget = TargetLibrary,+ depsPackages = [ "base",+ "base-compat",+ "ghc",+ "syb",+ "deepseq",+ "directory",+ "filepath",+ "process",+ "ghc-paths",+ "transformers"+ ]+ },+ BuildDepends { depsTarget = TargetExecutable "doctest",+ depsPackages = [ "base",+ "doctest"+ ]+ },+ BuildDepends { depsTarget = TargetTestSuite "spec",+ depsPackages = [ "base",+ "ghc",+ "syb",+ "deepseq",+ "directory",+ "filepath",+ "process",+ "ghc-paths",+ "transformers",+ "base-compat",+ "HUnit",+ "hspec",+ "QuickCheck",+ "stringbuilder",+ "silently",+ "setenv",+ "with-location"+ ]+ },+ BuildDepends { depsTarget = TargetTestSuite "doctests",+ depsPackages = [ "base", "doctest"+ ]+ }+ ]+ it "should merge conditional build-depends" $ do+ "conduit.cabal_test" `shouldBeParsedTo`+ [ BuildDepends { depsTarget = TargetLibrary,+ depsPackages = [ "base",+ "resourcet",+ "exceptions",+ "lifted-base",+ "transformers-base",+ "transformers",+ "mtl",+ "mmorph",+ "monad-control",+ "void"+ ]+ },+ BuildDepends { depsTarget = TargetTestSuite "test",+ depsPackages = [ "conduit",+ "base",+ "hspec",+ "QuickCheck",+ "transformers",+ "mtl",+ "resourcet",+ "containers",+ "exceptions",+ "safe",+ "void"+ ]+ },+ BuildDepends { depsTarget = TargetBenchmark "optimize-201408",+ depsPackages = [ "base",+ "conduit",+ "vector",+ "deepseq",+ "containers",+ "transformers",+ "hspec",+ "mwc-random",+ "criterion",+ "kan-extensions"+ ]+ },+ BuildDepends { depsTarget = TargetBenchmark "unfused",+ depsPackages = [ "base",+ "conduit",+ "criterion",+ "transformers"+ ]+ }+ ]
test/Staversion/Internal/ExecSpec.hs view
@@ -6,32 +6,43 @@ import System.FilePath ((</>)) import Test.Hspec +import Staversion.Internal.BuildPlan (_setLTSDisambiguator) import Staversion.Internal.Command (Command(..))-import Staversion.Internal.Exec (processCommand)+import Staversion.Internal.Exec+ ( processCommand,+ _processCommandWithCustomBuildPlanManager+ ) import Staversion.Internal.Query ( PackageName, Query(..), PackageSource(..),- Result(..),- ResultVersions,- resultVersionsFromList, ErrorMsg )+import Staversion.Internal.Result+ ( Result(..),+ ResultBody(..)+ ) import Staversion.Internal.Log (defaultLogger, _mockLogger, Logger(loggerThreshold), LogLevel(..))+import Staversion.Internal.Cabal (Target(..)) -import Staversion.Internal.TestUtil (ver, rvers)+import Staversion.Internal.TestUtil (ver, simpleResultBody, verPairs) main :: IO () main = hspec spec spec :: Spec-spec = describe "processCommand" $ do+spec = do+ spec_processCommand_basic+ spec_processCommand_disambiguates++spec_processCommand_basic :: Spec+spec_processCommand_basic = describe "processCommand" $ do specify "QueryName, SourceStackage, hit" $ do singleCase (SourceStackage "conpact_build_plan") (QueryName "drawille")- (Right $ rvers [("drawille", Just $ ver [0,1,0,6])])+ (Right $ simpleResultBody "drawille" [0,1,0,6]) specify "QueryName, SourceStackage, miss" $ do singleCase (SourceStackage "conpact_build_plan") (QueryName "unknown")- (Right $ rvers [("unknown", Nothing)])+ (Right $ SimpleResultBody "unknown" Nothing) specify "QueryName, SourceStackage, source not found" $ do (logger, logs) <- _mockLogger let src = SourceStackage "unknown"@@ -43,7 +54,7 @@ [got_ret] <- processCommand comm resultIn got_ret `shouldBe` src resultFor got_ret `shouldBe` query- case resultVersions got_ret of+ case resultBody got_ret of Right _ -> expectationFailure "it should fail" Left _ -> return () got_logs <- readIORef logs@@ -58,32 +69,55 @@ qc = QueryName "conduit" qa = QueryName "aeson" comm = baseCommand { commSources = [src2, src7], commQueries = [qc, qa] }- expected = [ Result { resultIn = src2, resultFor = qc,- resultVersions = Right $ rvers [("conduit", Just $ ver [1,2,5])]+ expected = [ Result { resultIn = src2, resultReallyIn = Nothing, resultFor = qc,+ resultBody = Right $ simpleResultBody "conduit" [1,2,5] },- Result { resultIn = src2, resultFor = qa,- resultVersions = Right $ rvers [("aeson", Just $ ver [0,8,0,2])]+ Result { resultIn = src2, resultReallyIn = Nothing, resultFor = qa,+ resultBody = Right $ simpleResultBody "aeson" [0,8,0,2] },- Result { resultIn = src7, resultFor = qc,- resultVersions = Right $ rvers [("conduit", Just $ ver [1,2,7])]+ Result { resultIn = src7, resultReallyIn = Nothing, resultFor = qc,+ resultBody = Right $ simpleResultBody "conduit" [1,2,7] },- Result { resultIn = src7, resultFor = qa,- resultVersions = Right $ rvers [("aeson", Just $ ver [0,11,2,1])]+ Result { resultIn = src7, resultReallyIn = Nothing, resultFor = qa,+ resultBody = Right $ simpleResultBody "aeson" [0,11,2,1] } ]- got <- processCommand comm- got `shouldBe` expected+ processCommand comm `shouldReturn` expected+ specify "QueryCabalfile, SourceStackage" $ do+ let src = SourceStackage "lts-4.2"+ cabal_file = ("test" </> "data" </> "foobar.cabal_test")+ query = QueryCabalFile cabal_file+ comm = baseCommand { commSources = [src], commQueries = [query] }+ ret t vps = Result { resultIn = src, resultReallyIn = Nothing, resultFor = query,+ resultBody = Right $ CabalResultBody cabal_file t vps+ }+ expected = [ ret TargetLibrary $ verPairs [ ("base", [4,8,2,0]),+ ("unordered-containers", [0,2,5,1])+ ],+ -- no result for "executable" section because it has no build-depends field.+ ret (TargetTestSuite "spec") $ verPairs [ ("base", [4,8,2,0]),+ ("staversion", []),+ ("text", [1,2,2,0]),+ ("filepath", [1,4,0,0]),+ ("bytestring", [0,10,6,0])+ ],+ ret (TargetTestSuite "network-test") $ verPairs [ ("base", [4,8,2,0]),+ ("http-client", [0,4,26,2])+ ]+ ]+ processCommand comm `shouldReturn` expected -singleCase :: PackageSource -> Query -> Either ErrorMsg ResultVersions -> IO ()+singleCase :: PackageSource -> Query -> Either ErrorMsg ResultBody -> IO () singleCase src query exp_ret_vers = singleCase' src query (`shouldBe` exp_ret_vers) -singleCase' :: PackageSource -> Query -> (Either ErrorMsg ResultVersions -> IO a) -> IO a+singleCase' :: PackageSource -> Query -> (Either ErrorMsg ResultBody -> IO a) -> IO a singleCase' src query checker = do [got_ret] <- processCommand comm resultIn got_ret `shouldBe` src+ resultReallyIn got_ret `shouldBe` Nothing resultFor got_ret `shouldBe` query- checker $ resultVersions got_ret+ checker $ resultBody got_ret where comm = baseCommand { commSources = [src], commQueries = [query]@@ -96,3 +130,18 @@ commQueries = [], commAllowNetwork = False }++spec_processCommand_disambiguates :: Spec+spec_processCommand_disambiguates = describe "processCommand" $ do+ it "disambiguates a partial resolver and sets resultReallyIn field" $ do+ let comm = baseCommand { commSources = [SourceStackage "lts"],+ commQueries = [QueryName "conduit"]+ }+ withMockDisam bp_man = do+ _setLTSDisambiguator bp_man 4 2+ return bp_man+ [got_ret] <- _processCommandWithCustomBuildPlanManager withMockDisam comm+ resultIn got_ret `shouldBe` SourceStackage "lts"+ resultReallyIn got_ret `shouldBe` (Just $ SourceStackage "lts-4.2")+ resultFor got_ret `shouldBe` QueryName "conduit"+ resultBody got_ret `shouldBe` Right (simpleResultBody "conduit" [1,2,6,1])
test/Staversion/Internal/FormatSpec.hs view
@@ -5,11 +5,13 @@ import Staversion.Internal.Format (formatResultsCabal) import Staversion.Internal.Query- ( Result(..), PackageSource(..), Query(..),+ ( PackageSource(..), Query(..), Resolver, PackageName )+import Staversion.Internal.Result (Result(..), ResultBody(..))+import Staversion.Internal.Cabal (Target(..)) -import Staversion.Internal.TestUtil (ver, rvers)+import Staversion.Internal.TestUtil (ver, simpleResultBody, verPairs) @@ -21,9 +23,9 @@ it "should return empty text for empty list" $ do formatResultsCabal [] `shouldBe` "" it "should format a Result in a Cabal way" $ do- let input = [ Result { resultIn = SourceStackage "lts-6.10",+ let input = [ Result { resultIn = SourceStackage "lts-6.10", resultReallyIn = Nothing, resultFor = QueryName "hoge",- resultVersions = Right $ rvers [("hoge", Just $ ver [3,4,5])]+ resultBody = Right $ simpleResultBody "hoge" [3,4,5] } ] expected = ( "------ lts-6.10\n"@@ -56,6 +58,30 @@ <> "\n" ) formatResultsCabal input `shouldBe` expected+ it "should group Results by PackageSource with preserved order (2)" $ do+ let input = [ simpleResult "lts-5.0" "pack-A" [1,0],+ simpleResult "lts-5.0" "pack-A" [2,0],+ simpleResult "lts-6.6" "pack-A" [3,0],+ simpleResult "lts-6.6" "pack-A" [4,0],+ simpleResult "lts-8.0" "pack-A" [5,0],+ simpleResult "lts-6.6" "pack-A" [6,0],+ simpleResult "lts-6.6" "pack-A" [7,0]+ ]+ expected = ( "------ lts-5.0\n"+ <> "pack-A ==1.0,\n"+ <> "pack-A ==2.0\n"+ <> "\n"+ <> "------ lts-6.6\n"+ <> "pack-A ==3.0,\n"+ <> "pack-A ==4.0,\n"+ <> "pack-A ==6.0,\n"+ <> "pack-A ==7.0\n"+ <> "\n"+ <> "------ lts-8.0\n"+ <> "pack-A ==5.0\n"+ <> "\n"+ )+ formatResultsCabal input `shouldBe` expected it "should not put comma at the last non-N/A entry even if it is followed by N/A entries" $ do let input = [ simpleResult "s" "hoge" [1,0,0], simpleResult "s" "not-found-1" [],@@ -72,13 +98,129 @@ <> "\n" ) formatResultsCabal input `shouldBe` expected+ it "should output resultReallyIn field" $ do+ let input = [ Result { resultIn = SourceStackage "lts",+ resultReallyIn = Just $ SourceStackage "lts-7.4",+ resultFor = QueryName "foobar",+ resultBody = Right $ simpleResultBody "foobar" [3,4,5]+ } ]+ expected = ( "------ lts (lts-7.4)\n"+ <> "foobar ==3.4.5\n"+ <> "\n"+ )+ formatResultsCabal input `shouldBe` expected+ it "should show ERROR if resultBody is Left, resultFor is QueryName" $ do+ let input = [ Result { resultIn = SourceStackage "lts-4.2", resultReallyIn = Nothing,+ resultFor = QueryName "hogehoge",+ resultBody = Left "some error"+ } ]+ expected = ( "------ lts-4.2\n"+ <> "-- hogehoge ERROR\n"+ <> "\n"+ )+ formatResultsCabal input `shouldBe` expected+ it "should show ERROR if resultBody is Left, resultFor is QueryCabalFile" $ do+ let input = [ Result { resultIn = SourceStackage "lts-5.3", resultReallyIn = Nothing,+ resultFor = QueryCabalFile "foobar.cabal",+ resultBody = Left "some error"+ },+ Result { resultIn = SourceStackage "lts-5.3", resultReallyIn = Nothing,+ resultFor = QueryName "hoge",+ resultBody = Right $ simpleResultBody "hoge" [5,5]+ }+ ]+ expected = ( "------ lts-5.3\n"+ <> "-- foobar.cabal ERROR\n"+ <> "\n"+ <> "hoge ==5.5\n"+ <> "\n"+ )+ formatResultsCabal input `shouldBe` expected+ it "should show build-depends blocks for CabalResultBody" $ do+ let mkRet = cabalResult "lts-7.0" "hoge.cabal"+ input = [ mkRet TargetLibrary [ ("base", [4,6,0,0]),+ ("foobar", [5,7])+ ],+ mkRet (TargetExecutable "hoge-exe") [ ("bytestring", [1,9]) ],+ mkRet (TargetTestSuite "hoge-test") [ ("hspec", [10,8,9]),+ ("QuickCheck", [5,4,2]),+ ("unknown", [])+ ],+ mkRet (TargetBenchmark "hoge-bench") [ ("base", [4,8,0,4]),+ ("quux", []),+ ("parsec", [3,0,2])+ ]+ ]+ expected = ( "------ lts-7.0\n"+ <> "-- hoge.cabal - library\n"+ <> "base ==4.6.0.0,\n"+ <> "foobar ==5.7\n"+ <> "\n"+ <> "-- hoge.cabal - executable hoge-exe\n"+ <> "bytestring ==1.9\n"+ <> "\n"+ <> "-- hoge.cabal - test-suite hoge-test\n"+ <> "hspec ==10.8.9,\n"+ <> "QuickCheck ==5.4.2\n"+ <> "-- unknown N/A\n"+ <> "\n"+ <> "-- hoge.cabal - benchmark hoge-bench\n"+ <> "base ==4.8.0.4,\n"+ <> "-- quux N/A,\n"+ <> "parsec ==3.0.2\n"+ <> "\n"+ )+ formatResultsCabal input `shouldBe` expected+ it "should show mixed blocks and lines" $ do+ let input = [ simpleResult "lts-5.0" "pack-A" [4,5],+ simpleResult "lts-5.0" "pack-B" [7,7],+ cabalResult "lts-5.0" "X.cabal" TargetLibrary [("pack-A", [4,5,1]), ("pack-B", [6,0])],+ cabalResult "lts-5.0" "X.cabal" (TargetExecutable "X-exe") [("pack-A", []), ("pack-B", [6,6]), ("pack-C", [0,10])],+ cabalResult "lts-6.6" "Y.cabal" TargetLibrary [("pack-C", [9,99,0])],+ simpleResult "lts-6.6" "pack-A" [],+ simpleResult "lts-7.2" "pack-B" [8,4],+ cabalResult "lts-6.6" "Y.cabal" (TargetTestSuite "X-test") [("pack-D", [1,0]), ("pack-A", [5,0]), ("pack-C", []), ("pack-B", [6,5])]+ ]+ expected = ( "------ lts-5.0\n"+ <> "pack-A ==4.5,\n"+ <> "pack-B ==7.7\n"+ <> "\n"+ <> "-- X.cabal - library\n"+ <> "pack-A ==4.5.1,\n"+ <> "pack-B ==6.0\n"+ <> "\n"+ <> "-- X.cabal - executable X-exe\n"+ <> "-- pack-A N/A,\n"+ <> "pack-B ==6.6,\n"+ <> "pack-C ==0.10\n"+ <> "\n"+ <> "------ lts-6.6\n"+ <> "-- Y.cabal - library\n"+ <> "pack-C ==9.99.0\n"+ <> "\n"+ <> "-- pack-A N/A\n"+ <> "\n"+ <> "-- Y.cabal - test-suite X-test\n"+ <> "pack-D ==1.0,\n"+ <> "pack-A ==5.0,\n"+ <> "-- pack-C N/A,\n"+ <> "pack-B ==6.5\n"+ <> "\n"+ <> "------ lts-7.2\n"+ <> "pack-B ==8.4\n"+ <> "\n"+ )+ formatResultsCabal input `shouldBe` expected simpleResult :: Resolver -> PackageName -> [Int] -> Result-simpleResult res name vs = Result { resultIn = SourceStackage res,+simpleResult res name vs = Result { resultIn = SourceStackage res, resultReallyIn = Nothing, resultFor = QueryName name,- resultVersions = Right $ rvers [(name, mversion)]+ resultBody = Right $ simpleResultBody name vs }- where- mversion = case vs of- [] -> Nothing- _ -> Just $ ver vs++cabalResult :: Resolver -> FilePath -> Target -> [(PackageName, [Int])] -> Result+cabalResult res file target vps =+ Result { resultIn = SourceStackage res, resultReallyIn = Nothing,+ resultFor = QueryCabalFile file,+ resultBody = Right $ CabalResultBody file target $ verPairs vps+ }
test/Staversion/Internal/TestUtil.hs view
@@ -1,13 +1,23 @@ module Staversion.Internal.TestUtil- ( ver, rvers+ ( ver, simpleResultBody,+ verPairs ) where import Data.Version (Version(..))-import Staversion.Internal.Query (PackageName, ResultVersions, resultVersionsFromList)+import Staversion.Internal.Query ( PackageName+ )+import Staversion.Internal.Result (ResultBody(..)) ver :: [Int] -> Version ver vs = Version vs [] -rvers :: [(PackageName, Maybe Version)] -> ResultVersions-rvers = resultVersionsFromList+verMaybe :: [Int] -> Maybe Version+verMaybe [] = Nothing+verMaybe vs = Just $ ver vs +verPairs :: [(PackageName, [Int])] -> [(PackageName, Maybe Version)]+verPairs = map f where+ f (pname, vs) = (pname, verMaybe vs)++simpleResultBody :: PackageName -> [Int] -> ResultBody+simpleResultBody name vs = SimpleResultBody name $ verMaybe vs
+ test/data/conduit.cabal_test view
@@ -0,0 +1,111 @@+Name: conduit+Version: 1.2.8+Synopsis: Streaming data processing library.+description:+ Hackage documentation generation is not reliable. For up to date documentation, please see: <http://www.stackage.org/package/conduit>.+ .+ @conduit@ is a solution to the streaming data problem, allowing for production, transformation, and consumption of streams of data in constant memory. It is an alternative to lazy I\/O which guarantees deterministic resource handling, and fits in the same general solution space as @enumerator@\/@iteratee@ and @pipes@. For a tutorial, please visit <https://www.fpcomplete.com/user/snoyberg/library-documentation/conduit-overview>.+License: MIT+License-file: LICENSE+Author: Michael Snoyman+Maintainer: michael@snoyman.com+Category: Data, Conduit+Build-type: Simple+Cabal-version: >=1.8+Homepage: http://github.com/snoyberg/conduit+extra-source-files: test/main.hs+ , README.md+ , ChangeLog.md+ , fusion-macros.h++Library+ Exposed-modules: Data.Conduit+ Data.Conduit.List+ Data.Conduit.Internal+ Data.Conduit.Lift+ Data.Conduit.Internal.Fusion+ Data.Conduit.Internal.List.Stream+ other-modules: Data.Conduit.Internal.Pipe+ Data.Conduit.Internal.Conduit+ Build-depends: base >= 4.5 && < 5+ , resourcet >= 1.1 && < 1.2+ , exceptions >= 0.6+ , lifted-base >= 0.1+ , transformers-base >= 0.4.1 && < 0.5+ , transformers >= 0.2.2+ , mtl+ , mmorph+ , monad-control+ if !impl(ghc>=7.9)+ build-depends: void >= 0.5.5+ ghc-options: -Wall+ include-dirs: .++test-suite test+ hs-source-dirs: test+ main-is: main.hs+ other-modules: Data.Conduit.Extra.ZipConduitSpec+ , Data.Conduit.StreamSpec+ type: exitcode-stdio-1.0+ cpp-options: -DTEST+ build-depends: conduit+ , base+ , hspec >= 1.3+ , QuickCheck >= 2.7+ , transformers+ , mtl+ , resourcet+ , containers+ , exceptions >= 0.6+ , safe+ if !impl(ghc>=7.9)+ build-depends: void+ ghc-options: -Wall++--test-suite doctests+-- hs-source-dirs: test+-- main-is: doctests.hs+-- type: exitcode-stdio-1.0+-- ghc-options: -threaded+-- build-depends: base, directory, doctest >= 0.8++-- benchmark utf8-memory-usage+-- type: exitcode-stdio-1.0+-- hs-source-dirs: benchmarks+-- build-depends: base+-- , text-stream-decode+-- , bytestring+-- , text+-- , conduit+-- main-is: utf8-memory-usage.hs+-- ghc-options: -Wall -O2 -with-rtsopts=-s++benchmark optimize-201408+ type: exitcode-stdio-1.0+ hs-source-dirs: benchmarks+ build-depends: base+ , conduit+ , vector+ , deepseq+ , containers+ , transformers+ , hspec+ , mwc-random+ , criterion+ , kan-extensions+ main-is: optimize-201408.hs+ ghc-options: -Wall -O2 -rtsopts++benchmark unfused+ type: exitcode-stdio-1.0+ hs-source-dirs: benchmarks+ build-depends: base+ , conduit+ , criterion+ , transformers+ main-is: unfused.hs+ ghc-options: -Wall -O2 -rtsopts++source-repository head+ type: git+ location: git://github.com/snoyberg/conduit.git
+ test/data/doctest.cabal_test view
@@ -0,0 +1,118 @@+name: doctest+version: 0.11.0+synopsis: Test interactive Haskell examples+description: The doctest program checks examples in source code comments.+ It is modeled after doctest for Python+ (<http://docs.python.org/library/doctest.html>).+ .+ Documentation is at+ <https://github.com/sol/doctest#readme>.+category: Testing+bug-reports: https://github.com/sol/doctest/issues+homepage: https://github.com/sol/doctest#readme+license: MIT+license-file: LICENSE+copyright: (c) 2009-2015 Simon Hengel+author: Simon Hengel <sol@typeful.net>+maintainer: Simon Hengel <sol@typeful.net>+build-type: Simple+cabal-version: >= 1.8+extra-source-files: example/example.cabal+ , example/src/Example.hs+ , example/test/doctests.hs++source-repository head+ type: git+ location: https://github.com/sol/doctest++library+ exposed-modules:+ Test.DocTest+ ghc-options:+ -Wall+ hs-source-dirs:+ src, ghci-wrapper/src+ other-modules:+ Extract+ , GhcUtil+ , Interpreter+ , Location+ , Help+ , PackageDBs+ , Parse+ , Paths_doctest+ , Property+ , Runner+ , Runner.Example+ , Run+ , Util+ , Sandbox+ , Language.Haskell.GhciWrapper+ build-depends:+ base == 4.*+ , base-compat >= 0.4.2+ , ghc >= 7.0 && < 8.2+ , syb >= 0.3+ , deepseq+ , directory+ , filepath+ , process+ , ghc-paths >= 0.1.0.9+ , transformers++executable doctest+ main-is:+ Main.hs+ ghc-options:+ -Wall -threaded+ hs-source-dirs:+ driver+ build-depends:+ base == 4.*+ , doctest++test-suite spec+ main-is:+ Spec.hs+ type:+ exitcode-stdio-1.0+ ghc-options:+ -Wall -threaded+ cpp-options:+ -DTEST+ hs-source-dirs:+ test, src, ghci-wrapper/src+ c-sources:+ test/integration/with-cbits/foo.c+ build-depends:+ base+ , ghc+ , syb+ , deepseq+ , directory+ , filepath+ , process+ , ghc-paths+ , transformers++ , base-compat+ , HUnit+ , hspec >= 1.5.1+ , QuickCheck >= 2.8.2+ , stringbuilder >= 0.4+ , silently >= 1.2.4+ , setenv+ , with-location++test-suite doctests+ main-is:+ doctests.hs+ type:+ exitcode-stdio-1.0+ ghc-options:+ -Wall -threaded+ hs-source-dirs:+ test+ build-depends:+ base+ , doctest
+ test/data/foobar.cabal_test view
@@ -0,0 +1,61 @@+name: foobar+version: 0.1.2.0+author: Toshio Ito <debug.ito@gmail.com>+maintainer: Toshio Ito <debug.ito@gmail.com>+license: BSD3+license-file: LICENSE+synopsis: foo+description: bar+category: Example+cabal-version: >= 1.10+build-type: Simple+-- extra-source-files: README.md, ChangeLog.md,+ +library+ default-language: Haskell2010+ hs-source-dirs: src+ ghc-options: -Wall -fno-warn-unused-imports+ default-extensions: OverloadedStrings+ other-extensions: CPP, DeriveDataTypeable, TupleSections+ other-modules: Paths_staversion,+ Staversion.Internal.HTTP+ build-depends: base >=4.6 && <4.10,+ unordered-containers >=0.2.3 && <0.3,++executable foobar+ default-language: Haskell2010+ hs-source-dirs: app+ main-is: Main.hs+ ghc-options: -Wall -fno-warn-unused-imports -rtsopts -threaded "-with-rtsopts=-N"+ -- other-modules: + -- default-extensions: + -- other-extensions: + -- build-depends: base, staversion+ -- -- (no build-depends case (quite unlikely, though..))++test-suite spec+ type: exitcode-stdio-1.0+ default-language: Haskell2010+ hs-source-dirs: test+ ghc-options: -Wall -fno-warn-unused-imports -fno-warn-orphans "-with-rtsopts=-M512m"+ main-is: Spec.hs+ default-extensions: OverloadedStrings+ build-depends: base, staversion, text, filepath, bytestring,+++flag network-test+ description: Enable network tests.+ default: False++test-suite network-test+ type: exitcode-stdio-1.0+ default-language: Haskell2010+ hs-source-dirs: test+ ghc-options: -Wall -fno-warn-unused-imports -fno-warn-orphans "-with-rtsopts=-M512m"+ main-is: NetworkTest.hs+ default-extensions: OverloadedStrings+ if !flag(network-test)+ buildable: False+ -- other-extensions: + other-modules: Staversion.Internal.TestUtil+ build-depends: base, http-client