1
+ {-# LANGUAGE LambdaCase #-}
1
2
{-# LANGUAGE NoImplicitPrelude #-}
2
3
{-# LANGUAGE OverloadedStrings #-}
3
4
@@ -7,18 +8,28 @@ module Stack.Unpack
7
8
, unpackPackages
8
9
) where
9
10
11
+ import qualified Data.List as L
10
12
import Path ( (</>) , parseRelDir )
11
13
import Path.IO ( doesDirExist , resolveDir' )
12
14
import Pantry ( loadSnapshot )
13
15
import qualified RIO.Map as Map
14
16
import RIO.Process ( HasProcessContext )
15
17
import qualified RIO.Set as Set
16
18
import qualified RIO.Text as T
17
- import Stack.Config ( makeConcreteResolver )
19
+ import Stack.Config ( loadProjectConfig , makeConcreteResolver )
18
20
import Stack.Prelude
19
21
import Stack.Runners ( ShouldReexec (.. ), withConfig )
22
+ import Stack.Types.Project ( Project (.. ) )
20
23
import Stack.Types.GlobalOpts ( GlobalOpts (.. ) )
24
+ import Stack.Types.ProjectConfig ( ProjectConfig (.. ) )
21
25
import Stack.Types.Runner ( Runner , globalOptsL )
26
+ import Stack.Types.StackYamlLoc ( StackYamlLoc (.. ) )
27
+ import Distribution.Types.PackageName ( unPackageName )
28
+
29
+ data Unpackable
30
+ = UnpackName PackageName
31
+ | UnpackIdent PackageIdentifierRevision
32
+ | UnpackRepoUrl (PackageName , RawPackageLocationImmutable )
22
33
23
34
-- | Type representing \'pretty\' exceptions thrown by functions exported by the
24
35
-- "Stack.Unpack" module.
@@ -54,24 +65,41 @@ unpackCmd ::
54
65
-> RIO Runner ()
55
66
unpackCmd (names, Nothing ) = unpackCmd (names, Just " ." )
56
67
unpackCmd (names, Just dstPath) = withConfig NoReexec $ do
68
+ mStackYaml <- view $ globalOptsL. to globalStackYaml
57
69
mresolver <- view $ globalOptsL. to globalResolver
58
70
mSnapshot <- forM mresolver $ \ resolver -> do
59
71
concrete <- makeConcreteResolver resolver
60
72
loc <- completeSnapshotLocation concrete
61
73
loadSnapshot loc
62
74
dstPath' <- resolveDir' $ T. unpack dstPath
63
- unpackPackages mSnapshot dstPath' names
75
+ unpackPackages mStackYaml mSnapshot dstPath' names
64
76
65
77
-- | Intended to work for the command line command.
66
78
unpackPackages ::
67
79
forall env . (HasPantryConfig env , HasProcessContext env , HasTerm env )
68
- => Maybe RawSnapshot -- ^ When looking up by name, take from this build plan.
80
+ => StackYamlLoc
81
+ -> Maybe RawSnapshot -- ^ When looking up by name, take from this build plan.
69
82
-> Path Abs Dir -- ^ Destination.
70
83
-> [String ] -- ^ Names or identifiers.
71
84
-> RIO env ()
72
- unpackPackages mSnapshot dest input = do
73
- let (errs1, (names, pirs1)) =
74
- fmap partitionEithers $ partitionEithers $ map parse input
85
+ unpackPackages mStackYaml mSnapshot dest input = do
86
+ parsed <- mapM (parse mStackYaml) input
87
+ let (errs1, unpackables) = partitionEithers parsed
88
+ let (names, pirs1, raws) = splitUnpackable unpackables
89
+
90
+ repos <- catMaybes <$> mapM
91
+ (\ case
92
+ (name, x@ RPLIRepo {}) -> do
93
+ suffix <- parseRelDir $ unPackageName name
94
+ pure $ Just (x, dest </> suffix)
95
+ (_, RPLIHackage {}) -> pure Nothing
96
+ (_, RPLIArchive {}) -> pure Nothing )
97
+ (longestUnique raws)
98
+
99
+ forM_ repos $ \ (loc, dest') -> do
100
+ unpackPackageLocationRaw dest' loc
101
+ prettyInfoL $ unpackMessage loc dest'
102
+
75
103
locs1 <- forM pirs1 $ \ pir -> do
76
104
loc <- fmap cplComplete $ completePackageLocation $ RPLIHackage pir Nothing
77
105
pure (loc, packageLocationIdent loc)
@@ -93,12 +121,7 @@ unpackPackages mSnapshot dest input = do
93
121
94
122
forM_ (Map. toList locs) $ \ (loc, dest') -> do
95
123
unpackPackageLocation dest' loc
96
- prettyInfoL
97
- [ " Unpacked"
98
- , fromString $ T. unpack $ textDisplay loc
99
- , " to"
100
- , pretty dest' <> " ."
101
- ]
124
+ prettyInfoL $ unpackMessage loc dest'
102
125
where
103
126
toLoc | Just snapshot <- mSnapshot = toLocSnapshot snapshot
104
127
| otherwise = toLocNoSnapshot
@@ -158,14 +181,79 @@ unpackPackages mSnapshot dest input = do
158
181
loc <- cplComplete <$> completePackageLocation (rspLocation sp)
159
182
pure $ Right (loc, packageLocationIdent loc)
160
183
161
- -- Possible future enhancement: parse names as name + version range
162
- parse s =
163
- case parsePackageName s of
164
- Just x -> Right $ Left x
184
+ -- Possible future enhancement: parse names as name + version range
185
+ parse ::
186
+ (HasPantryConfig env , HasTerm env )
187
+ => StackYamlLoc -> String -> RIO env (Either StyleDoc Unpackable )
188
+ parse mStackYaml s = do
189
+ extra <- toLocExtraDep mStackYaml (fromString s)
190
+ pure $ case extra of
191
+ Just x -> Right $ UnpackRepoUrl x
192
+ Nothing -> case parsePackageName s of
193
+ Just x -> Right $ UnpackName x
165
194
Nothing ->
166
195
case parsePackageIdentifierRevision (T. pack s) of
167
- Right x -> Right $ Right x
196
+ Right x -> Right $ UnpackIdent x
168
197
Left _ -> Left $ fillSep
169
198
[ flow " Could not parse as package name or identifier:"
170
199
, style Current (fromString s) <> " ."
171
200
]
201
+
202
+ toLocExtraDep ::
203
+ (HasPantryConfig env , HasTerm env )
204
+ => StackYamlLoc
205
+ -> PackageName
206
+ -> RIO env (Maybe (PackageName , RawPackageLocationImmutable ))
207
+ toLocExtraDep mstackYaml name = do
208
+ pc <- loadProjectConfig mstackYaml
209
+ case pc of
210
+ PCGlobalProject -> pure Nothing
211
+ PCNoProject {} -> pure Nothing
212
+ PCProject (Project {projectDependencies}, _, _) -> do
213
+ let hits = mapMaybe (\ case
214
+ RPLImmutable (RPLIRepo repo meta@ RawPackageMetadata {rpmName = Just n}) -> do
215
+ if n == name then Just (name, (repo, meta)) else Nothing
216
+ RPLImmutable (RPLIRepo repo@ Repo {repoUrl} meta) -> do
217
+ if T. isSuffixOf (T. pack $ unPackageName name) repoUrl then Just (name, (repo, meta)) else Nothing
218
+ RPLMutable {} -> Nothing
219
+ RPLImmutable {} -> Nothing ) projectDependencies
220
+
221
+ case hits of
222
+ [] -> pure Nothing
223
+ [(n, (repo, meta))] -> pure $ Just (n, RPLIRepo repo meta)
224
+ _ -> do
225
+ prettyWarnL
226
+ [ flow " Multiple matches for"
227
+ , style Current (fromString $ packageNameString name) <> " :"
228
+ ]
229
+ forM_ hits $ \ case
230
+ (_, (repo, RawPackageMetadata {rpmName})) -> do
231
+ prettyWarnL
232
+ [ style Current (fromString . T. unpack $ repoUrl repo)
233
+ , style Current (fromString $ maybe " " unPackageName rpmName)
234
+ ]
235
+ pure Nothing
236
+
237
+ splitUnpackable ::
238
+ [Unpackable ]
239
+ -> ([PackageName ], [PackageIdentifierRevision ], [(PackageName , RawPackageLocationImmutable )])
240
+ splitUnpackable = foldl' go ([] , [] , [] )
241
+ where
242
+ go (names, pirs, raws) = \ case
243
+ UnpackName name -> (name : names, pirs, raws)
244
+ UnpackIdent pir -> (names, pir : pirs, raws)
245
+ UnpackRepoUrl raw -> (names, pirs, raw : raws)
246
+
247
+ longestUnique ::
248
+ [(PackageName , RawPackageLocationImmutable )]
249
+ -> [(PackageName , RawPackageLocationImmutable )]
250
+ longestUnique xs =
251
+ L. concat $ L. groupBy (\ (_, p1) (_, p2) -> p1 == p2) (L. take 1 $ L. sortBy (flip (comparing fst )) xs)
252
+
253
+ unpackMessage :: Display a => a -> Path Abs Dir -> [StyleDoc ]
254
+ unpackMessage loc dest =
255
+ [ " Unpacked"
256
+ , fromString $ T. unpack $ textDisplay loc
257
+ , " to"
258
+ , pretty dest <> " ."
259
+ ]
0 commit comments