hie-bios 0.14.0 → 0.15.0
raw patch · 29 files changed
+420/−196 lines, 29 filesdep ~optparse-applicativedep ~template-haskelldep ~textPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: optparse-applicative, template-haskell, text
API changes (from Hackage documentation)
- HIE.Bios.Config.YAML: [$sel:arguments:DirectConfig] :: DirectConfig -> [String]
- HIE.Bios.Config.YAML: [$sel:cabalComponent:CabalComponent] :: CabalComponent -> String
- HIE.Bios.Config.YAML: [$sel:cabalComponentProject:CabalComponent] :: CabalComponent -> Maybe FilePath
- HIE.Bios.Config.YAML: [$sel:cabalComponents:CabalConfig] :: CabalConfig -> OneOrManyComponents CabalComponent
- HIE.Bios.Config.YAML: [$sel:cabalPath:CabalComponent] :: CabalComponent -> FilePath
- HIE.Bios.Config.YAML: [$sel:cabalProject:CabalConfig] :: CabalConfig -> Maybe FilePath
- HIE.Bios.Config.YAML: [$sel:callable:BiosConfig] :: BiosConfig -> Callable
- HIE.Bios.Config.YAML: [$sel:config:MultiSubComponent] :: MultiSubComponent a -> CradleConfigYAML a
- HIE.Bios.Config.YAML: [$sel:cradle:CradleConfigYAML] :: CradleConfigYAML a -> CradleComponent a
- HIE.Bios.Config.YAML: [$sel:dependencies:CradleConfigYAML] :: CradleConfigYAML a -> Maybe [FilePath]
- HIE.Bios.Config.YAML: [$sel:depsCallable:BiosConfig] :: BiosConfig -> Maybe Callable
- HIE.Bios.Config.YAML: [$sel:ghcPath:BiosConfig] :: BiosConfig -> Maybe FilePath
- HIE.Bios.Config.YAML: [$sel:originalYamlValue:OtherConfig] :: OtherConfig a -> Value
- HIE.Bios.Config.YAML: [$sel:otherConfig:OtherConfig] :: OtherConfig a -> a
- HIE.Bios.Config.YAML: [$sel:path:MultiSubComponent] :: MultiSubComponent a -> FilePath
- HIE.Bios.Config.YAML: [$sel:stackComponent:StackComponent] :: StackComponent -> String
- HIE.Bios.Config.YAML: [$sel:stackComponentYAML:StackComponent] :: StackComponent -> Maybe FilePath
- HIE.Bios.Config.YAML: [$sel:stackComponents:StackConfig] :: StackConfig -> OneOrManyComponents StackComponent
- HIE.Bios.Config.YAML: [$sel:stackPath:StackComponent] :: StackComponent -> FilePath
- HIE.Bios.Config.YAML: [$sel:stackYaml:StackConfig] :: StackConfig -> Maybe FilePath
+ HIE.Bios: [cradleErrorLoadingFiles] :: CradleError -> [FilePath]
+ HIE.Bios.Config.YAML: [arguments] :: DirectConfig -> [String]
+ HIE.Bios.Config.YAML: [cabalComponentProject] :: CabalComponent -> Maybe FilePath
+ HIE.Bios.Config.YAML: [cabalComponent] :: CabalComponent -> String
+ HIE.Bios.Config.YAML: [cabalComponents] :: CabalConfig -> OneOrManyComponents CabalComponent
+ HIE.Bios.Config.YAML: [cabalPath] :: CabalComponent -> FilePath
+ HIE.Bios.Config.YAML: [cabalProject] :: CabalConfig -> Maybe FilePath
+ HIE.Bios.Config.YAML: [callable] :: BiosConfig -> Callable
+ HIE.Bios.Config.YAML: [config] :: MultiSubComponent a -> CradleConfigYAML a
+ HIE.Bios.Config.YAML: [cradle] :: CradleConfigYAML a -> CradleComponent a
+ HIE.Bios.Config.YAML: [dependencies] :: CradleConfigYAML a -> Maybe [FilePath]
+ HIE.Bios.Config.YAML: [depsCallable] :: BiosConfig -> Maybe Callable
+ HIE.Bios.Config.YAML: [ghcPath] :: BiosConfig -> Maybe FilePath
+ HIE.Bios.Config.YAML: [originalYamlValue] :: OtherConfig a -> Value
+ HIE.Bios.Config.YAML: [otherConfig] :: OtherConfig a -> a
+ HIE.Bios.Config.YAML: [path] :: MultiSubComponent a -> FilePath
+ HIE.Bios.Config.YAML: [stackComponentYAML] :: StackComponent -> Maybe FilePath
+ HIE.Bios.Config.YAML: [stackComponent] :: StackComponent -> String
+ HIE.Bios.Config.YAML: [stackComponents] :: StackConfig -> OneOrManyComponents StackComponent
+ HIE.Bios.Config.YAML: [stackPath] :: StackComponent -> FilePath
+ HIE.Bios.Config.YAML: [stackYaml] :: StackConfig -> Maybe FilePath
+ HIE.Bios.Cradle: data ProgramVersions
+ HIE.Bios.Cradle: instance GHC.Show.Show HIE.Bios.Cradle.CradleProjectConfig
+ HIE.Bios.Cradle: isCabalMultipleCompSupported :: MonadIO m => ProgramVersions -> m Bool
+ HIE.Bios.Cradle: makeVersions :: LogAction IO (WithSeverity Log) -> FilePath -> ([String] -> IO (CradleLoadResult String)) -> IO ProgramVersions
+ HIE.Bios.Types: LogCabalLoad :: !FilePath -> !Maybe String -> ![FilePath] -> ![FilePath] -> Log
+ HIE.Bios.Types: LogCabalPath :: !Text -> Log
+ HIE.Bios.Types: [cradleErrorLoadingFiles] :: CradleError -> [FilePath]
- HIE.Bios: CradleError :: [FilePath] -> ExitCode -> [String] -> CradleError
+ HIE.Bios: CradleError :: [FilePath] -> ExitCode -> [String] -> [FilePath] -> CradleError
- HIE.Bios.Cradle: makeCradleResult :: (ExitCode, [String], FilePath, [String]) -> [FilePath] -> CradleLoadResult ComponentOptions
+ HIE.Bios.Cradle: makeCradleResult :: (ExitCode, [String], FilePath, [String]) -> [FilePath] -> [FilePath] -> CradleLoadResult ComponentOptions
- HIE.Bios.Ghc.Api: data () => SuccessFlag
+ HIE.Bios.Ghc.Api: data SuccessFlag
- 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: parseDynamicFlags :: MonadIO m => Logger -> DynFlags -> [Located String] -> m (DynFlags, [Located String], [Warn])
+ HIE.Bios.Ghc.Gap: parseDynamicFlags :: MonadIO m => Logger -> DynFlags -> [Located String] -> m (DynFlags, [Located String], Messages DriverMessage)
- HIE.Bios.Types: CradleError :: [FilePath] -> ExitCode -> [String] -> CradleError
+ HIE.Bios.Types: CradleError :: [FilePath] -> ExitCode -> [String] -> [FilePath] -> CradleError
- HIE.Bios.Types: CradleLoadResultT :: m (CradleLoadResult a) -> CradleLoadResultT m a
+ HIE.Bios.Types: CradleLoadResultT :: m (CradleLoadResult a) -> CradleLoadResultT (m :: Type -> Type) a
- HIE.Bios.Types: [runCradleResultT] :: CradleLoadResultT m a -> m (CradleLoadResult a)
+ HIE.Bios.Types: [runCradleResultT] :: CradleLoadResultT (m :: Type -> Type) a -> m (CradleLoadResult a)
- HIE.Bios.Types: newtype CradleLoadResultT m a
+ HIE.Bios.Types: newtype CradleLoadResultT (m :: Type -> Type) a
- HIE.Bios.Types: throwCE :: Monad m => CradleError -> CradleLoadResultT m a
+ HIE.Bios.Types: throwCE :: forall (m :: Type -> Type) a. Monad m => CradleError -> CradleLoadResultT m a
Files
- ChangeLog.md +12/−0
- exe/Main.hs +1/−1
- hie-bios.cabal +18/−11
- src/HIE/Bios/Cradle.hs +143/−80
- src/HIE/Bios/Internal/Debug.hs +4/−2
- src/HIE/Bios/Types.hs +14/−0
- tests/BiosTests.hs +103/−49
- tests/ParserTests.hs +3/−3
- tests/Utils.hs +51/−19
- tests/configs/cabal-with-both.yaml +1/−1
- tests/configs/cabal-with-project.yaml +1/−1
- tests/configs/multi-cabal-with-project.yaml +1/−1
- tests/projects/cabal-with-ghc-and-project/cabal.project.8.10.7 +0/−12
- tests/projects/cabal-with-ghc-and-project/cabal.project.9.2.8 +12/−0
- tests/projects/cabal-with-ghc-and-project/hie.yaml +1/−1
- tests/projects/cabal-with-ghc/cabal.project +1/−1
- tests/projects/cabal-with-project/cabal.project.8.10.7 +0/−5
- tests/projects/cabal-with-project/cabal.project.9.2.8 +5/−0
- tests/projects/cabal-with-project/hie.yaml +1/−1
- tests/projects/failing-multi-repl-cabal-project/NotInPath.hs +5/−0
- tests/projects/failing-multi-repl-cabal-project/multi-repl-cabal-fail/app/Main.hs +4/−0
- tests/projects/failing-multi-repl-cabal-project/multi-repl-cabal-fail/multi-repl-cabal-fail.cabal +21/−0
- tests/projects/failing-multi-repl-cabal-project/multi-repl-cabal-fail/src/Fail.hs +5/−0
- tests/projects/failing-multi-repl-cabal-project/multi-repl-cabal-fail/src/Lib.hs +5/−0
- tests/projects/multi-cabal-with-project/cabal.project.8.10.7 +0/−6
- tests/projects/multi-cabal-with-project/cabal.project.9.2.8 +6/−0
- tests/projects/multi-cabal-with-project/hie.yaml +2/−2
- tests/projects/multi-cabal/src/Lib.hs +0/−0
- tests/projects/multi-stack/src/Lib.hs +0/−0
ChangeLog.md view
@@ -1,5 +1,17 @@ # ChangeLog hie-bios +## 2025-04-25 - 0.15.0++* Use consistent logging for test cases [#461](https://github.com/haskell/hie-bios/pull/461)+* Use cabal path when available [#458](https://github.com/haskell/hie-bios/pull/458)+* Keep track of not loaded files for cabal [#453](https://github.com/haskell/hie-bios/pull/453)+* Allow GHC 9.12 [#449](https://github.com/haskell/hie-bios/pull/449)+* Fix cabal check [#448](https://github.com/haskell/hie-bios/pull/448)++## 2024-05-20 - 0.14.1++* Allow building with GHC 9.10.1 [#435](https://github.com/haskell/hie-bios/pull/435)+ ## 2024-04-22 - 0.14.0 * Add Loading Style option to 'runAction' [#433](https://github.com/haskell/hie-bios/pull/433)
exe/Main.hs view
@@ -87,7 +87,7 @@ res <- forM files $ \fp -> do res <- getCompilerOptions fp LoadFile cradle case res of- CradleFail (CradleError _deps _ex err) ->+ CradleFail (CradleError _deps _ex err _fps) -> return $ "Failed to show flags for \"" ++ fp ++ "\": " ++ show err
hie-bios.cabal view
@@ -1,6 +1,6 @@ Cabal-Version: 2.2 Name: hie-bios-Version: 0.14.0+Version: 0.15.0 Author: Matthew Pickering <matthewtpickering@gmail.com> Maintainer: Matthew Pickering <matthewtpickering@gmail.com> License: BSD-3-Clause@@ -22,11 +22,11 @@ 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/cabal.project.9.2.8 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/cabal.project.9.2.8 tests/projects/cabal-with-project/hie.yaml tests/projects/cabal-with-project/src/MyLib.hs tests/projects/symlink-test/a/A.hs@@ -47,7 +47,7 @@ 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/cabal.project.9.2.8 tests/projects/multi-cabal-with-project/hie.yaml tests/projects/monorepo-cabal/cabal.project tests/projects/monorepo-cabal/hie.yaml@@ -138,8 +138,13 @@ tests/projects/stack-with-yaml/hie.yaml tests/projects/stack-with-yaml/stack-with-yaml.cabal tests/projects/stack-with-yaml/src/Lib.hs+ tests/projects/failing-multi-repl-cabal-project/multi-repl-cabal-fail/app/Main.hs+ tests/projects/failing-multi-repl-cabal-project/multi-repl-cabal-fail/multi-repl-cabal-fail.cabal+ tests/projects/failing-multi-repl-cabal-project/multi-repl-cabal-fail/src/Fail.hs+ tests/projects/failing-multi-repl-cabal-project/multi-repl-cabal-fail/src/Lib.hs+ tests/projects/failing-multi-repl-cabal-project/NotInPath.hs -tested-with: GHC ==9.2.8 || ==9.4.8 || ==9.6.4 || ==9.8.2+tested-with: GHC ==9.4.8 || ==9.6.7 || ==9.8.4 || ==9.10.1 || ==9.12.2 Library Default-Language: Haskell2010@@ -173,13 +178,13 @@ cryptohash-sha1 >= 0.11.100 && < 0.12, directory >= 1.3.0 && < 1.4, filepath >= 1.4.1 && < 1.6,- time >= 1.8.0 && < 1.14,- extra >= 1.6.14 && < 1.8,+ time >= 1.8.0 && < 1.15,+ extra >= 1.6.14 && < 1.9, prettyprinter ^>= 1.6 || ^>= 1.7.0,- ghc >= 9.2.1 && < 9.9,+ ghc >= 9.2.1 && < 9.13, transformers >= 0.5.2 && < 0.7, temporary >= 1.2 && < 1.4,- template-haskell,+ template-haskell >= 2.18 && <2.24, text >= 1.2.3 && < 2.2, unix-compat >= 0.5.1 && < 0.8, unordered-containers >= 0.2.9 && < 0.3,@@ -193,6 +198,7 @@ Default-Language: Haskell2010 Main-Is: Main.hs Other-Modules: Paths_hie_bios+ autogen-modules: Paths_hie_bios GHC-Options: -Wall HS-Source-Dirs: exe Build-Depends: base >= 4.16 && < 5@@ -200,7 +206,7 @@ , directory , filepath , hie-bios- , optparse-applicative+ , optparse-applicative >= 0.18.1 && < 0.19 , prettyprinter test-suite parser-tests@@ -235,6 +241,7 @@ directory, prettyprinter, temporary,+ text, ghc hs-source-dirs: tests/@@ -244,4 +251,4 @@ Source-Repository head Type: git- Location: git://github.com/mpickering/hie-bios.git+ Location: https://github.com/haskell/hie-bios.git
src/HIE/Bios/Cradle.hs view
@@ -25,10 +25,14 @@ , makeCradleResult -- | Cradle project configuration types , CradleProjectConfig(..)++ -- expose to tests+ , makeVersions+ , isCabalMultipleCompSupported+ , ProgramVersions ) where import Control.Applicative ((<|>), optional)-import Data.Bifunctor (first) import Control.DeepSeq import Control.Exception (handleJust) import qualified Data.Yaml as Yaml@@ -42,6 +46,11 @@ import Control.Monad.Trans.Cont import Control.Monad.Trans.Maybe import Control.Monad.IO.Class+import Data.Aeson ((.:))+import qualified Data.Aeson as Aeson+import qualified Data.Aeson.Types as Aeson+import Data.Bifunctor (first)+import qualified Data.ByteString as BS import Data.Conduit.Process import qualified Data.Conduit.Combinators as C import qualified Data.Conduit as C@@ -49,9 +58,10 @@ import qualified Data.HashMap.Strict as Map import Data.Maybe (fromMaybe, maybeToList) import Data.List-import Data.List.Extra (trimEnd)+import Data.List.Extra (trimEnd, nubOrd) import Data.Ord (Down(..)) import qualified Data.Text as T+import qualified Data.Text.Encoding as T import System.Environment import System.FilePath import System.PosixCompat.Files@@ -73,6 +83,7 @@ import Data.Version import Data.IORef import Text.ParserCombinators.ReadP (readP_to_S)+import Data.Tuple.Extra (fst3, snd3, thd3) ---------------------------------------------------------------- @@ -129,6 +140,7 @@ | ConcreteOther a deriving Show + -- | ConcreteCradle augmented with information on which file the -- cradle applies data ResolvedCradle a@@ -243,7 +255,7 @@ 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)+ Nothing -> return $ CradleFail $ CradleError [] ExitSuccess (err_msg fp) [fp] , runGhcCmd = run_ghc_cmd } }@@ -518,7 +530,7 @@ -- delimited by newlines. -- Execute the bios action and add dependencies of the cradle. -- Removes all duplicates.- return $ makeCradleResult (ex, std, wdir, fromMaybe [] res) deps+ return $ makeCradleResult (ex, std, wdir, fromMaybe [] res) deps [fp] callableToProcess :: Callable -> Maybe String -> IO CreateProcess callableToProcess (Command shellCommand) file = do@@ -547,13 +559,17 @@ { 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 ""+ let vs = cradleProgramVersions cs+ callCabalPathForCompilerPath l vs wdir projectFile >>= \case+ Just p -> readProcessWithCwd_ l wdir p args ""+ Nothing -> 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 vs projectFile wdir "v2-exec" $ ["ghc", "-v0", "--"] ++ args+ readProcessWithCwd' l cabalProc "" } @@ -566,11 +582,19 @@ -- 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) -> CradleProjectConfig -> FilePath -> String -> [String] -> CradleLoadResultT IO CreateProcess-cabalProcess l cabalProject workDir command args = do- ghcDirs <- cabalGhcDirs l cabalProject workDir+cabalProcess :: LogAction IO (WithSeverity Log) -> ProgramVersions -> CradleProjectConfig -> FilePath -> String -> [String] -> CradleLoadResultT IO CreateProcess+cabalProcess l vs cabalProject workDir command args = do+ (ghcDirs, ghcPkgPath) <- callCabalPathForCompilerPath l vs workDir cabalProject >>= \case+ Just p -> do+ libdir <- readProcessWithCwd_ l workDir p ["--print-libdir"] ""+ pure ((p, trimEnd libdir), Nothing)+ Nothing -> do+ ghcDirs@(ghcBin, libdir) <- cabalGhcDirs l cabalProject workDir+ ghcPkgPath <- liftIO $ withGhcPkgTool ghcBin libdir+ pure (ghcDirs, Just ghcPkgPath)+ newEnvironment <- liftIO $ setupEnvironment ghcDirs- cabalProc <- liftIO $ setupCabalCommand ghcDirs+ cabalProc <- liftIO $ setupCabalCommand ghcPkgPath pure $ (cabalProc { env = Just newEnvironment , cwd = Just workDir@@ -585,17 +609,21 @@ environment <- getCleanEnvironment pure $ processEnvironment ghcDirs ++ environment - setupCabalCommand :: (FilePath, FilePath) -> IO CreateProcess- setupCabalCommand (ghcBin, libdir) = do+ setupCabalCommand :: Maybe FilePath -> IO CreateProcess+ setupCabalCommand ghcPkgPath = do wrapper_fp <- withGhcWrapperTool l ("ghc", []) workDir buildDir <- cabalBuildDir workDir- ghcPkgPath <- withGhcPkgTool ghcBin libdir- let extraCabalArgs =+ let hcPkgArgs = case ghcPkgPath of+ Nothing -> []+ Just p -> ["--with-hc-pkg", p]++ extraCabalArgs = [ "--builddir=" <> buildDir , command , "--with-compiler", wrapper_fp- , "--with-hc-pkg", ghcPkgPath- ] <> projectFileProcessArgs cabalProject+ ]+ <> hcPkgArgs+ <> projectFileProcessArgs cabalProject pure $ proc "cabal" (extraCabalArgs ++ args) -- | Discovers the location of 'ghc-pkg' given the absolute path to 'ghc'@@ -788,6 +816,37 @@ where projectFileArgs = projectFileProcessArgs cabalProject +callCabalPathForCompilerPath :: LogAction IO (WithSeverity Log) -> ProgramVersions -> FilePath -> CradleProjectConfig -> CradleLoadResultT IO (Maybe FilePath)+callCabalPathForCompilerPath l vs workDir projectFile = do+ isCabalPathSupported vs >>= \case+ False -> pure Nothing+ True -> do+ let+ args = ["path", "--output-format=json"] <> projectFileProcessArgs projectFile+ bs = BS.fromStrict . T.encodeUtf8 . T.pack+ parse_compiler_path = Aeson.parseEither ((.: "compiler") >=> (.: "path")) <=< Aeson.eitherDecode++ compiler_info <- readProcessWithCwd_ l workDir "cabal" args ""+ case parse_compiler_path (bs compiler_info) of+ Left err -> do+ liftIO $ l <& WithSeverity (LogCabalPath $ T.pack err) Warning+ pure Nothing+ Right a -> pure a++isCabalPathSupported :: MonadIO m => ProgramVersions -> m Bool+isCabalPathSupported vs = do+ v <- liftIO $ runCachedIO $ cabalVersion vs+ pure $ maybe False (>= makeVersion [3,14]) v++isCabalMultipleCompSupported :: MonadIO m => ProgramVersions -> m Bool+isCabalMultipleCompSupported vs = do+ cabal_version <- liftIO $ runCachedIO $ cabalVersion vs+ ghc_version <- liftIO $ runCachedIO $ ghcVersion vs+ -- determine which load style is supported by this cabal cradle.+ case (cabal_version, ghc_version) of+ (Just cabal, Just ghc) -> pure $ ghc >= makeVersion [9, 4] && cabal >= makeVersion [3, 11]+ _ -> pure False+ cabalAction :: ResolvedCradles a -> FilePath@@ -798,67 +857,57 @@ -> LoadStyle -> CradleLoadResultT IO ComponentOptions cabalAction (ResolvedCradles root cs vs) workDir mc l projectFile fp loadStyle = do- cabal_version <- liftIO $ runCachedIO $ cabalVersion vs- ghc_version <- liftIO $ runCachedIO $ ghcVersion vs+ multiCompSupport <- isCabalMultipleCompSupported vs -- determine which load style is supported by this cabal cradle.- determinedLoadStyle <- case (cabal_version, ghc_version) of- (Just cabal, Just ghc)- -- Multi-component supported from cabal-install 3.11- -- and ghc 9.4- | LoadWithContext _ <- loadStyle ->- if ghc >= makeVersion [9,4] && cabal >= makeVersion [3,11]- then pure loadStyle- else do- liftIO $ l <& WithSeverity- (LogLoadWithContextUnsupported "cabal"- $ Just "cabal or ghc version is too old. We require `cabal >= 3.11` and `ghc >= 9.4`"- )- Warning- pure LoadFile- _ -> pure LoadFile+ determinedLoadStyle <- case loadStyle of+ LoadWithContext _ | not multiCompSupport -> do+ liftIO $+ l+ <& WithSeverity+ ( LogLoadWithContextUnsupported "cabal" $+ Just "cabal or ghc version is too old. We require `cabal >= 3.11` and `ghc >= 9.4`"+ )+ Warning+ pure LoadFile+ _ -> pure loadStyle - let cabalArgs = case determinedLoadStyle of- LoadFile -> [fromMaybe (fixTargetPath fp) mc]- LoadWithContext fps -> concat- [ [ "--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- ]- ]+ let fpModule = fromMaybe (fixTargetPath fp) mc+ let (cabalArgs, loadingFiles, extraDeps) = case determinedLoadStyle of+ LoadFile -> ([fpModule], [fp], [])+ LoadWithContext fps ->+ let allModulesFpsDeps = ((fpModule, fp, []) : moduleFilesFromSameProject fps)+ allModules = nubOrd $ fst3 <$> allModulesFpsDeps+ allFiles = nubOrd $ snd3 <$> allModulesFpsDeps+ allFpsDeps = nubOrd $ concatMap thd3 allModulesFpsDeps+ in (["--keep-temp-files", "--enable-multi-repl"] ++ allModules, allFiles, allFpsDeps) liftIO $ l <& LogComputedCradleLoadStyle "cabal" determinedLoadStyle `WithSeverity` Info+ liftIO $ l <& LogCabalLoad fp mc (prefix <$> cs) loadingFiles `WithSeverity` Debug - let- cabalCommand = "v2-repl"+ let cabalCommand = "v2-repl" - cabalProc <- cabalProcess l projectFile workDir cabalCommand cabalArgs `modCradleError` \err -> do+ cabalProc <-+ cabalProcess l vs projectFile workDir cabalCommand cabalArgs `modCradleError` \err -> do deps <- cabalCradleDependencies projectFile workDir workDir- pure $ err { cradleErrorDependencies = cradleErrorDependencies err ++ deps }+ pure $ err {cradleErrorDependencies = cradleErrorDependencies err ++ deps} (ex, output, stde, [(_, maybeArgs)]) <- liftIO $ readProcessWithOutputs [hie_bios_output] l workDir cabalProc let args = fromMaybe [] maybeArgs let errorDetails =- ["Failed command: " <> prettyCmdSpec (cmdspec cabalProc)- , unlines output- , unlines stde- , unlines $ args- , "Process Environment:"]- <> prettyProcessEnv cabalProc+ [ "Failed command: " <> prettyCmdSpec (cmdspec cabalProc),+ unlines output,+ unlines stde,+ unlines args,+ "Process Environment:"+ ]+ <> prettyProcessEnv cabalProc when (ex /= ExitSuccess) $ do 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))+ throwCE (CradleError deps ex ([errorMsg] <> errorDetails) loadingFiles) case processCabalWrapperArgs args of Nothing -> do@@ -866,16 +915,26 @@ -- Best effort. Assume the working directory is the -- root of the component, so we are right in trivial cases at least. deps <- liftIO $ cabalCradleDependencies projectFile workDir workDir- throwCE (CradleError deps ex $ ["Failed to parse result of calling cabal" ] <> errorDetails)+ throwCE (CradleError (deps <> extraDeps) ex (["Failed to parse result of calling cabal"] <> errorDetails) loadingFiles) Just (componentDir, final_args) -> do deps <- liftIO $ cabalCradleDependencies projectFile workDir componentDir- CradleLoadResultT $ pure $ makeCradleResult (ex, stde, componentDir, final_args) deps+ CradleLoadResultT $ pure $ makeCradleResult (ex, stde, componentDir, final_args) (deps <> extraDeps) loadingFiles where -- Need to make relative on Windows, due to a Cabal bug with how it- -- parses file targets with a C: drive in it+ -- parses file targets with a C: drive in it. So we decide to make+ -- the paths relative to the working directory. fixTargetPath x | isWindows && hasDrive x = makeRelative workDir x | otherwise = x+ moduleFilesFromSameProject fps =+ [ (fromMaybe (fixTargetPath file) old_mc, file, deps)+ | file <- fps,+ -- Lookup the component for the old file+ Just (ResolvedCradle {concreteCradle = ConcreteCabal ct, cradleDeps = deps}) <- [selectCradle prefix file cs],+ -- Only include this file if the old component is in the same project+ (projectConfigFromMaybe root (cabalProjectFile ct)) == projectFile,+ let old_mc = cabalComponent ct+ ] removeInteractive :: [String] -> [String] removeInteractive = filter (/= "--interactive")@@ -928,7 +987,7 @@ data CradleProjectConfig = NoExplicitConfig | ExplicitConfig FilePath- deriving Eq+ deriving (Eq, Show) -- | Create an explicit project configuration. Expects a working directory -- followed by an optional name of the project configuration.@@ -987,7 +1046,7 @@ -> FilePath -> LoadStyle -> IO (CradleLoadResult ComponentOptions)-stackAction workDir mc syaml l _fp loadStyle = do+stackAction workDir mc syaml l fp loadStyle = do logCradleHasNoSupportForLoadWithContext l loadStyle "stack" let ghcProcArgs = ("stack", stackYamlProcessArgs syaml <> ["exec", "ghc", "--"]) -- Same wrapper works as with cabal@@ -1011,10 +1070,11 @@ -- the root of the component, so we are right in trivial cases at least. deps <- stackCradleDependencies workDir workDir syaml pure $ CradleFail- (CradleError deps ex1 $- [ "Failed to parse result of calling stack" ]+ (CradleError deps ex1+ ([ "Failed to parse result of calling stack" ] ++ stde- ++ args+ ++ args)+ [fp] ) Just (componentDir, ghc_args) -> do@@ -1025,6 +1085,7 @@ , ghc_args ++ pkg_ghc_args ) deps+ [fp] stackProcess :: CradleProjectConfig -> [String] -> CreateProcess stackProcess syaml args = proc "stack" $ stackYamlProcessArgs syaml <> args@@ -1217,10 +1278,10 @@ yes <- doesFileExist f when yes (removeFile f) -makeCradleResult :: (ExitCode, [String], FilePath, [String]) -> [FilePath] -> CradleLoadResult ComponentOptions-makeCradleResult (ex, err, componentDir, gopts) deps =+makeCradleResult :: (ExitCode, [String], FilePath, [String]) -> [FilePath] -> [FilePath] -> CradleLoadResult ComponentOptions+makeCradleResult (ex, err, componentDir, gopts) deps loadingFiles = case ex of- ExitFailure _ -> CradleFail (CradleError deps ex err)+ ExitFailure _ -> CradleFail (CradleError deps ex err loadingFiles) _ -> let compOpts = ComponentOptions gopts componentDir deps in CradleSuccess compOpts@@ -1252,11 +1313,13 @@ case mResult of Just (ExitSuccess, stdo, _) -> pure stdo Just (exitCode, stdo, stde) -> throwCE $- CradleError [] exitCode $- ["Error when calling " <> cmdString, stdo, stde] <> prettyProcessEnv createdProcess+ CradleError [] exitCode+ (["Error when calling " <> cmdString, stdo, stde] <> prettyProcessEnv createdProcess)+ [] Nothing -> throwCE $- CradleError [] ExitSuccess $- ["Couldn't execute " <> cmdString] <> prettyProcessEnv createdProcess+ CradleError [] ExitSuccess+ (["Couldn't execute " <> cmdString] <> prettyProcessEnv createdProcess)+ [] -- | Log that the cradle has no supported for loading with context, if and only if -- 'LoadWithContext' was requested.
src/HIE/Bios/Internal/Debug.hs view
@@ -48,11 +48,13 @@ , "Cradle: " ++ crdl , "Dependencies: " ++ unwords deps ]- CradleFail (CradleError deps ext stderr) ->+ CradleFail (CradleError deps ext stderr extraFiles) -> return ["Cradle failed to load" , "Deps: " ++ show deps , "Exit Code: " ++ show ext- , "Stderr: " ++ unlines stderr]+ , "Stderr: " ++ unlines stderr+ , "Failed: " ++ unlines extraFiles+ ] CradleNone -> return ["No cradle"] where
src/HIE/Bios/Types.hs view
@@ -99,6 +99,8 @@ | LogRequestedCradleLoadStyle !T.Text !LoadStyle | LogComputedCradleLoadStyle !T.Text !LoadStyle | LogLoadWithContextUnsupported !T.Text !(Maybe T.Text)+ | LogCabalLoad !FilePath !(Maybe String) ![FilePath] ![FilePath]+ | LogCabalPath !T.Text deriving (Show) instance Pretty Log where@@ -135,6 +137,14 @@ Nothing -> "." Just reason -> ", because:" <+> pretty reason <> "." <+> "Falling back loading to single file mode."+ pretty (LogCabalLoad file prefixes projectFile crs) =+ "Cabal Loading file" <+> pretty file+ <> line <> indent 4 "from project: " <+> pretty projectFile+ <> line <> indent 4 "with prefixes:" <+> pretty prefixes+ <> line <> indent 4 "with actual loading files:" <+> pretty crs+ pretty (LogCabalPath err) =+ "Could not parse json output of 'cabal path': "+ <> line <> indent 4 (pretty err) -- | The 'LoadStyle' instructs a cradle on how to load a given file target. data LoadStyle@@ -266,6 +276,10 @@ , cradleErrorStderr :: [String] -- ^ Standard error output that can be shown to users to explain -- the loading error.+ , cradleErrorLoadingFiles :: [FilePath]+ -- ^ files that were attempted to be loaded by the cradle.+ -- This can be useful if we are loading multiple files at once,+ -- e.g. in a cabal cradle with the multi-repl feature. } deriving (Show, Eq)
tests/BiosTests.hs view
@@ -19,7 +19,7 @@ import Data.List ( sort, isPrefixOf ) import Data.Typeable import System.Directory-import System.FilePath ((</>))+import System.FilePath ((</>), makeRelative) import System.Exit (ExitCode(ExitSuccess, ExitFailure)) import Control.Monad.Extra (unlessM) import qualified HIE.Bios.Ghc.Gap as Gap@@ -34,11 +34,24 @@ -- If you change this version, make sure to also update 'cabal.project' -- in 'tests\/projects\/cabal-with-ghc'. extraGhcVersion :: String-extraGhcVersion = "8.10.7"+extraGhcVersion = "9.2.8" extraGhc :: String extraGhc = "ghc-" ++ extraGhcVersion +-- | To get all logs, run the tests via:+--+-- @+-- cabal run test:bios-tests --test-options="--debug"+-- @+--+-- or+--+-- @+-- TASTY_DEBUG=TRUE cabal test test:bios-tests+-- @+--+-- to avoid recompilation. main :: IO () main = do writeStackYamlFiles@@ -46,7 +59,7 @@ cabalDep <- checkToolIsAvailable "cabal" extraGhcDep <- checkToolIsAvailable extraGhc - defaultMainWithIngredients (ignoreToolTests:defaultIngredients) $+ defaultMainWithIngredients (ignoreToolTests:verboseLogging:defaultIngredients) $ testGroup "Bios-tests" [ testGroup "Find cradle" findCradleTests , testGroup "Symlink" symbolicLinkTests@@ -60,7 +73,7 @@ symbolicLinkTests :: [TestTree] symbolicLinkTests =- [ testCaseSteps "Can load base module" $ runTestEnv "./symlink-test" $ do+ [ biosTestCase "Can load base module" $ runTestEnv "./symlink-test" $ do initCradle "doesNotExist.hs" assertCradle isMultiCradle step "Attempt to load symlinked module A"@@ -69,7 +82,7 @@ assertComponentOptions $ \opts -> componentOptions opts `shouldMatchList` ["a"] <> argDynamic - , testCaseSteps "Can load symlinked module" $ runTestEnv "./symlink-test" $ do+ , biosTestCase "Can load symlinked module" $ runTestEnv "./symlink-test" $ do initCradle "doesNotExist.hs" assertCradle isMultiCradle step "Attempt to load symlinked module A"@@ -80,7 +93,7 @@ loadComponentOptions "./b/A.hs" assertComponentOptions $ \opts -> componentOptions opts `shouldMatchList` ["b"] <> argDynamic- , testCaseSteps "Can not load symlinked module that is ignored" $ runTestEnv "./symlink-test" $ do+ , biosTestCase "Can not load symlinked module that is ignored" $ runTestEnv "./symlink-test" $ do initCradle "doesNotExist.hs" assertCradle isMultiCradle step "Attempt to load symlinked module A"@@ -94,14 +107,14 @@ biosTestCases :: [TestTree] biosTestCases =- [ testCaseSteps "failing-bios" $ runTestEnv "./failing-bios" $ do+ [ biosTestCase "failing-bios" $ runTestEnv "./failing-bios" $ do initCradle "B.hs" assertCradle isBiosCradle loadComponentOptions "B.hs" assertCradleError $ \CradleError {..} -> do cradleErrorExitCode @?= ExitFailure 1 cradleErrorDependencies `shouldMatchList` ["hie.yaml"]- , testCaseSteps "failing-bios-ghc" $ runTestEnv "./failing-bios-ghc" $ do+ , biosTestCase "failing-bios-ghc" $ runTestEnv "./failing-bios-ghc" $ do initCradle "B.hs" assertCradle isBiosCradle loadRuntimeGhcVersion@@ -111,9 +124,9 @@ cradleErrorDependencies `shouldMatchList` [] length cradleErrorStderr @?= 1 "Couldn't execute myGhc" `isPrefixOf` head cradleErrorStderr @? "Error message should contain basic information"- , testCaseSteps "simple-bios-shell" $ runTestEnv "./simple-bios-shell" $ do+ , biosTestCase "simple-bios-shell" $ runTestEnv "./simple-bios-shell" $ do testDirectoryM isBiosCradle "B.hs"- , testCaseSteps "simple-bios-shell-deps" $ runTestEnv "./simple-bios-shell" $ do+ , biosTestCase "simple-bios-shell-deps" $ runTestEnv "./simple-bios-shell" $ do biosCradleDeps "B.hs" ["hie.yaml"] ] <> concat [linuxTestCases | False] -- TODO(fendor), enable again where@@ -126,26 +139,40 @@ deps @?= componentDependencies opts linuxTestCases =- [ testCaseSteps "simple-bios" $ runTestEnv "./simple-bios" $+ [ biosTestCase "simple-bios" $ runTestEnv "./simple-bios" $ testDirectoryM isBiosCradle "B.hs"- , testCaseSteps "simple-bios-ghc" $ runTestEnv "./simple-bios-ghc" $+ , biosTestCase "simple-bios-ghc" $ runTestEnv "./simple-bios-ghc" $ testDirectoryM isBiosCradle "B.hs"- , testCaseSteps "simple-bios-deps" $ runTestEnv "./simple-bios" $ do+ , biosTestCase "simple-bios-deps" $ runTestEnv "./simple-bios" $ do biosCradleDeps "B.hs" ["hie-bios.sh", "hie.yaml"]- , testCaseSteps "simple-bios-deps-new" $ runTestEnv "./deps-bios-new" $ do+ , biosTestCase "simple-bios-deps-new" $ runTestEnv "./deps-bios-new" $ do biosCradleDeps "B.hs" ["hie-bios.sh", "hie.yaml"] ] cabalTestCases :: ToolDependency -> [TestTree] cabalTestCases extraGhcDep =- [ testCaseSteps "failing-cabal" $ runTestEnv "./failing-cabal" $ do+ [+ biosTestCase "failing-cabal" $ runTestEnv "./failing-cabal" $ do cabalAttemptLoad "MyLib.hs" assertCradleError (\CradleError {..} -> do cradleErrorExitCode @?= ExitFailure 1 cradleErrorDependencies `shouldMatchList` ["failing-cabal.cabal", "cabal.project", "cabal.project.local"])- , testCaseSteps "simple-cabal" $ runTestEnv "./simple-cabal" $ do+ , biosTestCase "failing-cabal-multi-repl-with-shrink-error-files" $ runTestEnv "./failing-multi-repl-cabal-project" $ do+ cabalAttemptLoadFiles "multi-repl-cabal-fail/app/Main.hs" ["multi-repl-cabal-fail/src/Lib.hs", "multi-repl-cabal-fail/src/Fail.hs", "NotInPath.hs"]+ root <- askRoot+ multiSupported <- isCabalMultipleCompSupported'+ if multiSupported+ then+ assertCradleError (\CradleError {..} -> do+ cradleErrorExitCode @?= ExitFailure 1+ cradleErrorDependencies `shouldMatchList` ["cabal.project","cabal.project.local","multi-repl-cabal-fail.cabal"]+ -- NotInPath.hs does not match the cradle for `app/Main.hs`, so it should not be tried.+ (makeRelative root <$> cradleErrorLoadingFiles) `shouldMatchList` ["multi-repl-cabal-fail/app/Main.hs","multi-repl-cabal-fail/src/Fail.hs","multi-repl-cabal-fail/src/Lib.hs"])+ else assertLoadSuccess >>= \ComponentOptions {} -> do+ return ()+ , biosTestCase "simple-cabal" $ runTestEnv "./simple-cabal" $ do testDirectoryM isCabalCradle "B.hs"- , testCaseSteps "nested-cabal" $ runTestEnv "./nested-cabal" $ do+ , biosTestCase "nested-cabal" $ runTestEnv "./nested-cabal" $ do cabalAttemptLoad "sub-comp/Lib.hs" assertComponentOptions $ \opts -> do componentDependencies opts `shouldMatchList`@@ -153,7 +180,7 @@ , "cabal.project" , "cabal.project.local" ]- , testCaseSteps "nested-cabal2" $ runTestEnv "./nested-cabal" $ do+ , biosTestCase "nested-cabal2" $ runTestEnv "./nested-cabal" $ do cabalAttemptLoad "MyLib.hs" assertComponentOptions $ \opts -> do componentDependencies opts `shouldMatchList`@@ -161,24 +188,24 @@ , "cabal.project" , "cabal.project.local" ]- , testCaseSteps "multi-cabal" $ runTestEnv "./multi-cabal" $ do+ , biosTestCase "multi-cabal" $ runTestEnv "./multi-cabal" $ do {- tests if both components can be loaded -} testDirectoryM isCabalCradle "app/Main.hs" testDirectoryM isCabalCradle "src/Lib.hs" , {- issue https://github.com/mpickering/hie-bios/issues/200 -}- testCaseSteps "monorepo-cabal" $ runTestEnv "./monorepo-cabal" $ do+ biosTestCase "monorepo-cabal" $ runTestEnv "./monorepo-cabal" $ do testDirectoryM isCabalCradle "A/Main.hs" testDirectoryM isCabalCradle "B/MyLib.hs" , testGroup "Implicit cradle tests" $- [ testCaseSteps "implicit-cabal" $ runTestEnv "./implicit-cabal" $ do+ [ biosTestCase "implicit-cabal" $ runTestEnv "./implicit-cabal" $ do testImplicitDirectoryM isCabalCradle "Main.hs"- , testCaseSteps "implicit-cabal-no-project" $ runTestEnv "./implicit-cabal-no-project" $ do+ , biosTestCase "implicit-cabal-no-project" $ runTestEnv "./implicit-cabal-no-project" $ do testImplicitDirectoryM isCabalCradle "Main.hs"- , testCaseSteps "implicit-cabal-deep-project" $ runTestEnv "./implicit-cabal-deep-project" $ do+ , biosTestCase "implicit-cabal-deep-project" $ runTestEnv "./implicit-cabal-deep-project" $ do testImplicitDirectoryM isCabalCradle "foo/Main.hs" ] , testGroupWithDependency extraGhcDep- [ testCaseSteps "Appropriate ghc and libdir" $ runTestEnvLocal "./cabal-with-ghc" $ do+ [ biosTestCase "Appropriate ghc and libdir" $ runTestEnvLocal "./cabal-with-ghc" $ do initCradle "src/MyLib.hs" assertCradle isCabalCradle loadRuntimeGhcLibDir@@ -187,14 +214,14 @@ assertGhcVersionIs extraGhcVersion ] , testGroup "Cabal cabalProject"- [ testCaseSteps "cabal-with-project, options propagated" $ runTestEnv "cabal-with-project" $ do+ [ biosTestCase "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+ , biosTestCase "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+ , biosTestCase "multi-cabal-with-project, options propagated" $ runTestEnv "multi-cabal-with-project" $ do optsAppA <- cabalLoadOptions "appA/src/Lib.hs" liftIO $ do "-O2" `elem` componentOptions optsAppA@@ -203,11 +230,11 @@ liftIO $ do "-O2" `notElem` componentOptions optsAppB @? "Options must not contain '-O2'"- , testCaseSteps "multi-cabal-with-project, load" $ runTestEnv "multi-cabal-with-project" $ do+ , biosTestCase "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+ [ biosTestCase "Honours extra ghc setting" $ runTestEnv "cabal-with-ghc-and-project" $ do initCradle "src/MyLib.hs" assertCradle isCabalCradle loadRuntimeGhcLibDir@@ -224,6 +251,12 @@ assertCradle isCabalCradle loadComponentOptions fp + cabalAttemptLoadFiles :: FilePath -> [FilePath] -> TestM ()+ cabalAttemptLoadFiles fp fps = do+ initCradle fp+ assertCradle isCabalCradle+ loadComponentOptionsMultiStyle fp fps+ cabalLoadOptions :: FilePath -> TestM ComponentOptions cabalLoadOptions fp = do initCradle fp@@ -234,42 +267,42 @@ stackTestCases :: [TestTree] stackTestCases = [ expectFailBecause "stack repl does not fail on an invalid cabal file" $- testCaseSteps "failing-stack" $ runTestEnv "./failing-stack" $ do+ biosTestCase "failing-stack" $ runTestEnv "./failing-stack" $ do stackAttemptLoad "src/Lib.hs" assertCradleError $ \CradleError {..} -> do cradleErrorExitCode @?= ExitFailure 1 cradleErrorDependencies `shouldMatchList` ["failing-stack.cabal", "stack.yaml", "package.yaml"]- , testCaseSteps "simple-stack" $ runTestEnv "./simple-stack" $ do+ , biosTestCase "simple-stack" $ runTestEnv "./simple-stack" $ do testDirectoryM isStackCradle "B.hs"- , testCaseSteps "multi-stack" $ runTestEnv "./multi-stack" $ do {- tests if both components can be loaded -}+ , biosTestCase "multi-stack" $ runTestEnv "./multi-stack" $ do {- tests if both components can be loaded -} testDirectoryM isStackCradle "app/Main.hs" testDirectoryM isStackCradle "src/Lib.hs"- , testCaseSteps "nested-stack" $ runTestEnv "./nested-stack" $ do+ , biosTestCase "nested-stack" $ runTestEnv "./nested-stack" $ do stackAttemptLoad "sub-comp/Lib.hs" assertComponentOptions $ \opts -> componentDependencies opts `shouldMatchList` ["sub-comp" </> "sub-comp.cabal", "sub-comp" </> "package.yaml", "stack.yaml"]- , testCaseSteps "nested-stack2" $ runTestEnv "./nested-stack" $ do+ , biosTestCase "nested-stack2" $ runTestEnv "./nested-stack" $ do stackAttemptLoad "MyLib.hs" assertComponentOptions $ \opts -> componentDependencies opts `shouldMatchList` ["nested-stack.cabal", "package.yaml", "stack.yaml"]- , testCaseSteps "stack-with-yaml" $ runTestEnv "./stack-with-yaml" $ do+ , biosTestCase "stack-with-yaml" $ runTestEnv "./stack-with-yaml" $ do {- tests if both components can be loaded -} testDirectoryM isStackCradle "app/Main.hs" testDirectoryM isStackCradle "src/Lib.hs"- , testCaseSteps "multi-stack-with-yaml" $ runTestEnv "./multi-stack-with-yaml" $ do+ , biosTestCase "multi-stack-with-yaml" $ runTestEnv "./multi-stack-with-yaml" $ do {- tests if both components can be loaded -} testDirectoryM isStackCradle "appA/src/Lib.hs" testDirectoryM isStackCradle "appB/src/Lib.hs" , -- Test for special characters in the path for parsing of the ghci-scripts. -- Issue https://github.com/mpickering/hie-bios/issues/162- testCaseSteps "space stack" $ runTestEnv "./space stack" $ do+ biosTestCase "space stack" $ runTestEnv "./space stack" $ do testDirectoryM isStackCradle "A.hs" testDirectoryM isStackCradle "B.hs" , testGroup "Implicit cradle tests"- [ testCaseSteps "implicit-stack" $ runTestEnv "./implicit-stack" $+ [ biosTestCase "implicit-stack" $ runTestEnv "./implicit-stack" $ testImplicitDirectoryM isStackCradle "Main.hs"- , testCaseSteps "implicit-stack-multi" $ runTestEnv "./implicit-stack-multi" $ do+ , biosTestCase "implicit-stack-multi" $ runTestEnv "./implicit-stack-multi" $ do testImplicitDirectoryM isStackCradle "Main.hs" testImplicitDirectoryM isStackCradle "other-package/Main.hs" ]@@ -283,9 +316,9 @@ directTestCases :: [TestTree] directTestCases =- [ testCaseSteps "simple-direct" $ runTestEnv "./simple-direct" $ do+ [ biosTestCase "simple-direct" $ runTestEnv "./simple-direct" $ do testDirectoryM isDirectCradle "B.hs"- , testCaseSteps "multi-direct" $ runTestEnv "./multi-direct" $ do+ , biosTestCase "multi-direct" $ runTestEnv "./multi-direct" $ do {- tests if both components can be loaded -} testDirectoryM isMultiCradle "A.hs" testDirectoryM isMultiCradle "B.hs"@@ -307,7 +340,7 @@ ] where cradleFileTest :: String -> FilePath -> FilePath -> Maybe FilePath -> TestTree- cradleFileTest testName dir fpTarget result = testCaseSteps testName $ do+ cradleFileTest testName dir fpTarget result = biosTestCase testName $ do runTestEnv dir $ do findCradleForModuleM fpTarget result @@ -320,6 +353,12 @@ infix 1 `shouldMatchList` +biosTestCase :: TestName -> (Bool -> Assertion) -> TestTree+biosTestCase name assertion = askOption @VerboseLogging (\case+ VerboseLogging verbose -> testCase name (assertion verbose)+ )++ -- ------------------------------------------------------------------ -- Stack related helper functions -- ------------------------------------------------------------------@@ -350,10 +389,12 @@ stackYamlResolver :: String stackYamlResolver =-#if (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,8,0,0)))- "nightly-2024-02-26" -- GHC 9.8.1+#if (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,10,0,0)))+ "nightly-2025-04-24" -- GHC 9.10.1+#elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,8,0,0)))+ "lts-23.19" -- GHC 9.8.4 #elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,6,0,0)))- "lts-22.12" -- GHC 9.6.4+ "lts-22.43" -- GHC 9.6.6 #elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,4,0,0))) "lts-21.25" -- GHC 9.4.8 #elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,2,0,0)))@@ -417,15 +458,28 @@ ignoreToolTests = Tasty.TestManager [Tasty.Option (Proxy :: Proxy IgnoreToolDeps)] $ \_opts _tree -> Nothing +newtype VerboseLogging = VerboseLogging Bool++instance Tasty.IsOption VerboseLogging where+ defaultValue = VerboseLogging False+ parseValue = fmap VerboseLogging . Tasty.safeReadBool+ optionName = pure "debug"+ optionHelp = pure "Run the tests with verbose logging"+ optionCLParser = Tasty.flagCLParser Nothing (VerboseLogging True)++-- | The ingredient that provides the "ignore missing run-time dependencies" functionality+verboseLogging :: Tasty.Ingredient+verboseLogging = Tasty.TestManager [Tasty.Option (Proxy :: Proxy VerboseLogging)] $+ \_opts _tree -> Nothing+ -- --------------------------------------------------------------------- Ignore test group if built with GHC 9.2.1 until GHC 9.2.4--- or GHC 9.8.1 or newer.+-- Ignore test group if built with GHC 9.6.7 and GHC 9.12.2 -- ------------------------------------------------------------------ ignoreOnUnsupportedGhc :: TestTree -> TestTree ignoreOnUnsupportedGhc tt =-#if (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,2,1,0) && !MIN_VERSION_GLASGOW_HASKELL(9,2,4,0)) || (MIN_VERSION_GLASGOW_HASKELL(9,8,1,0)))- ignoreTestBecause "Not supported on GHC >= 9.2.1 && < 9.2.4 || >= 9.8.1"+#if (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(9,6,7,0) && !MIN_VERSION_GLASGOW_HASKELL(9,6,8,0)) || (MIN_VERSION_GLASGOW_HASKELL(9,12,2,0) && !MIN_VERSION_GLASGOW_HASKELL(9,12,3,0)))+ ignoreTestBecause "Not supported on 9.6.7 and 9.12.2" #endif tt
tests/ParserTests.hs view
@@ -62,11 +62,11 @@ ,("./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")))+ (noDeps (Cabal $ CabalType Nothing (Just "cabal.project.9.2.8"))) assertParser "cabal-with-both.yaml"- (noDeps (Cabal $ CabalType (Just "hie-bios:hie") (Just "cabal.project.8.10.7")))+ (noDeps (Cabal $ CabalType (Just "hie-bios:hie") (Just "cabal.project.9.2.8"))) assertParser "multi-cabal-with-project.yaml"- (noDeps (CabalMulti (CabalType Nothing (Just "cabal.project.8.10.7"))+ (noDeps (CabalMulti (CabalType Nothing (Just "cabal.project.9.2.8")) [("./src", CabalType (Just "lib:hie-bios") Nothing) ,("./vendor", CabalType (Just "parser-tests") Nothing)])) -- Assertions for stack.yaml files
tests/Utils.hs view
@@ -26,6 +26,7 @@ -- * Ask for test environment askRoot, askStep,+ askLogger, askCradle, askLoadResult, askOrLoadLibDir,@@ -42,10 +43,12 @@ initCradle, initImplicitCradle, loadComponentOptions,+ loadComponentOptionsMultiStyle, loadRuntimeGhcLibDir, loadRuntimeGhcVersion, inCradleRootDir, loadFileGhc,+ isCabalMultipleCompSupported', -- * Assertion helpers assertCradle,@@ -80,12 +83,15 @@ import HIE.Bios.Ghc.Api import qualified HIE.Bios.Ghc.Gap as G import HIE.Bios.Ghc.Load-import HIE.Bios.Types+import HIE.Bios.Types as HIE import Prettyprinter import System.Directory import System.FilePath import System.IO.Temp import Test.Tasty.HUnit+import Colog.Core+import qualified Data.Text as Text+import Data.Function ((&)) -- --------------------------------------------------------------------------- -- Test configuration and information@@ -96,6 +102,7 @@ data TestConfig = TestConfig { useTemporaryDirectory :: Bool , testProjectRoots :: FilePath+ , testVerbose :: Bool } deriving (Eq, Show, Ord) @@ -105,20 +112,20 @@ , testLibDirResult :: Maybe (CradleLoadResult FilePath) , testGhcVersionResult :: Maybe (CradleLoadResult String) , testRootDir :: FilePath- , testStepFunction :: String -> IO ()+ , testLogger :: L.LogAction IO (L.WithSeverity HIE.Log) } defConfig :: TestConfig-defConfig = TestConfig True "./tests/projects"+defConfig = TestConfig True "./tests/projects" False -runTestEnv :: FilePath -> TestM a -> (String -> IO ()) -> IO a-runTestEnv = runTestEnv' defConfig+runTestEnv :: FilePath -> TestM a -> Bool -> IO a+runTestEnv fp act verbose = runTestEnv' defConfig{testVerbose=verbose} fp act -runTestEnvLocal :: FilePath -> TestM a -> (String -> IO ()) -> IO a-runTestEnvLocal = runTestEnv' defConfig{useTemporaryDirectory = False}+runTestEnvLocal :: FilePath -> TestM a -> Bool -> IO a+runTestEnvLocal fp act verbose = runTestEnv' defConfig{useTemporaryDirectory = False,testVerbose=verbose} fp act -runTestEnv' :: TestConfig -> FilePath -> TestM a -> (String -> IO ()) -> IO a-runTestEnv' config root act stepF = do+runTestEnv' :: TestConfig -> FilePath -> TestM a -> IO a+runTestEnv' config root act = do -- We need to copy over the directory to somewhere outside the source tree -- when we test, since the cabal.project/stack.yaml/hie.yaml file in the root -- of this repository interferes with the test cradles!@@ -133,12 +140,17 @@ , testLibDirResult = Nothing , testGhcVersionResult = Nothing , testRootDir = root'- , testStepFunction = stepF+ , testLogger = init_logger } realRoot = testProjectRoots config </> root wrapper realRoot $ \root' -> flip evalStateT (mkEnv root') $ do step $ "Run test in: " <> root' act+ where+ init_logger = L.logStringStderr+ & L.cmap printLog+ & L.filterBySeverity (if testVerbose config then Debug else Error) getSeverity+ printLog (L.WithSeverity l sev) = "[" ++ show sev ++ "] " ++ show (pretty l) -- --------------------------------------------------------------------------- -- Modification helpers@@ -170,7 +182,10 @@ askRoot = gets testRootDir askStep :: TestM (String -> IO ())-askStep = gets testStepFunction+askStep = do+ logger <- gets testLogger+ pure $ \s ->+ logger <& LogAny (Text.pack s) `WithSeverity` Info askCradle :: TestM (Cradle Void) askCradle =@@ -225,13 +240,16 @@ assertFailure "No GHC version set, use 'loadRuntimeGhcVersion' before asking for it" +askLogger :: TestM (L.LogAction IO (L.WithSeverity HIE.Log))+askLogger = gets testLogger+ -- --------------------------------------------------------------------------- -- Test setup helpers -- --------------------------------------------------------------------------- step :: String -> TestM () step msg = do- s <- gets testStepFunction+ s <- askStep liftIO $ s msg normFile :: FilePath -> TestM FilePath@@ -252,16 +270,18 @@ mcfg <- findCradleLoc a_fp relMcfg <- traverse relFile mcfg step $ "Loading Cradle: " <> show relMcfg+ logger <- askLogger crd <- case mcfg of- Just cfg -> liftIO $ loadCradle testLogger cfg- Nothing -> liftIO $ loadImplicitCradle testLogger a_fp+ Just cfg -> liftIO $ loadCradle logger cfg+ Nothing -> liftIO $ loadImplicitCradle logger a_fp setCradle crd initImplicitCradle :: FilePath -> TestM () initImplicitCradle fp = do a_fp <- normFile fp step $ "Loading implicit Cradle for: " <> fp- crd <- liftIO $ loadImplicitCradle testLogger a_fp+ logger <- askLogger+ crd <- liftIO $ loadImplicitCradle logger a_fp setCradle crd loadComponentOptions :: FilePath -> TestM ()@@ -272,6 +292,15 @@ clr <- liftIO $ getCompilerOptions a_fp LoadFile crd setLoadResult clr +loadComponentOptionsMultiStyle :: FilePath -> [FilePath] -> TestM ()+loadComponentOptionsMultiStyle fp fps = do+ a_fp <- normFile fp+ a_fps <- mapM normFile fps+ crd <- askCradle+ step $ "Initialise flags for: " <> fp <> " and " <> show fps+ clr <- liftIO $ getCompilerOptions a_fp (LoadWithContext a_fps) crd+ setLoadResult clr+ loadRuntimeGhcLibDir :: TestM () loadRuntimeGhcLibDir = do crd <- askCradle@@ -286,9 +315,12 @@ ghcVersionRes <- liftIO $ getRuntimeGhcVersion crd setGhcVersionResult ghcVersionRes -testLogger :: forall a . Pretty a => L.LogAction IO (L.WithSeverity a)-testLogger = L.cmap printLog L.logStringStderr- where printLog (L.WithSeverity l sev) = "[" ++ show sev ++ "] " ++ show (pretty l)+isCabalMultipleCompSupported' :: TestM Bool+isCabalMultipleCompSupported' = do+ cr <- askCradle+ root <- askRoot+ versions <- liftIO $ makeVersions (cradleLogger cr) root ((runGhcCmd . cradleOptsProg) cr)+ liftIO $ isCabalMultipleCompSupported versions inCradleRootDir :: TestM a -> TestM a inCradleRootDir act = do@@ -380,7 +412,7 @@ assertCradleLoadSuccess = \case (CradleSuccess x) -> pure x CradleNone -> liftIO $ assertFailure "Unexpected none-Cradle"- (CradleFail (CradleError _deps _ex stde)) ->+ (CradleFail (CradleError _deps _ex stde _err_loading_files)) -> liftIO $ assertFailure ("Unexpected cradle fail" <> unlines stde) assertCradleLoadError :: CradleLoadResult a -> TestM CradleError
tests/configs/cabal-with-both.yaml view
@@ -1,4 +1,4 @@ cradle: cabal:- cabalProject: "cabal.project.8.10.7"+ cabalProject: "cabal.project.9.2.8" component: "hie-bios:hie"
tests/configs/cabal-with-project.yaml view
@@ -1,3 +1,3 @@ cradle: cabal:- cabalProject: "cabal.project.8.10.7"+ cabalProject: "cabal.project.9.2.8"
tests/configs/multi-cabal-with-project.yaml view
@@ -1,6 +1,6 @@ cradle: cabal:- cabalProject: "cabal.project.8.10.7"+ cabalProject: "cabal.project.9.2.8" components: - path: "./src" component: "lib:hie-bios"
− tests/projects/cabal-with-ghc-and-project/cabal.project.8.10.7
@@ -1,12 +0,0 @@-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/cabal.project.9.2.8 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-9.2.8
tests/projects/cabal-with-ghc-and-project/hie.yaml view
@@ -1,3 +1,3 @@ cradle: cabal:- cabalProject: cabal.project.8.10.7+ cabalProject: cabal.project.9.2.8
tests/projects/cabal-with-ghc/cabal.project view
@@ -9,4 +9,4 @@ -- 'tests/BiosTests.hs'. -- -- Additionally, CI needs a proper setup to execute this test-case.-with-compiler: ghc-8.10.7+with-compiler: ghc-9.2.8
− tests/projects/cabal-with-project/cabal.project.8.10.7
@@ -1,5 +0,0 @@--- Test file.-packages: ./--package cabal-with-project- ghc-options: -O2
+ tests/projects/cabal-with-project/cabal.project.9.2.8 view
@@ -0,0 +1,5 @@+-- Test file.+packages: ./++package cabal-with-project+ ghc-options: -O2
tests/projects/cabal-with-project/hie.yaml view
@@ -1,3 +1,3 @@ cradle: cabal:- cabalProject: cabal.project.8.10.7+ cabalProject: cabal.project.9.2.8
+ tests/projects/failing-multi-repl-cabal-project/NotInPath.hs view
@@ -0,0 +1,5 @@+module NotInPath where++import System.FilePath ((</>))++foo = "test" </> "me"
+ tests/projects/failing-multi-repl-cabal-project/multi-repl-cabal-fail/app/Main.hs view
@@ -0,0 +1,4 @@++import System.Directory (getCurrentDirectory)++main = return ()
+ tests/projects/failing-multi-repl-cabal-project/multi-repl-cabal-fail/multi-repl-cabal-fail.cabal view
@@ -0,0 +1,21 @@+cabal-version: >=2.0+name: multi-cabal+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+++executable multi-cabal+ main-is: app/Main.hs+ -- other-modules:+ -- other-extensions:+ build-depends: base >=4.10 && < 5, directory+ -- hs-source-dirs:+ default-language: Haskell2010
+ tests/projects/failing-multi-repl-cabal-project/multi-repl-cabal-fail/src/Fail.hs view
@@ -0,0 +1,5 @@+module Fail where++import System.FilePath ((</>))++foo = "test" </> "me"
+ tests/projects/failing-multi-repl-cabal-project/multi-repl-cabal-fail/src/Lib.hs view
@@ -0,0 +1,5 @@+module Lib where++import System.FilePath ((</>))++foo = "test" </> "me"
− tests/projects/multi-cabal-with-project/cabal.project.8.10.7
@@ -1,6 +0,0 @@-packages: ./appA ./appB---- Only appA gets the config-package appA- ghc-options: -O2-
+ tests/projects/multi-cabal-with-project/cabal.project.9.2.8 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
@@ -6,11 +6,11 @@ cabal: - path: appA/src component: appA:lib- cabalProject: cabal.project.8.10.7+ cabalProject: cabal.project.9.2.8 - path: "appB" config: cradle: cabal: - path: appB/src component: appB:lib- cabalProject: cabal.project.8.10.7+ cabalProject: cabal.project.9.2.8
tests/projects/multi-cabal/src/Lib.hs view
tests/projects/multi-stack/src/Lib.hs view