packages feed

hpack 0.4.0 → 0.5.0

raw patch · 7 files changed

+358/−180 lines, 7 filesdep +GlobPVP ok

version bump matches the API change (PVP)

Dependencies added: Glob

API changes (from Hackage documentation)

- Hpack.Config: executableCppOptions :: Executable -> [CppOption]
- Hpack.Config: executableDefaultExtensions :: Executable -> [String]
- Hpack.Config: executableDependencies :: Executable -> [[Dependency]]
- Hpack.Config: executableGhcOptions :: Executable -> [GhcOption]
- Hpack.Config: executableSourceDirs :: Executable -> [FilePath]
- Hpack.Config: instance (Data a, FromJSON a) => FromJSON (CaptureUnknownFields a)
- Hpack.Config: instance Selector S1_0_17PackageConfig
- Hpack.Config: instance Selector S1_0_18PackageConfig
- Hpack.Config: instance Selector S1_0_19PackageConfig
- Hpack.Config: instance Selector S1_0_20PackageConfig
- Hpack.Config: instance Selector S1_0_21PackageConfig
- Hpack.Config: instance Selector S1_0_2ExecutableSection
- Hpack.Config: instance Selector S1_0_2LibrarySection
- Hpack.Config: instance Selector S1_0_3ExecutableSection
- Hpack.Config: instance Selector S1_0_3LibrarySection
- Hpack.Config: instance Selector S1_0_4ExecutableSection
- Hpack.Config: instance Selector S1_0_4LibrarySection
- Hpack.Config: instance Selector S1_0_5ExecutableSection
- Hpack.Config: instance Selector S1_0_5LibrarySection
- Hpack.Config: instance Selector S1_0_6ExecutableSection
- Hpack.Config: instance Selector S1_0_6LibrarySection
- Hpack.Config: libraryCppOptions :: Library -> [CppOption]
- Hpack.Config: libraryDefaultExtensions :: Library -> [String]
- Hpack.Config: libraryDependencies :: Library -> [[Dependency]]
- Hpack.Config: libraryGhcOptions :: Library -> [GhcOption]
- Hpack.Config: librarySourceDirs :: Library -> [FilePath]
+ Hpack.Config: Section :: a -> [FilePath] -> [[Dependency]] -> [String] -> [GhcOption] -> [CppOption] -> Section a
+ Hpack.Config: data Section a
+ Hpack.Config: instance (HasFieldNames a, FromJSON a) => FromJSON (CaptureUnknownFields a)
+ Hpack.Config: instance Constructor C1_0CommonOptions
+ Hpack.Config: instance Constructor C1_0WithCommonOptions
+ Hpack.Config: instance Data CommonOptions
+ Hpack.Config: instance Data a => Data (WithCommonOptions a)
+ Hpack.Config: instance Datatype D1CommonOptions
+ Hpack.Config: instance Datatype D1WithCommonOptions
+ Hpack.Config: instance Eq CommonOptions
+ Hpack.Config: instance Eq a => Eq (Section a)
+ Hpack.Config: instance Eq a => Eq (WithCommonOptions a)
+ Hpack.Config: instance Foldable Section
+ Hpack.Config: instance FromJSON CommonOptions
+ Hpack.Config: instance FromJSON a => FromJSON (WithCommonOptions a)
+ Hpack.Config: instance Functor Section
+ Hpack.Config: instance Generic (WithCommonOptions a)
+ Hpack.Config: instance Generic CommonOptions
+ Hpack.Config: instance HasFieldNames CommonOptions
+ Hpack.Config: instance HasFieldNames ExecutableSection
+ Hpack.Config: instance HasFieldNames LibrarySection
+ Hpack.Config: instance HasFieldNames PackageConfig
+ Hpack.Config: instance HasFieldNames a => HasFieldNames (WithCommonOptions a)
+ Hpack.Config: instance Selector S1_0_0CommonOptions
+ Hpack.Config: instance Selector S1_0_1CommonOptions
+ Hpack.Config: instance Selector S1_0_2CommonOptions
+ Hpack.Config: instance Selector S1_0_3CommonOptions
+ Hpack.Config: instance Selector S1_0_4CommonOptions
+ Hpack.Config: instance Show CommonOptions
+ Hpack.Config: instance Show a => Show (Section a)
+ Hpack.Config: instance Show a => Show (WithCommonOptions a)
+ Hpack.Config: instance Traversable Section
+ Hpack.Config: instance Typeable CommonOptions
+ Hpack.Config: instance Typeable WithCommonOptions
+ Hpack.Config: sectionCppOptions :: Section a -> [CppOption]
+ Hpack.Config: sectionData :: Section a -> a
+ Hpack.Config: sectionDefaultExtensions :: Section a -> [String]
+ Hpack.Config: sectionDependencies :: Section a -> [[Dependency]]
+ Hpack.Config: sectionGhcOptions :: Section a -> [GhcOption]
+ Hpack.Config: sectionSourceDirs :: Section a -> [FilePath]
+ Hpack.Run: formatDescription :: Int -> String -> String
- Hpack.Config: Executable :: String -> FilePath -> [FilePath] -> [String] -> [[Dependency]] -> [String] -> [GhcOption] -> [CppOption] -> Executable
+ Hpack.Config: Executable :: String -> FilePath -> [String] -> Executable
- Hpack.Config: Library :: [FilePath] -> [String] -> [String] -> [[Dependency]] -> [String] -> [GhcOption] -> [CppOption] -> Library
+ Hpack.Config: Library :: [String] -> [String] -> Library
- Hpack.Config: Package :: String -> String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> [String] -> [String] -> [String] -> Maybe String -> Maybe FilePath -> [FilePath] -> Maybe SourceRepository -> Maybe Library -> [Executable] -> [Executable] -> Package
+ Hpack.Config: Package :: String -> String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> [String] -> [String] -> [String] -> Maybe String -> Maybe FilePath -> [FilePath] -> Maybe SourceRepository -> Maybe (Section Library) -> [Section Executable] -> [Section Executable] -> Package
- Hpack.Config: packageExecutables :: Package -> [Executable]
+ Hpack.Config: packageExecutables :: Package -> [Section Executable]
- Hpack.Config: packageLibrary :: Package -> Maybe Library
+ Hpack.Config: packageLibrary :: Package -> Maybe (Section Library)
- Hpack.Config: packageTests :: Package -> [Executable]
+ Hpack.Config: packageTests :: Package -> [Section Executable]

Files

hpack.cabal view
@@ -3,7 +3,7 @@ -- see: https://github.com/sol/hpack  name:           hpack-version:        0.4.0+version:        0.5.0 synopsis:       An alternative format for Haskell packages homepage:       https://github.com/sol/hpack#readme bug-reports:    https://github.com/sol/hpack/issues@@ -31,6 +31,7 @@     , deepseq     , directory     , filepath+    , Glob     , text     , unordered-containers     , yaml@@ -47,6 +48,7 @@     , deepseq     , directory     , filepath+    , Glob     , text     , unordered-containers     , yaml@@ -74,6 +76,7 @@     , deepseq     , directory     , filepath+    , Glob     , text     , unordered-containers     , yaml
src/Hpack/Config.hs view
@@ -1,9 +1,14 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE ViewPatterns #-} module Hpack.Config (   packageConfig , readPackageConfig@@ -12,28 +17,29 @@ , GitRef(..) , packageDependencies , GhcOption+, Section(..) , Library(..) , Executable(..) , SourceRepository(..) ) where -import           Prelude ()-import           Prelude.Compat import           Control.Applicative import           Control.Monad.Compat-import           Data.List (nub, sort, (\\))-import           Data.String-import           Data.Maybe-import           Data.Yaml-import           GHC.Generics+import           Data.Aeson.Types+import           Data.Data import           Data.HashMap.Lazy (HashMap) import qualified Data.HashMap.Lazy as Map+import           Data.List (nub, sort, (\\))+import           Data.Maybe+import           Data.String import           Data.Text (Text) import qualified Data.Text as T-import           System.FilePath+import           Data.Yaml+import           GHC.Generics+import           Prelude ()+import           Prelude.Compat import           System.Directory-import           Data.Data-import           Data.Aeson.Types+import           System.FilePath  import           Hpack.Util @@ -51,12 +57,20 @@ hyphenize :: String -> String -> String hyphenize name = camelTo '-' . drop (length name) +class HasFieldNames a where+  fieldNames :: a -> [String]+  default fieldNames :: Data a => a -> [String]+  fieldNames a = map (hyphenize name) (constrFields constr)+    where+      constr = toConstr a+      name = showConstr constr+ data CaptureUnknownFields a = CaptureUnknownFields {   captureUnknownFieldsFields :: [String] , captureUnknownFieldsValue :: a } deriving (Eq, Show, Generic, Data, Typeable) -instance (Data a, FromJSON a) => FromJSON (CaptureUnknownFields a) where+instance (HasFieldNames a, FromJSON a) => FromJSON (CaptureUnknownFields a) where   parseJSON v = captureUnknownFields <$> parseJSON v     where       captureUnknownFields a = case v of@@ -64,37 +78,51 @@           where             unknown = keys \\ fields             keys = map T.unpack (Map.keys o)-            constr = toConstr a-            name = showConstr constr-            fields = map (hyphenize name) (constrFields constr)+            fields = fieldNames a         _ -> CaptureUnknownFields [] a  data LibrarySection = LibrarySection {-  librarySectionSourceDirs :: Maybe (List FilePath)-, librarySectionExposedModules :: Maybe (List String)+  librarySectionExposedModules :: Maybe (List String) , librarySectionOtherModules :: Maybe (List String)-, librarySectionDependencies :: Maybe (List Dependency)-, librarySectionDefaultExtensions :: Maybe (List String)-, librarySectionGhcOptions :: Maybe (List GhcOption)-, librarySectionCppOptions :: Maybe (List CppOption) } deriving (Eq, Show, Generic, Data, Typeable) +instance HasFieldNames LibrarySection+ instance FromJSON LibrarySection where   parseJSON = genericParseJSON_  data ExecutableSection = ExecutableSection {   executableSectionMain :: FilePath-, executableSectionSourceDirs :: Maybe (List FilePath) , executableSectionOtherModules :: Maybe (List String)-, executableSectionDependencies :: Maybe (List Dependency)-, executableSectionDefaultExtensions :: Maybe (List String)-, executableSectionGhcOptions :: Maybe (List GhcOption)-, executableSectionCppOptions :: Maybe (List CppOption) } deriving (Eq, Show, Generic, Data, Typeable) +instance HasFieldNames ExecutableSection+ instance FromJSON ExecutableSection where   parseJSON = genericParseJSON_ +data WithCommonOptions a = WithCommonOptions a CommonOptions+  deriving (Eq, Show, Generic, Data, Typeable)++instance HasFieldNames a => HasFieldNames (WithCommonOptions a) where+  fieldNames (WithCommonOptions a options) = (fieldNames a ++ fieldNames options) \\ ["config"] -- FIXME : test for removing "config"++instance FromJSON a => FromJSON (WithCommonOptions a) where+  parseJSON v = WithCommonOptions <$> parseJSON v <*> parseJSON v++data CommonOptions = CommonOptions {+  commonOptionsSourceDirs :: Maybe (List FilePath)+, commonOptionsDependencies :: Maybe (List Dependency)+, commonOptionsDefaultExtensions :: Maybe (List String)+, commonOptionsGhcOptions :: Maybe (List GhcOption)+, commonOptionsCppOptions :: Maybe (List CppOption)+} deriving (Eq, Show, Generic, Data, Typeable)++instance HasFieldNames CommonOptions++instance FromJSON CommonOptions where+  parseJSON = genericParseJSON_+ data PackageConfig = PackageConfig {   packageConfigName :: Maybe String , packageConfigVersion :: Maybe String@@ -110,21 +138,18 @@ , packageConfigLicense :: Maybe String , packageConfigExtraSourceFiles :: Maybe (List FilePath) , packageConfigGithub :: Maybe Text-, packageConfigSourceDirs :: Maybe (List FilePath)-, packageConfigDependencies :: Maybe (List Dependency)-, packageConfigDefaultExtensions :: Maybe (List String)-, packageConfigGhcOptions :: Maybe (List GhcOption)-, packageConfigCppOptions :: Maybe (List CppOption)-, packageConfigLibrary :: Maybe (CaptureUnknownFields LibrarySection)-, packageConfigExecutables :: Maybe (HashMap String (CaptureUnknownFields ExecutableSection))-, packageConfigTests :: Maybe (HashMap String (CaptureUnknownFields ExecutableSection))+, packageConfigLibrary :: Maybe (CaptureUnknownFields (WithCommonOptions LibrarySection))+, packageConfigExecutables :: Maybe (HashMap String (CaptureUnknownFields (WithCommonOptions ExecutableSection)))+, packageConfigTests :: Maybe (HashMap String (CaptureUnknownFields (WithCommonOptions ExecutableSection))) } deriving (Eq, Show, Generic, Data, Typeable) +instance HasFieldNames PackageConfig+ packageDependencies :: Package -> [Dependency] packageDependencies Package{..} = nub . sort $-     (concat $ concatMap executableDependencies packageExecutables)-  ++ (concat $ concatMap executableDependencies packageTests)-  ++ maybe [] (concat . libraryDependencies) packageLibrary+     (concat $ concatMap sectionDependencies packageExecutables)+  ++ (concat $ concatMap sectionDependencies packageTests)+  ++ maybe [] (concat . sectionDependencies) packageLibrary  instance FromJSON PackageConfig where   parseJSON value = handleNullValues <$> genericParseJSON_ value@@ -213,60 +238,55 @@ , packageLicenseFile :: Maybe FilePath , packageExtraSourceFiles :: [FilePath] , packageSourceRepository :: Maybe SourceRepository-, packageLibrary :: Maybe Library-, packageExecutables :: [Executable]-, packageTests :: [Executable]+, packageLibrary :: Maybe (Section Library)+, packageExecutables :: [Section Executable]+, packageTests :: [Section Executable] } deriving (Eq, Show)  data Library = Library {-  librarySourceDirs :: [FilePath]-, libraryExposedModules :: [String]+  libraryExposedModules :: [String] , libraryOtherModules :: [String]-, libraryDependencies :: [[Dependency]]-, libraryDefaultExtensions :: [String]-, libraryGhcOptions :: [GhcOption]-, libraryCppOptions :: [CppOption] } deriving (Eq, Show)  data Executable = Executable {   executableName :: String , executableMain :: FilePath-, executableSourceDirs :: [FilePath] , executableOtherModules :: [String]-, executableDependencies :: [[Dependency]]-, executableDefaultExtensions :: [String]-, executableGhcOptions :: [GhcOption]-, executableCppOptions :: [CppOption] } deriving (Eq, Show) +data Section a = Section {+  sectionData :: a+, sectionSourceDirs :: [FilePath]+, sectionDependencies :: [[Dependency]]+, sectionDefaultExtensions :: [String]+, sectionGhcOptions :: [GhcOption]+, sectionCppOptions :: [CppOption]+} deriving (Eq, Show, Functor, Foldable, Traversable)+ data SourceRepository = SourceRepository {   sourceRepositoryUrl :: String , sourceRepositorySubdir :: Maybe String } deriving (Eq, Show) -mkPackage :: (CaptureUnknownFields PackageConfig) -> IO ([String], Package)-mkPackage (CaptureUnknownFields unknownFields PackageConfig{..}) = do-  let dependencies = fromMaybeList packageConfigDependencies-      sourceDirs = fromMaybeList packageConfigSourceDirs-      defaultExtensions = fromMaybeList packageConfigDefaultExtensions-      ghcOptions = fromMaybeList packageConfigGhcOptions-      cppOptions = fromMaybeList packageConfigCppOptions-      convert f = f sourceDirs dependencies defaultExtensions ghcOptions cppOptions--  mLibrary <- mapM (convert toLibrary) mLibrarySection-  executables <- convert toExecutables mExecutableSections-  tests <- convert toExecutables mTestSections+mkPackage :: (CaptureUnknownFields (WithCommonOptions PackageConfig)) -> IO ([String], Package)+mkPackage (CaptureUnknownFields unknownFields (WithCommonOptions PackageConfig{..} globalOptions@CommonOptions{..})) = do+  mLibrary <- mapM (toLibrary globalOptions) mLibrarySection+  executables <- toExecutables globalOptions (map (fmap captureUnknownFieldsValue) executableSections)+  tests <- toExecutables globalOptions (map (fmap captureUnknownFieldsValue) testsSections)    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+       maybe [] sectionSourceDirs mLibrary+    ++ concatMap sectionSourceDirs executables+    ++ concatMap sectionSourceDirs tests     ) +  (extraSourceFilesWarnings, extraSourceFiles) <-+    expandGlobs (fromMaybeList packageConfigExtraSourceFiles)+   let package = Package {         packageName = name       , packageVersion = fromMaybe "0.0.0" packageConfigVersion@@ -281,7 +301,7 @@       , packageCopyright = fromMaybeList packageConfigCopyright       , packageLicense = packageConfigLicense       , packageLicenseFile = guard licenseFileExists >> Just "LICENSE"-      , packageExtraSourceFiles = fromMaybeList packageConfigExtraSourceFiles+      , packageExtraSourceFiles = extraSourceFiles       , packageSourceRepository = sourceRepository       , packageLibrary = mLibrary       , packageExecutables = executables@@ -291,26 +311,32 @@       warnings =            formatUnknownFields "package description" unknownFields         ++ maybe [] (formatUnknownFields "library section") (captureUnknownFieldsFields <$> packageConfigLibrary)-        ++ formatUnknownSectionFields "executable" packageConfigExecutables-        ++ formatUnknownSectionFields "test" packageConfigTests+        ++ formatUnknownSectionFields "executable" executableSections+        ++ formatUnknownSectionFields "test" testsSections         ++ formatMissingSourceDirs missingSourceDirs+        ++ extraSourceFilesWarnings    return (warnings, package)   where-    mLibrarySection :: Maybe LibrarySection-    mLibrarySection = captureUnknownFieldsValue <$> packageConfigLibrary+    executableSections :: [(String, CaptureUnknownFields (WithCommonOptions ExecutableSection))]+    executableSections = toList packageConfigExecutables -    mExecutableSections :: Maybe (HashMap String ExecutableSection)-    mExecutableSections = fmap captureUnknownFieldsValue <$> packageConfigExecutables+    testsSections :: [(String, CaptureUnknownFields (WithCommonOptions ExecutableSection))]+    testsSections = toList packageConfigTests -    mTestSections :: Maybe (HashMap String ExecutableSection)-    mTestSections = fmap captureUnknownFieldsValue <$> packageConfigTests+    toList :: Maybe (HashMap String a) -> [(String, a)]+    toList = Map.toList . fromMaybe mempty +    mLibrarySection :: Maybe (WithCommonOptions LibrarySection)+    mLibrarySection = captureUnknownFieldsValue <$> packageConfigLibrary++    formatUnknownFields :: String -> [String] -> [String]     formatUnknownFields name = map f . sort       where         f field = "Ignoring unknown field " ++ show field ++ " in " ++ name -    formatUnknownSectionFields sectionType = maybe [] (concatMap f . Map.toList . fmap captureUnknownFieldsFields)+    formatUnknownSectionFields :: String -> [(String, CaptureUnknownFields a)] -> [String]+    formatUnknownSectionFields sectionType = concatMap f . map (fmap captureUnknownFieldsFields)       where         f :: (String, [String]) -> [String]         f (section, fields) = formatUnknownFields (sectionType ++ " section " ++ show section) fields@@ -342,20 +368,53 @@       where         fromGithub = (++ "/issues") . sourceRepositoryUrl <$> sourceRepository -toLibrary :: [FilePath] -> [Dependency] -> [String] -> [GhcOption] -> [CppOption] -> LibrarySection -> IO Library-toLibrary globalSourceDirs globalDependencies globalDefaultExtensions globalGhcOptions globalCppOptions LibrarySection{..} = do-  modules <- concat <$> mapM getModules sourceDirs+toLibrary :: CommonOptions -> WithCommonOptions LibrarySection -> IO (Section Library)+toLibrary globalOptions library = traverse fromLibrarySection section+  where+    section :: Section LibrarySection+    section = toSection globalOptions library -  let (exposedModules, otherModules) = determineModules modules librarySectionExposedModules librarySectionOtherModules+    sourceDirs :: [FilePath]+    sourceDirs = sectionSourceDirs section -  return (Library sourceDirs exposedModules otherModules dependencies defaultExtensions ghcOptions cppOptions)+    fromLibrarySection :: LibrarySection -> IO Library+    fromLibrarySection LibrarySection{..} = do+      modules <- concat <$> mapM getModules sourceDirs+      let (exposedModules, otherModules) = determineModules modules librarySectionExposedModules librarySectionOtherModules+      return (Library exposedModules otherModules)++toExecutables :: CommonOptions -> [(String, WithCommonOptions ExecutableSection)] -> IO [Section Executable]+toExecutables globalOptions executables = mapM toExecutable sections   where-    sourceDirs = globalSourceDirs ++ fromMaybeList librarySectionSourceDirs-    dependencies = filter (not . null) [globalDependencies, fromMaybeList librarySectionDependencies]-    defaultExtensions = globalDefaultExtensions ++ fromMaybeList librarySectionDefaultExtensions-    ghcOptions = globalGhcOptions ++ fromMaybeList librarySectionGhcOptions-    cppOptions = globalCppOptions ++ fromMaybeList librarySectionCppOptions+    sections :: [(String, Section ExecutableSection)]+    sections = map (fmap $ toSection globalOptions) executables +    toExecutable :: (String, Section ExecutableSection) -> IO (Section Executable)+    toExecutable (name, section) = traverse fromExecutableSection section+      where+        sourceDirs :: [FilePath]+        sourceDirs = sectionSourceDirs section++        fromExecutableSection :: ExecutableSection -> IO Executable+        fromExecutableSection ExecutableSection{..} = do+          modules <- maybe (filterMain . concat <$> mapM getModules sourceDirs) (return . fromList) executableSectionOtherModules+          return (Executable name executableSectionMain modules)+          where+            filterMain :: [String] -> [String]+            filterMain = maybe id (filter . (/=)) (toModule $ splitDirectories executableSectionMain)++toSection :: CommonOptions -> WithCommonOptions a -> Section a+toSection globalOptions (WithCommonOptions a options)+  = Section a sourceDirs dependencies defaultExtensions ghcOptions cppOptions+  where+    sourceDirs = merge commonOptionsSourceDirs+    defaultExtensions = merge commonOptionsDefaultExtensions+    ghcOptions = merge commonOptionsGhcOptions+    cppOptions = merge commonOptionsCppOptions+    merge selector = fromMaybeList (selector globalOptions) ++ fromMaybeList (selector options)+    getDependencies = fromMaybeList . commonOptionsDependencies+    dependencies = filter (not . null) [getDependencies globalOptions, getDependencies options]+ determineModules :: [String] -> Maybe (List String) -> Maybe (List String) -> ([String], [String]) determineModules modules mExposedModules mOtherModules = case (mExposedModules, mOtherModules) of   (Nothing, Nothing) -> (modules, [])@@ -373,23 +432,6 @@   where     toModules :: [[FilePath]] -> [String]     toModules = catMaybes . map toModule--toExecutables :: [FilePath] -> [Dependency] -> [String] -> [GhcOption] -> [CppOption] -> Maybe (HashMap String ExecutableSection) -> IO [Executable]-toExecutables globalSourceDirs globalDependencies globalDefaultExtensions globalGhcOptions globalCppOptions executables =-  (mapM toExecutable . Map.toList) (fromMaybe mempty executables)-  where-    toExecutable (name, ExecutableSection{..}) = do-      modules <- maybe (filterMain . concat <$> mapM getModules sourceDirs) (return . fromList) executableSectionOtherModules-      return $ Executable name executableSectionMain sourceDirs modules dependencies defaultExtensions ghcOptions cppOptions-      where-        dependencies = filter (not . null) [globalDependencies, fromMaybeList executableSectionDependencies]-        sourceDirs = globalSourceDirs ++ fromMaybeList executableSectionSourceDirs-        defaultExtensions = globalDefaultExtensions ++ fromMaybeList executableSectionDefaultExtensions-        ghcOptions = globalGhcOptions ++ fromMaybeList executableSectionGhcOptions-        cppOptions = globalCppOptions ++ fromMaybeList executableSectionCppOptions--        filterMain :: [String] -> [String]-        filterMain = maybe id (filter . (/=)) (toModule $ splitDirectories executableSectionMain)  fromMaybeList :: Maybe (List a) -> [a] fromMaybeList = maybe [] fromList
src/Hpack/Run.hs view
@@ -1,15 +1,21 @@-{-# LANGUAGE QuasiQuotes, RecordWildCards #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ViewPatterns #-} module Hpack.Run (   run -- exported for testing , renderPackage , renderSourceRepository+, formatDescription ) where -import           Control.Applicative+import           Prelude ()+import           Prelude.Compat+ import           Control.Monad+import           Data.Char import           Data.Maybe-import           Data.List+import           Data.List.Compat import           System.Exit.Compat  import           Hpack.Util@@ -77,7 +83,7 @@         ("name", Just packageName)       , ("version", Just packageVersion)       , ("synopsis", packageSynopsis)-      , ("description", (formatDescription <$> packageDescription))+      , ("description", (formatDescription alignment <$> packageDescription))       , ("category", packageCategory)       , ("stability", packageStability)       , ("homepage", packageHomepage)@@ -99,11 +105,20 @@     defaultFieldOrder :: [String]     defaultFieldOrder = map fst fields -    formatDescription = intercalate separator . intersperse "." . lines-      where-        n = max alignment $ length ("description: ")-        separator = "\n" ++ replicate n ' '+formatDescription :: Int -> String -> String+formatDescription alignment description = case map emptyLineToDot $ lines description of+  x : xs -> intercalate "\n" (x : map (indentation ++) xs)+  [] -> ""+  where+    n = max alignment (length "description: ")+    indentation = replicate n ' ' +    emptyLineToDot xs+      | isEmptyLine xs = "."+      | otherwise = xs++    isEmptyLine = all isSpace+ renderSourceRepository :: SourceRepository -> String renderSourceRepository SourceRepository{..} = concat [     "source-repository head\n"@@ -112,47 +127,44 @@   , maybe "" (("  subdir: " ++) . (++ "\n")) sourceRepositorySubdir   ] -renderExecutables :: [Executable] -> [String]+renderExecutables :: [Section Executable] -> [String] renderExecutables = map renderExecutable -renderExecutable :: Executable -> String-renderExecutable executable@Executable{..} =+renderExecutable :: Section Executable -> String+renderExecutable section@(sectionData -> Executable{..}) =      "executable "   ++ executableName ++ "\n"-  ++ renderExecutableSection executable+  ++ renderExecutableSection section -renderTests :: [Executable] -> [String]+renderTests :: [Section Executable] -> [String] renderTests = map renderTest -renderTest :: Executable -> String-renderTest executable@Executable{..} =+renderTest :: Section Executable -> String+renderTest section@(sectionData -> Executable{..}) =      "test-suite " ++ executableName ++ "\n"   ++ "  type: exitcode-stdio-1.0\n"-  ++ renderExecutableSection executable+  ++ renderExecutableSection section -renderExecutableSection :: Executable -> String-renderExecutableSection Executable{..} =-     renderSourceDirs executableSourceDirs-  ++ "  main-is: " ++ executableMain ++ "\n"-  ++ renderOtherModules executableOtherModules-  ++ renderDependencies executableDependencies-  ++ renderDefaultExtensions executableDefaultExtensions-  ++ renderGhcOptions executableGhcOptions-  ++ renderCppOptions executableCppOptions-  ++ "  default-language: Haskell2010\n"+renderExecutableSection :: Section Executable -> String+renderExecutableSection section@(sectionData -> Executable{..}) =+     "  main-is: " ++ executableMain ++ "\n"+  ++ renderSection section -renderLibrary :: Library -> String-renderLibrary Library{..} =+renderLibrary :: Section Library -> String+renderLibrary section@(sectionData -> Library{..}) =     "library\n"-  ++ renderSourceDirs librarySourceDirs   ++ renderExposedModules libraryExposedModules   ++ renderOtherModules libraryOtherModules-  ++ renderDependencies libraryDependencies-  ++ renderDefaultExtensions libraryDefaultExtensions-  ++ renderGhcOptions libraryGhcOptions-  ++ renderCppOptions libraryCppOptions-  ++ "  default-language: Haskell2010\n"+  ++ renderSection section +renderSection :: Section a -> String+renderSection Section{..} =+     renderSourceDirs sectionSourceDirs+  ++ renderDependencies sectionDependencies+  ++ renderDefaultExtensions sectionDefaultExtensions+  ++ renderGhcOptions sectionGhcOptions+  ++ renderCppOptions sectionCppOptions+  ++ "  default-language: Haskell2010\n"  renderSourceDirs :: [String] -> String renderSourceDirs dirs
src/Hpack/Util.hs view
@@ -6,6 +6,7 @@ , tryReadFile , sniffAlignment , extractFieldOrderHint+, expandGlobs  -- exported for testing , splitField@@ -20,6 +21,7 @@ import           Data.List import           System.Directory import           System.FilePath+import           System.FilePath.Glob import           Data.Data  import           Data.Aeson.Types@@ -88,3 +90,23 @@   where     isNameChar = (`elem` nameChars)     nameChars = ['a'..'z'] ++ ['A'..'Z'] ++ "-"++expandGlobs :: [String] -> IO ([String], [FilePath])+expandGlobs patterns = do+  files <- (fst <$> globDir compiledPatterns ".") >>= mapM removeDirectories+  let warnings = [warn pattern | ([], pattern) <- zip files patterns]+  return (warnings, combineResults files)+  where+    combineResults = nub . map (makeRelative ".") . sort . concat+    warn pattern = "Specified pattern " ++ show pattern ++ " for extra-source-files does not match any files"+    compiledPatterns = map (compileWith options) patterns+    removeDirectories = filterM doesFileExist+    options = CompOptions {+        characterClasses = False+      , characterRanges = False+      , numberRanges = False+      , wildcards = True+      , recursiveWildcards = True+      , pathSepInRanges = False+      , errorRecovery = True+      }
test/Hpack/ConfigSpec.hs view
@@ -2,12 +2,12 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} module Hpack.ConfigSpec (-  main-, spec+  spec  , package , executable , library+, section ) where  import           Helper@@ -18,18 +18,18 @@  import           Hpack.Config -main :: IO ()-main = hspec spec- package :: Package package = Package "foo" "0.0.0" Nothing Nothing Nothing Nothing Nothing Nothing [] [] [] Nothing Nothing [] Nothing Nothing [] []  executable :: String -> String -> Executable-executable name main_ = Executable name main_ [] [] [] [] [] []+executable name main_ = Executable name main_ []  library :: Library-library = Library [] [] [] [] [] [] []+library = Library [] [] +section :: a -> Section a+section a = Section a [] [] [] [] []+ spec :: Spec spec = do   describe "parseJSON" $ do@@ -219,6 +219,8 @@           - CHANGES.markdown           - README.markdown         |]+      touch "CHANGES.markdown"+      touch "README.markdown"       Right (_, c) <- readPackageConfig "package.yaml"       packageExtraSourceFiles c `shouldBe` ["CHANGES.markdown", "README.markdown"] @@ -257,9 +259,9 @@         |]       Right (_, c) <- readPackageConfig "package.yaml"       c `shouldBe` package {-          packageLibrary = Just library {libraryCppOptions = ["-DFOO", "-DLIB"]}-        , packageExecutables = [(executable "foo" "Main.hs") {executableCppOptions = ["-DFOO", "-DFOO"]}]-        , packageTests = [(executable "spec" "Spec.hs") {executableCppOptions = ["-DFOO", "-DTEST"]}]+          packageLibrary = Just (section library) {sectionCppOptions = ["-DFOO", "-DLIB"]}+        , packageExecutables = [(section $ executable "foo" "Main.hs") {sectionCppOptions = ["-DFOO", "-DFOO"]}]+        , packageTests = [(section $ executable "spec" "Spec.hs") {sectionCppOptions = ["-DFOO", "-DTEST"]}]         }      context "when reading library section" $ do@@ -283,7 +285,7 @@               - bar           |]         Right (_, c) <- readPackageConfig "package.yaml"-        packageLibrary c `shouldBe` Just library {librarySourceDirs = ["foo", "bar"]}+        packageLibrary c `shouldBe` Just (section library) {sectionSourceDirs = ["foo", "bar"]}        it "accepts default-extensions" $ do         writeFile "package.yaml" [i|@@ -293,7 +295,7 @@               - Bar           |]         Right (_, c) <- readPackageConfig "package.yaml"-        packageLibrary c `shouldBe` Just library {libraryDefaultExtensions = ["Foo", "Bar"]}+        packageLibrary c `shouldBe` Just (section library) {sectionDefaultExtensions = ["Foo", "Bar"]}        it "accepts global default-extensions" $ do         writeFile "package.yaml" [i|@@ -303,7 +305,7 @@           library: {}           |]         Right (_, c) <- readPackageConfig "package.yaml"-        packageLibrary c `shouldBe` Just library {libraryDefaultExtensions = ["Foo", "Bar"]}+        packageLibrary c `shouldBe` Just (section library) {sectionDefaultExtensions = ["Foo", "Bar"]}        it "accepts global source-dirs" $ do         writeFile "package.yaml" [i|@@ -313,7 +315,7 @@           library: {}           |]         Right (_, c) <- readPackageConfig "package.yaml"-        packageLibrary c `shouldBe` Just library {librarySourceDirs = ["foo", "bar"]}+        packageLibrary c `shouldBe` Just (section library) {sectionSourceDirs = ["foo", "bar"]}        it "allows to specify exposed-modules" $ do         writeFile "package.yaml" [i|@@ -324,7 +326,7 @@         touch "src/Foo.hs"         touch "src/Bar.hs"         Right (_, c) <- readPackageConfig "package.yaml"-        packageLibrary c `shouldBe` Just library {librarySourceDirs = ["src"], libraryExposedModules = ["Foo"], libraryOtherModules = ["Bar"]}+        packageLibrary c `shouldBe` Just (section library{libraryExposedModules = ["Foo"], libraryOtherModules = ["Bar"]}) {sectionSourceDirs = ["src"]}        it "allows to specify other-modules" $ do         writeFile "package.yaml" [i|@@ -335,7 +337,7 @@         touch "src/Foo.hs"         touch "src/Bar.hs"         Right (_, c) <- readPackageConfig "package.yaml"-        packageLibrary c `shouldBe` Just library {librarySourceDirs = ["src"], libraryExposedModules = ["Foo"], libraryOtherModules = ["Bar"]}+        packageLibrary c `shouldBe` Just (section library{libraryExposedModules = ["Foo"], libraryOtherModules = ["Bar"]}) {sectionSourceDirs = ["src"]}        it "allows to specify both exposed-modules and other-modules" $ do         writeFile "package.yaml" [i|@@ -346,7 +348,7 @@           |]         touch "src/Baz.hs"         Right (_, c) <- readPackageConfig "package.yaml"-        packageLibrary c `shouldBe` Just library {librarySourceDirs = ["src"], libraryExposedModules = ["Foo"], libraryOtherModules = ["Bar"]}+        packageLibrary c `shouldBe` Just (section library{libraryExposedModules = ["Foo"], libraryOtherModules = ["Bar"]}) {sectionSourceDirs = ["src"]}        context "when neither exposed-module nor other-module are specified" $ do         it "exposes all modules" $ do@@ -357,7 +359,7 @@           touch "src/Foo.hs"           touch "src/Bar.hs"           Right (_, c) <- readPackageConfig "package.yaml"-          packageLibrary c `shouldBe` Just library {librarySourceDirs = ["src"], libraryExposedModules = ["Bar", "Foo"]}+          packageLibrary c `shouldBe` Just (section library{libraryExposedModules = ["Bar", "Foo"]}) {sectionSourceDirs = ["src"]}      context "when reading executable section" $ do       it "warns on unknown fields" $ do@@ -381,7 +383,7 @@               main: driver/Main.hs           |]         Right (_, c) <- readPackageConfig "package.yaml"-        packageExecutables c `shouldBe` [executable "foo" "driver/Main.hs"]+        packageExecutables c `shouldBe` [section $ executable "foo" "driver/Main.hs"]        it "accepts source-dirs" $ do         writeFile "package.yaml" [i|@@ -393,7 +395,7 @@                 - bar           |]         Right (_, c) <- readPackageConfig "package.yaml"-        packageExecutables c `shouldBe` [(executable "foo" "Main.hs") {executableSourceDirs = ["foo", "bar"]}]+        packageExecutables c `shouldBe` [(section $ executable "foo" "Main.hs") {sectionSourceDirs = ["foo", "bar"]}]        it "accepts global source-dirs" $ do         writeFile "package.yaml" [i|@@ -405,7 +407,7 @@               main: Main.hs           |]         Right (_, c) <- readPackageConfig "package.yaml"-        packageExecutables c `shouldBe` [(executable "foo" "Main.hs") {executableSourceDirs = ["foo", "bar"]}]+        packageExecutables c `shouldBe` [(section $ executable "foo" "Main.hs") {sectionSourceDirs = ["foo", "bar"]}]        it "infers other-modules" $ do         touch "src/Main.hs"@@ -419,7 +421,7 @@               source-dirs: src           |]         Right (_, [r]) <- (fmap . fmap) packageExecutables <$> readPackageConfig "package.yaml"-        executableOtherModules r `shouldBe` ["Bar", "Baz", "Foo"]+        executableOtherModules (sectionData r) `shouldBe` ["Bar", "Baz", "Foo"]        it "allows to specify other-modules" $ do         touch "src/Foo.hs"@@ -432,7 +434,7 @@               other-modules: Baz           |]         Right (_, [r]) <- (fmap . fmap) packageExecutables <$> readPackageConfig "package.yaml"-        executableOtherModules r `shouldBe` ["Baz"]+        executableOtherModules (sectionData r) `shouldBe` ["Baz"]        it "accepts default-extensions" $ do         writeFile "package.yaml" [i|@@ -444,7 +446,7 @@                 - Bar           |]         Right (_, c) <- readPackageConfig "package.yaml"-        packageExecutables c `shouldBe` [(executable "foo" "driver/Main.hs") {executableDefaultExtensions = ["Foo", "Bar"]}]+        packageExecutables c `shouldBe` [(section $ executable "foo" "driver/Main.hs") {sectionDefaultExtensions = ["Foo", "Bar"]}]        it "accepts global default-extensions" $ do         writeFile "package.yaml" [i|@@ -456,7 +458,7 @@               main: driver/Main.hs           |]         Right (_, c) <- readPackageConfig "package.yaml"-        packageExecutables c `shouldBe` [(executable "foo" "driver/Main.hs") {executableDefaultExtensions = ["Foo", "Bar"]}]+        packageExecutables c `shouldBe` [(section $ executable "foo" "driver/Main.hs") {sectionDefaultExtensions = ["Foo", "Bar"]}]        it "accepts GHC options" $ do         writeFile "package.yaml" [i|@@ -466,7 +468,7 @@               ghc-options: -Wall           |]         Right (_, c) <- readPackageConfig "package.yaml"-        c `shouldBe` package {packageExecutables = [(executable "foo" "driver/Main.hs") {executableGhcOptions = ["-Wall"]}]}+        c `shouldBe` package {packageExecutables = [(section $ executable "foo" "driver/Main.hs") {sectionGhcOptions = ["-Wall"]}]}        it "accepts global GHC options" $ do         writeFile "package.yaml" [i|@@ -476,7 +478,7 @@               main: driver/Main.hs           |]         Right (_, c) <- readPackageConfig "package.yaml"-        c `shouldBe` package {packageExecutables = [(executable "foo" "driver/Main.hs") {executableGhcOptions = ["-Wall"]}]}+        c `shouldBe` package {packageExecutables = [(section $ executable "foo" "driver/Main.hs") {sectionGhcOptions = ["-Wall"]}]}      context "when reading test section" $ do       it "warns on unknown fields" $ do@@ -500,7 +502,7 @@               main: test/Spec.hs           |]         Right (_, c) <- readPackageConfig "package.yaml"-        c `shouldBe` package {packageTests = [executable "spec" "test/Spec.hs"]}+        c `shouldBe` package {packageTests = [section $ executable "spec" "test/Spec.hs"]}        it "accepts single dependency" $ do         writeFile "package.yaml" [i|@@ -510,7 +512,7 @@               dependencies: hspec           |]         Right (_, c) <- readPackageConfig "package.yaml"-        c `shouldBe` package {packageTests = [(executable "spec" "test/Spec.hs") {executableDependencies = [["hspec"]]}]}+        c `shouldBe` package {packageTests = [(section $ executable "spec" "test/Spec.hs") {sectionDependencies = [["hspec"]]}]}        it "accepts list of dependencies" $ do         writeFile "package.yaml" [i|@@ -522,7 +524,7 @@                 - QuickCheck           |]         Right (_, c) <- readPackageConfig "package.yaml"-        c `shouldBe` package {packageTests = [(executable "spec" "test/Spec.hs") {executableDependencies = [["hspec", "QuickCheck"]]}]}+        c `shouldBe` package {packageTests = [(section $ executable "spec" "test/Spec.hs") {sectionDependencies = [["hspec", "QuickCheck"]]}]}        context "when both global and section specific dependencies are specified" $ do         it "combines dependencies" $ do@@ -536,7 +538,7 @@                 dependencies: hspec             |]           Right (_, c) <- readPackageConfig "package.yaml"-          c `shouldBe` package {packageTests = [(executable "spec" "test/Spec.hs") {executableDependencies = [["base"], ["hspec"]]}]}+          c `shouldBe` package {packageTests = [(section $ executable "spec" "test/Spec.hs") {sectionDependencies = [["base"], ["hspec"]]}]}      context "when a specified source directory does not exist" $ do       it "warns" $ do
test/Hpack/RunSpec.hs view
@@ -1,15 +1,13 @@ {-# LANGUAGE OverloadedStrings #-}-module Hpack.RunSpec (main, spec) where+module Hpack.RunSpec (spec) where  import           Test.Hspec+import           Data.List -import           Hpack.ConfigSpec hiding (main, spec)+import           Hpack.ConfigSpec hiding (spec) import           Hpack.Config import           Hpack.Run -main :: IO ()-main = hspec spec- spec :: Spec spec = do   describe "renderPackage" $ do@@ -30,7 +28,7 @@         ]      it "includes description" $ do-      renderPackage 0 [] package {packageDescription = Just "foo\nbar\n"} `shouldBe` unlines [+      renderPackage 0 [] package {packageDescription = Just "foo\n\nbar\n"} `shouldBe` unlines [           "name: foo"         , "version: 0.0.0"         , "description: foo"@@ -41,7 +39,7 @@         ]      it "aligns description" $ do-      renderPackage 16 [] package {packageDescription = Just "foo\nbar\n"} `shouldBe` unlines [+      renderPackage 16 [] package {packageDescription = Just "foo\n\nbar\n"} `shouldBe` unlines [           "name:           foo"         , "version:        0.0.0"         , "description:    foo"@@ -110,7 +108,7 @@      context "when rendering executable section" $ do       it "includes dependencies" $ do-        renderPackage 0 [] package {packageExecutables = [(executable "foo" "Main.hs") {executableDependencies = [["foo", "bar"], ["foo", "baz"]]}]} `shouldBe` unlines [+        renderPackage 0 [] package {packageExecutables = [(section $ executable "foo" "Main.hs") {sectionDependencies = [["foo", "bar"], ["foo", "baz"]]}]} `shouldBe` unlines [             "name: foo"           , "version: 0.0.0"           , "build-type: Simple"@@ -128,7 +126,7 @@           ]        it "includes GHC options" $ do-        renderPackage 0 [] package {packageExecutables = [(executable "foo" "Main.hs") {executableGhcOptions = ["-Wall", "-Werror"]}]} `shouldBe` unlines [+        renderPackage 0 [] package {packageExecutables = [(section $ executable "foo" "Main.hs") {sectionGhcOptions = ["-Wall", "-Werror"]}]} `shouldBe` unlines [             "name: foo"           , "version: 0.0.0"           , "build-type: Simple"@@ -139,6 +137,41 @@           , "  ghc-options: -Wall -Werror"           , "  default-language: Haskell2010"           ]++  describe "formatDescription" $ do+    it "formats description" $ do+      let description = unlines [+              "foo"+            , "bar"+            ]+      "description: " ++ formatDescription 0 description `shouldBe` intercalate "\n" [+          "description: foo"+        , "             bar"+        ]++    it "takes specified alignment into account" $ do+      let description = unlines [+              "foo"+            , "bar"+            , "baz"+            ]+      "description:   " ++ formatDescription 15 description `shouldBe` intercalate "\n" [+          "description:   foo"+        , "               bar"+        , "               baz"+        ]++    it "formats empty lines" $ do+      let description = unlines [+              "foo"+            , "   "+            , "bar"+            ]+      "description: " ++ formatDescription 0 description `shouldBe` intercalate "\n" [+          "description: foo"+        , "             ."+        , "             bar"+        ]    describe "renderSourceRepository" $ do     it "renders source-repository without subdir correctly" $ do
test/Hpack/UtilSpec.hs view
@@ -3,6 +3,8 @@  import           Helper import           Data.Aeson+import           Data.List+import           System.Directory  import           Hpack.Util @@ -100,3 +102,65 @@      it "rejects invalid fields" $ do       splitField "foo bar" `shouldBe` Nothing++  describe "expandGlobs" $ around_ inTempDirectory $ do+    it "accepts simple files" $ do+      touch "foo.js"+      expandGlobs ["foo.js"] `shouldReturn` ([], ["foo.js"])++    it "removes duplicates" $ do+      touch "foo.js"+      expandGlobs ["foo.js", "*.js"] `shouldReturn` ([], ["foo.js"])++    it "rejects directories" $ do+      touch "foo"+      createDirectory "bar"+      expandGlobs ["*"] `shouldReturn` ([], ["foo"])++    it "rejects character ranges" $ do+      touch "foo1"+      touch "foo2"+      touch "foo[1,2]"+      expandGlobs ["foo[1,2]"] `shouldReturn` ([], ["foo[1,2]"])++    context "when expanding *" $ do+      it "expands by extension" $ do+        let files = [+                "files/foo.js"+              , "files/bar.js"+              , "files/baz.js"]+        mapM_ touch files+        touch "files/foo.hs"+        expandGlobs ["files/*.js"] `shouldReturn` ([], sort files)++      it "rejects dot-files" $ do+        touch "foo/bar"+        touch "foo/.baz"+        expandGlobs ["foo/*"] `shouldReturn` ([], ["foo/bar"])++      it "accepts dot-files when explicitly asked to" $ do+        touch "foo/bar"+        touch "foo/.baz"+        expandGlobs ["foo/.*"] `shouldReturn` ([], ["foo/.baz"])++      it "matches at most one directory component" $ do+        touch "foo/bar/baz.js"+        touch "foo/bar.js"+        expandGlobs ["*/*.js"] `shouldReturn` ([], ["foo/bar.js"])++    context "when expanding **" $ do+      it "matches arbitrary many directory components" $ do+        let file = "foo/bar/baz.js"+        touch file+        expandGlobs ["**/*.js"] `shouldReturn` ([], [file])++    context "when a pattern does not match anything" $ do+      it "warns" $ do+        expandGlobs ["foo"] `shouldReturn`+          (["Specified pattern \"foo\" for extra-source-files does not match any files"], [])++    context "when a pattern only matches a directory" $ do+      it "warns" $ do+        createDirectory "foo"+        expandGlobs ["foo"] `shouldReturn`+          (["Specified pattern \"foo\" for extra-source-files does not match any files"], [])