@@ -16,6 +16,7 @@ module Distribution.Simple.Program.GHC
16
16
, ghcInvocation
17
17
, renderGhcOptions
18
18
, runGHC
19
+ , runGHCWithResponseFile
19
20
, packageDbArgsDb
20
21
, normaliseGhcArgs
21
22
) where
@@ -32,8 +33,10 @@ import Distribution.Simple.Compiler
32
33
import Distribution.Simple.Flag
33
34
import Distribution.Simple.GHC.ImplInfo
34
35
import Distribution.Simple.Program.Find (getExtraPathEnv )
36
+ import Distribution.Simple.Program.ResponseFile
35
37
import Distribution.Simple.Program.Run
36
38
import Distribution.Simple.Program.Types
39
+ import Distribution.Simple.Utils (TempFileOptions , infoNoWrap )
37
40
import Distribution.System
38
41
import Distribution.Types.ComponentId
39
42
import Distribution.Types.ParStrat
@@ -42,17 +45,19 @@ import Distribution.Utils.Path
42
45
import Distribution.Verbosity
43
46
import Distribution.Version
44
47
48
+ import GHC.IO.Encoding (TextEncoding )
45
49
import Language.Haskell.Extension
46
50
47
51
import Data.List (stripPrefix )
48
52
import qualified Data.Map as Map
49
53
import Data.Monoid (All (.. ), Any (.. ), Endo (.. ))
50
54
import qualified Data.Set as Set
55
+ import qualified System.Process as Process
51
56
52
57
normaliseGhcArgs :: Maybe Version -> PackageDescription -> [String ] -> [String ]
53
58
normaliseGhcArgs (Just ghcVersion) PackageDescription {.. } ghcArgs
54
59
| ghcVersion `withinRange` supportedGHCVersions =
55
- argumentFilters . filter simpleFilters . filterRtsOpts $ ghcArgs
60
+ argumentFilters . filter simpleFilters . filterRtsArgs $ ghcArgs
56
61
where
57
62
supportedGHCVersions :: VersionRange
58
63
supportedGHCVersions = orLaterVersion (mkVersion [8 , 0 ])
@@ -162,18 +167,9 @@ normaliseGhcArgs (Just ghcVersion) PackageDescription{..} ghcArgs
162
167
flagArgumentFilter
163
168
[" -ghci-script" , " -H" , " -interactive-print" ]
164
169
165
- filterRtsOpts :: [String ] -> [String ]
166
- filterRtsOpts = go False
167
- where
168
- go :: Bool -> [String ] -> [String ]
169
- go _ [] = []
170
- go _ (" +RTS" : opts) = go True opts
171
- go _ (" -RTS" : opts) = go False opts
172
- go isRTSopts (opt : opts) = addOpt $ go isRTSopts opts
173
- where
174
- addOpt
175
- | isRTSopts = id
176
- | otherwise = (opt : )
170
+ -- \| Remove RTS arguments from a list.
171
+ filterRtsArgs :: [String ] -> [String ]
172
+ filterRtsArgs = snd . splitRTSArgs
177
173
178
174
simpleFilters :: String -> Bool
179
175
simpleFilters =
@@ -647,6 +643,81 @@ runGHC verbosity ghcProg comp platform mbWorkDir opts = do
647
643
runProgramInvocation verbosity
648
644
=<< ghcInvocation verbosity ghcProg comp platform mbWorkDir opts
649
645
646
+ runGHCWithResponseFile
647
+ :: FilePath
648
+ -> Maybe TextEncoding
649
+ -> TempFileOptions
650
+ -> Verbosity
651
+ -> ConfiguredProgram
652
+ -> Compiler
653
+ -> Platform
654
+ -> Maybe (SymbolicPath CWD (Dir Pkg ))
655
+ -> GhcOptions
656
+ -> IO ()
657
+ runGHCWithResponseFile fileNameTemplate encoding tempFileOptions verbosity ghcProg comp platform maybeWorkDir opts = do
658
+ invocation <- ghcInvocation verbosity ghcProg comp platform maybeWorkDir opts
659
+
660
+ let compilerSupportsResponseFiles =
661
+ case compilerCompatVersion GHC comp of
662
+ -- GHC 9.4 is the first version which supports response files.
663
+ Just version -> version >= mkVersion [9 , 4 ]
664
+ Nothing -> False
665
+
666
+ args = progInvokeArgs invocation
667
+
668
+ -- Don't use response files if the first argument is `--interactive`, for
669
+ -- two related reasons.
670
+ --
671
+ -- `hie-bios` relies on a hack to intercept the command-line that `Cabal`
672
+ -- supplies to `ghc`. Specifically, `hie-bios` creates a script around
673
+ -- `ghc` that detects if the first option is `--interactive` and if so then
674
+ -- instead of running `ghc` it prints the command-line that `ghc` was given
675
+ -- instead of running the command:
676
+ --
677
+ -- https://github.com/haskell/hie-bios/blob/ce863dba7b57ded20160b4f11a487e4ff8372c08/wrappers/cabal#L7
678
+ --
679
+ -- … so we can't store that flag in the response file, otherwise that will
680
+ -- break. However, even if we were to add a special-case to keep that flag
681
+ -- out of the response file things would still break because `hie-bios`
682
+ -- stores the arguments to `ghc` that the wrapper script outputs and reuses
683
+ -- them later. That breaks if you use a response file because it will
684
+ -- store an argument like `@…/ghc36000-0.rsp` which is a temporary path
685
+ -- that no longer exists after the wrapper script completes.
686
+ --
687
+ -- The work-around here is that we don't use a response file at all if the
688
+ -- first argument (and only the first argument) to `ghc` is
689
+ -- `--interactive`. This ensures that `hie-bios` and all downstream
690
+ -- utilities (e.g. `haskell-language-server`) continue working.
691
+ --
692
+ --
693
+ useResponseFile =
694
+ case args of
695
+ " --interactive" : _ -> False
696
+ _ -> compilerSupportsResponseFiles
697
+
698
+ if not useResponseFile
699
+ then runProgramInvocation verbosity invocation
700
+ else do
701
+ let (rtsArgs, otherArgs) = splitRTSArgs args
702
+
703
+ withResponseFile
704
+ verbosity
705
+ tempFileOptions
706
+ fileNameTemplate
707
+ encoding
708
+ otherArgs
709
+ $ \ responseFile -> do
710
+ let newInvocation =
711
+ invocation{progInvokeArgs = (' @' : responseFile) : rtsArgs}
712
+
713
+ infoNoWrap verbosity $
714
+ " GHC response file arguments: "
715
+ <> case otherArgs of
716
+ [] -> " "
717
+ arg : args' -> Process. showCommandForUser arg args'
718
+
719
+ runProgramInvocation verbosity newInvocation
720
+
650
721
ghcInvocation
651
722
:: Verbosity
652
723
-> ConfiguredProgram
@@ -960,6 +1031,26 @@ packageDbArgs implInfo
960
1031
| flagPackageConf implInfo = packageDbArgsConf
961
1032
| otherwise = packageDbArgsDb
962
1033
1034
+ -- | Split a list of command-line arguments into RTS arguments and non-RTS
1035
+ -- arguments.
1036
+ splitRTSArgs :: [String ] -> ([String ], [String ])
1037
+ splitRTSArgs args =
1038
+ let addRTSArg arg ~ (rtsArgs, nonRTSArgs) = (arg : rtsArgs, nonRTSArgs)
1039
+ addNonRTSArg arg ~ (rtsArgs, nonRTSArgs) = (rtsArgs, arg : nonRTSArgs)
1040
+
1041
+ go _ [] = ([] , [] )
1042
+ go isRTSArg (arg : rest) =
1043
+ case arg of
1044
+ " +RTS" -> addRTSArg arg $ go True rest
1045
+ " -RTS" -> addRTSArg arg $ go False rest
1046
+ " --RTS" -> ([arg], rest)
1047
+ " --" -> ([] , arg : rest)
1048
+ _ ->
1049
+ if isRTSArg
1050
+ then addRTSArg arg $ go isRTSArg rest
1051
+ else addNonRTSArg arg $ go isRTSArg rest
1052
+ in go False args
1053
+
963
1054
-- -----------------------------------------------------------------------------
964
1055
-- Boilerplate Monoid instance for GhcOptions
965
1056
0 commit comments