forked from commercialhaskell/stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHoogle.hs
More file actions
206 lines (201 loc) · 8.77 KB
/
Hoogle.hs
File metadata and controls
206 lines (201 loc) · 8.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | A wrapper around hoogle.
module Stack.Hoogle
( hoogleCmd
) where
import Stack.Prelude
import qualified Data.ByteString.Lazy.Char8 as BL8
import Data.Char (isSpace)
import qualified Data.Text as T
import Distribution.PackageDescription (packageDescription, package)
import Distribution.Types.PackageName (mkPackageName)
import Distribution.Version (mkVersion)
import Lens.Micro ((?~))
import Path (parseAbsFile)
import Path.IO hiding (findExecutable)
import qualified Stack.Build
import Stack.Build.Target (NeedTargets(NeedTargets))
import Stack.Runners
import Stack.Types.Config
import Stack.Types.SourceMap
import qualified RIO.Map as Map
import RIO.Process
-- | Helper type to duplicate log messages
data Muted = Muted | NotMuted
-- | Hoogle command.
hoogleCmd :: ([String],Bool,Bool,Bool) -> RIO Runner ()
hoogleCmd (args,setup,rebuild,startServer) =
local (over globalOptsL modifyGO) $
withConfig YesReexec $
withDefaultEnvConfig $ do
hooglePath <- ensureHoogleInPath
generateDbIfNeeded hooglePath
runHoogle hooglePath args'
where
modifyGO :: GlobalOpts -> GlobalOpts
modifyGO = globalOptsBuildOptsMonoidL . buildOptsMonoidHaddockL ?~ True
args' :: [String]
args' = if startServer
then ["server", "--local", "--port", "8080"]
else []
++ args
generateDbIfNeeded :: Path Abs File -> RIO EnvConfig ()
generateDbIfNeeded hooglePath = do
databaseExists <- checkDatabaseExists
if databaseExists && not rebuild
then return ()
else if setup || rebuild
then do
logWarn
(if rebuild
then "Rebuilding database ..."
else "No Hoogle database yet. Automatically building haddocks and hoogle database (use --no-setup to disable) ...")
buildHaddocks
logInfo "Built docs."
generateDb hooglePath
logInfo "Generated DB."
else do
logError
"No Hoogle database. Not building one due to --no-setup"
bail
generateDb :: Path Abs File -> RIO EnvConfig ()
generateDb hooglePath = do
do dir <- hoogleRoot
createDirIfMissing True dir
runHoogle hooglePath ["generate", "--local"]
buildHaddocks :: RIO EnvConfig ()
buildHaddocks = do
config <- view configL
runRIO config $ -- a bit weird that we have to drop down like this
catch (withDefaultEnvConfig $ Stack.Build.build Nothing)
(\(_ :: ExitCode) -> return ())
hooglePackageName = mkPackageName "hoogle"
hoogleMinVersion = mkVersion [5, 0]
hoogleMinIdent =
PackageIdentifier hooglePackageName hoogleMinVersion
installHoogle :: RIO EnvConfig (Path Abs File)
installHoogle = requiringHoogle Muted $ do
Stack.Build.build Nothing
mhooglePath' <- findExecutable "hoogle"
case mhooglePath' of
Right hooglePath -> parseAbsFile hooglePath
Left _ -> do
logWarn "Couldn't find hoogle in path after installing. This shouldn't happen, may be a bug."
bail
requiringHoogle :: Muted -> RIO EnvConfig x -> RIO EnvConfig x
requiringHoogle muted f = do
hoogleTarget <- do
sourceMap <- view $ sourceMapL . to smDeps
case Map.lookup hooglePackageName sourceMap of
Just hoogleDep ->
case dpLocation hoogleDep of
PLImmutable pli ->
T.pack . packageIdentifierString <$>
restrictMinHoogleVersion muted (packageLocationIdent pli)
plm@(PLMutable _) -> do
T.pack . packageIdentifierString . package . packageDescription
<$> loadCabalFile plm
Nothing -> do
-- not muted because this should happen only once
logWarn "No hoogle version was found, trying to install the latest version"
mpir <- getLatestHackageVersion YesRequireHackageIndex hooglePackageName UsePreferredVersions
let hoogleIdent = case mpir of
Nothing -> hoogleMinIdent
Just (PackageIdentifierRevision _ ver _) ->
PackageIdentifier hooglePackageName ver
T.pack . packageIdentifierString <$>
restrictMinHoogleVersion muted hoogleIdent
config <- view configL
let boptsCLI = defaultBuildOptsCLI
{ boptsCLITargets = [hoogleTarget]
}
runRIO config $ withEnvConfig NeedTargets boptsCLI f
restrictMinHoogleVersion
:: HasLogFunc env
=> Muted -> PackageIdentifier -> RIO env PackageIdentifier
restrictMinHoogleVersion muted ident = do
if ident < hoogleMinIdent
then do
muteableLog LevelWarn muted $
"Minimum " <>
fromString (packageIdentifierString hoogleMinIdent) <>
" is not in your index. Installing the minimum version."
pure hoogleMinIdent
else do
muteableLog LevelInfo muted $
"Minimum version is " <>
fromString (packageIdentifierString hoogleMinIdent) <>
". Found acceptable " <>
fromString (packageIdentifierString ident) <>
" in your index, requiring its installation."
pure ident
muteableLog :: HasLogFunc env => LogLevel -> Muted -> Utf8Builder -> RIO env ()
muteableLog logLevel muted msg =
case muted of
Muted -> pure ()
NotMuted -> logGeneric "" logLevel msg
runHoogle :: Path Abs File -> [String] -> RIO EnvConfig ()
runHoogle hooglePath hoogleArgs = do
config <- view configL
menv <- liftIO $ configProcessContextSettings config envSettings
dbpath <- hoogleDatabasePath
let databaseArg = ["--database=" ++ toFilePath dbpath]
withProcessContext menv $ proc
(toFilePath hooglePath)
(hoogleArgs ++ databaseArg)
runProcess_
bail :: RIO EnvConfig a
bail = exitWith (ExitFailure (-1))
checkDatabaseExists = do
path <- hoogleDatabasePath
liftIO (doesFileExist path)
ensureHoogleInPath :: RIO EnvConfig (Path Abs File)
ensureHoogleInPath = do
config <- view configL
menv <- liftIO $ configProcessContextSettings config envSettings
mhooglePath <- runRIO menv (findExecutable "hoogle") <>
requiringHoogle NotMuted (findExecutable "hoogle")
eres <- case mhooglePath of
Left _ -> return $ Left "Hoogle isn't installed."
Right hooglePath -> do
result <- withProcessContext menv
$ proc hooglePath ["--numeric-version"]
$ tryAny . fmap fst . readProcess_
let unexpectedResult got = Left $ T.concat
[ "'"
, T.pack hooglePath
, " --numeric-version' did not respond with expected value. Got: "
, got
]
return $ case result of
Left err -> unexpectedResult $ T.pack (show err)
Right bs -> case parseVersion (takeWhile (not . isSpace) (BL8.unpack bs)) of
Nothing -> unexpectedResult $ T.pack (BL8.unpack bs)
Just ver
| ver >= hoogleMinVersion -> Right hooglePath
| otherwise -> Left $ T.concat
[ "Installed Hoogle is too old, "
, T.pack hooglePath
, " is version "
, T.pack $ versionString ver
, " but >= 5.0 is required."
]
case eres of
Right hooglePath -> parseAbsFile hooglePath
Left err
| setup -> do
logWarn $ display err <> " Automatically installing (use --no-setup to disable) ..."
installHoogle
| otherwise -> do
logWarn $ display err <> " Not installing it due to --no-setup."
bail
envSettings =
EnvSettings
{ esIncludeLocals = True
, esIncludeGhcPackagePath = True
, esStackExe = True
, esLocaleUtf8 = False
, esKeepGhcRts = False
}