1+ {-# LANGUAGE LambdaCase #-}
12{-# LANGUAGE NoImplicitPrelude #-}
23{-# LANGUAGE OverloadedStrings #-}
34
@@ -7,18 +8,28 @@ module Stack.Unpack
78 , unpackPackages
89 ) where
910
11+ import qualified Data.List as L
1012import Path ( (</>) , parseRelDir )
1113import Path.IO ( doesDirExist , resolveDir' )
1214import Pantry ( loadSnapshot )
1315import qualified RIO.Map as Map
1416import RIO.Process ( HasProcessContext )
1517import qualified RIO.Set as Set
1618import qualified RIO.Text as T
17- import Stack.Config ( makeConcreteResolver )
19+ import Stack.Config ( loadProjectConfig , makeConcreteResolver )
1820import Stack.Prelude
1921import Stack.Runners ( ShouldReexec (.. ), withConfig )
22+ import Stack.Types.Project ( Project (.. ) )
2023import Stack.Types.GlobalOpts ( GlobalOpts (.. ) )
24+ import Stack.Types.ProjectConfig ( ProjectConfig (.. ) )
2125import 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 )
2233
2334-- | Type representing \'pretty\' exceptions thrown by functions exported by the
2435-- "Stack.Unpack" module.
@@ -54,24 +65,41 @@ unpackCmd ::
5465 -> RIO Runner ()
5566unpackCmd (names, Nothing ) = unpackCmd (names, Just " ." )
5667unpackCmd (names, Just dstPath) = withConfig NoReexec $ do
68+ mStackYaml <- view $ globalOptsL. to globalStackYaml
5769 mresolver <- view $ globalOptsL. to globalResolver
5870 mSnapshot <- forM mresolver $ \ resolver -> do
5971 concrete <- makeConcreteResolver resolver
6072 loc <- completeSnapshotLocation concrete
6173 loadSnapshot loc
6274 dstPath' <- resolveDir' $ T. unpack dstPath
63- unpackPackages mSnapshot dstPath' names
75+ unpackPackages mStackYaml mSnapshot dstPath' names
6476
6577-- | Intended to work for the command line command.
6678unpackPackages ::
6779 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.
6982 -> Path Abs Dir -- ^ Destination.
7083 -> [String ] -- ^ Names or identifiers.
7184 -> 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+
75103 locs1 <- forM pirs1 $ \ pir -> do
76104 loc <- fmap cplComplete $ completePackageLocation $ RPLIHackage pir Nothing
77105 pure (loc, packageLocationIdent loc)
@@ -93,12 +121,7 @@ unpackPackages mSnapshot dest input = do
93121
94122 forM_ (Map. toList locs) $ \ (loc, dest') -> do
95123 unpackPackageLocation dest' loc
96- prettyInfoL
97- [ " Unpacked"
98- , fromString $ T. unpack $ textDisplay loc
99- , " to"
100- , pretty dest' <> " ."
101- ]
124+ prettyInfoL $ unpackMessage loc dest'
102125 where
103126 toLoc | Just snapshot <- mSnapshot = toLocSnapshot snapshot
104127 | otherwise = toLocNoSnapshot
@@ -158,14 +181,79 @@ unpackPackages mSnapshot dest input = do
158181 loc <- cplComplete <$> completePackageLocation (rspLocation sp)
159182 pure $ Right (loc, packageLocationIdent loc)
160183
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
165194 Nothing ->
166195 case parsePackageIdentifierRevision (T. pack s) of
167- Right x -> Right $ Right x
196+ Right x -> Right $ UnpackIdent x
168197 Left _ -> Left $ fillSep
169198 [ flow " Could not parse as package name or identifier:"
170199 , style Current (fromString s) <> " ."
171200 ]
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