Skip to content

Commit 4152ab2

Browse files
committed
Unpack a repo extra-dep
1 parent 259b47f commit 4152ab2

File tree

2 files changed

+106
-17
lines changed

2 files changed

+106
-17
lines changed

src/Stack/Config.hs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
module Stack.Config
1919
( loadConfig
2020
, loadConfigYaml
21+
, loadProjectConfig
2122
, packagesParser
2223
, getImplicitGlobalProjectDir
2324
, getSnapshots

src/Stack/Unpack.hs

+105-17
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
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
1012
import Path ( (</>), parseRelDir )
1113
import Path.IO ( doesDirExist, resolveDir' )
1214
import Pantry ( loadSnapshot )
1315
import qualified RIO.Map as Map
1416
import RIO.Process ( HasProcessContext )
1517
import qualified RIO.Set as Set
1618
import qualified RIO.Text as T
17-
import Stack.Config ( makeConcreteResolver )
19+
import Stack.Config ( loadProjectConfig, makeConcreteResolver )
1820
import Stack.Prelude
1921
import Stack.Runners ( ShouldReexec (..), withConfig )
22+
import Stack.Types.Project ( Project (..) )
2023
import Stack.Types.GlobalOpts ( GlobalOpts (..) )
24+
import Stack.Types.ProjectConfig ( ProjectConfig (..) )
2125
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)
2233

2334
-- | Type representing \'pretty\' exceptions thrown by functions exported by the
2435
-- "Stack.Unpack" module.
@@ -54,24 +65,41 @@ unpackCmd ::
5465
-> RIO Runner ()
5566
unpackCmd (names, Nothing) = unpackCmd (names, Just ".")
5667
unpackCmd (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.
6678
unpackPackages ::
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

Comments
 (0)