hpack 0.1.2 → 0.2.0
raw patch · 6 files changed
+304/−133 lines, 6 filesdep +textdep ~base
Dependencies added: text
Dependency ranges changed: base
Files
- driver/Main.hs +6/−4
- hpack.cabal +13/−7
- src/Config.hs +97/−24
- src/Run.hs +4/−5
- src/Util.hs +3/−7
- test/ConfigSpec.hs +181/−86
driver/Main.hs view
@@ -4,6 +4,7 @@ import Prelude.Compat import Control.Monad.Compat import Control.Exception+import System.IO import System.IO.Error import Data.List.Compat import Data.Version (showVersion)@@ -12,8 +13,8 @@ import Paths_hpack (version) import Run -header :: String-header = unlines [+header :: String -> String+header configFile = unlines [ "-- This file has been generated from " ++ configFile ++ " by hpack version " ++ showVersion version ++ "." , "--" , "-- see: https://github.com/sol/hpack"@@ -22,9 +23,10 @@ main :: IO () main = do- (name, new) <- run+ (configFile, warnings, name, new) <- run+ forM_ warnings $ \warning -> hPutStrLn stderr ("WARNING: " ++ warning) old <- force . either (const Nothing) (Just . stripHeader) <$> tryJust (guard . isDoesNotExistError) (readFile name)- unless (old == Just (lines new)) (writeFile name $ header ++ new)+ unless (old == Just (lines new)) (writeFile name $ header configFile ++ new) where stripHeader :: String -> [String] stripHeader = dropWhile null . dropWhile ("--" `isPrefixOf`) . lines
hpack.cabal view
@@ -1,9 +1,9 @@--- This file has been generated from package.yaml by hpack version 0.1.1.+-- This file has been generated from package.yml by hpack version 0.2.0. -- -- see: https://github.com/sol/hpack name: hpack-version: 0.1.2+version: 0.2.0 synopsis: An alternative format for Haskell packages homepage: https://github.com/sol/hpack#readme bug-reports: https://github.com/sol/hpack/issues@@ -18,8 +18,12 @@ location: https://github.com/sol/hpack executable hpack- hs-source-dirs: driver, src+ hs-source-dirs: src, driver main-is: Main.hs+ other-modules:+ Config+ Run+ Util build-depends: aeson >= 0.8 , base == 4.*@@ -27,6 +31,7 @@ , deepseq , directory , filepath+ , text , unordered-containers , yaml ghc-options: -Wall@@ -34,16 +39,16 @@ test-suite spec type: exitcode-stdio-1.0- hs-source-dirs: test, src+ hs-source-dirs: src, test main-is: Spec.hs other-modules:+ Config+ Run+ Util ConfigSpec Helper RunSpec UtilSpec- Config- Run- Util build-depends: aeson >= 0.8 , base == 4.*@@ -51,6 +56,7 @@ , deepseq , directory , filepath+ , text , unordered-containers , yaml
src/Config.hs view
@@ -1,4 +1,8 @@-{-# LANGUAGE DeriveGeneric, RecordWildCards #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-} module Config ( readPackageConfig , Package(..)@@ -12,18 +16,47 @@ import Prelude.Compat import Control.Applicative import Control.Monad.Compat-import Data.List ((\\))+import Data.List (nub, sort, (\\)) import Data.String import Data.Maybe import Data.Yaml import GHC.Generics import Data.HashMap.Lazy (HashMap) import qualified Data.HashMap.Lazy as Map+import qualified Data.Text as T import System.FilePath import System.Directory+import Data.Data+import Data.Aeson.Types import Util +genericParseJSON_ :: forall a. (Typeable a, Generic a, GFromJSON (Rep a)) => Value -> Parser a+genericParseJSON_ = genericParseJSON defaultOptions {fieldLabelModifier = hyphenize name}+ where+ name = (tyConName . typeRepTyCon . typeRep) (Proxy :: Proxy a)++hyphenize :: String -> String -> String+hyphenize name = camelTo '-' . drop (length name)++data CaptureUnknownFields a = CaptureUnknownFields {+ captureUnknownFieldsFields :: [String]+, captureUnknownFieldsValue :: a+} deriving (Eq, Show, Generic, Data, Typeable)++instance (Data a, FromJSON a) => FromJSON (CaptureUnknownFields a) where+ parseJSON v = captureUnknownFields <$> parseJSON v+ where+ captureUnknownFields a = case v of+ Object o -> CaptureUnknownFields unknown a+ where+ unknown = keys \\ fields+ keys = map T.unpack (Map.keys o)+ constr = toConstr a+ name = showConstr constr+ fields = map (hyphenize name) (constrFields constr)+ _ -> CaptureUnknownFields [] a+ data LibrarySection = LibrarySection { librarySectionSourceDirs :: Maybe (List FilePath) , librarySectionExposedModules :: Maybe (List String)@@ -31,10 +64,10 @@ , librarySectionDependencies :: Maybe (List Dependency) , librarySectionDefaultExtensions :: Maybe (List String) , librarySectionGhcOptions :: Maybe (List GhcOption)-} deriving (Eq, Show, Generic)+} deriving (Eq, Show, Generic, Data, Typeable) instance FromJSON LibrarySection where- parseJSON = genericParseJSON_ "LibrarySection"+ parseJSON = genericParseJSON_ data ExecutableSection = ExecutableSection { executableSectionMain :: FilePath@@ -43,10 +76,10 @@ , executableSectionDependencies :: Maybe (List Dependency) , executableSectionDefaultExtensions :: Maybe (List String) , executableSectionGhcOptions :: Maybe (List GhcOption)-} deriving (Eq, Show, Generic)+} deriving (Eq, Show, Generic, Data, Typeable) instance FromJSON ExecutableSection where- parseJSON = genericParseJSON_ "ExecutableSection"+ parseJSON = genericParseJSON_ data PackageConfig = PackageConfig { packageConfigName :: Maybe String@@ -67,13 +100,13 @@ , packageConfigDependencies :: Maybe (List Dependency) , packageConfigDefaultExtensions :: Maybe (List String) , packageConfigGhcOptions :: Maybe (List GhcOption)-, packageConfigLibrary :: Maybe LibrarySection-, packageConfigExecutables :: Maybe (HashMap String ExecutableSection)-, packageConfigTests :: Maybe (HashMap String ExecutableSection)-} deriving (Eq, Show, Generic)+, packageConfigLibrary :: Maybe (CaptureUnknownFields LibrarySection)+, packageConfigExecutables :: Maybe (HashMap String (CaptureUnknownFields ExecutableSection))+, packageConfigTests :: Maybe (HashMap String (CaptureUnknownFields ExecutableSection))+} deriving (Eq, Show, Generic, Data, Typeable) instance FromJSON PackageConfig where- parseJSON value = handleNullValues <$> genericParseJSON_ "PackageConfig" value+ parseJSON value = handleNullValues <$> genericParseJSON_ value where handleNullValues :: PackageConfig -> PackageConfig handleNullValues =@@ -92,14 +125,16 @@ where p = parseJSON >=> (.: fromString name) -readPackageConfig :: FilePath -> IO (Either String Package)+readPackageConfig :: FilePath -> IO (Either String ([String], Package)) readPackageConfig file = do config <- decodeFileEither file either (return . Left . errToString) (fmap Right . mkPackage) config where errToString err = file ++ case err of AesonException e -> ": " ++ e- InvalidYaml (Just e) -> let loc = yamlProblemMark e in ":" ++ show (yamlLine loc) ++ ":" ++ show (yamlColumn loc) ++ ": " ++ yamlProblem e ++ " " ++ yamlContext e+ InvalidYaml (Just (YamlException s)) -> ": " ++ s+ InvalidYaml (Just (YamlParseException{..})) -> ":" ++ show yamlLine ++ ":" ++ show yamlColumn ++ ": " ++ yamlProblem ++ " " ++ yamlContext+ where YamlMark{..} = yamlProblemMark _ -> ": " ++ show err type Dependency = String@@ -145,20 +180,28 @@ , executableGhcOptions :: [GhcOption] } deriving (Eq, Show) -mkPackage :: PackageConfig -> IO Package-mkPackage PackageConfig{..} = do+mkPackage :: (CaptureUnknownFields PackageConfig) -> IO ([String], Package)+mkPackage (CaptureUnknownFields unknownFields PackageConfig{..}) = do let dependencies = fromMaybeList packageConfigDependencies- let sourceDirs = fromMaybeList packageConfigSourceDirs- let defaultExtensions = fromMaybeList packageConfigDefaultExtensions- let ghcOptions = fromMaybeList packageConfigGhcOptions- mLibrary <- mapM (mkLibrary sourceDirs dependencies defaultExtensions ghcOptions) packageConfigLibrary- executables <- toExecutables sourceDirs dependencies defaultExtensions ghcOptions packageConfigExecutables- tests <- toExecutables sourceDirs dependencies defaultExtensions ghcOptions packageConfigTests+ sourceDirs = fromMaybeList packageConfigSourceDirs+ defaultExtensions = fromMaybeList packageConfigDefaultExtensions+ ghcOptions = fromMaybeList packageConfigGhcOptions+ convert f = f sourceDirs dependencies defaultExtensions ghcOptions + mLibrary <- mapM (convert toLibrary) mLibrarySection+ executables <- convert toExecutables mExecutableSections+ tests <- convert toExecutables mTestSections+ name <- maybe (takeBaseName <$> getCurrentDirectory) return packageConfigName licenseFileExists <- doesFileExist "LICENSE" + missingSourceDirs <- nub . sort <$> filterM (fmap not <$> doesDirectoryExist) (+ maybe [] librarySourceDirs mLibrary+ ++ concatMap executableSourceDirs executables+ ++ concatMap executableSourceDirs tests+ )+ let package = Package { packageName = name , packageVersion = fromMaybe "0.0.0" packageConfigVersion@@ -179,8 +222,38 @@ , packageExecutables = executables , packageTests = tests }- return package++ warnings =+ formatUnknownFields "package description" unknownFields+ ++ maybe [] (formatUnknownFields "library section") (captureUnknownFieldsFields <$> packageConfigLibrary)+ ++ formatUnknownSectionFields "executable" packageConfigExecutables+ ++ formatUnknownSectionFields "test" packageConfigTests+ ++ formatMissingSourceDirs missingSourceDirs++ return (warnings, package) where+ mLibrarySection :: Maybe LibrarySection+ mLibrarySection = captureUnknownFieldsValue <$> packageConfigLibrary++ mExecutableSections :: Maybe (HashMap String ExecutableSection)+ mExecutableSections = fmap captureUnknownFieldsValue <$> packageConfigExecutables++ mTestSections :: Maybe (HashMap String ExecutableSection)+ mTestSections = fmap captureUnknownFieldsValue <$> packageConfigTests++ formatUnknownFields name = map f . sort+ where+ f field = "Ignoring unknown field " ++ show field ++ " in " ++ name++ formatUnknownSectionFields sectionType = maybe [] (concatMap f . Map.toList . fmap captureUnknownFieldsFields)+ where+ f :: (String, [String]) -> [String]+ f (section, fields) = formatUnknownFields (sectionType ++ " section " ++ show section) fields++ formatMissingSourceDirs = map f+ where+ f name = "Specified source-dir " ++ show name ++ " does not exist"+ github = ("https://github.com/" ++) <$> packageConfigGithub homepage :: Maybe String@@ -197,8 +270,8 @@ where fromGithub = ((++ "/issues") <$> github) -mkLibrary :: [FilePath] -> [Dependency] -> [String] -> [GhcOption] -> LibrarySection -> IO Library-mkLibrary globalSourceDirs globalDependencies globalDefaultExtensions globalGhcOptions LibrarySection{..} = do+toLibrary :: [FilePath] -> [Dependency] -> [String] -> [GhcOption] -> LibrarySection -> IO Library+toLibrary globalSourceDirs globalDependencies globalDefaultExtensions globalGhcOptions LibrarySection{..} = do modules <- concat <$> mapM getModules sourceDirs let (exposedModules, otherModules) = determineModules modules librarySectionExposedModules librarySectionOtherModules
src/Run.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE QuasiQuotes, RecordWildCards #-} module Run ( run-, configFile -- exported for testing , renderPackage ) where@@ -16,20 +15,20 @@ import Config configFile :: FilePath-configFile = "package.yaml"+configFile = "package.yml" -run :: IO (FilePath, String)+run :: IO (FilePath, [String], FilePath, String) run = do mPackage <- readPackageConfig configFile case mPackage of- Right package -> do+ Right (warnings, package) -> do let cabalFile = packageName package ++ ".cabal" old <- tryReadFile cabalFile let alignment = fromMaybe 16 (old >>= sniffAlignment) output = renderPackage alignment (maybe [] extractFieldOrderHint old) package- return (cabalFile, output)+ return (configFile, warnings, cabalFile, output) Left err -> die err renderPackage :: Int -> [String] -> Package -> String
src/Util.hs view
@@ -1,8 +1,7 @@-{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE DeriveDataTypeable #-} module Util ( List(..) , toModule-, genericParseJSON_ , getFilesRecursive , tryReadFile , sniffAlignment@@ -21,15 +20,12 @@ import Data.List import System.Directory import System.FilePath+import Data.Data -import GHC.Generics import Data.Aeson.Types -genericParseJSON_ :: (Generic a, GFromJSON (Rep a)) => String -> Value -> Parser a-genericParseJSON_ name = genericParseJSON defaultOptions {fieldLabelModifier = camelTo '-' . drop (length name)}- newtype List a = List {fromList :: [a]}- deriving (Eq, Show)+ deriving (Eq, Show, Data, Typeable) instance FromJSON a => FromJSON (List a) where parseJSON v = List <$> (parseJSON v <|> (return <$> parseJSON v))
test/ConfigSpec.hs view
@@ -29,233 +29,279 @@ spec :: Spec spec = around_ (inTempDirectoryNamed "foo") $ do describe "readPackageConfig" $ do+ it "warns on unknown fields" $ do+ writeFile "package.yml" [i|+ bar: 23+ baz: 42+ |]+ fmap fst <$> readPackageConfig "package.yml" `shouldReturn` Right [+ "Ignoring unknown field \"bar\" in package description"+ , "Ignoring unknown field \"baz\" in package description"+ ]+ it "accepts name" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| name: bar |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageName = "bar"}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageName = "bar"} it "accepts version" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| version: 0.1.0 |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageVersion = "0.1.0"}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageVersion = "0.1.0"} it "accepts synopsis" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| synopsis: some synopsis |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageSynopsis = Just "some synopsis"}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageSynopsis = Just "some synopsis"} it "accepts description" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| description: some description |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageDescription = Just "some description"}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageDescription = Just "some description"} it "accepts category" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| category: Data |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageCategory = Just "Data"}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageCategory = Just "Data"} it "accepts author" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| author: John Doe |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageAuthor = ["John Doe"]}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageAuthor = ["John Doe"]} it "accepts maintainer" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| maintainer: John Doe <john.doe@example.com> |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageMaintainer = ["John Doe <john.doe@example.com>"]}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageMaintainer = ["John Doe <john.doe@example.com>"]} it "accepts copyright" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| copyright: (c) 2015 John Doe |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageCopyright = ["(c) 2015 John Doe"]}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageCopyright = ["(c) 2015 John Doe"]} it "accepts stability" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| stability: experimental |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageStability c `shouldBe` Just "experimental" it "accepts homepage URL" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| github: hspec/hspec homepage: https://example.com/ |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageHomepage c `shouldBe` Just "https://example.com/" it "infers homepage URL from github" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| github: hspec/hspec |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageHomepage c `shouldBe` Just "https://github.com/hspec/hspec#readme" it "omits homepage URL if it is null" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| github: hspec/hspec homepage: null |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageHomepage c `shouldBe` Nothing it "accepts bug-reports URL" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| github: hspec/hspec bug-reports: https://example.com/issues |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageBugReports c `shouldBe` Just "https://example.com/issues" it "infers bug-reports URL from github" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| github: hspec/hspec |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageBugReports c `shouldBe` Just "https://github.com/hspec/hspec/issues" it "omits bug-reports URL if it is null" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| github: hspec/hspec bug-reports: null |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageBugReports c `shouldBe` Nothing it "accepts license" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| license: MIT |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageLicense = Just "MIT"}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageLicense = Just "MIT"} it "infers license file" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| name: foo |] touch "LICENSE"- readPackageConfig "package.yaml" `shouldReturn` Right package {packageLicenseFile = Just "LICENSE"}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageLicenseFile = Just "LICENSE"} it "accepts extra-source-files" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| extra-source-files: - CHANGES.markdown - README.markdown |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageExtraSourceFiles c `shouldBe` ["CHANGES.markdown", "README.markdown"] it "accepts github" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| github: hspec/hspec |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageSourceRepository c `shouldBe` Just "https://github.com/hspec/hspec" context "when reading library section" $ do+ it "warns on unknown fields" $ do+ writeFile "package.yml" [i|+ library:+ bar: 23+ baz: 42+ |]++ fmap fst <$> readPackageConfig "package.yml" `shouldReturn` Right [+ "Ignoring unknown field \"bar\" in library section"+ , "Ignoring unknown field \"baz\" in library section"+ ]+ it "accepts source-dirs" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| library: source-dirs: - foo - bar |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageLibrary c `shouldBe` Just library {librarySourceDirs = ["foo", "bar"]} it "accepts default-extensions" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| library: default-extensions: - Foo - Bar |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageLibrary c `shouldBe` Just library {libraryDefaultExtensions = ["Foo", "Bar"]} it "accepts global default-extensions" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| default-extensions: - Foo - Bar library: {} |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageLibrary c `shouldBe` Just library {libraryDefaultExtensions = ["Foo", "Bar"]} it "accepts global source-dirs" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| source-dirs: - foo - bar library: {} |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageLibrary c `shouldBe` Just library {librarySourceDirs = ["foo", "bar"]} it "allows to specify exposed-modules" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| library: source-dirs: src exposed-modules: Foo |] touch "src/Foo.hs" touch "src/Bar.hs"- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageLibrary c `shouldBe` Just library {librarySourceDirs = ["src"], libraryExposedModules = ["Foo"], libraryOtherModules = ["Bar"]} it "allows to specify other-modules" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| library: source-dirs: src other-modules: Bar |] touch "src/Foo.hs" touch "src/Bar.hs"- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageLibrary c `shouldBe` Just library {librarySourceDirs = ["src"], libraryExposedModules = ["Foo"], libraryOtherModules = ["Bar"]} it "allows to specify both exposed-modules and other-modules" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| library: source-dirs: src exposed-modules: Foo other-modules: Bar |] touch "src/Baz.hs"- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageLibrary c `shouldBe` Just library {librarySourceDirs = ["src"], libraryExposedModules = ["Foo"], libraryOtherModules = ["Bar"]} context "when neither exposed-module nor other-module are specified" $ do it "exposes all modules" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| library: source-dirs: src |] touch "src/Foo.hs" touch "src/Bar.hs"- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageLibrary c `shouldBe` Just library {librarySourceDirs = ["src"], libraryExposedModules = ["Bar", "Foo"]} context "when reading executable section" $ do+ it "warns on unknown fields" $ do+ writeFile "package.yml" [i|+ executables:+ foo:+ main: Main.hs+ bar: 42+ baz: 23+ |]++ fmap fst <$> readPackageConfig "package.yml" `shouldReturn` Right [+ "Ignoring unknown field \"bar\" in executable section \"foo\""+ , "Ignoring unknown field \"baz\" in executable section \"foo\""+ ]+ it "reads executable section" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| executables: foo: main: driver/Main.hs |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageExecutables c `shouldBe` [executable "foo" "driver/Main.hs"] it "accepts source-dirs" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| executables: foo: main: Main.hs@@ -263,11 +309,11 @@ - foo - bar |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageExecutables c `shouldBe` [(executable "foo" "Main.hs") {executableSourceDirs = ["foo", "bar"]}] it "accepts global source-dirs" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| source-dirs: - foo - bar@@ -275,7 +321,7 @@ foo: main: Main.hs |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageExecutables c `shouldBe` [(executable "foo" "Main.hs") {executableSourceDirs = ["foo", "bar"]}] it "infers other-modules" $ do@@ -283,30 +329,30 @@ touch "src/Foo.hs" touch "src/Bar.hs" touch "src/Baz.lhs"- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| executables: foo: main: Main.hs source-dirs: src |]- Right [r] <- fmap packageExecutables <$> readPackageConfig "package.yaml"+ Right (_, [r]) <- (fmap . fmap) packageExecutables <$> readPackageConfig "package.yml" executableOtherModules r `shouldBe` ["Bar", "Baz", "Foo"] it "allows to specify other-modules" $ do touch "src/Foo.hs" touch "src/Bar.hs"- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| executables: foo: main: Main.hs source-dirs: src other-modules: Baz |]- Right [r] <- fmap packageExecutables <$> readPackageConfig "package.yaml"+ Right (_, [r]) <- (fmap . fmap) packageExecutables <$> readPackageConfig "package.yml" executableOtherModules r `shouldBe` ["Baz"] it "accepts default-extensions" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| executables: foo: main: driver/Main.hs@@ -314,11 +360,11 @@ - Foo - Bar |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageExecutables c `shouldBe` [(executable "foo" "driver/Main.hs") {executableDefaultExtensions = ["Foo", "Bar"]}] it "accepts global default-extensions" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| default-extensions: - Foo - Bar@@ -326,47 +372,65 @@ foo: main: driver/Main.hs |]- Right c <- readPackageConfig "package.yaml"+ Right (_, c) <- readPackageConfig "package.yml" packageExecutables c `shouldBe` [(executable "foo" "driver/Main.hs") {executableDefaultExtensions = ["Foo", "Bar"]}] it "accepts GHC options" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| executables: foo: main: driver/Main.hs ghc-options: -Wall |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageExecutables = [(executable "foo" "driver/Main.hs") {executableGhcOptions = ["-Wall"]}]}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageExecutables = [(executable "foo" "driver/Main.hs") {executableGhcOptions = ["-Wall"]}]} it "accepts global GHC options" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| ghc-options: -Wall executables: foo: main: driver/Main.hs |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageExecutables = [(executable "foo" "driver/Main.hs") {executableGhcOptions = ["-Wall"]}]}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageExecutables = [(executable "foo" "driver/Main.hs") {executableGhcOptions = ["-Wall"]}]} context "when reading test section" $ do+ it "warns on unknown fields" $ do+ writeFile "package.yml" [i|+ tests:+ foo:+ main: Main.hs+ bar: 42+ baz: 23+ |]++ fmap fst <$> readPackageConfig "package.yml" `shouldReturn` Right [+ "Ignoring unknown field \"bar\" in test section \"foo\""+ , "Ignoring unknown field \"baz\" in test section \"foo\""+ ]+ it "reads test section" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| tests: spec: main: test/Spec.hs |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageTests = [executable "spec" "test/Spec.hs"]}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageTests = [executable "spec" "test/Spec.hs"]} it "accepts single dependency" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| tests: spec: main: test/Spec.hs dependencies: hspec |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageTests = [(executable "spec" "test/Spec.hs") {executableDependencies = [["hspec"]]}]}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageTests = [(executable "spec" "test/Spec.hs") {executableDependencies = [["hspec"]]}]} it "accepts list of dependencies" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| tests: spec: main: test/Spec.hs@@ -374,11 +438,12 @@ - hspec - QuickCheck |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageTests = [(executable "spec" "test/Spec.hs") {executableDependencies = [["hspec", "QuickCheck"]]}]}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageTests = [(executable "spec" "test/Spec.hs") {executableDependencies = [["hspec", "QuickCheck"]]}]} context "when both top-level and section specific dependencies are specified" $ do it "combines dependencies" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| dependencies: - base @@ -387,21 +452,51 @@ main: test/Spec.hs dependencies: hspec |]- readPackageConfig "package.yaml" `shouldReturn` Right package {packageTests = [(executable "spec" "test/Spec.hs") {executableDependencies = [["base"], ["hspec"]]}]}+ Right (_, c) <- readPackageConfig "package.yml"+ c `shouldBe` package {packageTests = [(executable "spec" "test/Spec.hs") {executableDependencies = [["base"], ["hspec"]]}]} - context "when package.yaml can not be parsed" $ do+ context "when a specified source directory does not exist" $ do+ it "warns" $ do+ writeFile "package.yml" [i|+ source-dirs:+ - some-dir+ - some-existing-dir+ library:+ source-dirs: some-lib-dir+ executables:+ main:+ main: Main.hs+ source-dirs: some-exec-dir+ tests:+ spec:+ main: Main.hs+ source-dirs: some-test-dir+ |]+ touch "some-existing-dir/foo"+ fmap fst <$> readPackageConfig "package.yml" `shouldReturn` Right [+ "Specified source-dir " ++ show "some-dir" ++ " does not exist"+ , "Specified source-dir " ++ show "some-exec-dir" ++ " does not exist"+ , "Specified source-dir " ++ show "some-lib-dir" ++ " does not exist"+ , "Specified source-dir " ++ show "some-test-dir" ++ " does not exist"+ ]++ context "when package.yml can not be parsed" $ do it "returns an error" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| foo: bar foo baz |]- readPackageConfig "package.yaml" `shouldReturn` Left "package.yaml:3:10: could not find expected ':' while scanning a simple key"+ readPackageConfig "package.yml" `shouldReturn` Left "package.yml:3:10: could not find expected ':' while scanning a simple key" - context "when package.yaml is invalid" $ do+ context "when package.yml is invalid" $ do it "returns an error" $ do- writeFile "package.yaml" [i|+ writeFile "package.yml" [i| executables: foo: ain: driver/Main.hs |]- readPackageConfig "package.yaml" `shouldReturn` Left "package.yaml: The key \"main\" was not found"+ readPackageConfig "package.yml" `shouldReturn` Left "package.yml: The key \"main\" was not found"++ context "when package.yml does not exist" $ do+ it "returns an error" $+ readPackageConfig "package.yml" `shouldReturn` Left "package.yml: Yaml file not found: package.yml"