-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathCabalMeta.hs
231 lines (206 loc) · 7.91 KB
/
CabalMeta.hs
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
{-# LANGUAGE CPP, OverloadedStrings #-}
module CabalMeta (
Package (..)
, UnstablePackage (..)
, PackageSources (..)
, readPackages
, packageList
, vendor_dir
, unstablePackages
, diskPath
#ifdef TEST
, asList
, packages
#endif
) where
import Shelly hiding (tag)
import Prelude hiding (FilePath)
import Data.Text (Text, unpack)
import qualified Data.Text as T
import Filesystem.Path.CurrentOS (hasExtension, basename, dirname)
import Data.Maybe (fromMaybe, maybeToList, listToMaybe)
import Data.List (partition)
#if __GLASGOW_HASKELL__ < 704
import Data.Monoid (Monoid(..))
import Control.Monad (when, forM)
infixr 5 <>
(<>) :: Monoid m => m -> m -> m
(<>) = mappend
#else
import Control.Monad (forM)
import Data.Monoid ((<>),Monoid(..))
#endif
{--
import FileLocation (debug)
--}
source_file :: FilePath
source_file = "sources.txt"
data Package = Unstable UnstablePackage
| Package {
pLocation :: Text
, pFlags :: [Text]
} deriving (Show, Eq)
-- | An unstable package is one which has not been released to some
-- package repository
data UnstablePackage = Directory {
dLocation :: FilePath
, upFlags :: [Text]
} | GitPackage {
gitLocation :: Text
, upFlags :: [Text]
, gTag :: Maybe Text
} | DarcsPackage {
darcsLocation :: Text
, upFlags :: [Text]
, darcsTag :: Maybe Text
} deriving (Show, Eq)
asList :: Package -> [Text]
asList (Package l fs) = l : fs
asList (Unstable (GitPackage l fs tag)) = l : fs ++ maybeToList tag
asList (Unstable (DarcsPackage l fs tag)) = l : fs ++ maybeToList tag
asList (Unstable (Directory d fs)) = toTextIgnore d : fs
asInstallList :: Package -> [Text]
asInstallList p@(Package l _) = l : flags p
asInstallList p@(Unstable up) = dpath : flags p
where dpath = toTextIgnore (diskPath up)
flags :: Package -> [Text]
flags (Package _ fs) = fs
flags (Unstable (GitPackage _ fs _)) = fs
flags (Unstable (DarcsPackage _ fs _)) = fs
flags (Unstable (Directory _ fs)) = fs
diskPath :: UnstablePackage -> FilePath
diskPath p =
case p of
GitPackage l _ _ -> fromUrl l
DarcsPackage l _ _ -> fromUrl l
Directory d _ -> d
where
fromUrl x = vendor_dir </> basename (fromText x)
data PackageSources = PackageSources {
dirs :: [UnstablePackage]
, hackages :: [Package]
, https :: [UnstablePackage] -- also git for now
, gits :: [UnstablePackage]
, darcsen :: [UnstablePackage]
} deriving (Show, Eq)
packageList :: PackageSources -> [[Text]]
packageList = map asInstallList . packages
packages :: PackageSources -> [Package]
packages psources =
hackages psources ++
map Unstable (unstablePackages psources)
unstablePackages :: PackageSources -> [UnstablePackage]
unstablePackages psources =
dirs psources ++
gitPackages psources ++
darcsen psources
gitPackages :: PackageSources -> [UnstablePackage]
gitPackages psources =
gits psources ++ https psources
instance Monoid PackageSources where
mempty = PackageSources [] [] [] [] []
mappend (PackageSources d1 ha1 ht1 g1 da1) (PackageSources d2 ha2 ht2 g2 da2) =
PackageSources (mappend d1 d2) (mappend ha1 ha2)
(mappend ht1 ht2) (mappend g1 g2) (mappend da1 da2)
vendor_dir :: FilePath
vendor_dir = "vendor"
git_ :: Text -> [Text] -> Sh ()
git_ = command1_ "git" []
darcs_ :: Text -> [Text] -> Sh ()
darcs_ = command1_ "darcs" []
readPackages :: Bool -> FilePath -> Sh PackageSources
readPackages allowCabals startDir = do
fullDir <- canonic startDir
chdir fullDir $ do
cabalPresent <- if allowCabals then return False else isCabalPresent
if cabalPresent then return mempty else do
psources <- getSources
when (psources == mempty) $ terror $ "empty " <> toTextIgnore source_file
let remote_pkgs = gitPackages psources ++ darcsen psources
unless (null remote_pkgs) $ mkdir_p vendor_dir
child_vendor_pkgs <- forM remote_pkgs $ \pkg -> do
updatePackage pkg
kids <- readPackages False (diskPath pkg)
return (pkg, kids)
child_dir_pkgs <- forM (dirs psources) $ \dir -> do
b <- fmap (== fullDir) (canonic $ dLocation dir)
if b then return (dir, mempty)
else do
kids <- readPackages False (dLocation dir)
return (dir, kids)
let child_pkgs = child_dir_pkgs ++ child_vendor_pkgs
-- in the end we have either hackage packages or directories
-- a directory was either listed as a directory or a child found in a sources.txt in that directory
-- if there are no child, there will be an empty list [] of children
-- this would be easy to break & should be cleaned up
return $ mempty {
hackages = hackages psources ++ concatMap (hackages . snd) child_pkgs
, dirs = concatMap (\(p,ps) -> if null (dirs ps) then [p] else dirs ps) child_pkgs
}
where
isCabalFile = flip hasExtension "cabal"
isCabalPresent = fmap (any isCabalFile) (ls ".")
updatePackage :: UnstablePackage -> Sh ()
updatePackage p@(GitPackage repo _ t) = do
let d = diskPath p
e <- test_d d
if not e
then chdir (dirname d) $
git_ "clone" ["--recursive", repo]
else chdir d $ git_ "fetch" ["origin"]
chdir d $ do
git_ "checkout" [fromMaybe "master" t]
git_ "submodule" ["foreach", "git", "pull", "origin", "master"]
updatePackage p@(DarcsPackage repo _ mtag) = do
let d = diskPath p
tflags = case mtag of
Nothing -> []
Just t -> ["--tag", t]
e <- test_d d
if not e
then chdir (dirname d) $
darcs_ "get" $ ["--lazy", repo] ++ tflags
else chdir d $ darcs_ "pull" ["--all"]
updatePackage (Directory _ _) = return mempty
getSources :: Sh PackageSources
getSources = do
sourceContent <- readfile source_file
let sources = paritionSources [ source |
source <- map (T.words . T.strip) (T.lines sourceContent),
not . null $ source,
"--" /= head source
]
ds <- mapM fullPath (dirs sources)
return $ sources { dirs = ds }
where
fullPath package = do
fp <- canonic $ dLocation package
return package { dLocation = fp }
paritionSources :: [[Text]] -> PackageSources
paritionSources = go mempty
where
go sources [] = sources
go _ ([]:_) = error "impossible"
go sources ((name:flgs):more) = let n = T.head name in
case () of
_ | n `elem` ("./" :: String) -> next sources { dirs = mkDir: dirs sources }
| prefix "http" -> next sources { https = mkGit: https sources }
| prefix "https" -> next sources { gits = mkGit: https sources }
| prefix "git:" -> next sources { gits = mkGit: gits sources }
| prefix "ssh:" -> next sources { gits = mkGit: gits sources }
| prefix "darcs:" -> next sources { darcsen = mkDarcs: darcsen sources }
| otherwise -> next sources { hackages = mkPkg: hackages sources }
where
prefix x = x `T.isPrefixOf` name
next s2 = go s2 more
mkDir = Directory (fromText name) flgs
mkPkg = Package name flgs
mkGit = GitPackage name realFlags tag
mkDarcs =
case T.stripPrefix "darcs:" name of
Nothing -> error $ unpack $ "did not understand" <> T.intercalate " " (asList (Package name flgs))
Just realName -> DarcsPackage realName realFlags tag
(realFlags, tag) = let (rf, tags) = partition (T.isPrefixOf "-") flgs in
if length tags > 1
then error $ unpack $ "did not understand" <> T.intercalate " " (asList (Package name flgs))
else (rf, listToMaybe tags)