@@ -51,27 +51,84 @@ import HIE.Bios.Process
51
51
import GHC.Fingerprint (fingerprintString )
52
52
import GHC.ResponseFile (escapeArgs )
53
53
54
+ {- Note [Finding ghc-options with cabal]
55
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
56
+ We want to know how to compile a cabal component with GHC.
57
+ There are two main ways to obtain the ghc-options:
58
+
59
+ 1. `cabal --with-ghc <ghc-shim>` (for exe:cabal <3.15 or lib:Cabal <3.15)
60
+
61
+ In this approach, we generate a <ghc-shim> which is passed to the exe:cabal process.
62
+ If a package needs to be compiled, we compile the package with the same GHC process that
63
+ exe:cabal would have used.
64
+
65
+ If the first argument is `--interactive`, then we do not launch the GHCi process,
66
+ but record all the arguments for later processing.
67
+
68
+ 2. `cabal --with-repl <repl-shim>` (for exe:cabal >=3.15 and lib:Cabal >=3.15)
69
+
70
+ The <repl-shim> is notably simpler than the <ghc-shim>, as `--with-repl` invokes
71
+ <repl-shim> *only* as the final GHCi process, not for compiling dependencies or
72
+ executing preprocessors.
73
+
74
+ Thus, <repl-shim> merely needs to log all arguments that are passed to <repl-shim>.
75
+
76
+ This is the simpler, more maintainable approach, with fewer unintended side-effects.
77
+
78
+ === Finding the GHC process cabal uses to compile a project with
79
+
80
+ We want HLS and hie-bios to honour the `with-compiler` field in `cabal.project` files.
81
+ Again, we identify two ways to find the exact GHC program that is going to be invoked by cabal.
82
+
83
+ 1. `cabal exec -- ghc --interactive -e System.Environment.getExecutablePath` (for exe:cabal <3.14)
84
+
85
+ Ignoring a couple of details, we can get the path to the raw executable by asking
86
+ the GHCi process for its executable path.
87
+ The issue is that on linux, the executable path is insufficient, the GHC executable
88
+ invoked by the user is "wrapped" in a shim that specifies the libdir location, e.g.:
89
+
90
+ > cat /home/hugin/.ghcup/bin/ghc
91
+ #!/bin/sh
92
+ exedir="/home/hugin/.ghcup/ghc/9.6.7/lib/ghc-9.6.7/bin"
93
+ exeprog="./ghc-9.6.7"
94
+ executablename="/home/hugin/.ghcup/ghc/9.6.7/lib/ghc-9.6.7/bin/./ghc-9.6.7"
95
+ bindir="/home/hugin/.ghcup/ghc/9.6.7/bin"
96
+ libdir="/home/hugin/.ghcup/ghc/9.6.7/lib/ghc-9.6.7/lib"
97
+ docdir="/home/hugin/.ghcup/ghc/9.6.7/share/doc/ghc-9.6.7"
98
+ includedir="/home/hugin/.ghcup/ghc/9.6.7/include"
99
+
100
+ exec "$executablename" -B"$libdir" ${1+"$@"}
101
+
102
+ We find the libdir by asking GHC via `cabal exec -- ghc --print-libdir`.
103
+ Once we have these two paths, we also need to find the `ghc-pkg` location,
104
+ otherwise cabal will use the `ghc-pkg` that is found on PATH, which is not correct
105
+ if the user overwrites the compiler field via `with-compiler`.
106
+
107
+ To find `ghc-pkg`, we assume it is going to be located next to the `libdir`, and then
108
+ reconstruct the wrapper shim for `ghc-pkg`.
109
+
110
+ Then we reconstructed both the ghc and ghc-pkg program that is going to be used by cabal
111
+ and can use it in the <ghc-shim> and `cabal repl --with-compiler <ghc-shim> --with-hc-pkg <hc-pkg-shim>`.
112
+
113
+ Calling `cabal exec` can be very slow on a large codebase, over 1 second per invocation.
114
+
115
+ 2. `cabal path` (for exe:cabal >= 3.14)
116
+
117
+ We can skip the reconstruction of the GHC shim by using the output of `cabal path --compiler-info`.
118
+ This gives us the location of the GHC executable shim, so we don't need to reconstruct any shims.
119
+
120
+ However, we still have to reconstruct the ghc-pkg shim when using `cabal repl --with-compiler`.
121
+
122
+ `cabal path` is incredibly fast to invoke, as it circumvents running the cabal solver.
123
+ It is easier to maintain as well.
124
+ -}
125
+
54
126
-- | Main entry point into the cabal cradle invocation.
55
127
--
56
128
-- This function does a lot of work, supporting multiple cabal-install versions and
57
129
-- different ways of obtaining the component options.
58
130
--
59
- -- Generally, there are three different, supported ways to obtain the ghc-options:
60
- --
61
- -- * For cabal <3.14.
62
- -- The oldest supported way, we are doing roughly the following:
63
- -- 1. Find the 'ghc' version used by the cabal project (might be overwritten by a @cabal.project@ file)
64
- -- 2. Guess the libdir location and ghc-pkg version
65
- -- 3. Create wrapper shims for ghc-pkg (<ghc-pkg-shim>) and ghc (<ghc-shim>).
66
- -- 4. call @cabal repl --with-compiler <ghc-shim> --with-hc-pkg <ghc-pkg-shim>@ and log the ghc-options
67
- -- * For cabal >=3.14, we can use the new @cabal path@ command to speed up step 1. and 2. of the previous approach.
68
- -- * For cabal >=3.15, cabal supports the @--with-repl@ flag.
69
- -- This allows us to skip step 1-2 entirely, and we simply need to create a shell program
70
- -- which can be passed to @cabal repl --with-repl@, which records the final ghc-options.
71
- --
72
- -- However, this last approach doesn't work, if the user uses a custom-setup forcing a @lib:Cabal <3.15@.
73
- -- If this is the case, we fall back to @cabal path@ and creating wrapper shims.
74
- --
131
+ -- See Note [Finding ghc-options with cabal] for a detailed elaboration.
75
132
cabalAction ::
76
133
ResolvedCradles a ->
77
134
FilePath ->
@@ -149,7 +206,7 @@ cabalAction cradles workDir mc l projectFile fp loadStyle = do
149
206
where
150
207
-- | Run the given cabal process to obtain ghc options.
151
208
-- In the special case of 'cabal >= 3.15' but 'lib:Cabal <3.15' (via custom-setups),
152
- -- we gracefully fall back to the given action to create an alterantive cabal process which
209
+ -- we gracefully fall back to the given action to create an alternative cabal process which
153
210
-- we use to find the ghc options.
154
211
runCabalToGetGhcOptions ::
155
212
Process. CreateProcess ->
@@ -434,6 +491,10 @@ withGhcWrapperTool l mkGhcCall wdir = do
434
491
435
492
-- | Generate a script/binary that can be passed to cabal's '--with-repl'.
436
493
-- On windows, this compiles a Haskell file, while on other systems, we persist
494
+ -- a haskell source file and ad-hoc compile it with 'GhcProc'.
495
+ --
496
+ -- 'GhcProc' is unused on other platforms.
497
+ --
437
498
withReplWrapperTool :: LogAction IO (WithSeverity Log ) -> GhcProc -> FilePath -> IO FilePath
438
499
withReplWrapperTool l mkGhcCall wdir =
439
500
withWrapperTool l mkGhcCall wdir " repl-wrapper" cabalWithReplWrapperHs cabalWithReplWrapper
0 commit comments