packages feed

hie-bios 0.9.1 → 0.10.0

raw patch · 16 files changed

+449/−326 lines, 16 filesdep +co-log-coredep +prettyprinterdep −hsloggerdep ~aesonPVP ok

version bump matches the API change (PVP)

Dependencies added: co-log-core, prettyprinter

Dependencies removed: hslogger

Dependency ranges changed: aeson

API changes (from Hackage documentation)

- HIE.Bios.Config: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (HIE.Bios.Config.Config a)
- HIE.Bios.Config: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (HIE.Bios.Config.CradleConfig a)
- HIE.Bios.Config: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (HIE.Bios.Config.CradleType a)
- HIE.Bios.Flags: getCompilerOptionsWithLogger :: LoggingFunction -> FilePath -> Cradle a -> IO (CradleLoadResult ComponentOptions)
- HIE.Bios.Flags: type LoggingFunction = String -> IO ()
- HIE.Bios.Internal.Log: debugm :: MonadIO m => String -> m ()
- HIE.Bios.Internal.Log: errorm :: MonadIO m => String -> m ()
- HIE.Bios.Internal.Log: logm :: MonadIO m => String -> m ()
- HIE.Bios.Internal.Log: warningm :: MonadIO m => String -> m ()
- HIE.Bios.Types: type LoggingFunction = String -> IO ()
+ HIE.Bios.Config.YAML: Bios :: BiosConfig -> CradleComponent a
+ HIE.Bios.Config.YAML: BiosConfig :: Callable -> Maybe Callable -> Maybe FilePath -> BiosConfig
+ HIE.Bios.Config.YAML: Cabal :: CabalConfig -> CradleComponent a
+ HIE.Bios.Config.YAML: CabalComponent :: FilePath -> String -> CabalComponent
+ HIE.Bios.Config.YAML: CabalConfig :: OneOrManyComponents CabalComponent -> CabalConfig
+ HIE.Bios.Config.YAML: CradleConfigYAML :: CradleComponent a -> Maybe [FilePath] -> CradleConfigYAML a
+ HIE.Bios.Config.YAML: Direct :: DirectConfig -> CradleComponent a
+ HIE.Bios.Config.YAML: DirectConfig :: [String] -> DirectConfig
+ HIE.Bios.Config.YAML: ManyComponents :: [component] -> OneOrManyComponents component
+ HIE.Bios.Config.YAML: Multi :: [MultiSubComponent a] -> CradleComponent a
+ HIE.Bios.Config.YAML: MultiSubComponent :: FilePath -> CradleConfigYAML a -> MultiSubComponent a
+ HIE.Bios.Config.YAML: NoComponent :: OneOrManyComponents component
+ HIE.Bios.Config.YAML: None :: NoneConfig -> CradleComponent a
+ HIE.Bios.Config.YAML: NoneConfig :: NoneConfig
+ HIE.Bios.Config.YAML: Other :: OtherConfig a -> CradleComponent a
+ HIE.Bios.Config.YAML: OtherConfig :: a -> Value -> OtherConfig a
+ HIE.Bios.Config.YAML: Program :: FilePath -> Callable
+ HIE.Bios.Config.YAML: Shell :: String -> Callable
+ HIE.Bios.Config.YAML: SingleComponent :: String -> OneOrManyComponents component
+ HIE.Bios.Config.YAML: Stack :: StackConfig -> CradleComponent a
+ HIE.Bios.Config.YAML: StackComponent :: FilePath -> String -> Maybe String -> StackComponent
+ HIE.Bios.Config.YAML: StackConfig :: Maybe FilePath -> OneOrManyComponents StackComponent -> StackConfig
+ HIE.Bios.Config.YAML: [$sel:arguments:DirectConfig] :: DirectConfig -> [String]
+ HIE.Bios.Config.YAML: [$sel:cabalComponent:CabalComponent] :: CabalComponent -> String
+ HIE.Bios.Config.YAML: [$sel:cabalComponents:CabalConfig] :: CabalConfig -> OneOrManyComponents CabalComponent
+ HIE.Bios.Config.YAML: [$sel:cabalPath:CabalComponent] :: CabalComponent -> FilePath
+ HIE.Bios.Config.YAML: [$sel:callable:BiosConfig] :: BiosConfig -> Callable
+ HIE.Bios.Config.YAML: [$sel:config:MultiSubComponent] :: MultiSubComponent a -> CradleConfigYAML a
+ HIE.Bios.Config.YAML: [$sel:cradle:CradleConfigYAML] :: CradleConfigYAML a -> CradleComponent a
+ HIE.Bios.Config.YAML: [$sel:dependencies:CradleConfigYAML] :: CradleConfigYAML a -> Maybe [FilePath]
+ HIE.Bios.Config.YAML: [$sel:depsCallable:BiosConfig] :: BiosConfig -> Maybe Callable
+ HIE.Bios.Config.YAML: [$sel:ghcPath:BiosConfig] :: BiosConfig -> Maybe FilePath
+ HIE.Bios.Config.YAML: [$sel:originalYamlValue:OtherConfig] :: OtherConfig a -> Value
+ HIE.Bios.Config.YAML: [$sel:otherConfig:OtherConfig] :: OtherConfig a -> a
+ HIE.Bios.Config.YAML: [$sel:path:MultiSubComponent] :: MultiSubComponent a -> FilePath
+ HIE.Bios.Config.YAML: [$sel:stackComponent:StackComponent] :: StackComponent -> String
+ HIE.Bios.Config.YAML: [$sel:stackComponentYAML:StackComponent] :: StackComponent -> Maybe String
+ HIE.Bios.Config.YAML: [$sel:stackComponents:StackConfig] :: StackConfig -> OneOrManyComponents StackComponent
+ HIE.Bios.Config.YAML: [$sel:stackPath:StackComponent] :: StackComponent -> FilePath
+ HIE.Bios.Config.YAML: [$sel:stackYaml:StackConfig] :: StackConfig -> Maybe FilePath
+ HIE.Bios.Config.YAML: data BiosConfig
+ HIE.Bios.Config.YAML: data CabalComponent
+ HIE.Bios.Config.YAML: data CabalConfig
+ HIE.Bios.Config.YAML: data Callable
+ HIE.Bios.Config.YAML: data CradleComponent a
+ HIE.Bios.Config.YAML: data CradleConfigYAML a
+ HIE.Bios.Config.YAML: data DirectConfig
+ HIE.Bios.Config.YAML: data MultiSubComponent a
+ HIE.Bios.Config.YAML: data NoneConfig
+ HIE.Bios.Config.YAML: data OneOrManyComponents component
+ HIE.Bios.Config.YAML: data OtherConfig a
+ HIE.Bios.Config.YAML: data StackComponent
+ HIE.Bios.Config.YAML: data StackConfig
+ HIE.Bios.Config.YAML: instance Data.Aeson.Types.FromJSON.FromJSON HIE.Bios.Config.YAML.BiosConfig
+ HIE.Bios.Config.YAML: instance Data.Aeson.Types.FromJSON.FromJSON HIE.Bios.Config.YAML.CabalComponent
+ HIE.Bios.Config.YAML: instance Data.Aeson.Types.FromJSON.FromJSON HIE.Bios.Config.YAML.CabalConfig
+ HIE.Bios.Config.YAML: instance Data.Aeson.Types.FromJSON.FromJSON HIE.Bios.Config.YAML.DirectConfig
+ HIE.Bios.Config.YAML: instance Data.Aeson.Types.FromJSON.FromJSON HIE.Bios.Config.YAML.NoneConfig
+ HIE.Bios.Config.YAML: instance Data.Aeson.Types.FromJSON.FromJSON HIE.Bios.Config.YAML.StackComponent
+ HIE.Bios.Config.YAML: instance Data.Aeson.Types.FromJSON.FromJSON HIE.Bios.Config.YAML.StackConfig
+ HIE.Bios.Config.YAML: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (HIE.Bios.Config.YAML.CradleComponent a)
+ HIE.Bios.Config.YAML: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (HIE.Bios.Config.YAML.CradleConfigYAML a)
+ HIE.Bios.Config.YAML: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (HIE.Bios.Config.YAML.MultiSubComponent a)
+ HIE.Bios.Config.YAML: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (HIE.Bios.Config.YAML.OtherConfig a)
+ HIE.Bios.Config.YAML: instance Data.Aeson.Types.FromJSON.FromJSON component => Data.Aeson.Types.FromJSON.FromJSON (HIE.Bios.Config.YAML.OneOrManyComponents component)
+ HIE.Bios.Config.YAML: instance GHC.Generics.Generic (HIE.Bios.Config.YAML.CradleComponent a)
+ HIE.Bios.Config.YAML: instance GHC.Generics.Generic (HIE.Bios.Config.YAML.CradleConfigYAML a)
+ HIE.Bios.Config.YAML: instance GHC.Generics.Generic (HIE.Bios.Config.YAML.MultiSubComponent a)
+ HIE.Bios.Config.YAML: instance GHC.Generics.Generic HIE.Bios.Config.YAML.DirectConfig
+ HIE.Bios.Ghc.Check: instance Prettyprinter.Internal.Pretty HIE.Bios.Ghc.Check.Log
+ HIE.Bios.Ghc.Load: LogInitPlugins :: Int -> [ModuleName] -> Log
+ HIE.Bios.Ghc.Load: LogLoaded :: FilePath -> FilePath -> Log
+ HIE.Bios.Ghc.Load: LogModGraph :: ModuleGraph -> Log
+ HIE.Bios.Ghc.Load: LogSetTargets :: [(FilePath, FilePath)] -> Log
+ HIE.Bios.Ghc.Load: LogTypechecked :: [TypecheckedModule] -> Log
+ HIE.Bios.Ghc.Load: data Log
+ HIE.Bios.Ghc.Load: instance Prettyprinter.Internal.Pretty HIE.Bios.Ghc.Load.Log
+ HIE.Bios.Types: LogAny :: String -> Log
+ HIE.Bios.Types: LogProcessOutput :: String -> Log
+ HIE.Bios.Types: data Log
+ HIE.Bios.Types: instance GHC.Show.Show HIE.Bios.Types.Log
+ HIE.Bios.Types: instance Prettyprinter.Internal.Pretty HIE.Bios.Types.Log
- HIE.Bios: getCompilerOptions :: FilePath -> Cradle a -> IO (CradleLoadResult ComponentOptions)
+ HIE.Bios: getCompilerOptions :: LogAction IO (WithSeverity Log) -> FilePath -> Cradle a -> IO (CradleLoadResult ComponentOptions)
- HIE.Bios: loadFile :: GhcMonad m => (FilePath, FilePath) -> m (Maybe TypecheckedModule, [TypecheckedModule])
+ HIE.Bios: loadFile :: GhcMonad m => LogAction IO (WithSeverity Log) -> (FilePath, FilePath) -> m (Maybe TypecheckedModule, [TypecheckedModule])
- HIE.Bios.Cradle: readProcessWithOutputs :: Outputs -> LoggingFunction -> FilePath -> CreateProcess -> IO (ExitCode, [String], [String], [(OutputName, Maybe [String])])
+ HIE.Bios.Cradle: readProcessWithOutputs :: Outputs -> LogAction IO (WithSeverity Log) -> FilePath -> CreateProcess -> IO (ExitCode, [String], [String], [(OutputName, Maybe [String])])
- HIE.Bios.Flags: getCompilerOptions :: FilePath -> Cradle a -> IO (CradleLoadResult ComponentOptions)
+ HIE.Bios.Flags: getCompilerOptions :: LogAction IO (WithSeverity Log) -> FilePath -> Cradle a -> IO (CradleLoadResult ComponentOptions)
- HIE.Bios.Ghc.Api: initializeFlagsWithCradle :: GhcMonad m => FilePath -> Cradle a -> m (CradleLoadResult (m SuccessFlag, ComponentOptions))
+ HIE.Bios.Ghc.Api: initializeFlagsWithCradle :: GhcMonad m => LogAction IO (WithSeverity Log) -> FilePath -> Cradle a -> m (CradleLoadResult (m SuccessFlag, ComponentOptions))
- HIE.Bios.Ghc.Api: initializeFlagsWithCradleWithMessage :: GhcMonad m => Maybe Messager -> FilePath -> Cradle a -> m (CradleLoadResult (m SuccessFlag, ComponentOptions))
+ HIE.Bios.Ghc.Api: initializeFlagsWithCradleWithMessage :: GhcMonad m => LogAction IO (WithSeverity Log) -> Maybe Messager -> FilePath -> Cradle a -> m (CradleLoadResult (m SuccessFlag, ComponentOptions))
- HIE.Bios.Ghc.Check: check :: GhcMonad m => [FilePath] -> m (Either String String)
+ HIE.Bios.Ghc.Check: check :: GhcMonad m => LogAction IO (WithSeverity Log) -> [FilePath] -> m (Either String String)
- HIE.Bios.Ghc.Check: checkSyntax :: Show a => Cradle a -> [FilePath] -> IO String
+ HIE.Bios.Ghc.Check: checkSyntax :: Show a => LogAction IO (WithSeverity Log) -> Cradle a -> [FilePath] -> IO String
- HIE.Bios.Ghc.Load: loadFile :: GhcMonad m => (FilePath, FilePath) -> m (Maybe TypecheckedModule, [TypecheckedModule])
+ HIE.Bios.Ghc.Load: loadFile :: GhcMonad m => LogAction IO (WithSeverity Log) -> (FilePath, FilePath) -> m (Maybe TypecheckedModule, [TypecheckedModule])
- HIE.Bios.Ghc.Load: loadFileWithMessage :: GhcMonad m => Maybe Messager -> (FilePath, FilePath) -> m (Maybe TypecheckedModule, [TypecheckedModule])
+ HIE.Bios.Ghc.Load: loadFileWithMessage :: GhcMonad m => LogAction IO (WithSeverity Log) -> Maybe Messager -> (FilePath, FilePath) -> m (Maybe TypecheckedModule, [TypecheckedModule])
- HIE.Bios.Ghc.Load: setTargetFiles :: GhcMonad m => [(FilePath, FilePath)] -> m ()
+ HIE.Bios.Ghc.Load: setTargetFiles :: GhcMonad m => LogAction IO (WithSeverity Log) -> [(FilePath, FilePath)] -> m ()
- HIE.Bios.Ghc.Load: setTargetFilesWithMessage :: GhcMonad m => Maybe Messager -> [(FilePath, FilePath)] -> m ()
+ HIE.Bios.Ghc.Load: setTargetFilesWithMessage :: GhcMonad m => LogAction IO (WithSeverity Log) -> Maybe Messager -> [(FilePath, FilePath)] -> m ()
- HIE.Bios.Internal.Debug: debugInfo :: Show a => FilePath -> Cradle a -> IO String
+ HIE.Bios.Internal.Debug: debugInfo :: Show a => LogAction IO (WithSeverity Log) -> FilePath -> Cradle a -> IO String
- HIE.Bios.Types: CradleAction :: ActionName a -> (LoggingFunction -> FilePath -> IO (CradleLoadResult ComponentOptions)) -> ([String] -> IO (CradleLoadResult String)) -> CradleAction a
+ HIE.Bios.Types: CradleAction :: ActionName a -> (LogAction IO (WithSeverity Log) -> FilePath -> IO (CradleLoadResult ComponentOptions)) -> ([String] -> IO (CradleLoadResult String)) -> CradleAction a
- HIE.Bios.Types: [runCradle] :: CradleAction a -> LoggingFunction -> FilePath -> IO (CradleLoadResult ComponentOptions)
+ HIE.Bios.Types: [runCradle] :: CradleAction a -> LogAction IO (WithSeverity Log) -> FilePath -> IO (CradleLoadResult ComponentOptions)

Files

ChangeLog.md view
@@ -1,5 +1,15 @@ # ChangeLog hie-bios +## 2022-07-26 - 0.10.0++* Apply Hlint suggestions [#354](https://github.com/haskell/hie-bios/pull/354)+* Cabal cradle: change error message on failure [#353](https://github.com/haskell/hie-bios/pull/353)+* Refactor parsing of hie.yaml files [#329](https://github.com/haskell/hie-bios/pull/329)+* Make sure we test the same versions as HLS [#346](https://github.com/haskell/hie-bios/pull/346)+* Move logging from hslogger to co-log  [#347](https://github.com/haskell/hie-bios/pull/347)+  * Demote process output to Debug severity [#348](https://github.com/haskell/hie-bios/pull/348)+* Fix typos [#342](https://github.com/haskell/hie-bios/pull/342)+ ## 2022-03-07 - 0.9.1  * Ignore .ghci files while querying project GHC [#337](https://github.com/haskell/hie-bios/pull/337)
exe/Main.hs view
@@ -1,9 +1,12 @@ {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ScopedTypeVariables #-}  module Main where  import Control.Monad ( forM )+import qualified Colog.Core as L import Data.Version (showVersion)+import Data.Text.Prettyprint.Doc import Options.Applicative import System.Directory (getCurrentDirectory) import System.IO (stdout, hSetEncoding, utf8)@@ -66,18 +69,22 @@           Just yaml -> loadCradle yaml           Nothing -> loadImplicitCradle (cwd </> "File.hs") +    let+      printLog (L.WithSeverity l sev) = "[" ++ show sev ++ "] " ++ show (pretty l)+      logger :: forall a . Pretty a => L.LogAction IO (L.WithSeverity a)+      logger = L.cmap printLog L.logStringStderr      res <- case cmd of-      Check targetFiles -> checkSyntax cradle targetFiles+      Check targetFiles -> checkSyntax logger cradle targetFiles       Debug files -> case files of-        [] -> debugInfo (cradleRootDir cradle) cradle-        fp -> debugInfo fp cradle+        [] -> debugInfo logger (cradleRootDir cradle) cradle+        fp -> debugInfo logger fp cradle       Flags files -> case files of         -- TODO force optparse to acquire one         [] -> error "too few arguments"         _ -> do           res <- forM files $ \fp -> do-                  res <- getCompilerOptions fp cradle+                  res <- getCompilerOptions logger fp cradle                   case res of                       CradleFail (CradleError _deps _ex err) ->                         return $ "Failed to show flags for \""
hie-bios.cabal view
@@ -1,6 +1,6 @@ Cabal-Version:          2.2 Name:                   hie-bios-Version:                0.9.1+Version:                0.10.0 Author:                 Matthew Pickering <matthewtpickering@gmail.com> Maintainer:             Matthew Pickering <matthewtpickering@gmail.com> License:                BSD-3-Clause@@ -125,7 +125,7 @@                         tests/projects/stack-with-yaml/stack-with-yaml.cabal                         tests/projects/stack-with-yaml/src/Lib.hs -tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.1 || ==9.0.2 || ==9.2.1+tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.7 || ==9.0.1 || ==9.0.2 || ==9.2.1 || ==9.2.1  Library   Default-Language:     Haskell2010@@ -133,12 +133,12 @@   HS-Source-Dirs:       src   Exposed-Modules:      HIE.Bios                         HIE.Bios.Config+                        HIE.Bios.Config.YAML                         HIE.Bios.Cradle                         HIE.Bios.Environment                         HIE.Bios.Internal.Debug                         HIE.Bios.Flags                         HIE.Bios.Types-                        HIE.Bios.Internal.Log                         HIE.Bios.Ghc.Api                         HIE.Bios.Ghc.Check                         HIE.Bios.Ghc.Doc@@ -153,6 +153,7 @@                         aeson                >= 1.4.5 && < 2.1,                         base16-bytestring    >= 0.1.1 && < 1.1,                         bytestring           >= 0.10.8 && < 0.12,+                        co-log-core          ^>= 0.3.0,                         deepseq              >= 1.4.3 && < 1.5,                         exceptions           ^>= 0.10,                         containers           >= 0.5.10 && < 0.7,@@ -161,6 +162,7 @@                         filepath             >= 1.4.1 && < 1.5,                         time                 >= 1.8.0 && < 1.13,                         extra                >= 1.6.14 && < 1.8,+                        prettyprinter        ^>= 1.7.0,                         process              >= 1.6.1 && < 1.7,                         ghc                  >= 8.6.1 && < 9.3,                         transformers         >= 0.5.2 && < 0.7,@@ -170,7 +172,6 @@                         unordered-containers >= 0.2.9 && < 0.3,                         vector               >= 0.12.0 && < 0.13,                         yaml                 >= 0.10.0 && < 0.12,-                        hslogger             >= 1.2 && < 1.4,                         file-embed           >= 0.0.11 && < 1,                         conduit              >= 1.3 && < 2,                         conduit-extra        >= 1.3 && < 2@@ -183,11 +184,13 @@   GHC-Options:          -Wall   HS-Source-Dirs:       exe   Build-Depends:        base >= 4.9 && < 5+                      , co-log-core                       , directory                       , filepath                       , ghc                       , hie-bios                       , optparse-applicative+                      , prettyprinter  test-suite parser-tests   type: exitcode-stdio-1.0
src/HIE/Bios/Config.hs view
@@ -1,11 +1,7 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TupleSections #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE PatternSynonyms #-}-{-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE CPP #-} -- | Logic and datatypes for parsing @hie.yaml@ files. module HIE.Bios.Config(     readConfig,@@ -23,38 +19,16 @@     ) where  import Control.Exception-import qualified Data.Text as T-import qualified Data.Vector as V-#if MIN_VERSION_aeson(2,0,0)-import           Data.Aeson.Key (fromText)-import           Data.Aeson.KeyMap (KeyMap)-import qualified Data.Aeson.KeyMap as Map-#else-import qualified Data.HashMap.Strict as Map-#endif-import           Data.Maybe (mapMaybe)+import           Data.Maybe (mapMaybe, fromMaybe) import           Data.Monoid (Last(..))-import           Data.Foldable (foldrM) import           Data.Aeson (JSONPath) import           Data.Yaml import           Data.Yaml.Internal (Warning(..)) +import           HIE.Bios.Config.YAML (CradleConfigYAML)+import qualified HIE.Bios.Config.YAML as YAML -#if !MIN_VERSION_aeson(2,0,0)--- | Backwards compatible type-def for Key--- This used to be just a Text, but since aeson >= 2--- this is an opaque datatype.-type Key = T.Text--- | Backwards compatible type-def for KeyMap--- This used to be just a HashMap, but since aeson >= 2--- this is an opaque datatype.-type KeyMap v = Map.HashMap T.Text v --- | Create a Key from a Text.-fromText :: T.Text -> Key-fromText = id-#endif- -- | Configuration that can be used to load a 'Cradle'. -- A configuration has roughly the following form: --@@ -140,10 +114,6 @@     | Other { otherConfig :: a, originalYamlValue :: Value }     deriving (Eq, Functor) -instance FromJSON a => FromJSON (CradleType a) where-    parseJSON (Object o) = parseCradleType o-    parseJSON _ = fail "Not a known cradle type. Possible are: cabal, stack, bios, direct, default, none, multi"- instance Show (CradleType a) where     show (Cabal comp) = "Cabal {component = " ++ show (cabalComponent comp) ++ "}"     show (CabalMulti d a) = "CabalMulti {defaultCabal = " ++ show d ++ ", subCabalComponents = " ++ show a ++ "}"@@ -155,163 +125,50 @@     show (Multi a) = "Multi " ++ show a     show (Other _ val) = "Other {originalYamlValue = " ++ show val ++ "}" -parseCradleType :: FromJSON a => Object -> Parser (CradleType a)-parseCradleType o-    | Just val <- Map.lookup "cabal" o = parseCabal val-    | Just val <- Map.lookup "stack" o = parseStack val---    | Just _val <- Map.lookup "bazel" o = return Bazel---    | Just _val <- Map.lookup "obelisk" o = return Obelisk-    | Just val <- Map.lookup "bios" o = parseBios val-    | Just val <- Map.lookup "direct" o = parseDirect val-    | Just _val <- Map.lookup "none" o = return None-    | Just val  <- Map.lookup "multi" o = parseMulti val-    | Just val  <- Map.lookup "other" o = Other <$> parseJSON val <*> pure val-parseCradleType o = fail $ "Unknown cradle type: " ++ show o--parseSingleOrMultiple-  :: Monoid x-  => (x -> CradleType a)-  -> (x -> [(FilePath, x)] -> CradleType a)-  -> (KeyMap Value -> Parser x)-  -> Value-  -> Parser (CradleType a)-parseSingleOrMultiple single multiple parse = doParse where-    parseOne e-        | Object v <- e-        , Just (String prefix) <- Map.lookup "path" v-        = (T.unpack prefix,) <$> parse (Map.delete "path" v)-        | otherwise-        = fail "Expected an object with a path key"-    parseArray = foldrM (\v cs -> (: cs) <$> parseOne v) []-    doParse (Object v)-        | Just (Array x) <- Map.lookup "components" v-        = do-            d <- parse (Map.delete "components" v)-            xs <- parseArray x-            return $ multiple d xs-        | Just _ <- Map.lookup "components" v-        = fail "Expected components to be an array of subcomponents"-        | Nothing <- Map.lookup "components" v-        = single <$> parse v-    doParse (Array x)-        = do-            xs <- parseArray x-            return $ multiple mempty xs-    doParse Null = single <$> parse Map.empty-    doParse _ = fail "Configuration is expected to be an object or an array of objects."--parseStack :: Value -> Parser (CradleType a)-parseStack = parseSingleOrMultiple Stack StackMulti $-  \case x | Map.size x == 2-          , Just (String component) <- Map.lookup "component" x-          , Just (String syaml) <- Map.lookup "stackYaml" x-          -> return $ StackType (Just $ T.unpack component) (Just $ T.unpack syaml)-          | Map.size x == 1, Just (String component) <- Map.lookup "component" x-          -> return $ StackType (Just $ T.unpack component) Nothing-          | Map.size x == 1, Just (String syaml) <- Map.lookup "stackYaml" x-          -> return $ StackType Nothing (Just $ T.unpack syaml)-          | Map.null x-          -> return $ StackType Nothing Nothing-          | otherwise-          -> fail "Not a valid Stack configuration, following keys are allowed: component, stackYaml"--parseCabal :: Value -> Parser (CradleType a)-parseCabal = parseSingleOrMultiple Cabal CabalMulti $-  \case x | Map.size x == 1, Just (String component) <- Map.lookup "component" x-          -> return $ CabalType (Just $ T.unpack component)-          | Map.null x-          -> return $ CabalType Nothing-          | otherwise-          -> fail "Not a valid Cabal configuration, following keys are allowed: component"--parseBios :: Value -> Parser (CradleType a)-parseBios (Object x) =-    case biosCallable of-        Just bc -> return $ Bios bc biosDepsCallable ghcPath-        _ -> fail $ "Not a valid Bios Configuration type, following keys are allowed:" ++-                    "program or shell, dependency-program or dependency-shell, with-ghc"-    where-        biosCallable =-            exclusive-                (stringTypeFromMap Program "program")-                (stringTypeFromMap Command "shell")-        biosDepsCallable =-            exclusive-                (stringTypeFromMap Program "dependency-program")-                (stringTypeFromMap Command "dependency-shell")-        ghcPath =-            stringTypeFromMap id "with-ghc"--        exclusive :: Maybe a -> Maybe a -> Maybe a-        exclusive (Just _) (Just _) = Nothing-        exclusive l Nothing = l-        exclusive Nothing r = r-        stringTypeFromMap :: (String -> t) -> T.Text -> Maybe t-        stringTypeFromMap constructor name = constructor <$> (intoString =<< Map.lookup (fromText name) x)-        intoString :: Value -> Maybe String-        intoString (String s) = Just (T.unpack s)-        intoString _ = Nothing--parseBios _ = fail "Bios Configuration is expected to be an object."--parseDirect :: Value -> Parser (CradleType a)-parseDirect (Object x)-    | Map.size x == 1-    , Just (Array v) <- Map.lookup "arguments" x-    = return $ Direct [T.unpack s | String s <- V.toList v]--    | otherwise-    = fail "Not a valid Direct Configuration type, following keys are allowed: arguments"-parseDirect _ = fail "Direct Configuration is expected to be an object."--parseMulti :: FromJSON a => Value -> Parser (CradleType a)-parseMulti (Array x)-    = Multi <$> mapM parsePath (V.toList x)-parseMulti _ = fail "Multi Configuration is expected to be an array."--parsePath :: FromJSON a => Value -> Parser (FilePath, CradleConfig a)-parsePath (Object v)-  | Just (String path) <- Map.lookup "path" v-  , Just c <- Map.lookup "config" v-  = (T.unpack path,) <$> parseJSON c-parsePath o = fail ("Multi component is expected to be an object." ++ show o)--instance FromJSON a => FromJSON (CradleConfig a) where-    parseJSON (Object val) = do-            crd     <- val .: "cradle"-            crdDeps <- case Map.size val of-                1 -> return []-                2 -> val .: "dependencies"-                _ -> fail "Unknown key, following keys are allowed: cradle, dependencies"--            return $ CradleConfig { cradleType         = crd-                                  , cradleDependencies = crdDeps-                                  }--    parseJSON _ = fail "Expected a cradle: key containing the preferences, possible values: cradle, dependencies"-+readConfig :: FromJSON a => FilePath -> IO (Config a)+readConfig fp = do+  result <- decodeFileWithWarnings fp+  fmap fromYAMLConfig $ either throwIO failOnAnyDuplicate result+  where+    failOnAnyDuplicate :: ([Warning], CradleConfigYAML a) -> IO (CradleConfigYAML a)+    failOnAnyDuplicate (warnings, config) = do+        _ <- case mapMaybe failOnDuplicate warnings of+                dups@(_:_) -> throwIO $ InvalidYaml $ Just $ YamlException+                                      $ "Duplicate keys are not allowed, found: " ++ show dups+                _ -> return ()+        return config+    -- future proofing in case more warnings are added+    failOnDuplicate :: Warning -> Maybe JSONPath+    failOnDuplicate (DuplicateKey a) = Just a -instance FromJSON a => FromJSON (Config a) where-    parseJSON o = Config <$> parseJSON o+fromYAMLConfig :: CradleConfigYAML a -> Config a+fromYAMLConfig cradleYAML = Config $ CradleConfig (fromMaybe [] $ YAML.dependencies cradleYAML)+                                                  (toCradleType $ YAML.cradle cradleYAML) +toCradleType :: YAML.CradleComponent a -> CradleType a+toCradleType (YAML.Multi cpts)  =+  Multi $ (\(YAML.MultiSubComponent fp' cfg) -> (fp', cradle $ fromYAMLConfig cfg)) <$> cpts+toCradleType (YAML.Stack (YAML.StackConfig yaml cpts)) =+  case cpts of+    YAML.NoComponent          -> Stack $ StackType Nothing yaml+    (YAML.SingleComponent c)  -> Stack $ StackType (Just c) yaml+    (YAML.ManyComponents cs)  -> StackMulti (StackType Nothing yaml)+                                            ((\(YAML.StackComponent fp' c cYAML) ->+                                              (fp', StackType (Just c) cYAML)) <$> cs)+toCradleType (YAML.Cabal (YAML.CabalConfig cpts)) =+  case cpts of+    YAML.NoComponent          -> Cabal $ CabalType Nothing+    (YAML.SingleComponent c)  -> Cabal $ CabalType (Just c)+    (YAML.ManyComponents cs)  -> CabalMulti (CabalType Nothing)+                                            ((\(YAML.CabalComponent fp' c) -> (fp', CabalType (Just c))) <$> cs)+toCradleType (YAML.Direct cfg)  = Direct (YAML.arguments cfg)+toCradleType (YAML.Bios cfg)    = Bios  (toCallable $ YAML.callable cfg)+                                        (toCallable <$> YAML.depsCallable cfg)+                                        (YAML.ghcPath cfg)+toCradleType (YAML.None _)      = None+toCradleType (YAML.Other cfg)   = Other (YAML.otherConfig cfg)+                                        (YAML.originalYamlValue cfg) --- | Decode given file to a 'Config a' value.--- Type variable 'a' can be used to extend the 'hie.yaml' file format--- to extend configuration in the user-library.--- If the contents of the file is not a valid 'Config a',--- an 'Control.Exception.IOException' is thrown.-readConfig :: FromJSON a => FilePath -> IO (Config a)-readConfig fp = do-    result <- decodeFileWithWarnings fp-    either throwIO failOnAnyDuplicate result-    where-        failOnAnyDuplicate :: ([Warning], Config a) -> IO (Config a)-        failOnAnyDuplicate (warnings, config) = do-            _ <- case mapMaybe failOnDuplicate warnings of-                    dups@(_:_) -> throwIO $ InvalidYaml $ Just $ YamlException-                                          $ "Duplicate keys are not allowed, found: " ++ show dups-                    _ -> return ()-            return config-        -- future proofing in case more warnings are added-        failOnDuplicate :: Warning -> Maybe JSONPath-        failOnDuplicate (DuplicateKey a) = Just a+toCallable :: YAML.Callable -> Callable+toCallable (YAML.Program p) = Program p+toCallable (YAML.Shell c)   = Command c
+ src/HIE/Bios/Config/YAML.hs view
@@ -0,0 +1,176 @@+{-# LANGUAGE DeriveAnyClass        #-}+{-# LANGUAGE DeriveGeneric         #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE OverloadedStrings     #-}++-- | Datatypes for parsing @hie.yaml@ files+module HIE.Bios.Config.YAML+  ( CradleConfigYAML(..)+  , CradleComponent(..)+  , MultiSubComponent(..)+  , CabalConfig(..)+  , CabalComponent(..)+  , StackConfig(..)+  , StackComponent(..)+  , DirectConfig(..)+  , BiosConfig(..)+  , NoneConfig(..)+  , OtherConfig(..)+  , OneOrManyComponents(..)+  , Callable(..)+  ) where++import           Control.Applicative ((<|>))+import           Data.Aeson+import           Data.Aeson.KeyMap   (keys)+import           Data.Aeson.Types    (Object, Parser, Value (Null),+                                      typeMismatch)+import qualified Data.Char           as C (toLower)+import           Data.List           ((\\))+import           GHC.Generics        (Generic)++checkObjectKeys :: [Key] -> Object -> Parser ()+checkObjectKeys allowedKeys obj =+  let extraKeys = keys obj \\ allowedKeys+   in case extraKeys of+        []          -> pure ()+        _           -> fail $ mconcat [ "Unexpected keys "+                                      , show extraKeys+                                      , ", keys allowed: "+                                      , show allowedKeys+                                      ]++data CradleConfigYAML a+  = CradleConfigYAML { cradle       :: CradleComponent a+                     , dependencies :: Maybe [FilePath]+                     } deriving (Generic, FromJSON)++data CradleComponent a+  = Multi [MultiSubComponent a]+  | Cabal CabalConfig+  | Stack StackConfig+  | Direct DirectConfig+  | Bios BiosConfig+  | None NoneConfig+  | Other (OtherConfig a)+  deriving (Generic)++instance FromJSON a => FromJSON (CradleComponent a) where+  parseJSON = let opts = defaultOptions { constructorTagModifier = fmap C.toLower+                                        , sumEncoding = ObjectWithSingleField+                                        }+               in genericParseJSON opts++data NoneConfig = NoneConfig++data OtherConfig a+  = OtherConfig { otherConfig       :: a+                , originalYamlValue :: Value+                }++instance FromJSON a => FromJSON (OtherConfig a) where+  parseJSON v = OtherConfig+                  <$> parseJSON v+                  <*> pure v++instance FromJSON NoneConfig where+  parseJSON Null = pure NoneConfig+  parseJSON v    = typeMismatch "NoneConfig" v++data MultiSubComponent a+  = MultiSubComponent { path   :: FilePath+                      , config :: CradleConfigYAML a+                      } deriving (Generic, FromJSON)++data CabalConfig+  = CabalConfig { cabalComponents :: OneOrManyComponents CabalComponent }++instance FromJSON CabalConfig where+  parseJSON v@(Array _)     = CabalConfig . ManyComponents <$> parseJSON v+  parseJSON v@(Object obj)  = (checkObjectKeys ["component", "components"] obj) *> (CabalConfig <$> parseJSON v)+  parseJSON Null            = pure $ CabalConfig NoComponent+  parseJSON v               = typeMismatch "CabalConfig" v++data CabalComponent+  = CabalComponent { cabalPath      :: FilePath+                   , cabalComponent :: String+                   }++instance FromJSON CabalComponent where+  parseJSON =+    let parseCabalComponent obj = checkObjectKeys ["path", "component"] obj+                                    *> (CabalComponent+                                          <$> obj .: "path"+                                          <*> obj .: "component")+     in withObject "CabalComponent" parseCabalComponent++data StackConfig+  = StackConfig { stackYaml       :: Maybe FilePath+                , stackComponents :: OneOrManyComponents StackComponent+                }++data StackComponent+  = StackComponent { stackPath          :: FilePath+                   , stackComponent     :: String+                   , stackComponentYAML :: Maybe String+                   }++instance FromJSON StackConfig where+  parseJSON v@(Array _)     = StackConfig Nothing . ManyComponents <$> parseJSON v+  parseJSON v@(Object obj)  = (checkObjectKeys ["component", "components", "stackYaml"] obj)+                                *> (StackConfig+                                      <$> obj .:? "stackYaml"+                                      <*> parseJSON v+                                    )+  parseJSON Null            = pure $ StackConfig Nothing NoComponent+  parseJSON v               = typeMismatch "StackConfig" v++instance FromJSON StackComponent where+  parseJSON =+    let parseStackComponent obj = (checkObjectKeys ["path", "component", "stackYaml"] obj)+                                    *> (StackComponent+                                          <$> obj .: "path"+                                          <*> obj .: "component"+                                          <*> obj .:? "stackYaml")+     in withObject "StackComponent" parseStackComponent++data OneOrManyComponents component+  = SingleComponent String+  | ManyComponents [component]+  | NoComponent++instance FromJSON component => FromJSON (OneOrManyComponents component) where+  parseJSON =+    let parseComponents o = (parseSingleComponent o <|> parseSubComponents o <|> pure NoComponent)+        parseSingleComponent o = SingleComponent <$> o .: "component"+        parseSubComponents   o = ManyComponents <$> o .: "components"+     in withObject "Components" parseComponents++data DirectConfig+  = DirectConfig { arguments :: [String] }+  deriving (Generic, FromJSON)++data BiosConfig =+  BiosConfig { callable     :: Callable+             , depsCallable :: Maybe Callable+             , ghcPath      :: Maybe FilePath+             }++instance FromJSON BiosConfig where+  parseJSON = withObject "BiosConfig" parseBiosConfig++data Callable+  = Program FilePath+  | Shell String++parseBiosConfig :: Object -> Parser BiosConfig+parseBiosConfig obj =+  let parseCallable o = (Program <$> o .: "program") <|> (Shell <$> o .: "shell")+      parseDepsCallable o = (Just . Program <$> o .: "dependency-program")+                            <|> (Just . Shell <$> o .: "dependency-shell")+                            <|> (pure Nothing)+      parse o = BiosConfig  <$> parseCallable o+                            <*> parseDepsCallable o+                            <*> (o .:? "with-ghc")+      check = checkObjectKeys ["program", "shell", "dependency-program", "dependency-shell", "with-ghc"]+   in check obj *> parse obj
src/HIE/Bios/Cradle.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE BangPatterns #-}-{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-}@@ -32,6 +31,7 @@ import Data.Char (isSpace) import System.Exit import System.Directory hiding (findFile)+import Colog.Core (LogAction (..), WithSeverity (..), Severity (..), (<&)) import Control.Monad import Control.Monad.Extra (unlessM) import Control.Monad.Trans.Cont@@ -346,13 +346,14 @@       None -> True       _    -> False -multiAction ::  forall b a-            . (b -> Cradle a)-            -> FilePath-            -> [(FilePath, CradleConfig b)]-            -> LoggingFunction-            -> FilePath-            -> IO (CradleLoadResult ComponentOptions)+multiAction+  ::  forall b a+  . (b -> Cradle a)+  -> FilePath+  -> [(FilePath, CradleConfig b)]+  -> LogAction IO (WithSeverity Log)+  -> FilePath+  -> IO (CradleLoadResult ComponentOptions) multiAction buildCustomCradle cur_dir cs l cur_fp =     selectCradle =<< canonicalizeCradles @@ -415,7 +416,7 @@ biosWorkDir :: FilePath -> MaybeT IO FilePath biosWorkDir = findFileUpwards (".hie-bios" ==) -biosDepsAction :: LoggingFunction -> FilePath -> Maybe Callable -> FilePath -> IO [FilePath]+biosDepsAction :: LogAction IO (WithSeverity Log) -> FilePath -> Maybe Callable -> FilePath -> IO [FilePath] biosDepsAction l wdir (Just biosDepsCall) fp = do   biosDeps' <- callableToProcess biosDepsCall (Just fp)   (ex, sout, serr, [(_, args)]) <- readProcessWithOutputs [hie_bios_output] l wdir biosDeps'@@ -424,12 +425,13 @@     ExitSuccess -> return $ fromMaybe [] args biosDepsAction _ _ Nothing _ = return [] -biosAction :: FilePath-           -> Callable-           -> Maybe Callable-           -> LoggingFunction-           -> FilePath-           -> IO (CradleLoadResult ComponentOptions)+biosAction+  :: FilePath+  -> Callable+  -> Maybe Callable+  -> LogAction IO (WithSeverity Log)+  -> FilePath+  -> IO (CradleLoadResult ComponentOptions) biosAction wdir bios bios_deps l fp = do   bios' <- callableToProcess bios (Just fp)   (ex, _stdo, std, [(_, res),(_, mb_deps)]) <-@@ -689,15 +691,38 @@       ""   pure (trimEnd exe, trimEnd libdir) -cabalAction :: FilePath -> Maybe String -> LoggingFunction -> FilePath -> CradleLoadResultT IO ComponentOptions+cabalAction+  :: FilePath+  -> Maybe String+  -> LogAction IO (WithSeverity Log)+  -> FilePath+  -> CradleLoadResultT IO ComponentOptions cabalAction workDir mc l fp = do-  cabalProc <- cabalProcess workDir "v2-repl" [fromMaybe (fixTargetPath fp) mc] `modCradleError` \err -> do+  let+    cabalCommand = "v2-repl"+    cabalArgs = [fromMaybe (fixTargetPath fp) mc]++  cabalProc <- cabalProcess workDir cabalCommand cabalArgs `modCradleError` \err -> do       deps <- cabalCradleDependencies workDir workDir       pure $ err { cradleErrorDependencies = cradleErrorDependencies err ++ deps }    (ex, output, stde, [(_, maybeArgs)]) <- liftIO $ readProcessWithOutputs [hie_bios_output] l workDir cabalProc-   let args = fromMaybe [] maybeArgs++  let errorDetails =+        ["Failed command: " <> prettyCmdSpec (cmdspec cabalProc)+        , unlines output+        , unlines stde+        , unlines $ args+        , "Process Environment:"]+        <> prettyProcessEnv cabalProc++  when (ex /= ExitSuccess) $ do+    deps <- liftIO $ cabalCradleDependencies workDir workDir+    let cmd = show (["cabal", cabalCommand] <> cabalArgs)+    let errorMsg = "Failed to run " <> cmd <> " in directory \"" <> workDir <> "\". Consult the logs for full command and error."+    throwCE (CradleError deps ex ([errorMsg] <> errorDetails))+   case processCabalWrapperArgs args of     Nothing -> do       -- Provide some dependencies an IDE can look for to trigger a reload.@@ -705,13 +730,7 @@       -- root of the component, so we are right in trivial cases at least.       deps <- liftIO $ cabalCradleDependencies workDir workDir       throwCE (CradleError deps ex $-                [ "Failed to parse result of calling cabal"-                , "Failed command: " <> prettyCmdSpec (cmdspec cabalProc)-                , unlines output-                , unlines stde-                , unlines $ args-                , "Process Environment:"]-                <> prettyProcessEnv cabalProc)+                (["Failed to parse result of calling cabal" ] <> errorDetails))     Just (componentDir, final_args) -> do       deps <- liftIO $ cabalCradleDependencies workDir componentDir       CradleLoadResultT $ pure $ makeCradleResult (ex, stde, componentDir, final_args) deps@@ -822,7 +841,13 @@   return $ map normalise $     cabalFiles ++ [relFp </> "package.yaml", stackYamlLocationOrDefault syaml] -stackAction :: FilePath -> Maybe String -> StackYaml -> LoggingFunction -> FilePath -> IO (CradleLoadResult ComponentOptions)+stackAction+  :: FilePath+  -> Maybe String+  -> StackYaml+  -> LogAction IO (WithSeverity Log)+  -> FilePath+  -> IO (CradleLoadResult ComponentOptions) stackAction workDir mc syaml l _fp = do   let ghcProcArgs = ("stack", stackYamlProcessArgs syaml <> ["exec", "ghc", "--"])   -- Same wrapper works as with cabal@@ -999,7 +1024,7 @@ -- * The process is executed in the given directory. readProcessWithOutputs   :: Outputs  -- ^ Names of the outputs produced by this process-  -> LoggingFunction -- ^ Output of the process is streamed into this function.+  -> LogAction IO (WithSeverity Log) -- ^ Output of the process is emitted as logs.   -> FilePath -- ^ Working directory. Process is executed in this directory.   -> CreateProcess -- ^ Parameters for the process to be executed.   -> IO (ExitCode, [String], [String], [(OutputName, Maybe [String])])@@ -1013,7 +1038,7 @@      -- Windows line endings are not converted so you have to filter out `'r` characters   let loggingConduit = C.decodeUtf8  C..| C.lines C..| C.filterE (/= '\r')-        C..| C.map T.unpack C..| C.iterM l C..| C.sinkList+        C..| C.map T.unpack C..| C.iterM (\msg -> l <& LogProcessOutput msg `WithSeverity` Debug) C..| C.sinkList   (ex, stdo, stde) <- liftIO $ sourceProcessWithStreams process mempty loggingConduit loggingConduit    res <- forM output_files $ \(name,path) ->
src/HIE/Bios/Flags.hs view
@@ -1,25 +1,15 @@-module HIE.Bios.Flags (getCompilerOptions, getCompilerOptionsWithLogger, LoggingFunction) where+module HIE.Bios.Flags (getCompilerOptions) where  import HIE.Bios.Types-import HIE.Bios.Internal.Log +import qualified Colog.Core as L+import Data.Text.Prettyprint.Doc  -- | Initialize the 'DynFlags' relating to the compilation of a single -- file or GHC session according to the provided 'Cradle'.-getCompilerOptions ::-    FilePath -- The file we are loading it because of-    -> Cradle a-    -> IO (CradleLoadResult ComponentOptions)-getCompilerOptions =-  getCompilerOptionsWithLogger logm--getCompilerOptionsWithLogger ::-  LoggingFunction-  -> FilePath+getCompilerOptions+  :: L.LogAction IO (L.WithSeverity Log)+  -> FilePath -- The file we are loading it because of   -> Cradle a   -> IO (CradleLoadResult ComponentOptions)-getCompilerOptionsWithLogger l fp cradle =-  runCradle (cradleOptsProg cradle) l fp-------------------------------------------------------------------+getCompilerOptions l fp cradle = runCradle (cradleOptsProg cradle) l fp
src/HIE/Bios/Ghc/Api.hs view
@@ -21,6 +21,7 @@ import qualified HIE.Bios.Ghc.Gap as Gap import Control.Monad (void) import Control.Monad.IO.Class+import Colog.Core (LogAction (..), WithSeverity (..)) import HIE.Bios.Types import HIE.Bios.Environment import HIE.Bios.Flags@@ -30,22 +31,24 @@ -- | Initialize a GHC session by loading a given file into a given cradle. initializeFlagsWithCradle ::     GhcMonad m-    => FilePath -- ^ The file we are loading the 'Cradle' because of+    => LogAction IO (WithSeverity Log)+    -> FilePath -- ^ The file we are loading the 'Cradle' because of     -> Cradle a   -- ^ The cradle we want to load     -> m (CradleLoadResult (m G.SuccessFlag, ComponentOptions))-initializeFlagsWithCradle = initializeFlagsWithCradleWithMessage (Just Gap.batchMsg)+initializeFlagsWithCradle l = initializeFlagsWithCradleWithMessage l (Just Gap.batchMsg)  -- | The same as 'initializeFlagsWithCradle' but with an additional argument to control -- how the loading progress messages are displayed to the user. In @haskell-ide-engine@ -- the module loading progress is displayed in the UI by using a progress notification. initializeFlagsWithCradleWithMessage ::   GhcMonad m-  => Maybe G.Messager+  => LogAction IO (WithSeverity Log)+  -> Maybe G.Messager   -> FilePath -- ^ The file we are loading the 'Cradle' because of   -> Cradle a  -- ^ The cradle we want to load   -> m (CradleLoadResult (m G.SuccessFlag, ComponentOptions)) -- ^ Whether we actually loaded the cradle or not.-initializeFlagsWithCradleWithMessage msg fp cradle =-    fmap (initSessionWithMessage msg) <$> liftIO (getCompilerOptions fp cradle)+initializeFlagsWithCradleWithMessage l msg fp cradle =+    fmap (initSessionWithMessage msg) <$> liftIO (getCompilerOptions l fp cradle)  -- | Actually perform the initialisation of the session. Initialising the session corresponds to -- parsing the command line flags, setting the targets for the session and then attempting to load
src/HIE/Bios/Ghc/Check.hs view
@@ -1,4 +1,7 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ExistentialQuantification #-} module HIE.Bios.Ghc.Check (     checkSyntax   , check@@ -14,37 +17,49 @@ #endif  import Control.Exception+import Control.Monad.IO.Class+import Colog.Core (LogAction (..), WithSeverity (..), Severity (..), (<&), cmap)+import Data.Text.Prettyprint.Doc -import HIE.Bios.Environment import HIE.Bios.Ghc.Api import HIE.Bios.Ghc.Logger-import qualified HIE.Bios.Internal.Log as Log-import HIE.Bios.Types-import HIE.Bios.Ghc.Load-import Control.Monad.IO.Class+import HIE.Bios.Types hiding (Log (..))+import qualified HIE.Bios.Types as T+import qualified HIE.Bios.Ghc.Load as Load+import HIE.Bios.Environment  import System.IO.Unsafe (unsafePerformIO) import qualified HIE.Bios.Ghc.Gap as Gap +data Log =+  LoadLog Load.Log+  | LogAny T.Log+  | forall a . Show a => LogCradle (Cradle a) +instance Pretty Log where+  pretty (LoadLog l) = pretty l+  pretty (LogAny l) = pretty l+  pretty (LogCradle c) = "Cradle:" <+> viaShow c+ ----------------------------------------------------------------  -- | Checking syntax of a target file using GHC. --   Warnings and errors are returned. checkSyntax :: Show a-            => Cradle a+            => LogAction IO (WithSeverity Log)+            -> Cradle a             -> [FilePath]  -- ^ The target files.             -> IO String-checkSyntax _      []    = return ""-checkSyntax cradle files = do+checkSyntax _       _     []    = return ""+checkSyntax logger cradle files = do     libDirRes <- getRuntimeGhcLibDir cradle     handleRes libDirRes $ \libDir ->       G.runGhcT (Just libDir) $ do-        Log.debugm $ "Cradle: " ++ show cradle-        res <- initializeFlagsWithCradle (head files) cradle+        liftIO $ logger <& LogCradle cradle `WithSeverity` Info+        res <- initializeFlagsWithCradle (cmap (fmap LogAny) logger) (head files) cradle         handleRes res $ \(ini, _) -> do           _sf <- ini-          either id id <$> check files+          either id id <$> check logger files   where     handleRes (CradleSuccess x) f = f x     handleRes (CradleFail ce) _f = liftIO $ throwIO ce@@ -55,11 +70,12 @@ -- | Checking syntax of a target file using GHC. --   Warnings and errors are returned. check :: (GhcMonad m)-      => [FilePath]  -- ^ The target files.+      => LogAction IO (WithSeverity Log)+      -> [FilePath]  -- ^ The target files.       -> m (Either String String)-check fileNames = do+check logger fileNames = do   libDir <- G.topDir <$> G.getDynFlags-  withLogger (setAllWarningFlags libDir) $ setTargetFiles (map dup fileNames)+  withLogger (setAllWarningFlags libDir) $ Load.setTargetFiles (cmap (fmap LoadLog) logger) (map dup fileNames)  dup :: a -> (a, a) dup x = (x, x)
src/HIE/Bios/Ghc/Load.hs view
@@ -1,14 +1,17 @@+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE CPP #-} -- | Convenience functions for loading a file into a GHC API session-module HIE.Bios.Ghc.Load ( loadFileWithMessage, loadFile, setTargetFiles, setTargetFilesWithMessage) where+module HIE.Bios.Ghc.Load ( loadFileWithMessage, loadFile, setTargetFiles, setTargetFilesWithMessage, Log (..)) where  +import Colog.Core (LogAction (..), WithSeverity (..), Severity (..), (<&)) import Control.Monad (forM, void) import Control.Monad.IO.Class  import Data.List import Data.Time.Clock+import Data.Text.Prettyprint.Doc import Data.IORef  import GHC@@ -23,8 +26,24 @@ #endif  import qualified HIE.Bios.Ghc.Gap as Gap-import qualified HIE.Bios.Internal.Log as Log +data Log =+  LogLoaded FilePath FilePath+  | LogTypechecked [TypecheckedModule]+  | LogInitPlugins Int [ModuleName]+  | LogSetTargets [(FilePath, FilePath)]+  | LogModGraph ModuleGraph++instance Pretty Log where+  pretty (LogLoaded fp1 fp2) = "Loaded" <+> viaShow fp1 <+> "-" <+> viaShow fp2+  pretty (LogTypechecked tcs) = "Typechecked modules for:" <+> (cat $ map (viaShow . get_fp) tcs)+  pretty (LogInitPlugins n ns) = "Loaded" <+> viaShow n <+> "plugins, specified" <+> viaShow (length ns)+  pretty (LogSetTargets ts) = "Set targets:" <+> viaShow ts+  pretty (LogModGraph mod_graph) = "ModGraph:" <+> viaShow (map ms_location $ Gap.mgModSummaries mod_graph)++get_fp :: TypecheckedModule -> Maybe FilePath+get_fp = ml_hs_file . ms_location . pm_mod_summary . tm_parsed_module+ -- | Load a target into the GHC session. -- -- The target is represented as a tuple. The tuple consists of the@@ -38,19 +57,19 @@ -- together with all the typechecked modules that had to be loaded -- in order to typecheck the given target. loadFileWithMessage :: GhcMonad m-         => Maybe G.Messager -- ^ Optional messager hook+         => LogAction IO (WithSeverity Log)+         -> Maybe G.Messager -- ^ Optional messager hook                              -- to log messages produced by GHC.          -> (FilePath, FilePath)  -- ^ Target file to load.          -> m (Maybe TypecheckedModule, [TypecheckedModule])          -- ^ Typechecked module and modules that had to          -- be loaded for the target.-loadFileWithMessage msg file = do+loadFileWithMessage logger msg file = do   -- STEP 1: Load the file into the session, using collectASTs to also retrieve   -- typechecked and parsed modules.-  (_, tcs) <- collectASTs $ (setTargetFilesWithMessage msg [file])-  Log.debugm $ "loaded " ++ fst file ++ " - " ++ snd file-  let get_fp = ml_hs_file . ms_location . pm_mod_summary . tm_parsed_module-  Log.debugm $ "Typechecked modules for: " ++ (unlines $ map (show . get_fp) tcs)+  (_, tcs) <- collectASTs logger $ (setTargetFilesWithMessage logger msg [file])+  liftIO $ logger <& LogLoaded (fst file) (snd file) `WithSeverity` Debug+  liftIO $ logger <& LogTypechecked tcs `WithSeverity` Debug   -- Find the specific module in the list of returned typechecked modules if it exists.   let findMod [] = Nothing       findMod (x:xs) = case get_fp x of@@ -71,19 +90,24 @@ -- together with all the typechecked modules that had to be loaded -- in order to typecheck the given target. loadFile :: (GhcMonad m)-         => (FilePath, FilePath) -- ^ Target file to load.+         => LogAction IO (WithSeverity Log)+         -> (FilePath, FilePath) -- ^ Target file to load.          -> m (Maybe TypecheckedModule, [TypecheckedModule])          -- ^ Typechecked module and modules that had to          -- be loaded for the target.-loadFile = loadFileWithMessage (Just G.batchMsg)+loadFile logger = loadFileWithMessage logger (Just G.batchMsg)   -- | Set the files as targets and load them. This will reset GHC's targets so only the modules you -- set as targets and its dependencies will be loaded or reloaded. -- Produced diagnostics will be printed similar to the normal output of GHC. -- To configure this, use 'setTargetFilesWithMessage'.-setTargetFiles :: GhcMonad m => [(FilePath, FilePath)] -> m ()-setTargetFiles = setTargetFilesWithMessage (Just G.batchMsg)+setTargetFiles+  :: GhcMonad m+  => LogAction IO (WithSeverity Log)+  -> [(FilePath, FilePath)]+  -> m ()+setTargetFiles logger = setTargetFilesWithMessage logger (Just G.batchMsg)  msTargetIs :: ModSummary -> Target -> Bool msTargetIs ms t = case targetId t of@@ -102,25 +126,34 @@  -- | Set the files as targets and load them. This will reset GHC's targets so only the modules you -- set as targets and its dependencies will be loaded or reloaded.-setTargetFilesWithMessage :: (GhcMonad m)  => Maybe G.Messager -> [(FilePath, FilePath)] -> m ()-setTargetFilesWithMessage msg files = do+setTargetFilesWithMessage+  :: (GhcMonad m)+  => LogAction IO (WithSeverity Log)+  -> Maybe G.Messager+  -> [(FilePath, FilePath)]+  -> m ()+setTargetFilesWithMessage logger msg files = do     targets <- forM files guessTargetMapped-    Log.debugm $ "setTargets: " ++ show files+    liftIO $ logger <& LogSetTargets files `WithSeverity` Debug     G.setTargets targets     mod_graph <- updateTime targets =<< depanal [] False-    Log.debugm $ "modGraph: " ++ show (map ms_location $ Gap.mgModSummaries mod_graph)+    liftIO $ logger <& LogModGraph mod_graph `WithSeverity` Debug     void $ G.load' LoadAllTargets msg mod_graph  -- | Add a hook to record the contents of any 'TypecheckedModule's which are produced -- during compilation.-collectASTs :: (GhcMonad m) => m a -> m (a, [TypecheckedModule])-collectASTs action = do+collectASTs+  :: (GhcMonad m)+  => LogAction IO (WithSeverity Log)+  -> m a+  -> m (a, [TypecheckedModule])+collectASTs logger action = do   ref1 <- liftIO $ newIORef []   -- Modify session is much faster than `setSessionDynFlags`.-  Gap.modifySession $ Gap.setFrontEndHooks (Just (astHook ref1))+  Gap.modifySession $ Gap.setFrontEndHooks (Just (astHook logger ref1))   res <- action   tcs <- liftIO $ readIORef ref1-  -- Unset the hook so that we don't retain the reference ot the IORef so it can be gced.+  -- Unset the hook so that we don't retain the reference to the IORef so it can be GCed.   -- This stops the typechecked modules being retained in some cases.   liftIO $ writeIORef ref1 []   Gap.modifySession $ Gap.setFrontEndHooks Nothing@@ -128,22 +161,28 @@   return (res, tcs)  -- | This hook overwrites the default frontend action of GHC.-astHook :: IORef [TypecheckedModule] -> ModSummary -> Gap.Hsc Gap.FrontendResult-astHook tc_ref ms = ghcInHsc $ do-  p <- G.parseModule =<< initializePluginsGhc ms+astHook+  :: LogAction IO (WithSeverity Log)+  -> IORef [TypecheckedModule]+  -> ModSummary+  -> Gap.Hsc Gap.FrontendResult+astHook logger tc_ref ms = ghcInHsc $ do+  p <- G.parseModule =<< initializePluginsGhc logger ms   tcm <- G.typecheckModule p   let tcg_env = fst (tm_internals_ tcm)   liftIO $ modifyIORef tc_ref (tcm :)   return $ Gap.FrontendTypecheck tcg_env -initializePluginsGhc :: ModSummary -> Ghc ModSummary-initializePluginsGhc ms = do+initializePluginsGhc+  :: GhcMonad m+  => LogAction IO (WithSeverity Log)+  -> ModSummary+  -> m ModSummary+initializePluginsGhc logger ms = do   hsc_env <- getSession   (pluginsLoaded, pluginNames, newMs) <- liftIO $ Gap.initializePluginsForModSummary hsc_env ms-  Log.debugm ("init-plugins(loaded):" ++ show pluginsLoaded)-  Log.debugm ("init-plugins(specified):" ++ show (length pluginNames))+  liftIO $ logger <& LogInitPlugins pluginsLoaded pluginNames `WithSeverity` Debug   return newMs-  ghcInHsc :: Ghc a -> Gap.Hsc a ghcInHsc gm = do
src/HIE/Bios/Internal/Debug.hs view
@@ -2,6 +2,7 @@ module HIE.Bios.Internal.Debug (debugInfo, rootInfo, configInfo, cradleInfo) where  import Control.Monad+import Colog.Core (LogAction (..), WithSeverity (..)) import Data.Void  import qualified Data.Char as Char@@ -24,11 +25,12 @@ -- -- Otherwise, shows the error message and exit-code. debugInfo :: Show a-          => FilePath+          => LogAction IO (WithSeverity Log)+          -> FilePath           -> Cradle a           -> IO String-debugInfo fp cradle = unlines <$> do-    res <- getCompilerOptions fp cradle+debugInfo logger fp cradle = unlines <$> do+    res <- getCompilerOptions logger fp cradle     canonFp <- canonicalizePath fp     conf <- findConfig canonFp     crdl <- findCradle' canonFp
− src/HIE/Bios/Internal/Log.hs
@@ -1,16 +0,0 @@-module HIE.Bios.Internal.Log where--import Control.Monad.IO.Class-import System.Log.Logger--logm :: MonadIO m => String -> m ()-logm s = liftIO $ infoM "hie-bios" s--debugm :: MonadIO m => String -> m ()-debugm s = liftIO $ debugM "hie-bios" s--warningm :: MonadIO m => String -> m ()-warningm s = liftIO $ warningM "hie-bios" s--errorm :: MonadIO m => String -> m ()-errorm s = liftIO $ errorM "hie-bios" s
src/HIE/Bios/Types.hs view
@@ -1,16 +1,11 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE DeriveFoldable #-}-{-# OPTIONS_GHC -Wno-orphans #-}--{-# LANGUAGE DeriveFoldable #-}-{-# LANGUAGE DeriveTraversable #-} module HIE.Bios.Types where  import           System.Exit+import qualified Colog.Core as L import           Control.Exception              ( Exception ) import           Control.Monad import           Control.Monad.IO.Class@@ -18,7 +13,9 @@ #if MIN_VERSION_base(4,9,0) import qualified Control.Monad.Fail as Fail #endif+import Data.Text.Prettyprint.Doc + data BIOSVerbosity = Silent | Verbose  ----------------------------------------------------------------@@ -40,8 +37,6 @@   , cradleOptsProg   :: CradleAction a   } deriving (Show, Functor) -type LoggingFunction = String -> IO ()- data ActionName a   = Stack   | Cabal@@ -53,10 +48,19 @@   | Other a   deriving (Show, Eq, Ord, Functor) +data Log =+  LogAny String+  | LogProcessOutput String+  deriving Show++instance Pretty Log where+  pretty (LogAny s) = pretty s+  pretty (LogProcessOutput s) = pretty s+ data CradleAction a = CradleAction {                         actionName    :: ActionName a                       -- ^ Name of the action.-                      , runCradle     :: LoggingFunction -> FilePath -> IO (CradleLoadResult ComponentOptions)+                      , runCradle     :: L.LogAction IO (L.WithSeverity Log) -> FilePath -> IO (CradleLoadResult ComponentOptions)                       -- ^ Options to compile the given file with.                       , runGhcCmd :: [String] -> IO (CradleLoadResult String)                       -- ^ Executes the @ghc@ binary that is usually used to@@ -138,7 +142,7 @@ #endif  instance MonadTrans CradleLoadResultT where-    lift = CradleLoadResultT . liftM CradleSuccess+    lift = CradleLoadResultT . fmap CradleSuccess     {-# INLINE lift #-}  instance (MonadIO m) => MonadIO (CradleLoadResultT m) where
tests/BiosTests.hs view
@@ -75,7 +75,7 @@               crdl <- initialiseCradle isMultiCradle (addTrailingPathSeparator tmpdir) step               step "Load module A"               withCurrentDirectory (cradleRootDir crdl) $ do-                runCradle (cradleOptsProg crdl) (const (pure ())) "./a/A.hs"+                runCradle (cradleOptsProg crdl) mempty "./a/A.hs"                 >>= \case                   CradleSuccess r ->                     componentOptions r `shouldMatchList` ["a"] <> argDynamic@@ -90,7 +90,7 @@                 unlessM (doesFileExist "./b/A.hs")                   $ assertFailure "Test invariant broken, this file must exist." -                runCradle (cradleOptsProg crdl) (const (pure ())) "./b/A.hs"+                runCradle (cradleOptsProg crdl) mempty "./b/A.hs"                 >>= \case                   CradleSuccess r ->                     componentOptions r `shouldMatchList` ["b"] <> argDynamic@@ -105,7 +105,7 @@                 unlessM (doesFileExist "./c/A.hs")                   $ assertFailure "Test invariant broken, this file must exist." -                runCradle (cradleOptsProg crdl) (const (pure ())) "./c/A.hs"+                runCradle (cradleOptsProg crdl) mempty "./c/A.hs"                   >>= \case                     CradleNone -> pure ()                     _ -> assertFailure "Cradle loaded symlink"@@ -308,23 +308,27 @@     withCurrentDirectory (cradleRootDir crd) $       G.runGhc (Just libDir) $ do         let relFp = makeRelative (cradleRootDir crd) a_fp-        res <- initializeFlagsWithCradleWithMessage (Just (\_ n _ _ -> step (show n))) relFp crd+        liftIO (step "Cradle load")+        res <- initializeFlagsWithCradle mempty relFp crd         handleCradleResult res $ \(ini, _) -> do           liftIO (step "Initial module load")           sf <- ini           case sf of             -- Test resetting the targets-            Succeeded -> setTargetFilesWithMessage (Just (\_ n _ _ -> step (show n))) [(a_fp, a_fp)]+            Succeeded -> do+              liftIO (step "Set target files")+              setTargetFiles mempty [(a_fp, a_fp)]             Failed -> liftIO $ assertFailure "Module loading failed"  testLoadFileCradleFail :: Cradle a -> FilePath -> (CradleError -> Assertion) -> (String -> IO ()) -> IO () testLoadFileCradleFail crd a_fp cradleErrorExpectation step = do+  step "Loading cradle"   -- don't spin up a ghc session, just run the opts program manually since   -- we're not guaranteed to be able to get the ghc libdir if the cradle is   -- failing   withCurrentDirectory (cradleRootDir crd) $ do     let relFp = makeRelative (cradleRootDir crd) a_fp-    res <- runCradle (cradleOptsProg crd) (step . show) relFp+    res <- runCradle (cradleOptsProg crd) mempty relFp     case res of       CradleSuccess _ -> liftIO $ assertFailure "Cradle loaded successfully"       CradleNone -> liftIO $ assertFailure "Unexpected none-Cradle"@@ -341,7 +345,7 @@       withCurrentDirectory (cradleRootDir crd) $         G.runGhc (Just libDir) $ do           let relFp = makeRelative (cradleRootDir crd) a_fp-          res <- initializeFlagsWithCradleWithMessage (Just (\_ n _ _ -> step (show n))) relFp crd+          res <- initializeFlagsWithCradleWithMessage mempty (Just (\_ n _ _ -> step (show n))) relFp crd           handleCradleResult res $ \(_, options) ->             liftIO $ dependencyPred (componentDependencies options) 
tests/ParserTests.hs view
@@ -81,6 +81,7 @@         , (".", CradleConfig [] None)         ]))     assertParserFails "keys-not-unique-fails.yaml" invalidYamlException+    assertParser "cabal-empty-config.yaml" (noDeps (Cabal $ CabalType Nothing))  assertParser :: FilePath -> Config Void -> Assertion assertParser fp cc = do
+ tests/configs/cabal-empty-config.yaml view
@@ -0,0 +1,2 @@+cradle:+  cabal: