packages feed

hpack 0.29.5 → 0.29.6

raw patch · 8 files changed

+307/−213 lines, 8 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

+ Hpack: setProgramName :: ProgramName -> Options -> Options
+ Hpack.Config: ProgramName :: String -> ProgramName
+ Hpack.Config: [decodeOptionsProgramName] :: DecodeOptions -> ProgramName
+ Hpack.Config: instance Data.Aeson.Config.FromValue.FromValue Hpack.Config.ParseSpecVersion
+ Hpack.Config: instance Data.Aeson.Config.FromValue.FromValue a => Data.Aeson.Config.FromValue.FromValue (Hpack.Config.CheckSpecVersion a)
+ Hpack.Config: instance Data.String.IsString Hpack.Config.ProgramName
+ Hpack.Config: instance GHC.Classes.Eq Hpack.Config.ProgramName
+ Hpack.Config: instance GHC.Show.Show Hpack.Config.ProgramName
+ Hpack.Config: newtype ProgramName
- Hpack.Config: DecodeOptions :: FilePath -> Maybe FilePath -> FilePath -> IO (Either String Value) -> DecodeOptions
+ Hpack.Config: DecodeOptions :: ProgramName -> FilePath -> Maybe FilePath -> FilePath -> IO (Either String Value) -> DecodeOptions

Files

CHANGELOG.md view
@@ -1,3 +1,6 @@+## Changes in 0.29.6+  - Add `spec-version` (see #300)+ ## Changes in 0.29.5   - Fix a regression related to indentation sniffing (close #310) 
hpack.cabal view
@@ -1,14 +1,14 @@ cabal-version: >= 1.10 --- This file has been generated from package.yaml by hpack version 0.29.4.+-- This file has been generated from package.yaml by hpack version 0.29.5. -- -- see: https://github.com/sol/hpack ----- hash: c9c40490b86bd987b33ab63f10fd311c44b89e37d5a75d5af8cd25db72992e99+-- hash: d8b2a418f831494685f8d946c109f4cf86371e5f1a41613bdd538aa6ed27817c  name:           hpack-version:        0.29.5-synopsis:       An alternative format for Haskell packages+version:        0.29.6+synopsis:       A modern format for Haskell packages description:    See README at <https://github.com/sol/hpack#readme> category:       Development homepage:       https://github.com/sol/hpack#readme@@ -70,6 +70,7 @@       Hpack.Render.Hints       Hpack.Syntax.Defaults       Hpack.Syntax.Dependency+      Hpack.Syntax.DependencyVersion       Hpack.Syntax.Git       Hpack.Utf8       Hpack.Util@@ -184,6 +185,7 @@       Hpack.Render.Hints       Hpack.Syntax.Defaults       Hpack.Syntax.Dependency+      Hpack.Syntax.DependencyVersion       Hpack.Syntax.Git       Hpack.Utf8       Hpack.Util
src/Hpack.hs view
@@ -25,6 +25,7 @@  -- * Options , defaultOptions+, setProgramName , setTarget , setDecode , getOptions@@ -112,6 +113,10 @@ setTarget :: FilePath -> Options -> Options setTarget target options@Options{..} =   options {optionsDecodeOptions = optionsDecodeOptions {decodeOptionsTarget = target}}++setProgramName :: ProgramName -> Options -> Options+setProgramName name options@Options{..} =+  options {optionsDecodeOptions = optionsDecodeOptions {decodeOptionsProgramName = name}}  setDecode :: (FilePath -> IO (Either String Value)) -> Options -> Options setDecode decode options@Options{..} =
src/Hpack/Config.hs view
@@ -26,6 +26,7 @@ -- tool that supports Hpack (e.g. @stack@ or @cabal2nix@).    DecodeOptions(..)+, ProgramName(..) , defaultDecodeOptions , packageConfig , DecodeResult(..)@@ -83,6 +84,7 @@ import           Data.Maybe import           Data.Semigroup (Semigroup(..)) import           Data.Ord+import           Data.String import           Data.Text (Text) import qualified Data.Text as T import           Data.Scientific (Scientific)@@ -92,7 +94,7 @@ import           Control.Monad.Trans.Writer import           Control.Monad.Trans.Except import           Control.Monad.IO.Class-import           Data.Version+import           Data.Version (Version, makeVersion, showVersion)  import           Distribution.Pretty (prettyShow) import qualified Distribution.SPDX.License as SPDX@@ -106,9 +108,13 @@ import qualified Hpack.Util as Util import           Hpack.Defaults import qualified Hpack.Yaml as Yaml+import           Hpack.Syntax.DependencyVersion import           Hpack.Syntax.Dependency import           Hpack.License+import           Hpack.CabalFile (parseVersion) +import qualified Paths_hpack as Hpack (version)+ package :: String -> String -> Package package name version = Package {     packageName = name@@ -554,17 +560,24 @@ type Warnings m = WriterT [String] m type Errors = ExceptT String -decodeYaml :: FromValue a => FilePath -> Warnings (Errors IO) a-decodeYaml file = lift (ExceptT $ Yaml.decodeYaml file) >>= decodeValue file+decodeYaml :: FromValue a => ProgramName -> FilePath -> Warnings (Errors IO) a+decodeYaml programName file = lift (ExceptT $ Yaml.decodeYaml file) >>= decodeValue programName file  data DecodeOptions = DecodeOptions {-  decodeOptionsTarget :: FilePath+  decodeOptionsProgramName :: ProgramName+, decodeOptionsTarget :: FilePath , decodeOptionsUserDataDir :: Maybe FilePath , decodeOptionsDecode :: FilePath -> IO (Either String Value) } +newtype ProgramName = ProgramName String+  deriving (Eq, Show)++instance IsString ProgramName where+  fromString = ProgramName+ defaultDecodeOptions :: DecodeOptions-defaultDecodeOptions = DecodeOptions packageConfig Nothing Yaml.decodeYaml+defaultDecodeOptions = DecodeOptions "hpack" packageConfig Nothing Yaml.decodeYaml  data DecodeResult = DecodeResult {   decodeResultPackage :: Package@@ -574,12 +587,12 @@ } deriving (Eq, Show)  readPackageConfig :: DecodeOptions -> IO (Either String DecodeResult)-readPackageConfig (DecodeOptions file mUserDataDir readValue) = runExceptT $ fmap addCabalFile . runWriterT $ do+readPackageConfig (DecodeOptions programName file mUserDataDir readValue) = runExceptT $ fmap addCabalFile . runWriterT $ do   value <- lift . ExceptT $ readValue file-  config <- decodeValue file value+  config <- decodeValue programName file value   dir <- liftIO $ takeDirectory <$> canonicalizePath file   userDataDir <- liftIO $ maybe (getAppUserDataDirectory "hpack") return mUserDataDir-  toPackage userDataDir dir config+  toPackage programName userDataDir dir config   where     addCabalFile :: ((Package, String), [String]) -> DecodeResult     addCabalFile ((pkg, cabalVersion), warnings) = DecodeResult pkg cabalVersion (takeDirectory_ file </> (packageName pkg ++ ".cabal")) warnings@@ -665,7 +678,7 @@         makeVersion [1,22] <$ guard hasReexportedModules       , makeVersion [2,0]  <$ guard hasSignatures       , makeVersion [2,0] <$ guard hasGeneratedModules-      , makeVersion [2,2] <$ guard (hasCxxParams sect)+      , sectionCabalVersion sect       ]       where         hasReexportedModules = any (not . null . libraryReexportedModules) sect@@ -685,32 +698,56 @@     executableCabalVersion :: Section Executable -> Maybe Version     executableCabalVersion sect = maximum [         makeVersion [2,0] <$ guard (executableHasGeneratedModules sect)-      , makeVersion [2,2] <$ guard (hasCxxParams sect)+      , sectionCabalVersion sect       ]      executableHasGeneratedModules :: Section Executable -> Bool     executableHasGeneratedModules = any (not . null . executableGeneratedModules) -    hasCxxParams :: Section a -> Bool-    hasCxxParams sect = or [-        check sect-      , any (any check) (sectionConditionals sect)+    sectionCabalVersion :: Section a -> Maybe Version+    sectionCabalVersion sect = maximum [+        makeVersion [2,2] <$ guard (sectionSatisfies (not . null . sectionCxxSources) sect)+      , makeVersion [2,2] <$ guard (sectionSatisfies (not . null . sectionCxxOptions) sect)       ]-      where-        check s = or [-            (not . null . sectionCxxOptions) s-          , (not . null . sectionCxxSources) s-          ] -decodeValue :: FromValue a => FilePath -> Value -> Warnings (Errors IO) a-decodeValue file value = do-  (a, unknown) <- lift . ExceptT . return $ first (prefix ++) (Config.decodeValue value)-  tell (map formatUnknownField unknown)-  return a+    sectionSatisfies :: (Section a -> Bool) -> Section a -> Bool+    sectionSatisfies p sect = or [+        p sect+      , any (any (sectionSatisfies p)) (sectionConditionals sect)+      ]++decodeValue :: FromValue a => ProgramName -> FilePath -> Value -> Warnings (Errors IO) a+decodeValue (ProgramName programName) file value = do+  (r, unknown) <- lift . ExceptT . return $ first (prefix ++) (Config.decodeValue value)+  case r of+    UnsupportedSpecVersion v -> do+      lift $ throwE ("The file " ++ file ++ " requires version " ++ showVersion v ++ " of the Hpack package specification, however this version of " ++ programName ++ " only supports versions up to " ++ showVersion Hpack.version ++ ". Upgrading to the latest version of " ++ programName ++ " may resolve this issue.")+    SupportedSpecVersion a -> do+      tell (map formatUnknownField unknown)+      return a   where     prefix = file ++ ": "     formatUnknownField name = prefix ++ "Ignoring unrecognized field " ++ name +data CheckSpecVersion a = SupportedSpecVersion a | UnsupportedSpecVersion Version++instance FromValue a => FromValue (CheckSpecVersion a) where+  fromValue = withObject $ \ o -> o .:? "spec-version" >>= \ case+    Just (ParseSpecVersion v) | Hpack.version < v -> return $ UnsupportedSpecVersion v+    _ -> SupportedSpecVersion <$> fromValue (Object o)++newtype ParseSpecVersion = ParseSpecVersion Version++instance FromValue ParseSpecVersion where+  fromValue value = do+    s <- case value of+      Number n -> return (scientificToVersion n)+      String s -> return (T.unpack s)+      _ -> typeMismatch "Number or String" value+    case parseVersion s of+      Just v -> return (ParseSpecVersion v)+      Nothing -> fail ("invalid value " ++ show s)+ data Package = Package {   packageName :: String , packageVersion :: String@@ -830,39 +867,42 @@ type CommonOptionsWithDefaults a = Product DefaultsConfig (CommonOptions ParseCSources ParseCxxSources ParseJsSources a) type WithCommonOptionsWithDefaults a = Product DefaultsConfig (WithCommonOptions ParseCSources ParseCxxSources ParseJsSources a) -toPackage :: FilePath -> FilePath -> ConfigWithDefaults -> Warnings (Errors IO) (Package, String)-toPackage userDataDir dir =-      expandDefaultsInConfig userDataDir dir+toPackage :: ProgramName -> FilePath -> FilePath -> ConfigWithDefaults -> Warnings (Errors IO) (Package, String)+toPackage programName userDataDir dir =+      expandDefaultsInConfig programName userDataDir dir   >=> traverseConfig (expandForeignSources dir)   >=> toPackage_ dir  expandDefaultsInConfig-  :: FilePath+  :: ProgramName   -> FilePath+  -> FilePath   -> ConfigWithDefaults   -> Warnings (Errors IO) (Config ParseCSources ParseCxxSources ParseJsSources)-expandDefaultsInConfig userDataDir dir = bitraverse (expandGlobalDefaults userDataDir dir) (expandSectionDefaults userDataDir dir)+expandDefaultsInConfig programName userDataDir dir = bitraverse (expandGlobalDefaults programName userDataDir dir) (expandSectionDefaults programName userDataDir dir)  expandGlobalDefaults-  :: FilePath+  :: ProgramName   -> FilePath+  -> FilePath   -> CommonOptionsWithDefaults Empty   -> Warnings (Errors IO) (CommonOptions ParseCSources ParseCxxSources ParseJsSources Empty)-expandGlobalDefaults userDataDir dir = do-  fmap (`Product` Empty) >>> expandDefaults userDataDir dir >=> \ (Product c Empty) -> return c+expandGlobalDefaults programName userDataDir dir = do+  fmap (`Product` Empty) >>> expandDefaults programName userDataDir dir >=> \ (Product c Empty) -> return c  expandSectionDefaults-  :: FilePath+  :: ProgramName   -> FilePath+  -> FilePath   -> PackageConfigWithDefaults ParseCSources ParseCxxSources ParseJsSources   -> Warnings (Errors IO) (PackageConfig ParseCSources ParseCxxSources ParseJsSources)-expandSectionDefaults userDataDir dir p@PackageConfig{..} = do-  library <- traverse (expandDefaults userDataDir dir) packageConfigLibrary-  internalLibraries <- traverse (traverse (expandDefaults userDataDir dir)) packageConfigInternalLibraries-  executable <- traverse (expandDefaults userDataDir dir) packageConfigExecutable-  executables <- traverse (traverse (expandDefaults userDataDir dir)) packageConfigExecutables-  tests <- traverse (traverse (expandDefaults userDataDir dir)) packageConfigTests-  benchmarks <- traverse (traverse (expandDefaults userDataDir dir)) packageConfigBenchmarks+expandSectionDefaults programName userDataDir dir p@PackageConfig{..} = do+  library <- traverse (expandDefaults programName userDataDir dir) packageConfigLibrary+  internalLibraries <- traverse (traverse (expandDefaults programName userDataDir dir)) packageConfigInternalLibraries+  executable <- traverse (expandDefaults programName userDataDir dir) packageConfigExecutable+  executables <- traverse (traverse (expandDefaults programName userDataDir dir)) packageConfigExecutables+  tests <- traverse (traverse (expandDefaults programName userDataDir dir)) packageConfigTests+  benchmarks <- traverse (traverse (expandDefaults programName userDataDir dir)) packageConfigBenchmarks   return p{       packageConfigLibrary = library     , packageConfigInternalLibraries = internalLibraries@@ -874,11 +914,12 @@  expandDefaults   :: (FromValue a, Semigroup a, Monoid a)-  => FilePath+  => ProgramName   -> FilePath+  -> FilePath   -> WithCommonOptionsWithDefaults a   -> Warnings (Errors IO) (WithCommonOptions ParseCSources ParseCxxSources ParseJsSources a)-expandDefaults userDataDir = expand []+expandDefaults programName userDataDir = expand []   where     expand :: (FromValue a, Semigroup a, Monoid a) =>          [FilePath]@@ -898,7 +939,7 @@       file <- lift $ ExceptT (ensure userDataDir dir defaults)       seen_ <- lift (checkCycle seen file)       let dir_ = takeDirectory file-      decodeYaml file >>= expand seen_ dir_+      decodeYaml programName file >>= expand seen_ dir_      checkCycle :: [FilePath] -> FilePath -> Errors IO [FilePath]     checkCycle seen file = do@@ -925,15 +966,18 @@     globalVerbatim = commonOptionsVerbatim g     globalOptions = g {commonOptionsVerbatim = Nothing} -  mLibrary <- liftIO $ traverse (toLibrary dir packageName_ globalOptions) packageConfigLibrary+    toSect :: Monoid a => WithCommonOptions CSources CxxSources JsSources a -> Section a+    toSect = toSection . first ((mempty <$ globalOptions) <>) -  internalLibraries <- liftIO $ toInternalLibraries dir packageName_ globalOptions packageConfigInternalLibraries+    toLib = toLibrary dir packageName_ . toSect+    toExecutables = liftIO . maybe mempty (traverse $ toExecutable dir packageName_ . toSect) -  executables <- toExecutableMap packageName_ packageConfigExecutables packageConfigExecutable-    >>= liftIO . toExecutables dir packageName_ globalOptions+  mLibrary <- liftIO $ traverse toLib packageConfigLibrary+  internalLibraries <- liftIO $ maybe mempty (traverse toLib) packageConfigInternalLibraries -  tests <- liftIO $ toExecutables dir packageName_ globalOptions packageConfigTests-  benchmarks <- liftIO $ toExecutables dir packageName_ globalOptions packageConfigBenchmarks+  executables <- toExecutableMap packageName_ packageConfigExecutables packageConfigExecutable >>= toExecutables+  tests <- toExecutables packageConfigTests+  benchmarks <- toExecutables packageConfigBenchmarks    licenseFileExists <- liftIO $ doesFileExist (dir </> "LICENSE") @@ -1117,10 +1161,9 @@         r = fromConfig pathsModule inferableModules conf       return (outerModules ++ getInferredModules r, r) -toLibrary :: FilePath -> String -> GlobalOptions -> WithCommonOptions CSources CxxSources JsSources LibrarySection -> IO (Section Library)-toLibrary dir name globalOptions =+toLibrary :: FilePath -> String -> Section LibrarySection -> IO (Section Library)+toLibrary dir name =     inferModules dir name getMentionedLibraryModules getLibraryModules fromLibrarySectionTopLevel fromLibrarySectionInConditional-  . toSection (mempty <$ globalOptions)   where     getLibraryModules :: Library -> [String]     getLibraryModules Library{..} = libraryExposedModules ++ libraryOtherModules@@ -1159,21 +1202,14 @@   , librarySignatures = fromMaybeList librarySectionSignatures   } -toInternalLibraries :: FilePath -> String -> GlobalOptions -> Maybe (Map String (WithCommonOptions CSources CxxSources JsSources LibrarySection)) -> IO (Map String (Section Library))-toInternalLibraries dir packageName_ globalOptions = traverse (toLibrary dir packageName_ globalOptions) . fromMaybe mempty--toExecutables :: FilePath -> String -> GlobalOptions -> Maybe (Map String (WithCommonOptions CSources CxxSources JsSources ExecutableSection)) -> IO (Map String (Section Executable))-toExecutables dir packageName_ globalOptions = traverse (toExecutable dir packageName_ globalOptions) . fromMaybe mempty- getMentionedExecutableModules :: ExecutableSection -> [String] getMentionedExecutableModules (ExecutableSection main otherModules generatedModules)=   maybe id (:) (main >>= toModule . splitDirectories) $ fromMaybeList (otherModules <> generatedModules) -toExecutable :: FilePath -> String -> GlobalOptions -> WithCommonOptions CSources CxxSources JsSources ExecutableSection -> IO (Section Executable)-toExecutable dir packageName_ globalOptions =+toExecutable :: FilePath -> String -> Section ExecutableSection -> IO (Section Executable)+toExecutable dir packageName_ =     inferModules dir packageName_ getMentionedExecutableModules executableOtherModules fromExecutableSection (fromExecutableSection [])   . expandMain-  . toSection (mempty <$ globalOptions)   where     fromExecutableSection :: [String] -> [String] -> ExecutableSection -> Executable     fromExecutableSection pathsModule inferableModules ExecutableSection{..} =@@ -1201,11 +1237,10 @@       , sectionConditionals = map (fmap flatten) sectionConditionals       } -toSection :: CommonOptions CSources CxxSources JsSources a -> WithCommonOptions CSources CxxSources JsSources a -> Section a-toSection globalOptions (Product options a) = toSection_ (Product (globalOptions <> options) a)--toSection_ :: WithCommonOptions CSources CxxSources JsSources a -> Section a-toSection_ (Product CommonOptions{..} a) = Section {+toSection :: WithCommonOptions CSources CxxSources JsSources a -> Section a+toSection = go+  where+    go (Product CommonOptions{..} a) = Section {         sectionData = a       , sectionSourceDirs = fromMaybeList commonOptionsSourceDirs       , sectionDefaultExtensions = fromMaybeList commonOptionsDefaultExtensions@@ -1229,17 +1264,15 @@       , sectionBuildable = commonOptionsBuildable       , sectionDependencies = fromMaybe mempty commonOptionsDependencies       , sectionPkgConfigDependencies = fromMaybeList commonOptionsPkgConfigDependencies-      , sectionConditionals = conditionals+      , sectionConditionals = map toConditional (fromMaybeList commonOptionsWhen)       , sectionBuildTools = fromMaybe mempty commonOptionsBuildTools       , sectionVerbatim = fromMaybeList commonOptionsVerbatim       }-  where-    conditionals = map toConditional (fromMaybeList commonOptionsWhen)      toConditional :: ConditionalSection CSources CxxSources JsSources a -> Conditional (Section a)     toConditional x = case x of-      ThenElseConditional (Product (ThenElse then_ else_) c) -> conditional c (toSection_ then_) (Just $ toSection_ else_)-      FlatConditional (Product sect c) -> conditional c (toSection_ sect) Nothing+      ThenElseConditional (Product (ThenElse then_ else_) c) -> conditional c (go then_) (Just $ go else_)+      FlatConditional (Product sect c) -> conditional c (go sect) Nothing       where         conditional (Condition (Cond c)) = Conditional c 
src/Hpack/Syntax/Dependency.hs view
@@ -1,37 +1,22 @@-{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE LambdaCase #-} module Hpack.Syntax.Dependency (   Dependencies(..)-, DependencyVersion(..)-, SourceDependency(..)-, GitRef-, GitUrl-, githubBaseUrl-, scientificToVersion ) where +import           Data.Text (Text) import qualified Data.Text as T import           Data.Semigroup (Semigroup(..))-import           Text.PrettyPrint (renderStyle, Style(..), Mode(..)) import           Control.Monad-import           Distribution.Version (VersionRangeF(..))-import qualified Distribution.Compat.ReadP as D import qualified Distribution.Package as D-import qualified Distribution.Text as D-import qualified Distribution.Version as D import           Data.Map.Lazy (Map) import qualified Data.Map.Lazy as Map-import           Data.Scientific-import           Control.Applicative import           GHC.Exts  import           Data.Aeson.Config.FromValue -githubBaseUrl :: String-githubBaseUrl = "https://github.com/"+import           Hpack.Syntax.DependencyVersion  newtype Dependencies = Dependencies {   unDependencies :: Map String DependencyVersion@@ -42,18 +27,6 @@   fromList = Dependencies . Map.fromList   toList = Map.toList . unDependencies -data DependencyVersion =-    AnyVersion-  | VersionRange String-  | SourceDependency SourceDependency-  deriving (Eq, Show)--data SourceDependency = GitRef GitUrl GitRef (Maybe FilePath) | Local FilePath-  deriving (Eq, Show)--type GitUrl = String-type GitRef = String- instance FromValue Dependencies where   fromValue v = case v of     String _ -> dependenciesFromList . return <$> fromValue v@@ -67,54 +40,6 @@       dependenciesFromList :: [Dependency] -> Dependencies       dependenciesFromList = Dependencies . Map.fromList . map fromDependency --instance FromValue DependencyVersion where-  fromValue v = case v of-    Null -> return AnyVersion-    Object _ -> SourceDependency <$> fromValue v-    Number n -> return (scientificToDependencyVersion n)-    String s -> parseVersionRange ("== " ++ input) <|> parseVersionRange input-      where-        input = T.unpack s--    _ -> typeMismatch "Null, Object, Number, or String" v--scientificToDependencyVersion :: Scientific -> DependencyVersion-scientificToDependencyVersion n = VersionRange ("==" ++ version)-  where-    version = scientificToVersion n--scientificToVersion :: Scientific -> String-scientificToVersion n = version-  where-    version = formatScientific Fixed (Just decimalPlaces) n-    decimalPlaces-      | e < 0 = abs e-      | otherwise = 0-    e = base10Exponent n--instance FromValue SourceDependency where-  fromValue = withObject (\o -> let-    local :: Parser SourceDependency-    local = Local <$> o .: "path"--    git :: Parser SourceDependency-    git = GitRef <$> url <*> ref <*> subdir--    url :: Parser String-    url =-          ((githubBaseUrl ++) <$> o .: "github")-      <|> (o .: "git")-      <|> fail "neither key \"git\" nor key \"github\" present"--    ref :: Parser String-    ref = o .: "ref"--    subdir :: Parser (Maybe FilePath)-    subdir = o .:? "subdir"--    in local <|> git)- data Dependency = Dependency {   _dependencyName :: String , _dependencyVersion :: DependencyVersion@@ -122,73 +47,20 @@  instance FromValue Dependency where   fromValue v = case v of-    String s -> uncurry Dependency <$> parseDependency (T.unpack s)-    Object o -> addSourceDependency o+    String s -> uncurry Dependency <$> parseDependency "dependency" s+    Object o -> sourceDependency o     _ -> typeMismatch "Object or String" v     where-      addSourceDependency o = Dependency <$> name <*> (SourceDependency <$> fromValue v)+      sourceDependency o = Dependency <$> name <*> (SourceDependency <$> fromValue v)         where           name :: Parser String           name = o .: "name" -depPkgName :: D.Dependency -> String-#if MIN_VERSION_Cabal(2,0,0)-depPkgName = D.unPackageName . D.depPkgName-#else-depPkgName (D.Dependency (D.PackageName name) _) = name-#endif--depVerRange :: D.Dependency -> D.VersionRange-#if MIN_VERSION_Cabal(2,0,0)-depVerRange = D.depVerRange-#else-depVerRange (D.Dependency _ versionRange) = versionRange-#endif--parseDependency :: Monad m => String -> m (String, DependencyVersion)-parseDependency = liftM fromCabal . parseCabalDependency+parseDependency :: Monad m => String -> Text -> m (String, DependencyVersion)+parseDependency subject = liftM fromCabal . parseCabalDependency subject . T.unpack   where     fromCabal :: D.Dependency -> (String, DependencyVersion)-    fromCabal d = (depPkgName d, dependencyVersionFromCabal $ depVerRange d)--dependencyVersionFromCabal :: D.VersionRange -> DependencyVersion-dependencyVersionFromCabal versionRange-  | D.isAnyVersion versionRange = AnyVersion-  | otherwise = VersionRange . renderStyle style . D.disp $ toPreCabal2VersionRange versionRange-  where-    style = Style OneLineMode 0 0--parseCabalDependency :: Monad m => String -> m D.Dependency-parseCabalDependency = cabalParse "dependency"--parseVersionRange :: Monad m => String -> m DependencyVersion-parseVersionRange = liftM dependencyVersionFromCabal . parseCabalVersionRange--parseCabalVersionRange :: Monad m => String -> m D.VersionRange-parseCabalVersionRange = cabalParse "constraint"--cabalParse :: (Monad m, D.Text a) => String -> String -> m a-cabalParse subject s = case [d | (d, "") <- D.readP_to_S D.parse s] of-  [d] -> return d-  _ -> fail $ unwords ["invalid",  subject, show s]--toPreCabal2VersionRange :: D.VersionRange -> D.VersionRange-toPreCabal2VersionRange = D.embedVersionRange . D.cataVersionRange f-  where-    f :: VersionRangeF (VersionRangeF D.VersionRange) -> VersionRangeF D.VersionRange-    f = \ case-      MajorBoundVersionF v -> IntersectVersionRangesF (D.embedVersionRange lower) (D.embedVersionRange upper)-        where-          lower = OrLaterVersionF v-          upper = EarlierVersionF (D.majorUpperBound v)+    fromCabal d = (D.unPackageName $ D.depPkgName d, dependencyVersionFromCabal $ D.depVerRange d) -      AnyVersionF -> AnyVersionF-      ThisVersionF v -> ThisVersionF v-      LaterVersionF v -> LaterVersionF v-      OrLaterVersionF v -> OrLaterVersionF v-      EarlierVersionF v -> EarlierVersionF v-      OrEarlierVersionF v -> OrEarlierVersionF v-      WildcardVersionF v -> WildcardVersionF v-      UnionVersionRangesF a b -> UnionVersionRangesF (D.embedVersionRange a) (D.embedVersionRange b)-      IntersectVersionRangesF a b -> IntersectVersionRangesF (D.embedVersionRange a) (D.embedVersionRange b)-      VersionRangeParensF a -> VersionRangeParensF (D.embedVersionRange a)+parseCabalDependency :: Monad m => String -> String -> m D.Dependency+parseCabalDependency = cabalParse
+ src/Hpack/Syntax/DependencyVersion.hs view
@@ -0,0 +1,126 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE LambdaCase #-}+module Hpack.Syntax.DependencyVersion (+  githubBaseUrl+, GitRef+, GitUrl+, DependencyVersion(..)+, SourceDependency(..)+, dependencyVersionFromCabal++, scientificToVersion+, cabalParse+) where++import           Control.Applicative+import           Data.Scientific+import qualified Data.Text as T+import           Text.PrettyPrint (renderStyle, Style(..), Mode(..))++import           Distribution.Version (VersionRangeF(..))+import qualified Distribution.Text as D+import qualified Distribution.Version as D+import qualified Distribution.Parsec.Class as D++import           Data.Aeson.Config.FromValue++githubBaseUrl :: String+githubBaseUrl = "https://github.com/"++type GitUrl = String+type GitRef = String++data DependencyVersion =+    AnyVersion+  | VersionRange String+  | SourceDependency SourceDependency+  deriving (Eq, Show)++instance FromValue DependencyVersion where+  fromValue v = case v of+    Null -> return AnyVersion+    Object _ -> SourceDependency <$> fromValue v+    Number n -> return (scientificToDependencyVersion n)+    String s -> parseVersionRange ("== " ++ input) <|> parseVersionRange input+      where+        input = T.unpack s++    _ -> typeMismatch "Null, Object, Number, or String" v++data SourceDependency = GitRef GitUrl GitRef (Maybe FilePath) | Local FilePath+  deriving (Eq, Show)++instance FromValue SourceDependency where+  fromValue = withObject (\o -> let+    local :: Parser SourceDependency+    local = Local <$> o .: "path"++    git :: Parser SourceDependency+    git = GitRef <$> url <*> ref <*> subdir++    url :: Parser String+    url =+          ((githubBaseUrl ++) <$> o .: "github")+      <|> (o .: "git")+      <|> fail "neither key \"git\" nor key \"github\" present"++    ref :: Parser String+    ref = o .: "ref"++    subdir :: Parser (Maybe FilePath)+    subdir = o .:? "subdir"++    in local <|> git)++scientificToDependencyVersion :: Scientific -> DependencyVersion+scientificToDependencyVersion n = VersionRange ("==" ++ version)+  where+    version = scientificToVersion n++scientificToVersion :: Scientific -> String+scientificToVersion n = version+  where+    version = formatScientific Fixed (Just decimalPlaces) n+    decimalPlaces+      | e < 0 = abs e+      | otherwise = 0+    e = base10Exponent n++parseVersionRange :: Monad m => String -> m DependencyVersion+parseVersionRange = fmap dependencyVersionFromCabal . parseCabalVersionRange++parseCabalVersionRange :: Monad m => String -> m D.VersionRange+parseCabalVersionRange = cabalParse "constraint"++cabalParse :: (Monad m, D.Parsec a) => String -> String -> m a+cabalParse subject s = case D.eitherParsec s of+  Right d -> return d+  Left _ ->fail $ unwords ["invalid",  subject, show s]++dependencyVersionFromCabal :: D.VersionRange -> DependencyVersion+dependencyVersionFromCabal versionRange+  | D.isAnyVersion versionRange = AnyVersion+  | otherwise = VersionRange . renderStyle style . D.disp $ toPreCabal2VersionRange versionRange+  where+    style = Style OneLineMode 0 0++    toPreCabal2VersionRange :: D.VersionRange -> D.VersionRange+    toPreCabal2VersionRange = D.embedVersionRange . D.cataVersionRange f+      where+        f :: VersionRangeF (VersionRangeF D.VersionRange) -> VersionRangeF D.VersionRange+        f = \ case+          MajorBoundVersionF v -> IntersectVersionRangesF (D.embedVersionRange lower) (D.embedVersionRange upper)+            where+              lower = OrLaterVersionF v+              upper = EarlierVersionF (D.majorUpperBound v)++          AnyVersionF -> AnyVersionF+          ThisVersionF v -> ThisVersionF v+          LaterVersionF v -> LaterVersionF v+          OrLaterVersionF v -> OrLaterVersionF v+          EarlierVersionF v -> EarlierVersionF v+          OrEarlierVersionF v -> OrEarlierVersionF v+          WildcardVersionF v -> WildcardVersionF v+          UnionVersionRangesF a b -> UnionVersionRangesF (D.embedVersionRange a) (D.embedVersionRange b)+          IntersectVersionRangesF a b -> IntersectVersionRangesF (D.embedVersionRange a) (D.embedVersionRange b)+          VersionRangeParensF a -> VersionRangeParensF (D.embedVersionRange a)
test/EndToEndSpec.hs view
@@ -16,11 +16,14 @@ import           Data.List import           Data.String.Interpolate import           Data.String.Interpolate.Util+import           Data.Version (showVersion)  import qualified Hpack.Render as Hpack import           Hpack.Config (packageConfig, readPackageConfig, DecodeOptions(..), DecodeResult(..), defaultDecodeOptions) import           Hpack.Render.Hints (FormattingHints(..), sniffFormattingHints) +import qualified Paths_hpack as Hpack (version)+ writeFile :: FilePath -> String -> IO () writeFile file c = touch file >> Prelude.writeFile file c @@ -36,7 +39,38 @@       other-modules:           Paths_foo       |]+    describe "spec-version" $ do+      it "accepts spec-version" $ do+        [i|+        spec-version: 0.29.5+        |] `shouldRenderTo` package [i|+        |] +      it "fails on malformed spec-version" $ do+        [i|+        spec-version: foo+        |] `shouldFailWith` "package.yaml: Error while parsing $.spec-version - invalid value \"foo\""++      it "fails on unsupported spec-version" $ do+        [i|+        spec-version: 25.0+        dependencies: foo == bar+        |] `shouldFailWith` ("The file package.yaml requires version 25.0 of the Hpack package specification, however this version of hpack only supports versions up to " ++ showVersion Hpack.version ++ ". Upgrading to the latest version of hpack may resolve this issue.")++      it "fails on unsupported spec-version from defaults" $ do+        let file = joinPath ["defaults", "sol", "hpack-template", "2017", "defaults.yaml"]+        writeFile file [i|+        spec-version: 25.0+        |]++        [i|+        defaults:+          github: sol/hpack-template+          path: defaults.yaml+          ref: "2017"+        library: {}+        |] `shouldFailWith` ("The file " ++ file ++ " requires version 25.0 of the Hpack package specification, however this version of hpack only supports versions up to " ++ showVersion Hpack.version ++ ". Upgrading to the latest version of hpack may resolve this issue.")+     describe "data-files" $ do       it "accepts data-files" $ do         touch "data/foo/index.html"@@ -553,6 +587,24 @@         |] `shouldRenderTo` (executable_ "foo" [i|         cxx-options: -Wall         |]) {packageCabalVersion = "2.2"}++      context "when used inside a nested conditional" $ do+        it "infers correct cabal-version" $ do+          [i|+          executable:+            when:+              condition: True+              when:+                condition: True+                when:+                  condition: True+                  cxx-options: -Wall+          |] `shouldRenderTo` (executable_ "foo" [i|+          if true+            if true+              if true+                cxx-options: -Wall+          |]) {packageCabalVersion = "2.2"}      describe "cxx-sources" $ before_ (touch "foo.cc" >> touch "cxxbits/bar.cc") $ do       it "accepts cxx-sources" $ do
test/Hpack/Syntax/DependencySpec.hs view
@@ -9,6 +9,7 @@ import           Data.Aeson.Config.FromValueSpec (shouldDecodeTo, shouldDecodeTo_)  import           Data.Aeson.Config.FromValue+import           Hpack.Syntax.DependencyVersion import           Hpack.Syntax.Dependency  left :: String -> Result Dependencies