packages feed

hie-bios 0.12.1 → 0.13.0

raw patch · 34 files changed

+821/−464 lines, 34 filesdep ~ghcdep ~textPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: ghc, text

API changes (from Hackage documentation)

- HIE.Bios.Config: [cradleType] :: CradleConfig a -> CradleType a
- HIE.Bios.Config: data CradleType a
- HIE.Bios.Config: instance GHC.Base.Functor HIE.Bios.Config.CradleType
- HIE.Bios.Config: instance GHC.Classes.Eq a => GHC.Classes.Eq (HIE.Bios.Config.CradleType a)
- HIE.Bios.Config: instance GHC.Show.Show (HIE.Bios.Config.CradleType a)
- HIE.Bios.Types: Silent :: BIOSVerbosity
- HIE.Bios.Types: Verbose :: BIOSVerbosity
- HIE.Bios.Types: data BIOSVerbosity
+ HIE.Bios: [cradleLogger] :: Cradle a -> LogAction IO (WithSeverity Log)
+ HIE.Bios.Config: [cradleTree] :: CradleConfig a -> CradleTree a
+ HIE.Bios.Config: cabalProjectFile :: CabalType -> Maybe FilePath
+ HIE.Bios.Config: data CradleTree a
+ HIE.Bios.Config: instance GHC.Base.Functor HIE.Bios.Config.CradleTree
+ HIE.Bios.Config: instance GHC.Classes.Eq a => GHC.Classes.Eq (HIE.Bios.Config.CradleTree a)
+ HIE.Bios.Config: instance GHC.Show.Show (HIE.Bios.Config.CradleTree a)
+ HIE.Bios.Config.YAML: [$sel:cabalComponentProject:CabalComponent] :: CabalComponent -> Maybe FilePath
+ HIE.Bios.Config.YAML: [$sel:cabalProject:CabalConfig] :: CabalConfig -> Maybe FilePath
+ HIE.Bios.Cradle: ExplicitConfig :: FilePath -> CradleProjectConfig
+ HIE.Bios.Cradle: NoExplicitConfig :: CradleProjectConfig
+ HIE.Bios.Cradle: data CradleProjectConfig
+ HIE.Bios.Cradle: instance GHC.Classes.Eq HIE.Bios.Cradle.CradleProjectConfig
+ HIE.Bios.Cradle: instance GHC.Show.Show a => GHC.Show.Show (HIE.Bios.Cradle.ConcreteCradle a)
+ HIE.Bios.Cradle: instance GHC.Show.Show a => GHC.Show.Show (HIE.Bios.Cradle.ResolvedCradle a)
+ HIE.Bios.Types: LogCreateProcessRun :: CreateProcess -> Log
+ HIE.Bios.Types: LogProcessRun :: FilePath -> [FilePath] -> Log
+ HIE.Bios.Types: [cradleLogger] :: Cradle a -> LogAction IO (WithSeverity Log)
+ HIE.Bios.Types: hie_bios_arg :: String
+ HIE.Bios.Types: hie_bios_deps :: String
+ HIE.Bios.Types: hie_bios_ghc :: String
+ HIE.Bios.Types: hie_bios_ghc_args :: String
+ HIE.Bios.Types: hie_bios_output :: String
+ HIE.Bios.Types: prettyCmdSpec :: CmdSpec -> String
+ HIE.Bios.Types: prettyProcessEnv :: CreateProcess -> [String]
- HIE.Bios: Cradle :: FilePath -> CradleAction a -> Cradle a
+ HIE.Bios: Cradle :: FilePath -> CradleAction a -> LogAction IO (WithSeverity Log) -> Cradle a
- HIE.Bios: defaultCradle :: FilePath -> Cradle a
+ HIE.Bios: defaultCradle :: LogAction IO (WithSeverity Log) -> FilePath -> Cradle a
- HIE.Bios: getCompilerOptions :: LogAction IO (WithSeverity Log) -> FilePath -> Cradle a -> IO (CradleLoadResult ComponentOptions)
+ HIE.Bios: getCompilerOptions :: FilePath -> [FilePath] -> Cradle a -> IO (CradleLoadResult ComponentOptions)
- HIE.Bios: loadCradle :: FilePath -> IO (Cradle Void)
+ HIE.Bios: loadCradle :: LogAction IO (WithSeverity Log) -> FilePath -> IO (Cradle Void)
- HIE.Bios: loadImplicitCradle :: Show a => FilePath -> IO (Cradle a)
+ HIE.Bios: loadImplicitCradle :: Show a => LogAction IO (WithSeverity Log) -> FilePath -> IO (Cradle a)
- HIE.Bios.Config: Bios :: Callable -> Maybe Callable -> Maybe FilePath -> CradleType a
+ HIE.Bios.Config: Bios :: Callable -> Maybe Callable -> Maybe FilePath -> CradleTree a
- HIE.Bios.Config: Cabal :: !CabalType -> CradleType a
+ HIE.Bios.Config: Cabal :: !CabalType -> CradleTree a
- HIE.Bios.Config: CabalMulti :: !CabalType -> [(FilePath, CabalType)] -> CradleType a
+ HIE.Bios.Config: CabalMulti :: !CabalType -> [(FilePath, CabalType)] -> CradleTree a
- HIE.Bios.Config: CradleConfig :: [FilePath] -> CradleType a -> CradleConfig a
+ HIE.Bios.Config: CradleConfig :: [FilePath] -> CradleTree a -> CradleConfig a
- HIE.Bios.Config: Direct :: [String] -> CradleType a
+ HIE.Bios.Config: Direct :: [String] -> CradleTree a
- HIE.Bios.Config: Multi :: [(FilePath, CradleConfig a)] -> CradleType a
+ HIE.Bios.Config: Multi :: [(FilePath, CradleConfig a)] -> CradleTree a
- HIE.Bios.Config: None :: CradleType a
+ HIE.Bios.Config: None :: CradleTree a
- HIE.Bios.Config: Other :: a -> Value -> CradleType a
+ HIE.Bios.Config: Other :: a -> Value -> CradleTree a
- HIE.Bios.Config: Stack :: !StackType -> CradleType a
+ HIE.Bios.Config: Stack :: !StackType -> CradleTree a
- HIE.Bios.Config: StackMulti :: !StackType -> [(FilePath, StackType)] -> CradleType a
+ HIE.Bios.Config: StackMulti :: !StackType -> [(FilePath, StackType)] -> CradleTree a
- HIE.Bios.Config: [arguments] :: CradleType a -> [String]
+ HIE.Bios.Config: [arguments] :: CradleTree a -> [String]
- HIE.Bios.Config: [cabalType] :: CradleType a -> !CabalType
+ HIE.Bios.Config: [cabalType] :: CradleTree a -> !CabalType
- HIE.Bios.Config: [call] :: CradleType a -> Callable
+ HIE.Bios.Config: [call] :: CradleTree a -> Callable
- HIE.Bios.Config: [defaultCabal] :: CradleType a -> !CabalType
+ HIE.Bios.Config: [defaultCabal] :: CradleTree a -> !CabalType
- HIE.Bios.Config: [defaultStack] :: CradleType a -> !StackType
+ HIE.Bios.Config: [defaultStack] :: CradleTree a -> !StackType
- HIE.Bios.Config: [depsCall] :: CradleType a -> Maybe Callable
+ HIE.Bios.Config: [depsCall] :: CradleTree a -> Maybe Callable
- HIE.Bios.Config: [ghcPath] :: CradleType a -> Maybe FilePath
+ HIE.Bios.Config: [ghcPath] :: CradleTree a -> Maybe FilePath
- HIE.Bios.Config: [originalYamlValue] :: CradleType a -> Value
+ HIE.Bios.Config: [originalYamlValue] :: CradleTree a -> Value
- HIE.Bios.Config: [otherConfig] :: CradleType a -> a
+ HIE.Bios.Config: [otherConfig] :: CradleTree a -> a
- HIE.Bios.Config: [stackType] :: CradleType a -> !StackType
+ HIE.Bios.Config: [stackType] :: CradleTree a -> !StackType
- HIE.Bios.Config: [subCabalComponents] :: CradleType a -> [(FilePath, CabalType)]
+ HIE.Bios.Config: [subCabalComponents] :: CradleTree a -> [(FilePath, CabalType)]
- HIE.Bios.Config: [subStackComponents] :: CradleType a -> [(FilePath, StackType)]
+ HIE.Bios.Config: [subStackComponents] :: CradleTree a -> [(FilePath, StackType)]
- HIE.Bios.Config: pattern CabalType :: Maybe String -> CabalType
+ HIE.Bios.Config: pattern CabalType :: Maybe String -> Maybe FilePath -> CabalType
- HIE.Bios.Config: pattern StackType :: Maybe String -> Maybe String -> StackType
+ HIE.Bios.Config: pattern StackType :: Maybe String -> Maybe FilePath -> StackType
- HIE.Bios.Config: stackYaml :: StackType -> Maybe String
+ HIE.Bios.Config: stackYaml :: StackType -> Maybe FilePath
- HIE.Bios.Config.YAML: CabalComponent :: FilePath -> String -> CabalComponent
+ HIE.Bios.Config.YAML: CabalComponent :: FilePath -> String -> Maybe FilePath -> CabalComponent
- HIE.Bios.Config.YAML: CabalConfig :: OneOrManyComponents CabalComponent -> CabalConfig
+ HIE.Bios.Config.YAML: CabalConfig :: Maybe FilePath -> OneOrManyComponents CabalComponent -> CabalConfig
- HIE.Bios.Config.YAML: StackComponent :: FilePath -> String -> Maybe String -> StackComponent
+ HIE.Bios.Config.YAML: StackComponent :: FilePath -> String -> Maybe FilePath -> StackComponent
- HIE.Bios.Config.YAML: [$sel:stackComponentYAML:StackComponent] :: StackComponent -> Maybe String
+ HIE.Bios.Config.YAML: [$sel:stackComponentYAML:StackComponent] :: StackComponent -> Maybe FilePath
- HIE.Bios.Cradle: defaultCradle :: FilePath -> Cradle a
+ HIE.Bios.Cradle: defaultCradle :: LogAction IO (WithSeverity Log) -> FilePath -> Cradle a
- HIE.Bios.Cradle: getCradle :: (b -> Cradle a) -> (CradleConfig b, FilePath) -> Cradle a
+ HIE.Bios.Cradle: getCradle :: Show a => LogAction IO (WithSeverity Log) -> (b -> CradleAction a) -> (CradleConfig b, FilePath) -> IO (Cradle a)
- HIE.Bios.Cradle: loadCradle :: FilePath -> IO (Cradle Void)
+ HIE.Bios.Cradle: loadCradle :: LogAction IO (WithSeverity Log) -> FilePath -> IO (Cradle Void)
- HIE.Bios.Cradle: loadImplicitCradle :: Show a => FilePath -> IO (Cradle a)
+ HIE.Bios.Cradle: loadImplicitCradle :: Show a => LogAction IO (WithSeverity Log) -> FilePath -> IO (Cradle a)
- HIE.Bios.Environment: getRuntimeGhcLibDir :: LogAction IO (WithSeverity Log) -> Cradle a -> IO (CradleLoadResult FilePath)
+ HIE.Bios.Environment: getRuntimeGhcLibDir :: Cradle a -> IO (CradleLoadResult FilePath)
- HIE.Bios.Environment: getRuntimeGhcVersion :: LogAction IO (WithSeverity Log) -> Cradle a -> IO (CradleLoadResult String)
+ HIE.Bios.Environment: getRuntimeGhcVersion :: Cradle a -> IO (CradleLoadResult String)
- HIE.Bios.Flags: getCompilerOptions :: LogAction IO (WithSeverity Log) -> FilePath -> Cradle a -> IO (CradleLoadResult ComponentOptions)
+ HIE.Bios.Flags: getCompilerOptions :: FilePath -> [FilePath] -> Cradle a -> IO (CradleLoadResult ComponentOptions)
- HIE.Bios.Ghc.Api: data SuccessFlag
+ HIE.Bios.Ghc.Api: data () => SuccessFlag
- HIE.Bios.Ghc.Api: initializeFlagsWithCradle :: GhcMonad m => LogAction IO (WithSeverity Log) -> FilePath -> Cradle a -> m (CradleLoadResult (m SuccessFlag, ComponentOptions))
+ HIE.Bios.Ghc.Api: initializeFlagsWithCradle :: GhcMonad m => 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.Api: initializeFlagsWithCradleWithMessage :: GhcMonad m => Maybe Messager -> FilePath -> Cradle a -> m (CradleLoadResult (m SuccessFlag, ComponentOptions))
- HIE.Bios.Ghc.Check: checkSyntax :: Show a => LogAction IO (WithSeverity Log) -> LogAction IO (WithSeverity Log) -> Cradle a -> [FilePath] -> IO String
+ HIE.Bios.Ghc.Check: checkSyntax :: Show a => LogAction IO (WithSeverity Log) -> Cradle a -> [FilePath] -> IO String
- HIE.Bios.Ghc.Gap: data FrontendResult
+ HIE.Bios.Ghc.Gap: data () => FrontendResult
- HIE.Bios.Ghc.Gap: data Hsc a
+ HIE.Bios.Ghc.Gap: data () => Hsc a
- HIE.Bios.Ghc.Gap: data PprStyle
+ HIE.Bios.Ghc.Gap: data () => PprStyle
- HIE.Bios.Ghc.Gap: data Session
+ HIE.Bios.Ghc.Gap: data () => Session
- HIE.Bios.Ghc.Gap: guessTarget :: GhcMonad m => String -> a -> Maybe Phase -> m Target
+ HIE.Bios.Ghc.Gap: guessTarget :: GhcMonad m => String -> Maybe UnitId -> Maybe Phase -> m Target
- HIE.Bios.Ghc.Gap: load' :: GhcMonad m => a -> LoadHowMuch -> Maybe Messager -> ModuleGraph -> m SuccessFlag
+ HIE.Bios.Ghc.Gap: load' :: GhcMonad m => Maybe ModIfaceCache -> LoadHowMuch -> Maybe Messager -> ModuleGraph -> m SuccessFlag
- HIE.Bios.Internal.Debug: cradleInfo :: [FilePath] -> IO String
+ HIE.Bios.Internal.Debug: cradleInfo :: LogAction IO (WithSeverity Log) -> [FilePath] -> IO String
- HIE.Bios.Internal.Debug: debugInfo :: Show a => LogAction IO (WithSeverity Log) -> FilePath -> Cradle a -> IO String
+ HIE.Bios.Internal.Debug: debugInfo :: Show a => FilePath -> Cradle a -> IO String
- HIE.Bios.Types: Cradle :: FilePath -> CradleAction a -> Cradle a
+ HIE.Bios.Types: Cradle :: FilePath -> CradleAction a -> LogAction IO (WithSeverity Log) -> Cradle a
- HIE.Bios.Types: CradleAction :: ActionName a -> (LogAction IO (WithSeverity Log) -> FilePath -> IO (CradleLoadResult ComponentOptions)) -> (LogAction IO (WithSeverity Log) -> [String] -> IO (CradleLoadResult String)) -> CradleAction a
+ HIE.Bios.Types: CradleAction :: ActionName a -> (FilePath -> [FilePath] -> IO (CradleLoadResult ComponentOptions)) -> ([String] -> IO (CradleLoadResult String)) -> CradleAction a
- HIE.Bios.Types: [runCradle] :: CradleAction a -> LogAction IO (WithSeverity Log) -> FilePath -> IO (CradleLoadResult ComponentOptions)
+ HIE.Bios.Types: [runCradle] :: CradleAction a -> FilePath -> [FilePath] -> IO (CradleLoadResult ComponentOptions)
- HIE.Bios.Types: [runGhcCmd] :: CradleAction a -> LogAction IO (WithSeverity Log) -> [String] -> IO (CradleLoadResult String)
+ HIE.Bios.Types: [runGhcCmd] :: CradleAction a -> [String] -> IO (CradleLoadResult String)

Files

ChangeLog.md view
@@ -1,8 +1,13 @@ # ChangeLog hie-bios -## 2023-11-13 - 0.12.1+## 2023-98-22 - 0.13.0 -* 9.8 support [#417](https://github.com/haskell/hie-bios/pull/417)+* Multi Component cabal support [#409](https://github.com/haskell/hie-bios/pull/409)+* Make sure cabal caches can be found [#408](https://github.com/haskell/hie-bios/pull/408)+* Rename project-file to cabalProject in hie.yaml [#407](https://github.com/haskell/hie-bios/pull/407)+* Update README for new project-file key [#403](https://github.com/haskell/hie-bios/pull/403)+* Add more informative log messages for cradle running [#406](https://github.com/haskell/hie-bios/pull/406)+* Add cabal.project support for cabal cradles [#357](https://github.com/haskell/hie-bios/pull/357)  ## 2023-03-13 - 0.12.0 
README.md view
@@ -298,13 +298,10 @@     * This is convenient if components are overlapping.  Similarly to `multi-stack` configurations, you can also specify multiple components using a `components` subkey.-While this is currently not used for anything, this syntax gives you a place to put defaults, directly under-the `cabal` entry.  ```yaml cradle:   cabal:-    # Reserved for future default options     components:     - path: "./src"       component: "lib:hie-bios"@@ -317,6 +314,33 @@ ```  This way we specified which component needs to be compiled given a certain source file for our whole project.++Some projects have multiple `cabal.project` files for multiple versions of ghc or development options. In this case you+can specify an alternate relative file to use by using the `cabalProject` option. The path is relative to the+`hie.yaml`.++```yaml+cradle:+  cabal:+    cabalProject: "./cabal.project.dev"+```++We can combine the `cabalProject` field with `components`:++```yaml+cradle:+  cabal:+    cabalProject: "./cabal.project.dev"+    components:+    - path: "./src"+      component: "lib:hie-bios"+    - path: "./exe"+      component: "exe:hie-bios"+    - path: "./tests/BiosTests.hs"+      component: "test:hie-bios"+    - path: "./tests/ParserTests.hs"+      component: "test:parser-tests"+```  #### Debugging a `cabal` cradle 
exe/Main.hs view
@@ -63,28 +63,28 @@     hSetEncoding stdout utf8     cwd <- getCurrentDirectory     cmd <- execParser progInfo-    cradle <--        -- find cradle does a takeDirectory on the argument, so make it into a file-        findCradle (cwd </> "File.hs") >>= \case-          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 +    cradle <-+        -- find cradle does a takeDirectory on the argument, so make it into a file+        findCradle (cwd </> "File.hs") >>= \case+          Just yaml -> loadCradle logger yaml+          Nothing -> loadImplicitCradle logger (cwd </> "File.hs")+     res <- case cmd of-      Check targetFiles -> checkSyntax logger logger cradle targetFiles+      Check targetFiles -> checkSyntax logger cradle targetFiles       Debug files -> case files of-        [] -> debugInfo logger (cradleRootDir cradle) cradle-        fp -> debugInfo logger fp cradle+        [] -> debugInfo (cradleRootDir cradle) cradle+        fp -> debugInfo 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 logger fp cradle+                  res <- getCompilerOptions fp [] cradle                   case res of                       CradleFail (CradleError _deps _ex err) ->                         return $ "Failed to show flags for \""@@ -97,7 +97,7 @@                       CradleNone -> return $ "No flags/None Cradle: component " ++ fp ++ " should not be loaded"           return (unlines res)       ConfigInfo files -> configInfo files-      CradleInfo files -> cradleInfo files+      CradleInfo files -> cradleInfo logger files       Root    -> rootInfo cradle       Version -> return progVersion     putStr res
hie-bios.cabal view
@@ -1,11 +1,11 @@ Cabal-Version:          2.2 Name:                   hie-bios-Version:                0.12.1+Version:                0.13.0 Author:                 Matthew Pickering <matthewtpickering@gmail.com> Maintainer:             Matthew Pickering <matthewtpickering@gmail.com> License:                BSD-3-Clause License-File:           LICENSE-Homepage:               https://github.com/mpickering/hie-bios+Homepage:               https://github.com/haskell/hie-bios Synopsis:               Set up a GHC API session Description:            Set up a GHC API session and obtain flags required to compile a source file @@ -21,6 +21,14 @@                         tests/projects/cabal-with-ghc/cabal.project                         tests/projects/cabal-with-ghc/hie.yaml                         tests/projects/cabal-with-ghc/src/MyLib.hs+                        tests/projects/cabal-with-ghc-and-project/cabal-with-ghc.cabal+                        tests/projects/cabal-with-ghc-and-project/cabal.project.8.10.7+                        tests/projects/cabal-with-ghc-and-project/hie.yaml+                        tests/projects/cabal-with-ghc-and-project/src/MyLib.hs+                        tests/projects/cabal-with-project/cabal-with-project.cabal+                        tests/projects/cabal-with-project/cabal.project.8.10.7+                        tests/projects/cabal-with-project/hie.yaml+                        tests/projects/cabal-with-project/src/MyLib.hs                         tests/projects/symlink-test/a/A.hs                         tests/projects/symlink-test/hie.yaml                         tests/projects/deps-bios-new/A.hs@@ -35,6 +43,12 @@                         tests/projects/multi-cabal/hie.yaml                         tests/projects/multi-cabal/multi-cabal.cabal                         tests/projects/multi-cabal/src/Lib.hs+                        tests/projects/multi-cabal-with-project/appA/appA.cabal+                        tests/projects/multi-cabal-with-project/appA/src/Lib.hs+                        tests/projects/multi-cabal-with-project/appB/appB.cabal+                        tests/projects/multi-cabal-with-project/appB/src/Lib.hs+                        tests/projects/multi-cabal-with-project/cabal.project.8.10.7+                        tests/projects/multi-cabal-with-project/hie.yaml                         tests/projects/monorepo-cabal/cabal.project                         tests/projects/monorepo-cabal/hie.yaml                         tests/projects/monorepo-cabal/A/Main.hs@@ -125,7 +139,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.7 || ==9.0.2 || ==9.2.7 || ==9.4.4+tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.8 || ==9.4.6 || ==9.6.2  Library   Default-Language:     Haskell2010@@ -162,11 +176,11 @@                         time                 >= 1.8.0 && < 1.13,                         extra                >= 1.6.14 && < 1.8,                         prettyprinter        ^>= 1.6 || ^>= 1.7.0,-                        ghc                  >= 8.6.1 && < 9.9,+                        ghc                  >= 8.6.1 && < 9.7,                         transformers         >= 0.5.2 && < 0.7,                         temporary            >= 1.2 && < 1.4,                         template-haskell,-                        text                 >= 1.2.3 && < 2.2,+                        text                 >= 1.2.3 && < 2.1,                         unix-compat          >= 0.5.1 && < 0.8,                         unordered-containers >= 0.2.9 && < 0.3,                         yaml                 >= 0.10.0 && < 0.12,
src/HIE/Bios/Config.hs view
@@ -10,11 +10,12 @@     CabalType,     pattern CabalType,     cabalComponent,+    cabalProjectFile,     StackType,     pattern StackType,     stackComponent,     stackYaml,-    CradleType(..),+    CradleTree(..),     Callable(..)     ) where @@ -46,7 +47,7 @@         -- ^ Dependencies of a cradle.         -- Dependencies are expected to be relative to the root directory.         -- The given files are not required to exist.-        , cradleType :: CradleType a+        , cradleTree :: CradleTree a         -- ^ Type of the cradle to use. Actions to obtain         -- compiler flags from are dependant on this field.         }@@ -55,18 +56,28 @@ data Callable = Program FilePath | Command String     deriving (Show, Eq) +-- | A cabal yaml configuration consists of component configuration and project configuration.+--+-- The former specifies how we can find the compilation flags for any filepath+-- in the project.+-- There might be an explicit mapping from source directories to components,+-- or we let cabal figure it out on its own.+--+-- Project configuration is the 'cabal.project' file, we is by default named+-- 'cabal.project'. We allow to override that name to have an HLS specific+-- project configuration file. data CabalType-    = CabalType_ { _cabalComponent :: !(Last String) }+    = CabalType_ { _cabalComponent :: !(Last String), _cabalProjectFile :: !(Last FilePath) }     deriving (Eq)  instance Semigroup CabalType where-    CabalType_ cr <> CabalType_ cl = CabalType_ (cr <> cl)+    CabalType_ cr cpr <> CabalType_ cl cpl = CabalType_ (cr <> cl) (cpr <> cpl)  instance Monoid CabalType where-    mempty = CabalType_ mempty+    mempty = CabalType_ mempty mempty -pattern CabalType :: Maybe String -> CabalType-pattern CabalType { cabalComponent } = CabalType_ (Last cabalComponent)+pattern CabalType :: Maybe String -> Maybe FilePath -> CabalType+pattern CabalType { cabalComponent, cabalProjectFile } = CabalType_ (Last cabalComponent) (Last cabalProjectFile) {-# COMPLETE CabalType #-}  instance Show CabalType where@@ -82,14 +93,14 @@ instance Monoid StackType where     mempty = StackType_ mempty mempty -pattern StackType :: Maybe String -> Maybe String -> StackType+pattern StackType :: Maybe String -> Maybe FilePath -> StackType pattern StackType { stackComponent, stackYaml } = StackType_ (Last stackComponent) (Last stackYaml) {-# COMPLETE StackType #-}  instance Show StackType where   show = show . Stack -data CradleType a+data CradleTree a     = Cabal { cabalType :: !CabalType }     | CabalMulti { defaultCabal :: !CabalType, subCabalComponents :: [ (FilePath, CabalType) ] }     | Stack { stackType :: !StackType }@@ -114,7 +125,7 @@     | Other { otherConfig :: a, originalYamlValue :: Value }     deriving (Eq, Functor) -instance Show (CradleType a) where+instance Show (CradleTree a) where     show (Cabal comp) = "Cabal {component = " ++ show (cabalComponent comp) ++ "}"     show (CabalMulti d a) = "CabalMulti {defaultCabal = " ++ show d ++ ", subCabalComponents = " ++ show a ++ "}"     show (Stack comp) = "Stack {component = " ++ show (stackComponent comp) ++ ", stackYaml = " ++ show (stackYaml comp) ++ "}"@@ -143,30 +154,31 @@  fromYAMLConfig :: CradleConfigYAML a -> Config a fromYAMLConfig cradleYAML = Config $ CradleConfig (fromMaybe [] $ YAML.dependencies cradleYAML)-                                                  (toCradleType $ YAML.cradle cradleYAML)+                                                  (toCradleTree $ YAML.cradle cradleYAML) -toCradleType :: YAML.CradleComponent a -> CradleType a-toCradleType (YAML.Multi cpts)  =+toCradleTree :: YAML.CradleComponent a -> CradleTree a+toCradleTree (YAML.Multi cpts)  =   Multi $ (\(YAML.MultiSubComponent fp' cfg) -> (fp', cradle $ fromYAMLConfig cfg)) <$> cpts-toCradleType (YAML.Stack (YAML.StackConfig yaml cpts)) =+toCradleTree (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)) =+toCradleTree (YAML.Cabal (YAML.CabalConfig prjFile 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)+    YAML.NoComponent          -> Cabal $ CabalType Nothing prjFile+    (YAML.SingleComponent c)  -> Cabal $ CabalType (Just c) prjFile+    (YAML.ManyComponents cs)  -> CabalMulti (CabalType Nothing prjFile)+                                            ((\(YAML.CabalComponent fp' c cPrjFile) ->+                                              (fp', CabalType (Just c) cPrjFile)) <$> cs)+toCradleTree (YAML.Direct cfg)  = Direct (YAML.arguments cfg)+toCradleTree (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)+toCradleTree (YAML.None _)      = None+toCradleTree (YAML.Other cfg)   = Other (YAML.otherConfig cfg)                                         (YAML.originalYamlValue cfg)  toCallable :: YAML.Callable -> Callable
src/HIE/Bios/Config/YAML.hs view
@@ -29,8 +29,7 @@ import qualified Data.HashMap.Strict as Map import qualified Data.Text           as T #endif-import           Data.Aeson.Types    (Object, Parser, Value (Null),-                                      typeMismatch)+import           Data.Aeson.Types    (Parser, typeMismatch) import qualified Data.Char           as C (toLower) import           Data.List           ((\\)) import           GHC.Generics        (Generic)@@ -44,7 +43,7 @@ -- This used to be just a HashMap, but since aeson >= 2 -- this is an opaque datatype. type KeyMap v = Map.HashMap T.Text v- + keys :: KeyMap v -> [Key] keys = Map.keys #endif@@ -103,25 +102,33 @@                       } deriving (Generic, FromJSON)  data CabalConfig-  = CabalConfig { cabalComponents :: OneOrManyComponents CabalComponent }+  = CabalConfig { cabalProject    :: Maybe FilePath+                , 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@(Array _)     = CabalConfig Nothing . ManyComponents <$> parseJSON v+  parseJSON v@(Object obj)  = (checkObjectKeys ["cabalProject", "component", "components"] obj)+                                *> (CabalConfig+                                  <$> obj .:? "cabalProject"+                                  <*> parseJSON v)+  parseJSON Null            = pure $ CabalConfig Nothing NoComponent   parseJSON v               = typeMismatch "CabalConfig" v  data CabalComponent   = CabalComponent { cabalPath      :: FilePath                    , cabalComponent :: String+                   , cabalComponentProject :: Maybe FilePath                    }  instance FromJSON CabalComponent where   parseJSON =-    let parseCabalComponent obj = checkObjectKeys ["path", "component"] obj+    let parseCabalComponent obj = checkObjectKeys ["path", "component", "cabalProject"] obj                                     *> (CabalComponent                                           <$> obj .: "path"-                                          <*> obj .: "component")+                                          <*> obj .: "component"+                                          <*> obj .:? "cabalProject"+                                        )      in withObject "CabalComponent" parseCabalComponent  data StackConfig@@ -132,7 +139,7 @@ data StackComponent   = StackComponent { stackPath          :: FilePath                    , stackComponent     :: String-                   , stackComponentYAML :: Maybe String+                   , stackComponentYAML :: Maybe FilePath                    }  instance FromJSON StackConfig where
src/HIE/Bios/Cradle.hs view
@@ -2,6 +2,9 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RecursiveDo #-}+{-# LANGUAGE RecordWildCards #-} module HIE.Bios.Cradle (       findCradle     , loadCradle@@ -20,6 +23,9 @@     , readProcessWithOutputs     , readProcessWithCwd     , makeCradleResult+    -- | Cradle project configuration types+    , CradleProjectConfig(..)+    ,   ) where  import Control.Applicative ((<|>), optional)@@ -65,41 +71,9 @@ import GHC.Fingerprint (fingerprintString) import GHC.ResponseFile (escapeArgs) -------------------------------------------------------------------- Environment variables used by hie-bios.------ If you need more, add a constant here.--------------------------------------------------------------------- | Environment variable containing the filepath to which--- cradle actions write their results to.--- If the filepath does not exist, cradle actions must create them.-hie_bios_output :: String-hie_bios_output = "HIE_BIOS_OUTPUT"---- | Environment variable pointing to the GHC location used by--- cabal's and stack's GHC wrapper.------ If not set, will default to sensible defaults.-hie_bios_ghc :: String-hie_bios_ghc = "HIE_BIOS_GHC"---- | Environment variable with extra arguments passed to the GHC location--- in cabal's and stack's GHC wrapper.------ If not set, assume no extra arguments.-hie_bios_ghc_args :: String-hie_bios_ghc_args = "HIE_BIOS_GHC_ARGS"---- | Environment variable pointing to the source file location that caused--- the cradle action to be executed.-hie_bios_arg :: String-hie_bios_arg = "HIE_BIOS_ARG"---- | Environment variable pointing to a filepath to which dependencies--- of a cradle can be written to by the cradle action.-hie_bios_deps :: String-hie_bios_deps = "HIE_BIOS_DEPS"+import Data.Version+import System.IO.Unsafe (unsafeInterleaveIO)+import Text.ParserCombinators.ReadP (readP_to_S)  ---------------------------------------------------------------- @@ -110,76 +84,216 @@     runMaybeT (yamlConfig wdir)  -- | Given root\/hie.yaml load the Cradle.-loadCradle :: FilePath -> IO (Cradle Void)-loadCradle = loadCradleWithOpts absurd+loadCradle :: LogAction IO (WithSeverity Log) -> FilePath -> IO (Cradle Void)+loadCradle l = loadCradleWithOpts l absurd  -- | Given root\/foo\/bar.hs, load an implicit cradle-loadImplicitCradle :: Show a => FilePath -> IO (Cradle a)-loadImplicitCradle wfile = do+loadImplicitCradle :: Show a => LogAction IO (WithSeverity Log) -> FilePath -> IO (Cradle a)+loadImplicitCradle l wfile = do   let wdir = takeDirectory wfile   cfg <- runMaybeT (implicitConfig wdir)-  return $ case cfg of-    Just bc -> getCradle absurd bc-    Nothing -> defaultCradle wdir+  case cfg of+    Just bc -> getCradle l absurd bc+    Nothing -> return $ defaultCradle l wdir  -- | Finding 'Cradle'. --   Find a cabal file by tracing ancestor directories. --   Find a sandbox according to a cabal sandbox config --   in a cabal directory.-loadCradleWithOpts :: (Yaml.FromJSON b) => (b -> Cradle a) -> FilePath -> IO (Cradle a)-loadCradleWithOpts buildCustomCradle wfile = do+loadCradleWithOpts :: (Yaml.FromJSON b, Show a) => LogAction IO (WithSeverity Log) -> (b -> CradleAction a) -> FilePath -> IO (Cradle a)+loadCradleWithOpts l buildCustomCradle wfile = do     cradleConfig <- readCradleConfig wfile-    return $ getCradle buildCustomCradle (cradleConfig, takeDirectory wfile)+    getCradle l buildCustomCradle (cradleConfig, takeDirectory wfile) -getCradle :: (b -> Cradle a) -> (CradleConfig b, FilePath) -> Cradle a-getCradle buildCustomCradle (cc, wdir) = addCradleDeps cradleDeps $ case cradleType cc of-    Cabal CabalType{ cabalComponent = mc } -> cabalCradle wdir mc-    CabalMulti dc ms ->-      getCradle buildCustomCradle-        (CradleConfig cradleDeps-          (Multi [(p, CradleConfig [] (Cabal $ dc <> c)) | (p, c) <- ms])-        , wdir)-    Stack StackType{ stackComponent = mc, stackYaml = syaml} ->-      let-        stackYamlConfig = stackYamlFromMaybe wdir syaml-      in-        stackCradle wdir mc stackYamlConfig-    StackMulti ds ms ->-      getCradle buildCustomCradle-        (CradleConfig cradleDeps-          (Multi [(p, CradleConfig [] (Stack $ ds <> c)) | (p, c) <- ms])-        , wdir)- --   Bazel -> rulesHaskellCradle wdir- --   Obelisk -> obeliskCradle wdir-    Bios bios deps mbGhc -> biosCradle wdir bios deps mbGhc-    Direct xs -> directCradle wdir xs-    None      -> noneCradle wdir-    Multi ms  -> multiCradle buildCustomCradle wdir ms-    Other a _ -> buildCustomCradle a-    where-      cradleDeps = cradleDependencies cc+getCradle :: Show a => LogAction IO (WithSeverity Log) ->  (b -> CradleAction a) -> (CradleConfig b, FilePath) -> IO (Cradle a)+getCradle l buildCustomCradle (cc, wdir) = do+    rcs <- canonicalizeResolvedCradles wdir cs+    resolvedCradlesToCradle l buildCustomCradle wdir rcs+  where+    cs = resolveCradleTree wdir cc -addCradleDeps :: [FilePath] -> Cradle a -> Cradle a-addCradleDeps deps c =-  c { cradleOptsProg = addActionDeps (cradleOptsProg c) }++-- | The actual type of action we will be using to process a file+data ConcreteCradle a+  = ConcreteCabal CabalType+  | ConcreteStack StackType+  | ConcreteBios Callable (Maybe Callable) (Maybe FilePath)+  | ConcreteDirect [String]+  | ConcreteNone+  | ConcreteOther a+  deriving Show++-- | ConcreteCradle augmented with information on which file the+-- cradle applies+data ResolvedCradle a+ = ResolvedCradle+ { prefix :: FilePath -- ^ the prefix to match files+ , cradleDeps :: [FilePath] -- ^ accumulated dependencies+ , concreteCradle :: ConcreteCradle a+ } deriving Show++-- | The final cradle config that specifies the cradle for+-- each prefix we know how to handle+data ResolvedCradles a+ = ResolvedCradles+ { cradleRoot :: FilePath+ , resolvedCradles :: [ResolvedCradle a] -- ^ In order of decreasing specificity+ , cradleProgramVersions :: ProgramVersions+ }++data ProgramVersions =+  ProgramVersions { cabalVersion  :: Maybe Version+                  , stackVersion  :: Maybe Version+                  , ghcVersion    :: Maybe Version+                  }++makeVersions :: LogAction IO (WithSeverity Log) -> FilePath -> ([String] -> IO (CradleLoadResult String)) -> IO ProgramVersions+makeVersions l wdir ghc = do+  cabalVersion <- unsafeInterleaveIO (getCabalVersion l wdir)+  stackVersion <- unsafeInterleaveIO (getStackVersion l wdir)+  ghcVersion <- unsafeInterleaveIO (getGhcVersion ghc)+  pure ProgramVersions{..}++getCabalVersion :: LogAction IO (WithSeverity Log) -> FilePath -> IO (Maybe Version)+getCabalVersion l wdir = do+  res <- readProcessWithCwd l wdir "cabal" ["--numeric-version"] ""+  case res of+    CradleSuccess stdo ->+      pure $ versionMaybe stdo+    _ -> pure Nothing++getStackVersion :: LogAction IO (WithSeverity Log) -> FilePath -> IO (Maybe Version)+getStackVersion l wdir = do+  res <- readProcessWithCwd l wdir "stack" ["--numeric-version"] ""+  case res of+    CradleSuccess stdo ->+      pure $ versionMaybe stdo+    _ -> pure Nothing++getGhcVersion :: ([String] -> IO (CradleLoadResult String)) -> IO (Maybe Version)+getGhcVersion ghc = do+  res <- ghc ["--numeric-version"]+  case res of+    CradleSuccess stdo ->+      pure $ versionMaybe stdo+    _ -> pure Nothing++versionMaybe :: String -> Maybe Version+versionMaybe xs = case reverse $ readP_to_S parseVersion xs of+  [] -> Nothing+  (x:_) -> Just (fst x)+++addActionDeps :: [FilePath] -> CradleLoadResult ComponentOptions -> CradleLoadResult ComponentOptions+addActionDeps deps =+  cradleLoadResult+      CradleNone+      (\err -> CradleFail (err { cradleErrorDependencies = cradleErrorDependencies err `union` deps }))+      (\(ComponentOptions os' dir ds) -> CradleSuccess (ComponentOptions os' dir (ds `union` deps)))+  ++resolvedCradlesToCradle :: Show a => LogAction IO (WithSeverity Log) -> (b -> CradleAction a) -> FilePath -> [ResolvedCradle b] -> IO (Cradle a)+resolvedCradlesToCradle logger buildCustomCradle root cs = mdo+  let run_ghc_cmd args =+        -- We're being lazy here and just returning the ghc path for the+        -- first non-none cradle. This shouldn't matter in practice: all+        -- sub cradles should be using the same ghc version!+        case filter (notNoneType . actionName) $ map snd cradleActions of+          [] -> return CradleNone+          (act:_) ->+            runGhcCmd+              act+              args+  versions <- makeVersions logger root run_ghc_cmd+  let rcs = ResolvedCradles root cs versions+      cradleActions = [ (c, resolveCradleAction logger buildCustomCradle rcs root c) | c <- cs ]+      err_msg fp+        = ["Multi Cradle: No prefixes matched"+          , "pwd: " ++ root+          , "filepath: " ++ fp+          , "prefixes:"+          ] ++ [show (prefix pf, actionName cc) | (pf, cc) <- cradleActions]+  pure $ Cradle+    { cradleRootDir = root+    , cradleLogger = logger+    , cradleOptsProg = CradleAction+      { actionName = multiActionName+      , runCradle  = \fp prev -> do+          absfp <- makeAbsolute fp+          case selectCradle (prefix . fst) absfp cradleActions of+            Just (rc, act) -> do+              addActionDeps (cradleDeps rc) <$> runCradle act fp prev+            Nothing -> return $ CradleFail $ CradleError [] ExitSuccess (err_msg fp)+      , runGhcCmd = run_ghc_cmd+      }+    }   where-    addActionDeps :: CradleAction a -> CradleAction a-    addActionDeps ca =-      ca { runCradle = \l fp ->-            (cradleLoadResult-                CradleNone-                (\err -> CradleFail (err { cradleErrorDependencies = cradleErrorDependencies err `union` deps }))-                (\(ComponentOptions os' dir ds) -> CradleSuccess (ComponentOptions os' dir (ds `union` deps)))-            )-            <$> runCradle ca l fp-         }+    multiActionName+      | all (\c -> isStackCradleConfig c || isNoneCradleConfig c) cs+      = Types.Stack+      | all (\c -> isCabalCradleConfig c || isNoneCradleConfig c) cs+      = Types.Cabal+      | [True] <- map isBiosCradleConfig $ filter (not . isNoneCradleConfig) cs+      = Types.Bios+      | [True] <- map isDirectCradleConfig $ filter (not . isNoneCradleConfig) cs+      = Types.Direct+      | otherwise+      = Types.Multi +    isStackCradleConfig cfg = case concreteCradle cfg of+      ConcreteStack{} -> True+      _               -> False++    isCabalCradleConfig cfg = case concreteCradle cfg of+      ConcreteCabal{} -> True+      _               -> False++    isBiosCradleConfig cfg = case concreteCradle cfg of+      ConcreteBios{}  -> True+      _               -> False++    isDirectCradleConfig cfg = case concreteCradle cfg of+      ConcreteDirect{} -> True+      _                -> False++    isNoneCradleConfig cfg = case concreteCradle cfg of+      ConcreteNone{} -> True+      _              -> False++    notNoneType Types.None = False+    notNoneType _ = True+++resolveCradleAction :: LogAction IO (WithSeverity Log) ->  (b -> CradleAction a) -> ResolvedCradles b -> FilePath -> ResolvedCradle b -> CradleAction a+resolveCradleAction l buildCustomCradle cs root cradle =+  case concreteCradle cradle of+    ConcreteCabal t -> cabalCradle l cs root (cabalComponent t) (projectConfigFromMaybe root (cabalProjectFile t))+    ConcreteStack t -> stackCradle l root (stackComponent t) (projectConfigFromMaybe root (stackYaml t))+    ConcreteBios bios deps mbGhc -> biosCradle l root bios deps mbGhc+    ConcreteDirect xs -> directCradle l root xs+    ConcreteNone -> noneCradle+    ConcreteOther a -> buildCustomCradle a++resolveCradleTree :: FilePath -> CradleConfig a -> [ResolvedCradle a]+resolveCradleTree root (CradleConfig deps tree) = go root deps tree+  where+    go pfix deps tree = case tree of+      Cabal t              -> [ResolvedCradle pfix deps (ConcreteCabal t)]+      Stack t              -> [ResolvedCradle pfix deps (ConcreteStack t)]+      Bios bios dcmd mbGhc -> [ResolvedCradle pfix deps (ConcreteBios bios dcmd mbGhc)]+      Direct xs            -> [ResolvedCradle pfix deps (ConcreteDirect xs)]+      None                 -> [ResolvedCradle pfix deps ConcreteNone]+      Other a _            -> [ResolvedCradle pfix deps (ConcreteOther a)]+      CabalMulti dc xs     -> [ResolvedCradle p    deps (ConcreteCabal (dc <> c)) | (p, c) <- xs ]+      StackMulti dc xs     -> [ResolvedCradle p    deps (ConcreteStack (dc <> c)) | (p, c) <- xs ]+      Multi xs             -> concat [ go pfix' (deps ++ deps') tree' | (pfix', CradleConfig deps' tree') <- xs]+ -- | Try to infer an appropriate implicit cradle type from stuff we can find in the enclosing directories: --   * If a .hie-bios file is found, we can treat this as a @Bios@ cradle --   * If a stack.yaml file is found, we can treat this as a @Stack@ cradle --   * If a cabal.project or an xyz.cabal file is found, we can treat this as a @Cabal@ cradle-inferCradleType :: FilePath -> MaybeT IO (CradleType a, FilePath)-inferCradleType fp =+inferCradleTree :: FilePath -> MaybeT IO (CradleTree a, FilePath)+inferCradleTree fp =        maybeItsBios    <|> maybeItsStack    <|> maybeItsCabal@@ -191,16 +305,16 @@    maybeItsStack = stackExecutable >> (Stack $ StackType Nothing Nothing,) <$> stackWorkDir fp -  maybeItsCabal = (Cabal $ CabalType Nothing,) <$> cabalWorkDir fp+  maybeItsCabal = (Cabal $ CabalType Nothing Nothing,) <$> cabalWorkDir fp    -- maybeItsObelisk = (Obelisk,) <$> obeliskWorkDir fp    -- maybeItsBazel = (Bazel,) <$> rulesHaskellWorkDir fp  --- | Wraps up the cradle inferred by @inferCradleType@ as a @CradleConfig@ with no dependencies+-- | Wraps up the cradle inferred by @inferCradleTree@ as a @CradleConfig@ with no dependencies implicitConfig :: FilePath -> MaybeT IO (CradleConfig a, FilePath)-implicitConfig = (fmap . first) (CradleConfig noDeps) . inferCradleType+implicitConfig = (fmap . first) (CradleConfig noDeps) . inferCradleTree   where   noDeps :: [FilePath]   noDeps = []@@ -275,158 +389,85 @@  -- | Default cradle has no special options, not very useful for loading -- modules.-defaultCradle :: FilePath -> Cradle a-defaultCradle cur_dir =+defaultCradle :: LogAction IO (WithSeverity Log) -> FilePath -> Cradle a+defaultCradle l cur_dir =   Cradle     { cradleRootDir = cur_dir+    , cradleLogger = l     , cradleOptsProg = CradleAction         { actionName = Types.Default         , runCradle = \_ _ ->             return (CradleSuccess (ComponentOptions argDynamic cur_dir []))-        , runGhcCmd = \l -> runGhcCmdOnPath l cur_dir+        , runGhcCmd = runGhcCmdOnPath l cur_dir         }     }  --------------------------------------------------------------- -- | The none cradle tells us not to even attempt to load a certain directory -noneCradle :: FilePath -> Cradle a-noneCradle cur_dir =-  Cradle-    { cradleRootDir = cur_dir-    , cradleOptsProg = CradleAction-        { actionName = Types.None-        , runCradle = \_ _ -> return CradleNone-        , runGhcCmd = \_ _ -> return CradleNone-        }-    }+noneCradle :: CradleAction a+noneCradle =+  CradleAction+      { actionName = Types.None+      , runCradle = \_ _ -> return CradleNone+      , runGhcCmd = \_ -> return CradleNone+      }  --------------------------------------------------------------- -- | The multi cradle selects a cradle based on the filepath -multiCradle :: (b -> Cradle a) -> FilePath -> [(FilePath, CradleConfig b)] -> Cradle a-multiCradle buildCustomCradle cur_dir cs =-  Cradle-    { cradleRootDir  = cur_dir-    , cradleOptsProg = CradleAction-        { actionName = multiActionName-        , runCradle  = \l fp -> makeAbsolute fp >>= multiAction buildCustomCradle cur_dir cs l-        , runGhcCmd = \l args ->-            -- We're being lazy here and just returning the ghc path for the-            -- first non-none cradle. This shouldn't matter in practice: all-            -- sub cradles should be using the same ghc version!-            case filter (not . isNoneCradleConfig) $ map snd cs of-              [] -> return CradleNone-              (cfg:_) ->-                runGhcCmd-                  (cradleOptsProg $ getCradle buildCustomCradle (cfg, cur_dir))-                  l-                  args-        }-    }-  where-    cfgs = map snd cs--    multiActionName-      | all (\c -> isStackCradleConfig c || isNoneCradleConfig c) cfgs-      = Types.Stack-      | all (\c -> isCabalCradleConfig c || isNoneCradleConfig c) cfgs-      = Types.Cabal-      | otherwise-      = Types.Multi--    isStackCradleConfig cfg = case cradleType cfg of-      Stack{}      -> True-      StackMulti{} -> True-      _            -> False--    isCabalCradleConfig cfg = case cradleType cfg of-      Cabal{}      -> True-      CabalMulti{} -> True-      _            -> False--    isNoneCradleConfig cfg = case cradleType cfg of-      None -> True-      _    -> False--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--  where-    err_msg = ["Multi Cradle: No prefixes matched"-              , "pwd: " ++ cur_dir-              , "filepath: " ++ cur_fp-              , "prefixes:"-              ] ++ [show (pf, cradleType cc) | (pf, cc) <- cs]--    -- Canonicalize the relative paths present in the multi-cradle and-    -- also order the paths by most specific first. In the cradle selection-    -- function we want to choose the most specific cradle possible.-    canonicalizeCradles :: IO [(FilePath, CradleConfig b)]-    canonicalizeCradles =-      sortOn (Down . fst)-        <$> mapM (\(p, c) -> (,c) <$> makeAbsolute (cur_dir </> p)) cs+-- Canonicalize the relative paths present in the multi-cradle and+-- also order the paths by most specific first. In the cradle selection+-- function we want to choose the most specific cradle possible.+canonicalizeResolvedCradles :: FilePath -> [ResolvedCradle a] -> IO [ResolvedCradle a]+canonicalizeResolvedCradles cur_dir cs =+  sortOn (Down . prefix)+    <$> mapM (\c -> (\abs -> c {prefix = abs}) <$> makeAbsolute (cur_dir </> prefix c)) cs -    selectCradle [] =-      return (CradleFail (CradleError [] ExitSuccess err_msg))-    selectCradle ((p, c): css) =-        if p `isPrefixOf` cur_fp-          then runCradle-                  (cradleOptsProg (getCradle buildCustomCradle (c, cur_dir)))-                  l-                  cur_fp-          else selectCradle css+selectCradle :: (a -> FilePath) -> FilePath -> [a] -> Maybe a+selectCradle _ _ [] = Nothing+selectCradle k cur_fp (c: css) =+    if k c `isPrefixOf` cur_fp+      then Just c+      else selectCradle k cur_fp css   ------------------------------------------------------------------------- -directCradle :: FilePath -> [String] -> Cradle a-directCradle wdir args =-  Cradle-    { cradleRootDir = wdir-    , cradleOptsProg = CradleAction-        { actionName = Types.Direct-        , runCradle = \_ _ ->-            return (CradleSuccess (ComponentOptions (args ++ argDynamic) wdir []))-        , runGhcCmd = \l -> runGhcCmdOnPath l wdir-        }-    }+directCradle :: LogAction IO (WithSeverity Log) -> FilePath -> [String] -> CradleAction a+directCradle l wdir args+  = CradleAction+      { actionName = Types.Direct+      , runCradle = \_ _ ->+          return (CradleSuccess (ComponentOptions (args ++ argDynamic) wdir []))+      , runGhcCmd = runGhcCmdOnPath l wdir+      }+      -------------------------------------------------------------------------   -- | Find a cradle by finding an executable `hie-bios` file which will -- be executed to find the correct GHC options to use.-biosCradle :: FilePath -> Callable -> Maybe Callable -> Maybe FilePath -> Cradle a-biosCradle wdir biosCall biosDepsCall mbGhc =-  Cradle-    { cradleRootDir    = wdir-    , cradleOptsProg   = CradleAction-        { actionName = Types.Bios-        , runCradle = biosAction wdir biosCall biosDepsCall-        , runGhcCmd = \l args -> readProcessWithCwd l wdir (fromMaybe "ghc" mbGhc) args ""-        }-    }+biosCradle :: LogAction IO (WithSeverity Log) -> FilePath -> Callable -> Maybe Callable -> Maybe FilePath -> CradleAction a+biosCradle l wdir biosCall biosDepsCall mbGhc+  = CradleAction+      { actionName = Types.Bios+      , runCradle = biosAction wdir biosCall biosDepsCall l+      , runGhcCmd = \args -> readProcessWithCwd l wdir (fromMaybe "ghc" mbGhc) args ""+      }  biosWorkDir :: FilePath -> MaybeT IO FilePath biosWorkDir = findFileUpwards (".hie-bios" ==) -biosDepsAction :: LogAction IO (WithSeverity Log) -> FilePath -> Maybe Callable -> FilePath -> IO [FilePath]-biosDepsAction l wdir (Just biosDepsCall) fp = do-  biosDeps' <- callableToProcess biosDepsCall (Just fp)+biosDepsAction :: LogAction IO (WithSeverity Log) -> FilePath -> Maybe Callable -> FilePath -> [FilePath] -> IO [FilePath]+biosDepsAction l wdir (Just biosDepsCall) fp _prevs = do+  biosDeps' <- callableToProcess biosDepsCall (Just fp) -- TODO multi pass the previous files too   (ex, sout, serr, [(_, args)]) <- readProcessWithOutputs [hie_bios_output] l wdir biosDeps'   case ex of     ExitFailure _ ->  error $ show (ex, sout, serr)     ExitSuccess -> return $ fromMaybe [] args-biosDepsAction _ _ Nothing _ = return []+biosDepsAction _ _ Nothing _ _ = return []  biosAction   :: FilePath@@ -434,15 +475,16 @@   -> Maybe Callable   -> LogAction IO (WithSeverity Log)   -> FilePath+  -> [FilePath]   -> IO (CradleLoadResult ComponentOptions)-biosAction wdir bios bios_deps l fp = do-  bios' <- callableToProcess bios (Just fp)+biosAction wdir bios bios_deps l fp fps = do+  bios' <- callableToProcess bios (Just fp) -- TODO pass all the files instead of listToMaybe   (ex, _stdo, std, [(_, res),(_, mb_deps)]) <-     readProcessWithOutputs [hie_bios_output, hie_bios_deps] l wdir bios'    deps <- case mb_deps of     Just x  -> return x-    Nothing -> biosDepsAction l wdir bios_deps fp+    Nothing -> biosDepsAction l wdir bios_deps fp fps         -- Output from the program should be written to the output file and         -- delimited by newlines.         -- Execute the bios action and add dependencies of the cradle.@@ -458,25 +500,33 @@   return $ proc canon_path (maybeToList file)  ------------------------------------------------------------------------++projectFileProcessArgs :: CradleProjectConfig -> [String]+projectFileProcessArgs (ExplicitConfig prjFile) = ["--project-file", prjFile]+projectFileProcessArgs NoExplicitConfig = []++projectLocationOrDefault :: CradleProjectConfig -> [FilePath]+projectLocationOrDefault = \case+  NoExplicitConfig -> ["cabal.project", "cabal.project.local"]+  (ExplicitConfig prjFile) -> [prjFile, prjFile <.> "local"]+ -- |Cabal Cradle -- Works for new-build by invoking `v2-repl`.-cabalCradle :: FilePath -> Maybe String -> Cradle a-cabalCradle wdir mc =-  Cradle-    { cradleRootDir    = wdir-    , cradleOptsProg   = CradleAction-        { actionName = Types.Cabal-        , runCradle = \l f -> runCradleResultT . cabalAction wdir mc l $ f-        , runGhcCmd = \l args -> runCradleResultT $ do-            buildDir <- liftIO $ cabalBuildDir wdir-            -- Workaround for a cabal-install bug on 3.0.0.0:-            -- ./dist-newstyle/tmp/environment.-24811: createDirectory: does not exist (No such file or directory)-            liftIO $ createDirectoryIfMissing True (buildDir </> "tmp")-            -- Need to pass -v0 otherwise we get "resolving dependencies..."-            cabalProc <- cabalProcess l wdir "v2-exec" $ ["ghc", "-v0", "--"] ++ args-            readProcessWithCwd' cabalProc ""-        }+cabalCradle :: LogAction IO (WithSeverity Log) -> ResolvedCradles b -> FilePath -> Maybe String -> CradleProjectConfig -> CradleAction a+cabalCradle l cs wdir mc projectFile+  = CradleAction+    { actionName = Types.Cabal+    , runCradle = \fp -> runCradleResultT . cabalAction cs wdir mc l projectFile fp+    , runGhcCmd = \args -> runCradleResultT $ do+        buildDir <- liftIO $ cabalBuildDir wdir+        -- Workaround for a cabal-install bug on 3.0.0.0:+        -- ./dist-newstyle/tmp/environment.-24811: createDirectory: does not exist (No such file or directory)+        liftIO $ createDirectoryIfMissing True (buildDir </> "tmp")+        -- Need to pass -v0 otherwise we get "resolving dependencies..."+        cabalProc <- cabalProcess l projectFile wdir "v2-exec" $ ["ghc", "-v0", "--"] ++ args+        readProcessWithCwd' l cabalProc ""     }+      -- | Execute a cabal process in our custom cache-build directory configured -- with the custom ghc executable.@@ -487,9 +537,9 @@ -- to the custom ghc wrapper via 'hie_bios_ghc' environment variable which -- the custom ghc wrapper may use as a fallback if it can not respond to certain -- queries, such as ghc version or location of the libdir.-cabalProcess :: LogAction IO (WithSeverity Log) -> FilePath -> String -> [String] -> CradleLoadResultT IO CreateProcess-cabalProcess l workDir command args = do-  ghcDirs <- cabalGhcDirs l workDir+cabalProcess :: LogAction IO (WithSeverity Log) -> CradleProjectConfig -> FilePath -> String -> [String] -> CradleLoadResultT IO CreateProcess+cabalProcess l cabalProject workDir command args = do+  ghcDirs <- cabalGhcDirs l cabalProject workDir   newEnvironment <- liftIO $ setupEnvironment ghcDirs   cabalProc <- liftIO $ setupCabalCommand ghcDirs   pure $ (cabalProc@@ -516,8 +566,8 @@             , command             , "--with-compiler", wrapper_fp             , "--with-hc-pkg", ghcPkgPath-            ]-      loggedProc l "cabal" (extraCabalArgs ++ args)+            ] <> projectFileProcessArgs cabalProject+      pure $ proc "cabal" (extraCabalArgs ++ args)  -- | Discovers the location of 'ghc-pkg' given the absolute path to 'ghc' -- and its '$libdir' (obtainable by running @ghc --print-libdir@).@@ -578,24 +628,27 @@     -- Escape the filepath and trim excess newlines added by 'escapeArgs'     escapeFilePath fp = trimEnd $ escapeArgs [fp] --- | @'cabalCradleDependencies' rootDir componentDir@.+-- | @'cabalCradleDependencies' projectFile rootDir componentDir@. -- Compute the dependencies of the cabal cradle based--- on the cradle root and the component directory.+-- on cabal project configuration, the cradle root and the component directory. --+-- The @projectFile@ and @projectFile <> ".local"@ are always added to the list+-- of dependencies.+-- -- Directory 'componentDir' is a sub-directory where we look for -- package specific cradle dependencies, such as a '.cabal' file. -- -- Found dependencies are relative to 'rootDir'.-cabalCradleDependencies :: FilePath -> FilePath -> IO [FilePath]-cabalCradleDependencies rootDir componentDir = do+cabalCradleDependencies :: CradleProjectConfig -> FilePath -> FilePath -> IO [FilePath]+cabalCradleDependencies projectFile rootDir componentDir = do     let relFp = makeRelative rootDir componentDir     cabalFiles' <- findCabalFiles componentDir     let cabalFiles = map (relFp </>) cabalFiles'-    return $ map normalise $ cabalFiles ++ ["cabal.project", "cabal.project.local"]+    return $ map normalise $ cabalFiles ++ projectLocationOrDefault projectFile  -- |Find .cabal files in the given directory. ----- Might return multiple results, as we can not know in advance+-- Might return multiple results,biosAction as we can not know in advance -- which one is important to the user. findCabalFiles :: FilePath -> IO [FilePath] findCabalFiles wdir = do@@ -633,10 +686,12 @@         withSystemTempDirectory "hie-bios" $ \ tmpDir -> do           let wrapper_hs = wrapper_fp -<.> "hs"           writeFile wrapper_hs wrapperContents-          ghc <- loggedProc l mbGhc $-                      ghcArgs ++ ["-rtsopts=ignore", "-outputdir", tmpDir, "-o", wrapper_fp, wrapper_hs]-          let ghc' = ghc { cwd = Just wdir }-          readCreateProcess ghc' "" >>= putStr+          let ghcArgsWithExtras = ghcArgs ++ ["-rtsopts=ignore", "-outputdir", tmpDir, "-o", wrapper_fp, wrapper_hs]+          let ghcProc = (proc mbGhc ghcArgsWithExtras)+                      { cwd = Just wdir+                      }+          l <& LogCreateProcessRun ghcProc `WithSeverity` Debug+          readCreateProcess ghcProc "" >>= putStr       else writeFile wrapper_fp wrapperContents  -- | Create and cache a file in hie-bios's cache directory.@@ -681,32 +736,68 @@ -- 'withGhcWrapperTool'. -- -- If cabal can not figure it out, a 'CradleError' is returned.-cabalGhcDirs :: LogAction IO (WithSeverity Log) -> FilePath -> CradleLoadResultT IO (FilePath, FilePath)-cabalGhcDirs l workDir = do-  libdir <- readProcessWithCwd_ l workDir "cabal" ["exec", "-v0", "--", "ghc", "--print-libdir"] ""+cabalGhcDirs :: LogAction IO (WithSeverity Log) -> CradleProjectConfig -> FilePath -> CradleLoadResultT IO (FilePath, FilePath)+cabalGhcDirs l cabalProject workDir = do+  libdir <- readProcessWithCwd_ l workDir "cabal"+      (["exec"] +++       projectFileArgs +++       ["-v0", "--", "ghc", "--print-libdir"]+      )+      ""   exe <- readProcessWithCwd_ l workDir "cabal"       -- DON'T TOUCH THIS CODE       -- This works with 'NoImplicitPrelude', with 'RebindableSyntax' and other shenanigans.       -- @-package-env=-@ doesn't work with ghc prior 8.4.x-      [ "exec", "-v0", "--" , "ghc", "-package-env=-", "-ignore-dot-ghci", "-e"-      , "Control.Monad.join (Control.Monad.fmap System.IO.putStr System.Environment.getExecutablePath)"-      ]+      ([ "exec"] +++       projectFileArgs +++       [ "-v0", "--" , "ghc", "-package-env=-", "-ignore-dot-ghci", "-e"+       , "Control.Monad.join (Control.Monad.fmap System.IO.putStr System.Environment.getExecutablePath)"+       ]+      )       ""   pure (trimEnd exe, trimEnd libdir)+  where+    projectFileArgs = projectFileProcessArgs cabalProject + cabalAction-  :: FilePath+  :: ResolvedCradles a+  -> FilePath   -> Maybe String   -> LogAction IO (WithSeverity Log)+  -> CradleProjectConfig   -> FilePath+  -> [FilePath]   -> CradleLoadResultT IO ComponentOptions-cabalAction workDir mc l fp = do+cabalAction (ResolvedCradles root cs vs) workDir mc l projectFile fp fps = do   let     cabalCommand = "v2-repl"-    cabalArgs = [fromMaybe (fixTargetPath fp) mc]+    cabal_version = cabalVersion vs+    ghc_version = ghcVersion vs+    cabalArgs = case (cabal_version, ghc_version) of+      (Just cabal, Just ghc)+        -- Multi-component supported from cabal-install 3.11+        -- and ghc 9.4+        | ghc   >= makeVersion [9,4]+        , cabal >= makeVersion [3,11]+        -> case fps of+          [] -> [fromMaybe (fixTargetPath fp) mc]+          -- Start a multi-component session with all the old files+          _ -> "--keep-temp-files"+             : "--enable-multi-repl"+             : [fromMaybe (fixTargetPath fp) mc]+            ++ [fromMaybe (fixTargetPath old_fp) old_mc+               | old_fp <- fps+               -- Lookup the component for the old file+               , Just (ResolvedCradle{concreteCradle = ConcreteCabal ct}) <- [selectCradle prefix old_fp cs]+               -- Only include this file if the old component is in the same project+               , (projectConfigFromMaybe root (cabalProjectFile ct)) == projectFile+               , let old_mc = cabalComponent ct+               ]+      _ -> [fromMaybe (fixTargetPath fp) mc] -  cabalProc <- cabalProcess l workDir cabalCommand cabalArgs `modCradleError` \err -> do-      deps <- cabalCradleDependencies workDir workDir+  cabalProc <- cabalProcess l projectFile workDir cabalCommand cabalArgs `modCradleError` \err -> do+      deps <- cabalCradleDependencies projectFile workDir workDir       pure $ err { cradleErrorDependencies = cradleErrorDependencies err ++ deps }    (ex, output, stde, [(_, maybeArgs)]) <- liftIO $ readProcessWithOutputs [hie_bios_output] l workDir cabalProc@@ -721,7 +812,7 @@         <> prettyProcessEnv cabalProc    when (ex /= ExitSuccess) $ do-    deps <- liftIO $ cabalCradleDependencies workDir workDir+    deps <- liftIO $ cabalCradleDependencies projectFile 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))@@ -731,11 +822,11 @@       -- Provide some dependencies an IDE can look for to trigger a reload.       -- Best effort. Assume the working directory is the       -- root of the component, so we are right in trivial cases at least.-      deps <- liftIO $ cabalCradleDependencies workDir workDir+      deps <- liftIO $ cabalCradleDependencies projectFile workDir workDir       throwCE (CradleError deps ex $                 (["Failed to parse result of calling cabal" ] <> errorDetails))     Just (componentDir, final_args) -> do-      deps <- liftIO $ cabalCradleDependencies workDir componentDir+      deps <- liftIO $ cabalCradleDependencies projectFile workDir componentDir       CradleLoadResultT $ pure $ makeCradleResult (ex, stde, componentDir, final_args) deps   where     -- Need to make relative on Windows, due to a Cabal bug with how it@@ -786,45 +877,47 @@       findFileUpwards (== "cabal.project") wdir   <|> findFileUpwards (\fp -> takeExtension fp == ".cabal") wdir + ------------------------------------------------------------------------ --- | Explicit data-type for stack.yaml configuration location.+-- | Explicit data-type for project configuration location. -- It is basically a 'Maybe' type, but helps to document the API -- and helps to avoid incorrect usage.-data StackYaml-  = NoExplicitYaml-  | ExplicitYaml FilePath+data CradleProjectConfig+  = NoExplicitConfig+  | ExplicitConfig FilePath+  deriving Eq --- | Create an explicit StackYaml configuration from the-stackYamlFromMaybe :: FilePath -> Maybe FilePath -> StackYaml-stackYamlFromMaybe _wdir Nothing = NoExplicitYaml-stackYamlFromMaybe wdir (Just fp) = ExplicitYaml (wdir </> fp)+-- | Create an explicit project configuration. Expects a working directory+-- followed by an optional name of the project configuration.+projectConfigFromMaybe :: FilePath -> Maybe FilePath -> CradleProjectConfig+projectConfigFromMaybe _wdir Nothing = NoExplicitConfig+projectConfigFromMaybe wdir (Just fp) = ExplicitConfig (wdir </> fp) -stackYamlProcessArgs :: StackYaml -> [String]-stackYamlProcessArgs (ExplicitYaml yaml) = ["--stack-yaml", yaml]-stackYamlProcessArgs NoExplicitYaml = []+------------------------------------------------------------------------ -stackYamlLocationOrDefault :: StackYaml -> FilePath-stackYamlLocationOrDefault NoExplicitYaml = "stack.yaml"-stackYamlLocationOrDefault (ExplicitYaml yaml) = yaml+stackYamlProcessArgs :: CradleProjectConfig -> [String]+stackYamlProcessArgs (ExplicitConfig yaml) = ["--stack-yaml", yaml]+stackYamlProcessArgs NoExplicitConfig = [] +stackYamlLocationOrDefault :: CradleProjectConfig -> FilePath+stackYamlLocationOrDefault NoExplicitConfig = "stack.yaml"+stackYamlLocationOrDefault (ExplicitConfig yaml) = yaml+ -- | Stack Cradle -- Works for by invoking `stack repl` with a wrapper script-stackCradle :: FilePath -> Maybe String -> StackYaml -> Cradle a-stackCradle wdir mc syaml =-  Cradle-    { cradleRootDir    = wdir-    , cradleOptsProg   = CradleAction-        { actionName = Types.Stack-        , runCradle = stackAction wdir mc syaml-        , runGhcCmd = \l args -> runCradleResultT $ do-            -- Setup stack silently, since stack might print stuff to stdout in some cases (e.g. on Win)-            -- Issue 242 from HLS: https://github.com/haskell/haskell-language-server/issues/242-            _ <- readProcessWithCwd_ l wdir "stack" (stackYamlProcessArgs syaml <> ["setup", "--silent"]) ""-            readProcessWithCwd_ l wdir "stack"-              (stackYamlProcessArgs syaml <> ["exec", "ghc", "--"] <> args)-              ""-        }+stackCradle :: LogAction IO (WithSeverity Log) ->  FilePath -> Maybe String -> CradleProjectConfig -> CradleAction a+stackCradle l wdir mc syaml =+  CradleAction+    { actionName = Types.Stack+    , runCradle = stackAction wdir mc syaml l+    , runGhcCmd = \args -> runCradleResultT $ do+        -- Setup stack silently, since stack might print stuff to stdout in some cases (e.g. on Win)+        -- Issue 242 from HLS: https://github.com/haskell/haskell-language-server/issues/242+        _ <- readProcessWithCwd_ l wdir "stack" (stackYamlProcessArgs syaml <> ["setup", "--silent"]) ""+        readProcessWithCwd_ l wdir "stack"+          (stackYamlProcessArgs syaml <> ["exec", "ghc", "--"] <> args)+          ""     }  -- | @'stackCradleDependencies' rootDir componentDir@.@@ -836,7 +929,7 @@ -- a '.cabal' file. -- -- Found dependencies are relative to 'rootDir'.-stackCradleDependencies :: FilePath -> FilePath -> StackYaml -> IO [FilePath]+stackCradleDependencies :: FilePath -> FilePath -> CradleProjectConfig -> IO [FilePath] stackCradleDependencies wdir componentDir syaml = do   let relFp = makeRelative wdir componentDir   cabalFiles' <- findCabalFiles componentDir@@ -847,22 +940,25 @@ stackAction   :: FilePath   -> Maybe String-  -> StackYaml+  -> CradleProjectConfig   -> LogAction IO (WithSeverity Log)   -> FilePath+  -> [FilePath]   -> IO (CradleLoadResult ComponentOptions)-stackAction workDir mc syaml l _fp = do+stackAction workDir mc syaml l _fp _fps = do   let ghcProcArgs = ("stack", stackYamlProcessArgs syaml <> ["exec", "ghc", "--"])   -- Same wrapper works as with cabal   wrapper_fp <- withGhcWrapperTool l ghcProcArgs workDir   (ex1, _stdo, stde, [(_, maybeArgs)]) <--    stackProcess l syaml-                (["repl", "--no-nix-pure", "--with-ghc", wrapper_fp]-                    <> [ comp | Just comp <- [mc] ]) >>=-      readProcessWithOutputs [hie_bios_output] l workDir +    readProcessWithOutputs [hie_bios_output] l workDir+      $ stackProcess syaml+          $  ["repl", "--no-nix-pure", "--with-ghc", wrapper_fp]+          <> [ comp | Just comp <- [mc] ]+   (ex2, pkg_args, stdr, _) <--    stackProcess l syaml ["path", "--ghc-package-path"] >>=-      readProcessWithOutputs [hie_bios_output] l workDir+    readProcessWithOutputs [hie_bios_output] l workDir+      $ stackProcess syaml ["path", "--ghc-package-path"]+   let split_pkgs = concatMap splitSearchPath pkg_args       pkg_ghc_args = concatMap (\p -> ["-package-db", p] ) split_pkgs       args = fromMaybe [] maybeArgs@@ -887,8 +983,8 @@                   )                   deps -stackProcess :: LogAction IO (WithSeverity Log) -> StackYaml -> [String] -> IO CreateProcess-stackProcess l syaml args = loggedProc l "stack" $ stackYamlProcessArgs syaml <> args+stackProcess :: CradleProjectConfig -> [String] -> CreateProcess+stackProcess syaml args = proc "stack" $ stackYamlProcessArgs syaml <> args  combineExitCodes :: [ExitCode] -> ExitCode combineExitCodes = foldr go ExitSuccess@@ -1042,6 +1138,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 (\msg -> l <& LogProcessOutput msg `WithSeverity` Debug) C..| C.sinkList+  liftIO $ l <& LogCreateProcessRun process `WithSeverity` Info   (ex, stdo, stde) <- liftIO $ sourceProcessWithStreams process mempty loggingConduit loggingConduit    res <- forM output_files $ \(name,path) ->@@ -1099,15 +1196,15 @@ readProcessWithCwd_ :: LogAction IO (WithSeverity Log) -> FilePath -> FilePath -> [String] -> String -> CradleLoadResultT IO String readProcessWithCwd_ l dir cmd args stdin = do   cleanEnv <- liftIO getCleanEnvironment-  createdProc <- liftIO $ loggedProc l cmd args-  let createdProc' = createdProc { cwd = Just dir, env = Just cleanEnv }-  readProcessWithCwd' createdProc' stdin+  let createdProc' = (proc cmd args) { cwd = Just dir, env = Just cleanEnv }+  readProcessWithCwd' l createdProc' stdin  -- | Wrapper around 'readCreateProcessWithExitCode', wrapping the result in -- a 'CradleLoadResult'. Provides better error messages than raw 'readCreateProcess'.-readProcessWithCwd' :: CreateProcess -> String -> CradleLoadResultT IO String-readProcessWithCwd' createdProcess stdin = do+readProcessWithCwd' :: LogAction IO (WithSeverity Log) -> CreateProcess -> String -> CradleLoadResultT IO String+readProcessWithCwd' l createdProcess stdin = do   mResult <- liftIO $ optional $ readCreateProcessWithExitCode createdProcess stdin+  liftIO $ l <& LogCreateProcessRun createdProcess `WithSeverity` Debug   let cmdString = prettyCmdSpec $ cmdspec createdProcess   case mResult of     Just (ExitSuccess, stdo, _) -> pure stdo@@ -1117,27 +1214,3 @@     Nothing -> throwCE $       CradleError [] ExitSuccess $         ["Couldn't execute " <> cmdString] <> prettyProcessEnv createdProcess---- | Prettify 'CmdSpec', so we can show the command to a user-prettyCmdSpec :: CmdSpec -> String-prettyCmdSpec (ShellCommand s) = s-prettyCmdSpec (RawCommand cmd args) = cmd ++ " " ++ unwords args---- | Pretty print hie-bios's relevant environment variables.-prettyProcessEnv :: CreateProcess -> [String]-prettyProcessEnv p =-  [ key <> ": " <> value-  | (key, value) <- fromMaybe [] (env p)-  , key `elem` [ hie_bios_output-               , hie_bios_ghc-               , hie_bios_ghc_args-               , hie_bios_arg-               , hie_bios_deps-               ]-  ]--loggedProc :: LogAction IO (WithSeverity Log) -> FilePath -> [String] -> IO CreateProcess-loggedProc l command args = do-  l <& LogProcessOutput (unwords $ "executing command:":command:args) `WithSeverity` Debug-  pure $ proc command args- 
src/HIE/Bios/Environment.hs view
@@ -68,20 +68,18 @@ -- -- -- Obtains libdir by calling 'runCradleGhc' on the provided cradle.-getRuntimeGhcLibDir :: LogAction IO (WithSeverity Log)-                    -> Cradle a+getRuntimeGhcLibDir :: Cradle a                     -> IO (CradleLoadResult FilePath)-getRuntimeGhcLibDir l cradle = fmap (fmap trim) $-      runGhcCmd (cradleOptsProg cradle) l ["--print-libdir"]+getRuntimeGhcLibDir cradle = fmap (fmap trim) $+      runGhcCmd (cradleOptsProg cradle) ["--print-libdir"]  -- | Gets the version of ghc used when compiling the cradle. It is based off of -- 'getRuntimeGhcLibDir'. If it can't work out the verison reliably, it will -- return a 'CradleError'-getRuntimeGhcVersion :: LogAction IO (WithSeverity Log)-                     -> Cradle a+getRuntimeGhcVersion :: Cradle a                      -> IO (CradleLoadResult String)-getRuntimeGhcVersion l cradle =-  fmap (fmap trim) $ runGhcCmd (cradleOptsProg cradle) l ["--numeric-version"]+getRuntimeGhcVersion cradle =+  fmap (fmap trim) $ runGhcCmd (cradleOptsProg cradle) ["--numeric-version"]  ---------------------------------------------------------------- 
src/HIE/Bios/Flags.hs view
@@ -7,10 +7,10 @@ -- | Initialize the 'DynFlags' relating to the compilation of a single -- file or GHC session according to the provided 'Cradle'. getCompilerOptions-  :: LogAction IO (WithSeverity Log)-  -> FilePath -- The file we are loading it because of+  :: FilePath -- ^ The file we are loading it because of+  -> [FilePath] -- ^ previous files we might want to include in the build   -> Cradle a   -> IO (CradleLoadResult ComponentOptions)-getCompilerOptions l fp cradle = do-  l <& LogProcessOutput "invoking build tool to determine build flags (this may take some time depending on the cache)" `WithSeverity` Info-  runCradle (cradleOptsProg cradle) l fp+getCompilerOptions fp fps cradle = do+  (cradleLogger cradle) <& LogProcessOutput "invoking build tool to determine build flags (this may take some time depending on the cache)" `WithSeverity` Info+  runCradle (cradleOptsProg cradle) fp fps
src/HIE/Bios/Ghc/Api.hs view
@@ -33,24 +33,22 @@ -- | Initialize a GHC session by loading a given file into a given cradle. initializeFlagsWithCradle ::     GhcMonad m-    => LogAction IO (WithSeverity Log)-    -> FilePath -- ^ The file we are loading the 'Cradle' because of+    => 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 l = initializeFlagsWithCradleWithMessage l (Just Gap.batchMsg)+initializeFlagsWithCradle = initializeFlagsWithCradleWithMessage (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-  => LogAction IO (WithSeverity Log)-  -> Maybe G.Messager+  => 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 l msg fp cradle =-    fmap (initSessionWithMessage msg) <$> liftIO (getCompilerOptions l fp cradle)+initializeFlagsWithCradleWithMessage msg fp cradle =+    fmap (initSessionWithMessage msg) <$> liftIO (getCompilerOptions 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
@@ -46,18 +46,17 @@ -- | Checking syntax of a target file using GHC. --   Warnings and errors are returned. checkSyntax :: Show a-            => LogAction IO (WithSeverity T.Log)-            -> LogAction IO (WithSeverity Log)+            => LogAction IO (WithSeverity Log)             -> Cradle a             -> [FilePath]  -- ^ The target files.             -> IO String-checkSyntax _       _          _      []    = return ""-checkSyntax logger checkLogger cradle files = do-    libDirRes <- getRuntimeGhcLibDir logger cradle+checkSyntax _           _      []    = return ""+checkSyntax checkLogger cradle files = do+    libDirRes <- getRuntimeGhcLibDir cradle     handleRes libDirRes $ \libDir ->       G.runGhcT (Just libDir) $ do         liftIO $ checkLogger <& LogCradle cradle `WithSeverity` Info-        res <- initializeFlagsWithCradle (cmap (fmap LogAny) checkLogger) (head files) cradle+        res <- initializeFlagsWithCradle (head files) cradle         handleRes res $ \(ini, _) -> do           _sf <- ini           either id id <$> check checkLogger files
src/HIE/Bios/Ghc/Gap.hs view
@@ -158,11 +158,6 @@ import qualified GhcMake as G #endif -#if __GLASGOW_HASKELL__ >= 907-import GHC.Types.Error (mkUnknownDiagnostic, Messages)-import GHC.Driver.Errors.Types (DriverMessage)-#endif- ghcVersion :: String ghcVersion = VERSION_ghc @@ -174,10 +169,7 @@ homeUnitId_ = homeUnitId #endif -#if __GLASGOW_HASKELL__ >= 907-load' :: GhcMonad m => Maybe G.ModIfaceCache -> LoadHowMuch -> Maybe Messager -> ModuleGraph -> m SuccessFlag-load' mhmi_cache how_much = G.load' mhmi_cache how_much mkUnknownDiagnostic-#elif __GLASGOW_HASKELL__ >= 904+#if __GLASGOW_HASKELL__ >= 904 load' :: GhcMonad m => Maybe G.ModIfaceCache -> LoadHowMuch -> Maybe Messager -> ModuleGraph -> m SuccessFlag load' = G.load' #else@@ -482,12 +474,7 @@     => Logger     -> DynFlags     -> [G.Located String]-    -> m (DynFlags, [G.Located String]-#if __GLASGOW_HASKELL__ >= 907-          , Messages DriverMessage)-#else-          , [CmdLine.Warn])-#endif+    -> m (DynFlags, [G.Located String], [CmdLine.Warn]) #if __GLASGOW_HASKELL__ >= 902 parseDynamicFlags = G.parseDynamicFlags #else
src/HIE/Bios/Internal/Debug.hs view
@@ -25,17 +25,17 @@ -- -- Otherwise, shows the error message and exit-code. debugInfo :: Show a-          => LogAction IO (WithSeverity Log)-          -> FilePath+          => FilePath           -> Cradle a           -> IO String-debugInfo logger fp cradle = unlines <$> do-    res <- getCompilerOptions logger fp cradle+debugInfo fp cradle = unlines <$> do+    let logger = cradleLogger cradle+    res <- getCompilerOptions fp [] cradle     canonFp <- canonicalizePath fp     conf <- findConfig canonFp-    crdl <- findCradle' canonFp-    ghcLibDir <- getRuntimeGhcLibDir logger cradle-    ghcVer <- getRuntimeGhcVersion logger cradle+    crdl <- findCradle' logger canonFp+    ghcLibDir <- getRuntimeGhcLibDir cradle+    ghcVer <- getRuntimeGhcVersion cradle     case res of       CradleSuccess (ComponentOptions gopts croot deps) -> do         return [@@ -84,19 +84,19 @@  ---------------------------------------------------------------- -cradleInfo :: [FilePath] -> IO String-cradleInfo [] = return "No files given"-cradleInfo args =+cradleInfo :: LogAction IO (WithSeverity Log) -> [FilePath] -> IO String+cradleInfo _ [] = return "No files given"+cradleInfo l args =   fmap unlines $ forM args $ \fp -> do     fp' <- canonicalizePath fp-    (("Cradle for \"" ++ fp' ++ "\": ") ++)  <$> findCradle' fp'+    (("Cradle for \"" ++ fp' ++ "\": ") ++)  <$> findCradle' l fp' -findCradle' :: FilePath -> IO String-findCradle' fp =+findCradle' :: LogAction IO (WithSeverity Log) -> FilePath -> IO String+findCradle' l fp =   findCradle fp >>= \case     Just yaml -> do-      crdl <- loadCradle yaml+      crdl <- loadCradle l yaml       return $ show crdl     Nothing -> do-      crdl <- loadImplicitCradle fp :: IO (Cradle Void)+      crdl <- loadImplicitCradle l fp :: IO (Cradle Void)       return $ show crdl
src/HIE/Bios/Types.hs view
@@ -2,22 +2,57 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE OverloadedStrings #-} 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 import           Control.Monad.Trans.Class #if MIN_VERSION_base(4,9,0) import qualified Control.Monad.Fail as Fail #endif import Data.Text.Prettyprint.Doc+import System.Process.Extra (CreateProcess (env, cmdspec), CmdSpec (..))+import Data.Maybe (fromMaybe) +----------------------------------------------------------------+-- Environment variables used by hie-bios.+--+-- If you need more, add a constant here.+---------------------------------------------------------------- -data BIOSVerbosity = Silent | Verbose+-- | Environment variable containing the filepath to which+-- cradle actions write their results to.+-- If the filepath does not exist, cradle actions must create them.+hie_bios_output :: String+hie_bios_output = "HIE_BIOS_OUTPUT" +-- | Environment variable pointing to the GHC location used by+-- cabal's and stack's GHC wrapper.+--+-- If not set, will default to sensible defaults.+hie_bios_ghc :: String+hie_bios_ghc = "HIE_BIOS_GHC"++-- | Environment variable with extra arguments passed to the GHC location+-- in cabal's and stack's GHC wrapper.+--+-- If not set, assume no extra arguments.+hie_bios_ghc_args :: String+hie_bios_ghc_args = "HIE_BIOS_GHC_ARGS"++-- | Environment variable pointing to the source file location that caused+-- the cradle action to be executed.+hie_bios_arg :: String+hie_bios_arg = "HIE_BIOS_ARG"++-- | Environment variable pointing to a filepath to which dependencies+-- of a cradle can be written to by the cradle action.+hie_bios_deps :: String+hie_bios_deps = "HIE_BIOS_DEPS"+ ----------------------------------------------------------------  -- | The environment of a single 'Cradle'.@@ -35,8 +70,15 @@   -- | The action which needs to be executed to get the correct   -- command line arguments.   , cradleOptsProg   :: CradleAction a-  } deriving (Show, Functor)+  , cradleLogger  :: L.LogAction IO (L.WithSeverity Log)+  } deriving (Functor) +instance Show a => Show (Cradle a) where+  show (Cradle root prog _)+    = "Cradle{ cradleRootDir = " ++ show root+          ++", cradleOptsProg = " ++ show prog+          ++"}"+ data ActionName a   = Stack   | Cabal@@ -48,21 +90,39 @@   | Other a   deriving (Show, Eq, Ord, Functor) -data Log =-  LogAny String+data Log +  = LogAny String   | LogProcessOutput String+  | LogCreateProcessRun CreateProcess+  | LogProcessRun FilePath [FilePath]   deriving Show  instance Pretty Log where   pretty (LogAny s) = pretty s   pretty (LogProcessOutput s) = pretty s+  pretty (LogProcessRun fp args) = pretty fp <+> pretty (unwords args)+  pretty (LogCreateProcessRun cp) =   +    vcat $ +      [ case cmdspec cp of+          ShellCommand sh -> pretty sh+          RawCommand cmd args -> pretty cmd <+> pretty (unwords args)+      ]+      <>+      if null envText+        then []+        else+          [ indent 2 $ "Environment Variables"+          , indent 2 $ vcat envText+          ]+    where+      envText = map (indent 2 . pretty) $ prettyProcessEnv cp  data CradleAction a = CradleAction {                         actionName    :: ActionName a                       -- ^ Name of the action.-                      , runCradle     :: L.LogAction IO (L.WithSeverity Log) -> FilePath -> IO (CradleLoadResult ComponentOptions)+                      , runCradle     :: FilePath -> [FilePath] -> IO (CradleLoadResult ComponentOptions)                       -- ^ Options to compile the given file with.-                      , runGhcCmd     :: L.LogAction IO (L.WithSeverity Log) -> [String] -> IO (CradleLoadResult String)+                      , runGhcCmd     :: [String] -> IO (CradleLoadResult String)                       -- ^ Executes the @ghc@ binary that is usually used to                       -- build the cradle. E.g. for a cabal cradle this should be                       -- equivalent to @cabal exec ghc -- args@@@ -191,3 +251,24 @@   -- changes the options that a Cradle may return, thus, needs reload   -- as soon as these files are created.   } deriving (Eq, Ord, Show)+++-- ------------------------------------------------++-- | Prettify 'CmdSpec', so we can show the command to a user+prettyCmdSpec :: CmdSpec -> String+prettyCmdSpec (ShellCommand s) = s+prettyCmdSpec (RawCommand cmd args) = cmd ++ " " ++ unwords args++-- | Pretty print hie-bios's relevant environment variables.+prettyProcessEnv :: CreateProcess -> [String]+prettyProcessEnv p =+  [ key <> ": " <> value+  | (key, value) <- fromMaybe [] (env p)+  , key `elem` [ hie_bios_output+               , hie_bios_ghc+               , hie_bios_ghc_args+               , hie_bios_arg+               , hie_bios_deps+               ]+  ]
tests/BiosTests.hs view
@@ -196,6 +196,36 @@         loadRuntimeGhcVersion         assertGhcVersionIs extraGhcVersion     ]+  , testGroup "Cabal cabalProject"+    [ testCaseSteps "cabal-with-project, options propagated" $ runTestEnv "cabal-with-project" $ do+        opts <- cabalLoadOptions "src/MyLib.hs"+        liftIO $ do+          "-O2" `elem` componentOptions opts+            @? "Options must contain '-O2'"+    , testCaseSteps "cabal-with-project, load" $ runTestEnv "cabal-with-project" $ do+        testDirectoryM isCabalCradle "src/MyLib.hs"+    , testCaseSteps "multi-cabal-with-project, options propagated" $ runTestEnv "multi-cabal-with-project" $ do+        optsAppA <- cabalLoadOptions "appA/src/Lib.hs"+        liftIO $ do+          "-O2" `elem` componentOptions optsAppA+            @? "Options must contain '-O2'"+        optsAppB <- cabalLoadOptions "appB/src/Lib.hs"+        liftIO $ do+          "-O2" `notElem` componentOptions optsAppB+            @? "Options must not contain '-O2'"+    , testCaseSteps "multi-cabal-with-project, load" $ runTestEnv "multi-cabal-with-project" $ do+        testDirectoryM isCabalCradle "appB/src/Lib.hs"+        testDirectoryM isCabalCradle "appB/src/Lib.hs"+    , testGroupWithDependency extraGhcDep+      [ testCaseSteps "Honours extra ghc setting" $ runTestEnv "cabal-with-ghc-and-project" $ do+          initCradle "src/MyLib.hs"+          assertCradle isCabalCradle+          loadRuntimeGhcLibDir+          assertLibDirVersionIs extraGhcVersion+          loadRuntimeGhcVersion+          assertGhcVersionIs extraGhcVersion+      ]+    ]   ]   where     cabalAttemptLoad :: FilePath -> TestM ()@@ -204,6 +234,13 @@       assertCradle isCabalCradle       loadComponentOptions fp +    cabalLoadOptions :: FilePath -> TestM ComponentOptions+    cabalLoadOptions fp = do+      initCradle fp+      assertCradle isCabalCradle+      loadComponentOptions fp+      assertLoadSuccess+ stackTestCases :: [TestTree] stackTestCases =   [ expectFailBecause "stack repl does not fail on an invalid cabal file" $@@ -303,10 +340,12 @@  stackYamlResolver :: String stackYamlResolver =-#if (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,4,4,0)))-  "nightly-2023-03-13" -- GHC 9.4.4+#if (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,6,2,0)))+  "nightly-2023-08-22" -- GHC 9.6.2+#elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,4,4,0)))+  "lts-21.8" -- GHC 9.4.6 #elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,2,7,0)))-  "lts-20.14" -- GHC 9.2.7+  "lts-20.26" -- GHC 9.2.8 #elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,2,1,0)))   "lts-20.11" -- GHC 9.2.5 #elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,0,1,0)))
tests/ParserTests.hs view
@@ -26,7 +26,7 @@ main :: IO () main = defaultMain $   testCase "Parser Tests" $ do-    assertParser "cabal-1.yaml" (noDeps (Cabal $ CabalType (Just "lib:hie-bios")))+    assertParser "cabal-1.yaml" (noDeps (Cabal $ CabalType (Just "lib:hie-bios") Nothing))     assertParser "stack-config.yaml" (noDeps (Stack $ StackType Nothing Nothing))     --assertParser "bazel.yaml" (noDeps Bazel)     assertParser "bios-1.yaml" (noDeps (Bios (Program "program") Nothing Nothing))@@ -34,16 +34,16 @@     assertParser "bios-3.yaml" (noDeps (Bios (Command "shellcommand") Nothing Nothing))     assertParser "bios-4.yaml" (noDeps (Bios (Command "shellcommand") (Just (Command "dep-shellcommand")) Nothing))     assertParser "bios-5.yaml" (noDeps (Bios (Command "shellcommand") (Just (Program "dep-program")) Nothing))-    assertParser "dependencies.yaml" (Config (CradleConfig ["depFile"] (Cabal $ CabalType (Just "lib:hie-bios"))))+    assertParser "dependencies.yaml" (Config (CradleConfig ["depFile"] (Cabal $ CabalType (Just "lib:hie-bios") Nothing)))     assertParser "direct.yaml" (noDeps (Direct ["list", "of", "arguments"]))     assertParser "none.yaml" (noDeps None)     --assertParser "obelisk.yaml" (noDeps Obelisk)-    assertParser "multi.yaml" (noDeps (Multi [("./src", CradleConfig [] (Cabal $ CabalType (Just "lib:hie-bios")))-                                             ,("./test", CradleConfig [] (Cabal $ CabalType (Just "test")) ) ]))+    assertParser "multi.yaml" (noDeps (Multi [("./src", CradleConfig [] (Cabal $ CabalType (Just "lib:hie-bios") Nothing))+                                             ,("./test", CradleConfig [] (Cabal $ CabalType (Just "test") Nothing))])) -    assertParser "cabal-multi.yaml" (noDeps (CabalMulti (CabalType Nothing)-                                                        [("./src", CabalType $ Just "lib:hie-bios")-                                                        ,("./", CabalType $ Just "lib:hie-bios")]))+    assertParser "cabal-multi.yaml" (noDeps (CabalMulti (CabalType Nothing Nothing)+                                                        [("./src", CabalType (Just "lib:hie-bios") Nothing)+                                                        ,("./", CabalType (Just "lib:hie-bios") Nothing)]))      assertParser "stack-multi.yaml" (noDeps (StackMulti (StackType Nothing Nothing)                                                         [("./src", StackType (Just "lib:hie-bios") Nothing)@@ -51,15 +51,25 @@      assertParser "nested-cabal-multi.yaml" (noDeps (Multi [("./test/testdata", CradleConfig [] None)                                                           ,("./", CradleConfig [] (-                                                                    CabalMulti (CabalType Nothing)-                                                                               [("./src", CabalType $ Just "lib:hie-bios")-                                                                               ,("./tests", CabalType $ Just "parser-tests")]))]))+                                                                    CabalMulti (CabalType Nothing Nothing)+                                                                               [("./src", CabalType (Just "lib:hie-bios") Nothing)+                                                                               ,("./tests", CabalType (Just "parser-tests") Nothing)]))]))      assertParser "nested-stack-multi.yaml" (noDeps (Multi [("./test/testdata", CradleConfig [] None)                                                           ,("./", CradleConfig [] (                                                                     StackMulti (StackType Nothing Nothing)                                                                                [("./src", StackType (Just "lib:hie-bios") Nothing)                                                                                ,("./tests", StackType (Just "parser-tests") Nothing)]))]))+    -- Assertions for cabal.project files+    assertParser "cabal-with-project.yaml"+      (noDeps (Cabal $ CabalType Nothing (Just "cabal.project.8.10.7")))+    assertParser "cabal-with-both.yaml"+      (noDeps (Cabal $ CabalType (Just "hie-bios:hie") (Just "cabal.project.8.10.7")))+    assertParser "multi-cabal-with-project.yaml"+      (noDeps (CabalMulti (CabalType Nothing (Just "cabal.project.8.10.7"))+                          [("./src", CabalType (Just "lib:hie-bios") Nothing)+                          ,("./vendor", CabalType (Just "parser-tests") Nothing)]))+    -- Assertions for stack.yaml files     assertParser "stack-with-yaml.yaml"       (noDeps (Stack $ StackType Nothing (Just "stack-8.8.3.yaml")))     assertParser "stack-with-both.yaml"@@ -77,11 +87,11 @@       (noDeps (Multi         [ ("./src", CradleConfig [] (Other CabalHelperStack $ simpleCabalHelperYaml "stack"))         , ("./input", CradleConfig [] (Other CabalHelperCabal $ simpleCabalHelperYaml "cabal"))-        , ("./test", CradleConfig [] (Cabal $ CabalType (Just "test")))+        , ("./test", CradleConfig [] (Cabal $ CabalType (Just "test") Nothing))         , (".", CradleConfig [] None)         ]))     assertParserFails "keys-not-unique-fails.yaml" invalidYamlException-    assertParser "cabal-empty-config.yaml" (noDeps (Cabal $ CabalType Nothing))+    assertParser "cabal-empty-config.yaml" (noDeps (Cabal $ CabalType Nothing Nothing))  assertParser :: FilePath -> Config Void -> Assertion assertParser fp cc = do@@ -104,7 +114,7 @@                           , "Expected: " ++ show cc                           , "Actual: " ++ show conf ]) -noDeps :: CradleType a -> Config a+noDeps :: CradleTree a -> Config a noDeps c = Config (CradleConfig [] c)  shouldThrow :: (HasCallStack, Exception e) => IO a -> Selector e -> Assertion
tests/Utils.hs view
@@ -253,15 +253,15 @@   relMcfg <- traverse relFile mcfg   step $ "Loading Cradle: " <> show relMcfg   crd <- case mcfg of-    Just cfg -> liftIO $ loadCradle cfg-    Nothing -> liftIO $ loadImplicitCradle a_fp+    Just cfg -> liftIO $ loadCradle testLogger cfg+    Nothing -> liftIO $ loadImplicitCradle testLogger a_fp   setCradle crd  initImplicitCradle :: FilePath -> TestM () initImplicitCradle fp = do   a_fp <- normFile fp   step $ "Loading implicit Cradle for: " <> fp-  crd <- liftIO $ loadImplicitCradle a_fp+  crd <- liftIO $ loadImplicitCradle testLogger a_fp   setCradle crd  loadComponentOptions :: FilePath -> TestM ()@@ -269,21 +269,21 @@   a_fp <- normFile fp   crd <- askCradle   step $ "Initialise flags for: " <> fp-  clr <- liftIO $ getCompilerOptions mempty a_fp crd+  clr <- liftIO $ getCompilerOptions a_fp [] crd   setLoadResult clr  loadRuntimeGhcLibDir :: TestM () loadRuntimeGhcLibDir = do   crd <- askCradle   step "Load run-time ghc libdir"-  libdirRes <- liftIO $ getRuntimeGhcLibDir testLogger crd+  libdirRes <- liftIO $ getRuntimeGhcLibDir crd   setLibDirResult libdirRes  loadRuntimeGhcVersion :: TestM () loadRuntimeGhcVersion = do   crd <- askCradle   step "Load run-time ghc version"-  ghcVersionRes <- liftIO $ getRuntimeGhcVersion testLogger crd+  ghcVersionRes <- liftIO $ getRuntimeGhcVersion crd   setGhcVersionResult ghcVersionRes  testLogger :: forall a . Pretty a => L.LogAction IO (L.WithSeverity a)@@ -325,7 +325,7 @@ assertCradle :: (Cradle Void -> Bool) -> TestM () assertCradle cradlePred = do   crd <- askCradle-  liftIO $ cradlePred crd @? "Must be the correct kind of cradle"+  liftIO $ cradlePred crd @? "Must be the correct kind of cradle, got " ++ show (actionName $ cradleOptsProg crd)  assertLibDirVersion :: TestM () assertLibDirVersion = assertLibDirVersionIs VERSION_ghc
+ tests/configs/cabal-with-both.yaml view
@@ -0,0 +1,4 @@+cradle:+  cabal:+    cabalProject: "cabal.project.8.10.7"+    component: "hie-bios:hie"
+ tests/configs/cabal-with-project.yaml view
@@ -0,0 +1,3 @@+cradle:+  cabal:+    cabalProject: "cabal.project.8.10.7"
+ tests/configs/multi-cabal-with-project.yaml view
@@ -0,0 +1,8 @@+cradle:+  cabal:+    cabalProject: "cabal.project.8.10.7"+    components:+      - path: "./src"+        component: "lib:hie-bios"+      - path: "./vendor"+        component: "parser-tests"
+ tests/projects/cabal-with-ghc-and-project/cabal-with-ghc.cabal view
@@ -0,0 +1,9 @@+cabal-version:      2.4+name:               cabal-with-ghc+version:            0.1.0.0++library+    exposed-modules:  MyLib+    build-depends:    base+    hs-source-dirs:   src+    default-language: Haskell2010
+ tests/projects/cabal-with-ghc-and-project/cabal.project.8.10.7 view
@@ -0,0 +1,12 @@+packages: .++-- It is intended that this ghc version is different to at least one ghc version+-- that is tested in CI.+--+-- Project validates that hie-bios honours this field.+--+-- If you change this, make sure to also change the 'extraGhc' constant in+-- 'tests/BiosTests.hs'.+--+-- Additionally, CI needs a proper setup to execute this test-case.+with-compiler: ghc-8.10.7
+ tests/projects/cabal-with-ghc-and-project/hie.yaml view
@@ -0,0 +1,3 @@+cradle:+  cabal:+    cabalProject: cabal.project.8.10.7
+ tests/projects/cabal-with-ghc-and-project/src/MyLib.hs view
@@ -0,0 +1,4 @@+module MyLib (someFunc) where++someFunc :: IO ()+someFunc = putStrLn "someFunc"
+ tests/projects/cabal-with-project/cabal-with-project.cabal view
@@ -0,0 +1,9 @@+cabal-version:      2.4+name:               cabal-with-project+version:            0.1.0.0++library+    exposed-modules:  MyLib+    build-depends:    base+    hs-source-dirs:   src+    default-language: Haskell2010
+ tests/projects/cabal-with-project/cabal.project.8.10.7 view
@@ -0,0 +1,5 @@+-- Test file.+packages: ./++package cabal-with-project+    ghc-options: -O2
+ tests/projects/cabal-with-project/hie.yaml view
@@ -0,0 +1,3 @@+cradle:+  cabal:+    cabalProject: cabal.project.8.10.7
+ tests/projects/cabal-with-project/src/MyLib.hs view
@@ -0,0 +1,4 @@+module MyLib (someFunc) where++someFunc :: IO ()+someFunc = putStrLn "someFunc"
+ tests/projects/multi-cabal-with-project/appA/appA.cabal view
@@ -0,0 +1,10 @@+cabal-version:       2.0+name:                appA+version:             0.1.0.0+build-type:          Simple++library+  exposed-modules: Lib+  build-depends:       base >=4.10 && < 5, filepath+  hs-source-dirs:      src+  default-language:    Haskell2010
+ tests/projects/multi-cabal-with-project/appA/src/Lib.hs view
@@ -0,0 +1,1 @@+module Lib where
+ tests/projects/multi-cabal-with-project/appB/appB.cabal view
@@ -0,0 +1,12 @@+cabal-version:       2.0+name:                appB+version:             0.1.0.0+build-type:          Simple++library+  exposed-modules: Lib+  -- other-modules:+  -- other-extensions:+  build-depends:       base >=4.10 && < 5, filepath+  hs-source-dirs:      src+  default-language:    Haskell2010
+ tests/projects/multi-cabal-with-project/appB/src/Lib.hs view
@@ -0,0 +1,1 @@+module Lib where
+ tests/projects/multi-cabal-with-project/cabal.project.8.10.7 view
@@ -0,0 +1,6 @@+packages: ./appA ./appB++-- Only appA gets the config+package appA+    ghc-options: -O2+
+ tests/projects/multi-cabal-with-project/hie.yaml view
@@ -0,0 +1,16 @@+cradle:+  multi:+    - path: "appA"+      config:+        cradle:+          cabal:+            - path: appA/src+              component: appA:lib+              cabalProject: cabal.project.8.10.7+    - path: "appB"+      config:+        cradle:+          cabal:+            - path: appB/src+              component: appB:lib+              cabalProject: cabal.project.8.10.7