@@ -20,6 +20,7 @@ module Stack.Ghci
2020import Stack.Prelude hiding (Display (.. ))
2121import Control.Monad.State.Strict (State , execState , get , modify )
2222import qualified Data.ByteString.Char8 as S8
23+ import qualified Data.ByteString.Lazy as LBS
2324import Data.List
2425import qualified Data.Map.Strict as M
2526import qualified Data.Set as S
@@ -31,6 +32,7 @@ import Path
3132import Path.Extra (toFilePathNoTrailingSep )
3233import Path.IO hiding (withSystemTempDir )
3334import qualified RIO
35+ import RIO.Process (HasProcessContext , exec , proc , readProcess_ )
3436import Stack.Build
3537import Stack.Build.Installed
3638import Stack.Build.Source
@@ -50,7 +52,7 @@ import Stack.Types.PackageIdentifier
5052import Stack.Types.PackageName
5153import Stack.Types.Runner
5254import System.IO (putStrLn , putStr , getLine )
53- import RIO.Process ( HasProcessContext , execSpawn , proc , readProcess_ )
55+ import System.IO.Temp ( getCanonicalTemporaryDirectory )
5456
5557#ifndef WINDOWS
5658import qualified System.Posix.Files as Posix
@@ -366,7 +368,7 @@ runGhci GhciOpts{..} targets mainIsTargets pkgs extraFiles exposePackages = do
366368 mconcat (intersperse " , " (map (RIO. display . ghciPkgName) pkgs))
367369 let execGhci extras = do
368370 menv <- liftIO $ configProcessContextSettings config defaultEnvSettings
369- withProcessContext menv $ execSpawn
371+ withProcessContext menv $ exec
370372 (fromMaybe (compilerExeName wc) ghciGhcCommand)
371373 ((" --interactive" : ) $
372374 -- This initial "-i" resets the include directories to
@@ -387,32 +389,62 @@ runGhci GhciOpts{..} targets mainIsTargets pkgs extraFiles exposePackages = do
387389 $ runGrabFirstLine (fromMaybe (compilerExeName wc) ghciGhcCommand) [" --version" ]
388390 return $ " Intero" `isPrefixOf` output
389391 _ -> return False
390- withSystemTempDir " ghci" $ \ tmpDirectory -> do
391- macrosOptions <- writeMacrosFile tmpDirectory pkgs
392- if ghciNoLoadModules
393- then execGhci macrosOptions
394- else do
395- checkForDuplicateModules pkgs
396- isIntero <- checkIsIntero
397- bopts <- view buildOptsL
398- mainFile <- figureOutMainFile bopts mainIsTargets targets pkgs
399- scriptPath <- writeGhciScript tmpDirectory (renderScript isIntero pkgs mainFile ghciOnlyMain extraFiles)
400- execGhci (macrosOptions ++ [" -ghci-script=" <> toFilePath scriptPath])
392+ -- Since usage of 'exec' does not return, we cannot do any cleanup
393+ -- on ghci exit. So, instead leave the generated files. To make this
394+ -- more efficient and avoid gratuitous generation of garbage, the
395+ -- file names are determined by hashing. This also has the nice side
396+ -- effect of making it possible to copy the ghci invocation out of
397+ -- the log and have it still work.
398+ tmpDirectory <-
399+ (</> $ (mkRelDir " haskell-stack-ghci" )) <$>
400+ (parseAbsDir =<< liftIO getCanonicalTemporaryDirectory)
401+ ensureDir tmpDirectory
402+ macrosOptions <- writeMacrosFile tmpDirectory pkgs
403+ if ghciNoLoadModules
404+ then execGhci macrosOptions
405+ else do
406+ checkForDuplicateModules pkgs
407+ isIntero <- checkIsIntero
408+ bopts <- view buildOptsL
409+ mainFile <- figureOutMainFile bopts mainIsTargets targets pkgs
410+ scriptOptions <- writeGhciScript tmpDirectory (renderScript isIntero pkgs mainFile ghciOnlyMain extraFiles)
411+ execGhci (macrosOptions ++ scriptOptions)
401412
402413writeMacrosFile :: HasRunner env => Path Abs Dir -> [GhciPkgInfo ] -> RIO env [String ]
403- writeMacrosFile tmpDirectory packages = do
404- preprocessCabalMacros packages macrosFile
405- where
406- macrosFile = tmpDirectory </> $ (mkRelFile " cabal_macros.h" )
414+ writeMacrosFile tmpDirectory pkgs = do
415+ fps <- fmap (nubOrd . catMaybes . concat ) $
416+ forM pkgs $ \ pkg -> forM (ghciPkgOpts pkg) $ \ (_, bio) -> do
417+ let cabalMacros = bioCabalMacros bio
418+ exists <- liftIO $ doesFileExist cabalMacros
419+ if exists
420+ then return $ Just cabalMacros
421+ else do
422+ prettyWarnL [" Didn't find expected autogen file:" , display cabalMacros]
423+ return Nothing
424+ files <- liftIO $ mapM (S8. readFile . toFilePath) fps
425+ if null files then return [] else do
426+ out <- liftIO $ writeHashedFile tmpDirectory $ (mkRelFile " cabal_macros.h" ) $
427+ S8. concat $ map (<> " \n #undef CURRENT_PACKAGE_KEY\n #undef CURRENT_COMPONENT_ID\n " ) files
428+ return [" -optP-include" , " -optP" <> toFilePath out]
407429
408- writeGhciScript :: (MonadIO m ) => Path Abs Dir -> GhciScript -> m ( Path Abs File )
430+ writeGhciScript :: (MonadIO m ) => Path Abs Dir -> GhciScript -> m [ String ]
409431writeGhciScript tmpDirectory script = do
410- liftIO $ scriptToFile scriptPath script
432+ scriptPath <- liftIO $ writeHashedFile tmpDirectory $ (mkRelFile " ghci-script" ) $
433+ LBS. toStrict $ scriptToLazyByteString script
434+ let scriptFilePath = toFilePath scriptPath
411435 setScriptPerms scriptFilePath
412- return scriptPath
413- where
414- scriptPath = tmpDirectory </> $ (mkRelFile " ghci-script" )
415- scriptFilePath = toFilePath scriptPath
436+ return [" -ghci-script=" <> scriptFilePath]
437+
438+ writeHashedFile :: Path Abs Dir -> Path Rel File -> ByteString -> IO (Path Abs File )
439+ writeHashedFile tmpDirectory relFile contents = do
440+ relSha <- shaPathForBytes contents
441+ let outDir = tmpDirectory </> relSha
442+ outFile = outDir </> relFile
443+ alreadyExists <- doesFileExist outFile
444+ unless alreadyExists $ do
445+ ensureDir outDir
446+ S8. writeFile (toFilePath outFile) contents
447+ return outFile
416448
417449renderScript :: Bool -> [GhciPkgInfo ] -> Maybe (Path Abs File ) -> Bool -> [Path Abs File ] -> GhciScript
418450renderScript isIntero pkgs mainFile onlyMain extraFiles = do
@@ -809,23 +841,6 @@ getExtraLoadDeps loadAllDeps sourceMap targets =
809841 (_, Just PSIndex {}) -> return loadAllDeps
810842 (_, _) -> return False
811843
812- preprocessCabalMacros :: HasRunner env => [GhciPkgInfo ] -> Path Abs File -> RIO env [String ]
813- preprocessCabalMacros pkgs out = do
814- fps <- fmap (nubOrd . catMaybes . concat ) $
815- forM pkgs $ \ pkg -> forM (ghciPkgOpts pkg) $ \ (_, bio) -> do
816- let cabalMacros = bioCabalMacros bio
817- exists <- liftIO $ doesFileExist cabalMacros
818- if exists
819- then return $ Just cabalMacros
820- else do
821- prettyWarnL [" Didn't find expected autogen file:" , display cabalMacros]
822- return Nothing
823- files <- liftIO $ mapM (S8. readFile . toFilePath) fps
824- if null files then return [] else do
825- liftIO $ S8. writeFile (toFilePath out) $ S8. concat $
826- map (<> " \n #undef CURRENT_PACKAGE_KEY\n #undef CURRENT_COMPONENT_ID\n " ) files
827- return [" -optP-include" , " -optP" <> toFilePath out]
828-
829844setScriptPerms :: MonadIO m => FilePath -> m ()
830845#ifdef WINDOWS
831846setScriptPerms _ = do
0 commit comments