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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
|
module CPM.Manage ( main )
where
import Directory ( copyFile, doesFileExist, doesDirectoryExist
, createDirectoryIfMissing, getCurrentDirectory )
import FilePath ( (</>), replaceExtension )
import IOExts ( evalCmd )
import List ( findIndex, nub, replace, sortBy, sum, union )
import System ( getArgs, exitWith, system )
import Time ( getLocalTime, toDayString )
import CPM.Config ( Config, repositoryDir, packageInstallDir
, readConfigurationWith )
import CPM.ErrorLogger
import CPM.FileUtil ( inDirectory, inTempDir, recreateDirectory )
import CPM.Package
import CPM.PackageCopy ( renderPackageInfo )
import CPM.Repository ( allPackages, listPackages, readRepository
, readPackageFromRepository, cleanRepositoryCache )
import CPM.Resolution ( isCompatibleToCompiler )
import HTML.Base
import ShowDotGraph
cpmBaseURL :: String
cpmBaseURL = "http://www.informatik.uni-kiel.de/~curry/cpm/DOC/"
cpmHtmlDir :: String
cpmHtmlDir = "/net/medoc/home/mh/public_html/curry/cpm"
main :: IO ()
main = do
args <- getArgs
case args of
["genhtml"] -> writeAllPackagesAsHTML
["gendocs"] -> generateDocsOfAllPackages
["testall"] -> testAllPackages
["add"] -> addNewPackage
["update"] -> updatePackage
["showgraph"] -> showAllPackageDependencies
["--help"] -> putStrLn helpText
["-h"] -> putStrLn helpText
_ -> do putStrLn $ "Wrong arguments!\n\n" ++ helpText
exitWith 1
helpText :: String
helpText = unlines $
[ "Options:", ""
, "add : add this package version to the central repository"
, "update : tag git repository of local package with current version"
, " and update central index with current package specification"
, "genhtml : generate HTML pages of central repository (in directory"
, " '" ++ cpmHtmlDir ++ "')"
, "gendocs : generate HTML documentations of all packages (in directory"
, " '" ++ cpmHtmlDir </> "DOC" ++ "')"
, "testall : test all packages of the central repository"
, "showgraph : visualize all package dependencies as dot graph"
]
getAllPackageSpecs :: Bool -> IO (Config,[Package])
getAllPackageSpecs compat = do
config <- readConfigurationWith [] >>= \c ->
case c of
Left err -> do putStrLn $ "Error reading .cpmrc settings: " ++ err
exitWith 1
Right c' -> return c'
putStrLn "Reading repository..."
repo <- readRepository config True
let allpkgs = sortBy (\ps1 ps2 -> name ps1 <= name ps2)
(concatMap (filterCompatPkgs config)
(listPackages repo))
return (config,allpkgs)
where
filterCompatPkgs cfg pkgs =
let comppkgs = filter (isCompatibleToCompiler cfg) pkgs
in if null comppkgs
then if compat then [] else take 1 pkgs
else [head comppkgs]
writeAllPackagesAsHTML :: IO ()
writeAllPackagesAsHTML = inDirectory cpmHtmlDir $ do
(config,repopkgs) <- getAllPackageSpecs False
putStrLn "Reading all package specifications..."
allpkgs <- mapIO (fromErrorLogger . readPackageFromRepository config) repopkgs
let indexfile = "index.html"
ltime <- getLocalTime
putStrLn $ "Writing '" ++ indexfile ++ "'..."
writeReadableFile indexfile $ showHtmlPage $
cpmHtmlPage "Curry Packages in the CPM Repository" $
[h1 [htxt "Curry Packages in the ",
href "http://www.curry-language.org/tools/cpm" [htxt "CPM"]
`addAttr` ("target","_blank"),
htxt $ " Repository (" ++ toDayString ltime ++ ")"],
packageInfosAsHtmlTable allpkgs]
mapIO_ writePackageAsHTML allpkgs
system "rm -f allpkgs.csv" >> done
where
writePackageAsHTML pkg = do
let pname = name pkg
htmlfile = pname ++ ".html"
putStrLn $ "Writing '" ++ htmlfile ++ "'..."
let pkginfo = renderPackageInfo True True True pkg
manref = manualRef pkg False
writeReadableFile htmlfile $ showHtmlPage $
cpmTitledHtmlPage ("Curry Package '"++pname++"'") $
[blockstyle "reference" $ apiRef pkg False] ++
(if null manref then [] else [blockstyle "reference" manref]) ++
[blockstyle "metadata"
[h3 [htxt "Package metadata:"],
verbatim pkginfo]]
writeReadableFile :: String -> String -> IO ()
writeReadableFile f s = writeFile f s >> system ("chmod 644 " ++ f) >> done
apiRef :: Package -> Bool -> [HtmlExp]
apiRef pkg small =
let title = if small then "API" else "API documentation"
addArrow he = if small then he else addClass he "arrow"
in [addArrow $ href (cpmBaseURL ++ packageId pkg) [htxt title]]
manualRef :: Package -> Bool -> [HtmlExp]
manualRef pkg small =
let title = if small then "PDF" else "Manual (PDF)"
addArrow he = if small then he else addClass he "arrow"
in case documentation pkg of
Nothing -> []
Just (PackageDocumentation _ docmain _) ->
[addArrow $ href (cpmBaseURL ++ packageId pkg </>
replaceExtension docmain ".pdf")
[htxt title]]
packageInfosAsHtmlTable :: [Package] -> HtmlExp
packageInfosAsHtmlTable pkgs =
headedTable $
[map ((:[]) . htxt) ["Name", "API", "Doc","Executable","Synopsis", "Version"] ] ++
map formatPkg pkgs
where
formatPkg pkg =
[ [href (name pkg ++ ".html") [htxt $ name pkg]]
, apiRef pkg True
, let manref = manualRef pkg True
in if null manref then [nbsp] else manref
, [htxt $ maybe ""
(\ (PackageExecutable n _ _) -> n)
(executableSpec pkg)]
, [htxt $ synopsis pkg]
, [htxt $ showVersion (version pkg)] ]
cpmHtmlPage :: String -> [HtmlExp] -> HtmlPage
cpmHtmlPage title hexps =
page title hexps `addPageParam` pageCSS "css/cpm.css"
cpmTitledHtmlPage :: String -> [HtmlExp] -> HtmlPage
cpmTitledHtmlPage title hexps = cpmHtmlPage title (h1 [htxt title] : hexps)
generateDocsOfAllPackages :: IO ()
generateDocsOfAllPackages = do
(_,allpkgs) <- getAllPackageSpecs True
mapIO_ genDocOfPackage allpkgs
system "rm -f allpkgs.csv" >> done
where
genDocOfPackage pkg = inTempDir $ do
let pname = name pkg
pversion = showVersion (version pkg)
putStrLn $ unlines [dline, "Documenting: " ++ pname, dline]
let cmd = unwords [ "rm -rf", pname, "&&"
, "cypm","checkout", pname, pversion, "&&"
, "cd", pname, "&&"
, "cypm", "install", "--noexec", "&&"
, "cypm", "doc", "--docdir", cpmHtmlDir </> "DOC"
, "--url", cpmBaseURL, "&&"
, "cd ..", "&&"
, "rm -rf", pname
]
putStrLn $ "CMD: " ++ cmd
system cmd
testAllPackages :: IO ()
testAllPackages = do
(_,allpkgs) <- getAllPackageSpecs True
results <- mapIO checkoutAndTestPackage allpkgs
if sum (map fst results) == 0
then putStrLn $ show (length allpkgs) ++ " PACKAGES SUCCESSFULLY TESTED!"
else do putStrLn $ "ERRORS OCCURRED IN PACKAGES: " ++
unwords (map snd (filter ((> 0) . fst) results))
exitWith 1
system "rm -f allpkgs.csv" >> done
dline :: String
dline = take 78 (repeat '=')
addNewPackage :: IO ()
addNewPackage = do
config <- readConfigurationWith [] >>= \c -> case c of
Left err -> do
putStrLn $ "Error reading .cpmrc file: " ++ err
exitWith 1
Right c' -> return c'
pkg <- fromErrorLogger (loadPackageSpec ".")
setTagInGit pkg
let pkgName = name pkg
pkgVersion = version pkg
pkgIndexDir = pkgName </> showVersion pkgVersion
pkgRepositoryDir = repositoryDir config </> pkgIndexDir
expkgdir <- doesDirectoryExist pkgRepositoryDir
when expkgdir (error $ "Package repository directory '" ++ pkgRepositoryDir ++
"' already exists!")
putStrLn $ "Create directory: " ++ pkgRepositoryDir
createDirectoryIfMissing True pkgRepositoryDir
copyFile packageSpecFile (pkgRepositoryDir </> packageSpecFile)
cleanRepositoryCache config
putStrLn $ "Package repository directory '" ++ pkgRepositoryDir ++ "' added."
(ecode,_) <- checkoutAndTestPackage pkg
when (ecode>0) $ do
system $ "rm -rf " ++ pkgRepositoryDir
system $ "rm -rf " ++ packageInstallDir config </> packageId pkg
cleanRepositoryCache config
putStrLn "Unable to checkout, package deleted in repository directory!"
exitWith 1
putStrLn $ "\nEverything looks fine..."
putStrLn $ "\nTo publish the new repository directory, run command:\n"
putStrLn $ "pushd " ++ repositoryDir config ++
" && git add " ++ pkgIndexDir </> packageSpecFile ++
" && git commit -m\"" ++ pkgIndexDir ++ " added\" " ++
" && git push origin master && popd"
checkoutAndTestPackage :: Package -> IO (Int,String)
checkoutAndTestPackage pkg = do
curdir <- inTempDir getCurrentDirectory
let bindir = curdir </> "pkgbin"
recreateDirectory bindir
let pkgname = name pkg
pkgversion = version pkg
pkgid = packageId pkg
putStrLn $ unlines [dline, "Testing package: " ++ pkgid, dline]
let checkoutdir = pkgname
cmd = unwords
[ "rm -rf", checkoutdir, "&&"
, "cypm", "checkout", pkgname, showVersion pkgversion, "&&"
, "cd", checkoutdir, "&&"
, "cypm", "-d bin_install_path="++bindir, "install", "&&"
, "export PATH="++bindir++":$PATH", "&&"
, "cypm", "test", "&&"
, "cypm", "-d bin_install_path="++bindir, "uninstall"
]
putStrLn $ "...with command:\n" ++ cmd
ecode <- inTempDir $ system cmd
inTempDir (system $ unwords ["rm -rf ", checkoutdir, bindir])
when (ecode>0) $ putStrLn $ "ERROR OCCURED IN PACKAGE '"++pkgid++ "'!"
return (ecode,pkgid)
setTagInGit :: Package -> IO ()
setTagInGit pkg = do
let ts = 'v' : showVersion (version pkg)
(_,gittag,_) <- evalCmd "git" ["tag","-l",ts] ""
let deltag = if null gittag then [] else ["git tag -d",ts,"&&"]
cmd = unwords $ deltag ++ ["git tag -a",ts,"-m",ts,"&&",
"git push --tags -f"]
putStrLn $ "Execute: " ++ cmd
ecode <- system cmd
when (ecode > 0) $ error "ERROR in setting the git tag"
updatePackage :: IO ()
updatePackage = do
config <- readConfigurationWith [] >>= \c -> case c of
Left err -> do putStrLn $ "Error reading .cpmrc file: " ++ err
exitWith 1
Right c' -> return c'
pkg <- fromErrorLogger (loadPackageSpec ".")
let pkgIndexDir = name pkg </> showVersion (version pkg)
pkgRepositoryDir = repositoryDir config </> pkgIndexDir
pkgInstallDir = packageInstallDir config </> packageId pkg
setTagInGit pkg
putStrLn $ "Deleting old repo copy '" ++ pkgInstallDir ++ "'..."
system $ "rm -rf " ++ pkgInstallDir
(ecode,_) <- checkoutAndTestPackage pkg
when (ecode > 0) $ do putStrLn $ "ERROR in package, CPM index not updated!"
exitWith 1
let cmd = unwords
["cp -f", packageSpecFile, pkgRepositoryDir </> packageSpecFile]
putStrLn $ "Execute: " ++ cmd
system cmd
cleanRepositoryCache config
showAllPackageDependencies :: IO ()
showAllPackageDependencies = do
config <- readConfigurationWith [] >>= \c -> case c of
Left err -> do
putStrLn $ "Error reading .cpmrc file: " ++ err
exitWith 1
Right c' -> return c'
pkgs <- readRepository config False >>= return . allPackages
let alldeps = map (\p -> (name p, map (\ (Dependency p' _) -> p')
(dependencies p)))
pkgs
dotgraph = depsToGraph alldeps
putStrLn $ "Show dot graph..."
viewDotGraph dotgraph
depsToGraph :: [(String, [String])] -> DotGraph
depsToGraph cpmdeps =
Graph "CPM Dependencies"
(map (\s -> Node s []) (nub (map fst cpmdeps ++ concatMap snd cpmdeps)))
(map (\ (s,t) -> Edge s t [])
(nub (concatMap (\ (p,ds) -> map (\d -> (p,d)) ds) cpmdeps)))
packageSpecFile :: String
packageSpecFile = "package.json"
|