-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathMain.hs
223 lines (187 loc) · 7.97 KB
/
Main.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
{-# LANGUAGE DeriveDataTypeable #-}
-- |
-- Module : cabal2arch: convert cabal packages to Arch Linux PKGBUILD format
-- Copyright : (c) Don Stewart, 2008 .. 2010
-- License : BSD3
--
-- Maintainer: Don Stewart <[email protected]>
-- Stability : provisional
-- Portability:
--
-- TODO: if build-type: Configure, accurate C library dependecies
-- require downloading the source, and running configure
--
-- C libraries are dynamically linked, should be listed in depends,
-- rather than makedepends
import Distribution.PackageDescription.Parse
import Distribution.Simple.Utils hiding (die)
import Distribution.Verbosity
import Distribution.Text
-- from the archlinux package:
import Distribution.ArchLinux.PkgBuild
import Distribution.ArchLinux.CabalTranslation
import Control.Monad
import Control.Monad.Error
import qualified Control.Exception as CE
import Data.List
import Text.PrettyPrint
import Paths_cabal2arch
import Data.Version (showVersion)
import System.Directory
import System.Exit
import System.FilePath
import System.IO
import System.Process hiding(cwd)
import System.Console.CmdArgs
import Cabal2Arch.Util
data CmdLnArgs
= CmdLnConvertOne { argCabalFile :: String, argCreateTar :: Bool, argDataFiles :: String }
deriving (Data, Typeable)
cmdLnConvertOne :: CmdLnArgs
cmdLnConvertOne = CmdLnConvertOne
{ argCabalFile = "" &= argPos 0 &= typ "FILE|DIR|URL"
, argCreateTar = False &= name "tar" &= explicit &= help "Create a tar-ball for the source package."
, argDataFiles = "" &= name "sysinfo" &= typDir &= explicit &= help "Use custom system information files."
} &= auto &= name "conv" &= help "Convert a single CABAL file."
cmdLnArgs :: CmdLnArgs
cmdLnArgs = modes [cmdLnConvertOne]
&= program "cabal2arch"
&= summary ("cabal2arch, v. " ++ showVersion version ++ ": Convert .cabal file to ArchLinux source package")
main :: IO ()
main = cmdArgs cmdLnArgs >>= subCmd
subCmd :: CmdLnArgs -> IO ()
subCmd (CmdLnConvertOne cabalLoc createTar dataFiles) =
CE.bracket
-- We do all our work in a temp directory
(do _cwd <- getCurrentDirectory
etmp <- myReadProcess "mktemp" ["-d"] []
case etmp of
Left _ -> die "Unable to create temp directory"
Right d -> do
let dir = makeValid (init d) -- drop newline
setCurrentDirectory dir
return (dir, _cwd))
-- Always remember to clean up
(\(d, _cwd) -> do
setCurrentDirectory _cwd
removeDirectoryRecursive d)
-- Now, get to work:
$ \(tmp, _cwd) -> do
-- myArgs <- cmdArgs cmdLnArgs
email <- do
r <- getEnvMaybe "ARCH_HASKELL"
case r of
Nothing -> do
hPutStrLn stderr "Warning: ARCH_HASKELL environment variable not set. Set this to the maintainer contact you wish to use. \n E.g. 'Arch Haskell Team <[email protected]>'"
return []
Just s -> return s
cabalfile <- findCabalFile cabalLoc _cwd tmp
hPutStrLn stderr $ "Using " ++ cabalfile
cabalsrc <- readPackageDescription normal cabalfile
-- Create a package description with all configurations resolved.
maybeSysProvides <- runErrorT $ getSystemProvidesFromPath dataFiles
sysProvides <- case maybeSysProvides of
Left s -> die s
Right sp -> return sp
let finalcabal = preprocessCabal cabalsrc sysProvides
finalcabal' <- case finalcabal of
Nothing -> die "Aborting..."
Just f -> return f
let (pkgbuild', hooks) = cabal2pkg finalcabal' sysProvides
apkgbuild' <- getMD5 pkgbuild'
let apkgbuild = apkgbuild' { pkgBuiltWith = Just version }
pkgbuild = pkgBody apkgbuild
doc = pkg2doc email apkgbuild
dir = arch_pkgname pkgbuild
setCurrentDirectory _cwd
createDirectoryIfMissing False dir
setCurrentDirectory dir
writeFile "PKGBUILD" (render doc ++ "\n")
-- print pkgname.install
case hooks of
Nothing -> return ()
Just i -> writeFile (install_hook_name (arch_pkgname pkgbuild)) i
setCurrentDirectory _cwd
_ <- system $ "rm -rf " ++ dir </> "{pkg,src,*.tar.gz}"
when createTar $ do
tarred <- myReadProcess "tar" ["-zcvvf",(dir <.> "tar.gz"), dir] []
case tarred of
Left (_,s,_) -> do
hPutStrLn stderr s
die "Unable to tar package"
Right _ -> putStrLn ("Created " ++ (_cwd </> dir <.> "tar.gz"))
-- If the user created a .cabal2arch.log file, append log results there.
mh <- getEnvMaybe "HOME"
case mh of
Nothing -> return ()
Just home -> do
b <- doesFileExist $ home </> ".cabal2arch.log"
if not b
then return ()
else do
-- Log to build file.
appendFile (home </> ".cabal2arch.log") $ (show $ (,,)
(arch_pkgname pkgbuild ++ "-" ++ (display $ arch_pkgver pkgbuild))
(arch_pkgdesc pkgbuild)
(arch_url pkgbuild)) ++ "\n"
------------------------------------------------------------------------
-- | Given an abstract pkgbuild, run "makepkg -g" to compute md5
-- of source files (possibly cached locally), and modify the PkgBuild
-- accordingly.
--
getMD5 :: AnnotatedPkgBuild -> IO AnnotatedPkgBuild
getMD5 pkg = do
putStrLn "Feeding the PKGBUILD to `makepkg -g`..."
eres <- readProcessWithExitCode "makepkg" ["-g"] $ display pkg
case eres of
(ExitFailure _,_,err) -> do
hPutStrLn stderr err
hPutStrLn stderr $ "makepkg encountered an error while calculating MD5."
return pkg
(ExitSuccess,out,err) -> do
-- s should be "md5sums=(' ... ')"
hPutStrLn stderr err
if "md5sums=('" `isPrefixOf` out
then
let md5sum = takeWhile (\x -> x `elem` "0123456789abcdef") $ drop 10 out
in return pkg { pkgBody = (pkgBody pkg) { arch_md5sum = ArchList [md5sum] } }
else do
hPutStrLn stderr $ "Incorrect output from makepkg."
return pkg
-- Return the path to a .cabal file.
-- If not arguments are specified, use ".",
-- if the argument looks like a url, download that
-- otherwise, assume its a directory
--
findCabalFile :: String -> FilePath -> FilePath -> IO FilePath
findCabalFile file _cwd tmp = do
let epath
| null file
= Right _cwd
| "http://" `isPrefixOf` file
= Left file
| ".cabal" `isSuffixOf` file
= Right (makeValid (joinPath [_cwd,file]))
| otherwise -- a directory path
= Right file
-- download url to .cabal
case epath of
Left url -> do
eres <- myReadProcess "wget" [url] []
case eres of
Left (_,s,_) -> do
hPutStrLn stderr s
die $ "Couldn't download .cabal file: " ++ show url
Right _ -> findPackageDesc tmp -- tmp dir
-- it might be a .cabal file
Right f | ".cabal" `isSuffixOf` f -> do
b <- doesFileExist f
if not b
then die $ ".cabal file doesn't exist: " ++ show f
else return f
-- or assume it is a dir to a file:
Right dir -> do
b <- doesDirectoryExist dir
if not b
then die $ "directory doesn't exist: " ++ show dir
else findPackageDesc dir