forked from commercialhaskell/stack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathResolver.hs
More file actions
155 lines (138 loc) · 6.03 KB
/
Resolver.hs
File metadata and controls
155 lines (138 loc) · 6.03 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
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE StandaloneDeriving #-}
module Stack.Types.Resolver
(Resolver
,IsLoaded(..)
,LoadedResolver
,ResolverThat's(..)
,parseResolverText
,resolverDirName
,resolverName
,customResolverHash
,toResolverNotLoaded
,AbstractResolver(..)
,readAbstractResolver
) where
import Control.Applicative
import Control.Monad.Catch (MonadThrow, throwM)
import Data.Aeson.Extended
(ToJSON, toJSON, FromJSON, parseJSON, object,
WithJSONWarnings(..), Value(String, Object), (.=),
noJSONWarnings, (..:), withObjectWarnings)
import Data.Monoid.Extra
import Data.Text (Text)
import qualified Data.Text as T
import Data.Text.Encoding (decodeUtf8)
import Data.Text.Read (decimal)
import Options.Applicative (ReadM)
import qualified Options.Applicative as OA
import qualified Options.Applicative.Types as OA
import Prelude
import Stack.Types.BuildPlan (parseSnapName, renderSnapName, SnapName, SnapshotHash,
trimmedSnapshotHash)
import {-# SOURCE #-} Stack.Types.Config (ConfigException(..))
import Stack.Types.Compiler
data IsLoaded = Loaded | NotLoaded
type LoadedResolver = ResolverThat's 'Loaded
type Resolver = ResolverThat's 'NotLoaded
-- TODO: once GHC 8.0 is the lowest version we support, make these into
-- actual haddock comments...
-- | How we resolve which dependencies to install given a set of packages.
data ResolverThat's (l :: IsLoaded) where
-- Use an official snapshot from the Stackage project, either an LTS
-- Haskell or Stackage Nightly.
ResolverSnapshot :: !SnapName -> ResolverThat's l
-- Require a specific compiler version, but otherwise provide no
-- build plan. Intended for use cases where end user wishes to
-- specify all upstream dependencies manually, such as using a
-- dependency solver.
ResolverCompiler :: !CompilerVersion -> ResolverThat's l
-- A custom resolver based on the given name and URL. When a URL is
-- provided, it file is to be completely immutable. Filepaths are
-- always loaded. This constructor is used before the build-plan has
-- been loaded, as we do not yet know the custom snapshot's hash.
ResolverCustom :: !Text -> !Text -> ResolverThat's 'NotLoaded
-- Like 'ResolverCustom', but after loading the build-plan, so we
-- have a hash. This is necessary in order to identify the location
-- files are stored for the resolver.
ResolverCustomLoaded :: !Text -> !Text -> !SnapshotHash -> ResolverThat's 'Loaded
deriving instance Eq (ResolverThat's k)
deriving instance Show (ResolverThat's k)
instance ToJSON (ResolverThat's k) where
toJSON x = case x of
ResolverSnapshot{} -> toJSON $ resolverName x
ResolverCompiler{} -> toJSON $ resolverName x
ResolverCustom n l -> handleCustom n l
ResolverCustomLoaded n l _ -> handleCustom n l
where
handleCustom n l = object
[ "name" .= n
, "location" .= l
]
instance FromJSON (WithJSONWarnings (ResolverThat's 'NotLoaded)) where
-- Strange structuring is to give consistent error messages
parseJSON v@(Object _) = withObjectWarnings "Resolver" (\o -> ResolverCustom
<$> o ..: "name"
<*> o ..: "location") v
parseJSON (String t) = either (fail . show) return (noJSONWarnings <$> parseResolverText t)
parseJSON _ = fail "Invalid Resolver, must be Object or String"
-- | Convert a Resolver into its @Text@ representation, as will be used by
-- directory names
resolverDirName :: LoadedResolver -> Text
resolverDirName (ResolverSnapshot name) = renderSnapName name
resolverDirName (ResolverCompiler v) = compilerVersionText v
resolverDirName (ResolverCustomLoaded name _ hash) = "custom-" <> name <> "-" <> decodeUtf8 (trimmedSnapshotHash hash)
-- | Convert a Resolver into its @Text@ representation for human
-- presentation.
resolverName :: ResolverThat's l -> Text
resolverName (ResolverSnapshot name) = renderSnapName name
resolverName (ResolverCompiler v) = compilerVersionText v
resolverName (ResolverCustom name _) = "custom-" <> name
resolverName (ResolverCustomLoaded name _ _) = "custom-" <> name
customResolverHash :: LoadedResolver-> Maybe SnapshotHash
customResolverHash (ResolverCustomLoaded _ _ hash) = Just hash
customResolverHash _ = Nothing
-- | Try to parse a @Resolver@ from a @Text@. Won't work for complex resolvers (like custom).
parseResolverText :: MonadThrow m => Text -> m Resolver
parseResolverText t
| Right x <- parseSnapName t = return $ ResolverSnapshot x
| Just v <- parseCompilerVersion t = return $ ResolverCompiler v
| otherwise = throwM $ ParseResolverException t
toResolverNotLoaded :: LoadedResolver -> Resolver
toResolverNotLoaded r = case r of
ResolverSnapshot s -> ResolverSnapshot s
ResolverCompiler v -> ResolverCompiler v
ResolverCustomLoaded n l _ -> ResolverCustom n l
-- | Either an actual resolver value, or an abstract description of one (e.g.,
-- latest nightly).
data AbstractResolver
= ARLatestNightly
| ARLatestLTS
| ARLatestLTSMajor !Int
| ARResolver !Resolver
| ARGlobal
deriving Show
readAbstractResolver :: ReadM AbstractResolver
readAbstractResolver = do
s <- OA.readerAsk
case s of
"global" -> return ARGlobal
"nightly" -> return ARLatestNightly
"lts" -> return ARLatestLTS
'l':'t':'s':'-':x | Right (x', "") <- decimal $ T.pack x ->
return $ ARLatestLTSMajor x'
_ ->
case parseResolverText $ T.pack s of
Left e -> OA.readerError $ show e
Right x -> return $ ARResolver x