1
- {-# LANGUAGE NoImplicitPrelude #-}
2
- {-# LANGUAGE OverloadedRecordDot #-}
3
- {-# LANGUAGE OverloadedStrings #-}
1
+ {-# LANGUAGE NoImplicitPrelude #-}
2
+ {-# LANGUAGE DuplicateRecordFields #-}
3
+ {-# LANGUAGE NoFieldSelectors #-}
4
+ {-# LANGUAGE OverloadedRecordDot #-}
5
+ {-# LANGUAGE OverloadedStrings #-}
4
6
5
7
{-|
6
8
Module : Stack.IDE
@@ -11,33 +13,68 @@ Types and functions related to Stack's @ide@ command.
11
13
-}
12
14
13
15
module Stack.IDE
14
- ( OutputStream (.. )
15
- , ListPackagesCmd (.. )
16
- , idePackagesCmd
16
+ ( idePackagesCmd
17
17
, ideTargetsCmd
18
- , listPackages
19
- , listTargets
18
+ , ideGhcOptionsCmd
20
19
) where
21
20
21
+ import qualified Data.ByteString as BS
22
22
import qualified Data.Map as Map
23
+ import qualified Data.Map.Strict as M
23
24
import qualified Data.Set as Set
24
25
import qualified Data.Text as T
25
26
import Data.Tuple ( swap )
27
+ import Stack.Build.FileTargets
28
+ ( findFileTargets , getAllLocalTargets , getAllNonLocalTargets
29
+ , getGhciPkgInfos , loadGhciPkgDescs , optsAndMacros
30
+ )
31
+ import Stack.Build.Installed ( toInstallMap )
32
+ import Stack.Build.Source ( localDependencies , projectLocalPackages )
33
+ import Stack.Build.Target ( NeedTargets (.. ) )
34
+ import Stack.Package ( topSortPackageComponent )
35
+ import Path.Extra ( forgivingResolveFile' )
26
36
import Stack.Prelude
27
37
import Stack.Runners
28
- ( ShouldReexec (.. ), withBuildConfig , withConfig )
38
+ ( ShouldReexec (.. ), withBuildConfig , withConfig
39
+ , withEnvConfig
40
+ )
29
41
import Stack.Types.BuildConfig
30
42
( BuildConfig (.. ), HasBuildConfig (.. ) )
43
+ import Stack.Types.BuildOpts ( BuildOpts (.. ) )
44
+ import qualified Stack.Types.BuildOpts as BenchmarkOpts ( BenchmarkOpts (.. ) )
45
+ import qualified Stack.Types.BuildOpts as TestOpts ( TestOpts (.. ) )
46
+ import Stack.Types.BuildOptsCLI
47
+ ( BuildOptsCLI (.. ), defaultBuildOptsCLI )
48
+ import Stack.Types.Config ( buildOptsL )
49
+ import Stack.Types.EnvConfig ( EnvConfig (.. ), HasEnvConfig (.. ) )
31
50
import Stack.Types.IdeOpts ( ListPackagesCmd (.. ), OutputStream (.. ) )
32
51
import Stack.Types.NamedComponent
33
- ( NamedComponent , isCBench , isCExe , isCTest
52
+ ( NamedComponent , isCBench , isCExe , isCSubLib , isCTest
34
53
, renderPkgComponent
35
54
)
55
+ import Stack.Types.Package ( LocalPackage (.. ), Package (.. ) )
36
56
import Stack.Types.Runner ( Runner )
37
57
import Stack.Types.SourceMap
38
58
( ProjectPackage (.. ), SMWanted (.. ), ppComponentsMaybe )
39
59
import System.IO ( putStrLn )
40
60
61
+ -- | Type representing \'pretty\' exceptions thrown by functions exported by the
62
+ -- "Stack.IDE" module.
63
+ newtype IdePrettyException
64
+ = FileTargetIsInvalidAbsFile String
65
+ deriving (Show , Typeable )
66
+
67
+ instance Pretty IdePrettyException where
68
+ pretty (FileTargetIsInvalidAbsFile name) =
69
+ " [S-9208]"
70
+ <> line
71
+ <> fillSep
72
+ [ flow " Cannot work out a valid path for file target"
73
+ , style File (fromString name) <> " ."
74
+ ]
75
+
76
+ instance Exception IdePrettyException
77
+
41
78
-- | Function underlying the @stack ide packages@ command. List packages in the
42
79
-- project.
43
80
idePackagesCmd :: (OutputStream , ListPackagesCmd ) -> RIO Runner ()
@@ -93,3 +130,70 @@ listTargets stream isCompType = do
93
130
toNameAndComponent pkgName' =
94
131
fmap (map (pkgName',) . Set. toList) . ppComponentsMaybe (\ x ->
95
132
if isCompType x then Just x else Nothing )
133
+
134
+ -- | Function underlying the @stack ide ghc-options@ command.
135
+ ideGhcOptionsCmd :: Text -> RIO Runner ()
136
+ ideGhcOptionsCmd rawTarget =
137
+ let boptsCLI = defaultBuildOptsCLI { initialBuildSteps = True }
138
+ in withConfig YesReexec $ withEnvConfig AllowNoTargets boptsCLI $ do
139
+ bopts <- view buildOptsL
140
+ -- override env so running of tests and benchmarks is disabled
141
+ let boptsLocal = bopts
142
+ { testOpts = bopts. testOpts { TestOpts. runTests = False }
143
+ , benchmarkOpts =
144
+ bopts. benchmarkOpts { BenchmarkOpts. runBenchmarks = False }
145
+ }
146
+ local (set buildOptsL boptsLocal) (ideGhcOptions rawTarget)
147
+
148
+ ideGhcOptions :: HasEnvConfig env => Text -> RIO env ()
149
+ ideGhcOptions rawTarget = do
150
+ sourceMap <- view $ envConfigL . to (. sourceMap)
151
+ installMap <- toInstallMap sourceMap
152
+ locals <- projectLocalPackages
153
+ depLocals <- localDependencies
154
+ let localMap = M. fromList [(lp. package. name, lp) | lp <- locals ++ depLocals]
155
+ -- Parse to either file targets or build targets
156
+ (inputTargets, mfileTargets) <- processRawTarget rawTarget >>= maybe
157
+ (pure (mempty , Nothing ))
158
+ -- Figure out targets based on file target
159
+ (findFileTargets locals . pure )
160
+ -- Get a list of all the local target packages.
161
+ (directlyWanted, extraLoadDeps) <-
162
+ getAllLocalTargets True inputTargets Nothing localMap
163
+ -- Get a list of all the non-local target packages.
164
+ nonLocalTargets <- getAllNonLocalTargets inputTargets
165
+ let localTargets = directlyWanted <> extraLoadDeps
166
+ getInternalDependencies target localPackage =
167
+ topSortPackageComponent localPackage. package target False
168
+ internalDependencies =
169
+ M. intersectionWith getInternalDependencies inputTargets localMap
170
+ relevantDependencies = M. filter (any isCSubLib) internalDependencies
171
+ -- Load package descriptions.
172
+ pkgDescs <- loadGhciPkgDescs mempty localTargets
173
+ pkgs <- getGhciPkgInfos installMap [] (fmap fst mfileTargets) pkgDescs
174
+ (omittedOpts, pkgopts, macros) <-
175
+ optsAndMacros
176
+ Nothing
177
+ localTargets
178
+ pkgs
179
+ nonLocalTargets
180
+ relevantDependencies
181
+ let outputDivider = liftIO $ putStrLn " ---"
182
+ outputDivider
183
+ mapM_ (liftIO . putStrLn ) pkgopts
184
+ outputDivider
185
+ liftIO $ BS. putStr macros
186
+ outputDivider
187
+ mapM_ (liftIO . putStrLn ) omittedOpts
188
+ outputDivider
189
+
190
+ processRawTarget :: HasEnvConfig env => Text -> RIO env (Maybe (Path Abs File ))
191
+ processRawTarget rawTarget =
192
+ if " .hs" `T.isSuffixOf` rawTarget || " .lhs" `T.isSuffixOf` rawTarget
193
+ then
194
+ forgivingResolveFile' rawTarget' >>= maybe
195
+ (prettyThrowM $ FileTargetIsInvalidAbsFile rawTarget')
196
+ (pure . Just )
197
+ else pure Nothing
198
+ where
199
+ rawTarget' = T. unpack rawTarget
0 commit comments