packages feed

cblrepo (empty) → 0.2.0

raw patch · 19 files changed

+1304/−0 lines, 19 filesdep +Cabaldep +HUnitdep +Unixutilsbuild-type:Customsetup-changed

Dependencies added: Cabal, HUnit, Unixutils, ansi-wl-pprint, base, bytestring, cmdargs, directory, filepath, json, mtl, process, tar, test-framework, test-framework-hunit, test-framework-th, unix, zlib

Files

+ LICENSE-2.0 view
@@ -0,0 +1,202 @@++                                 Apache License+                           Version 2.0, January 2004+                        http://www.apache.org/licenses/++   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION++   1. Definitions.++      "License" shall mean the terms and conditions for use, reproduction,+      and distribution as defined by Sections 1 through 9 of this document.++      "Licensor" shall mean the copyright owner or entity authorized by+      the copyright owner that is granting the License.++      "Legal Entity" shall mean the union of the acting entity and all+      other entities that control, are controlled by, or are under common+      control with that entity. For the purposes of this definition,+      "control" means (i) the power, direct or indirect, to cause the+      direction or management of such entity, whether by contract or+      otherwise, or (ii) ownership of fifty percent (50%) or more of the+      outstanding shares, or (iii) beneficial ownership of such entity.++      "You" (or "Your") shall mean an individual or Legal Entity+      exercising permissions granted by this License.++      "Source" form shall mean the preferred form for making modifications,+      including but not limited to software source code, documentation+      source, and configuration files.++      "Object" form shall mean any form resulting from mechanical+      transformation or translation of a Source form, including but+      not limited to compiled object code, generated documentation,+      and conversions to other media types.++      "Work" shall mean the work of authorship, whether in Source or+      Object form, made available under the License, as indicated by a+      copyright notice that is included in or attached to the work+      (an example is provided in the Appendix below).++      "Derivative Works" shall mean any work, whether in Source or Object+      form, that is based on (or derived from) the Work and for which the+      editorial revisions, annotations, elaborations, or other modifications+      represent, as a whole, an original work of authorship. For the purposes+      of this License, Derivative Works shall not include works that remain+      separable from, or merely link (or bind by name) to the interfaces of,+      the Work and Derivative Works thereof.++      "Contribution" shall mean any work of authorship, including+      the original version of the Work and any modifications or additions+      to that Work or Derivative Works thereof, that is intentionally+      submitted to Licensor for inclusion in the Work by the copyright owner+      or by an individual or Legal Entity authorized to submit on behalf of+      the copyright owner. For the purposes of this definition, "submitted"+      means any form of electronic, verbal, or written communication sent+      to the Licensor or its representatives, including but not limited to+      communication on electronic mailing lists, source code control systems,+      and issue tracking systems that are managed by, or on behalf of, the+      Licensor for the purpose of discussing and improving the Work, but+      excluding communication that is conspicuously marked or otherwise+      designated in writing by the copyright owner as "Not a Contribution."++      "Contributor" shall mean Licensor and any individual or Legal Entity+      on behalf of whom a Contribution has been received by Licensor and+      subsequently incorporated within the Work.++   2. Grant of Copyright License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      copyright license to reproduce, prepare Derivative Works of,+      publicly display, publicly perform, sublicense, and distribute the+      Work and such Derivative Works in Source or Object form.++   3. Grant of Patent License. Subject to the terms and conditions of+      this License, each Contributor hereby grants to You a perpetual,+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable+      (except as stated in this section) patent license to make, have made,+      use, offer to sell, sell, import, and otherwise transfer the Work,+      where such license applies only to those patent claims licensable+      by such Contributor that are necessarily infringed by their+      Contribution(s) alone or by combination of their Contribution(s)+      with the Work to which such Contribution(s) was submitted. If You+      institute patent litigation against any entity (including a+      cross-claim or counterclaim in a lawsuit) alleging that the Work+      or a Contribution incorporated within the Work constitutes direct+      or contributory patent infringement, then any patent licenses+      granted to You under this License for that Work shall terminate+      as of the date such litigation is filed.++   4. Redistribution. You may reproduce and distribute copies of the+      Work or Derivative Works thereof in any medium, with or without+      modifications, and in Source or Object form, provided that You+      meet the following conditions:++      (a) You must give any other recipients of the Work or+          Derivative Works a copy of this License; and++      (b) You must cause any modified files to carry prominent notices+          stating that You changed the files; and++      (c) You must retain, in the Source form of any Derivative Works+          that You distribute, all copyright, patent, trademark, and+          attribution notices from the Source form of the Work,+          excluding those notices that do not pertain to any part of+          the Derivative Works; and++      (d) If the Work includes a "NOTICE" text file as part of its+          distribution, then any Derivative Works that You distribute must+          include a readable copy of the attribution notices contained+          within such NOTICE file, excluding those notices that do not+          pertain to any part of the Derivative Works, in at least one+          of the following places: within a NOTICE text file distributed+          as part of the Derivative Works; within the Source form or+          documentation, if provided along with the Derivative Works; or,+          within a display generated by the Derivative Works, if and+          wherever such third-party notices normally appear. The contents+          of the NOTICE file are for informational purposes only and+          do not modify the License. You may add Your own attribution+          notices within Derivative Works that You distribute, alongside+          or as an addendum to the NOTICE text from the Work, provided+          that such additional attribution notices cannot be construed+          as modifying the License.++      You may add Your own copyright statement to Your modifications and+      may provide additional or different license terms and conditions+      for use, reproduction, or distribution of Your modifications, or+      for any such Derivative Works as a whole, provided Your use,+      reproduction, and distribution of the Work otherwise complies with+      the conditions stated in this License.++   5. Submission of Contributions. Unless You explicitly state otherwise,+      any Contribution intentionally submitted for inclusion in the Work+      by You to the Licensor shall be under the terms and conditions of+      this License, without any additional terms or conditions.+      Notwithstanding the above, nothing herein shall supersede or modify+      the terms of any separate license agreement you may have executed+      with Licensor regarding such Contributions.++   6. Trademarks. This License does not grant permission to use the trade+      names, trademarks, service marks, or product names of the Licensor,+      except as required for reasonable and customary use in describing the+      origin of the Work and reproducing the content of the NOTICE file.++   7. Disclaimer of Warranty. Unless required by applicable law or+      agreed to in writing, Licensor provides the Work (and each+      Contributor provides its Contributions) on an "AS IS" BASIS,+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or+      implied, including, without limitation, any warranties or conditions+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A+      PARTICULAR PURPOSE. You are solely responsible for determining the+      appropriateness of using or redistributing the Work and assume any+      risks associated with Your exercise of permissions under this License.++   8. Limitation of Liability. In no event and under no legal theory,+      whether in tort (including negligence), contract, or otherwise,+      unless required by applicable law (such as deliberate and grossly+      negligent acts) or agreed to in writing, shall any Contributor be+      liable to You for damages, including any direct, indirect, special,+      incidental, or consequential damages of any character arising as a+      result of this License or out of the use or inability to use the+      Work (including but not limited to damages for loss of goodwill,+      work stoppage, computer failure or malfunction, or any and all+      other commercial damages or losses), even if such Contributor+      has been advised of the possibility of such damages.++   9. Accepting Warranty or Additional Liability. While redistributing+      the Work or Derivative Works thereof, You may choose to offer,+      and charge a fee for, acceptance of support, warranty, indemnity,+      or other liability obligations and/or rights consistent with this+      License. However, in accepting such obligations, You may act only+      on Your own behalf and on Your sole responsibility, not on behalf+      of any other Contributor, and only if You agree to indemnify,+      defend, and hold each Contributor harmless for any liability+      incurred by, or claims asserted against, such Contributor by reason+      of your accepting any such warranty or additional liability.++   END OF TERMS AND CONDITIONS++   APPENDIX: How to apply the Apache License to your work.++      To apply the Apache License to your work, attach the following+      boilerplate notice, with the fields enclosed by brackets "[]"+      replaced with your own identifying information. (Don't include+      the brackets!)  The text should be enclosed in the appropriate+      comment syntax for the file format. We also recommend that a+      file or class name and description of purpose be included on the+      same "printed page" as the copyright notice for easier+      identification within third-party archives.++   Copyright [yyyy] [name of copyright owner]++   Licensed under the Apache License, Version 2.0 (the "License");+   you may not use this file except in compliance with the License.+   You may obtain a copy of the License at++       http://www.apache.org/licenses/LICENSE-2.0++   Unless required by applicable law or agreed to in writing, software+   distributed under the License is distributed on an "AS IS" BASIS,+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+   See the License for the specific language governing permissions and+   limitations under the License.
+ Setup.hs view
@@ -0,0 +1,21 @@+#! /usr/bin/env runhaskell++{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++import Distribution.Simple++main = defaultMain
+ cblrepo.cabal view
@@ -0,0 +1,47 @@+name: cblrepo+version: 0.2.0+cabal-version: >= 1.6+license: OtherLicense+license-file: LICENSE-2.0+copyright: Copyright (c) 2011 Magnus Therning+author: Magnus Therning+maintainer: Magnus Therning <magnus@therning.org>+stability: experimental+bug-reports: mailto:magnus@therning.org+synopsis: Tool to maintain a database of CABAL packages and their dependencies+description:+    Helper tool for people maintaining a set of CABAL packages for their+    distribution.  It maintains a database of packages and verifies+    dependencies of the entire set as packages are added or updated.  It also+    makes it trivial to track packages as new versions are released on Hackage.++    It can also be used to build source packages for the ArchLinux distribution.+category: Utils, Distribution+build-type: Custom++flag BuildTests+    Description: Build tests+    Default: False++executable cblrepo+    hs-source-dirs: src+    main-is: Main.hs+    other-modules: Util.Misc PkgDB AddBase AddCabal BumpPkgs BuildPkgs IdxSync+        IdxVersion Updates ListPkgs Urls+    build-depends: base ==4.3.*, cmdargs ==0.7.*, filepath ==1.2.*,+        directory ==1.1.*, Cabal ==1.10.*, json ==0.4.*,+        bytestring ==0.9.*, tar ==0.3.*, zlib ==0.5.*, mtl ==2.0.*,+        process ==1.0.*, Unixutils ==1.36.*, unix ==2.4.*,+        ansi-wl-pprint ==0.6.*++executable tests+    hs-source-dirs: tst src+    main-is: tst.hs+    other-modules: TestSystem TestPkgDB+    if flag(BuildTests)+        build-depends: base ==4.3.*, test-framework-th ==0.1.*, HUnit ==1.2.*,+            test-framework-hunit ==0.2.*, test-framework ==0.3.*+    else+        buildable: False++-- vim: set tw=0 :
+ src/AddBase.hs view
@@ -0,0 +1,50 @@+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module AddBase where++import PkgDB+import Util.Misc++import Control.Monad+import Control.Monad.Reader+import Data.List+import Data.Maybe+import Distribution.Text+import Distribution.Version+import System.FilePath++addBase :: ReaderT Cmds IO ()+addBase = do+    pkgs <- cfgGet pkgVers+    dR <- cfgGet dryRun+    guard $ isJust $ (sequence $ map (simpleParse . snd) pkgs :: Maybe [Version])+    let ps = map (\ (n, v) -> (n, fromJust $ simpleParse v)) pkgs+    dbFn <- cfgGet dbFile+    db <- liftIO $ readDb dbFn+    case doAddBase db ps of+        Left brkOthrs -> liftIO $ mapM_ printBrksOth brkOthrs+        Right newDb -> liftIO $ unless dR $ saveDb newDb dbFn++doAddBase db pkgs = let+        (_, fails) = partition (\ (n, v) -> canBeAdded db n v) pkgs+        newDb = foldl (\ d (n, v) -> addBasePkg d n v) db pkgs+        brkOthrs = map (\ (n, v) -> ((n, v), checkDependants db n v)) fails+    in if null fails+        then Right newDb+        else Left brkOthrs++canBeAdded db n v = null $ checkDependants db n v
+ src/AddCabal.hs view
@@ -0,0 +1,87 @@+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module AddCabal where++import PkgDB+import Util.Misc++import Codec.Archive.Tar as Tar+import Codec.Compression.GZip as GZip+import Control.Monad+import Control.Monad.Error+import Control.Monad.Reader+import Data.List+import Data.Maybe+import Data.Version+import Distribution.Compiler+import Distribution.PackageDescription+import Distribution.PackageDescription.Configuration+import Distribution.PackageDescription.Parse+import Distribution.System+import Distribution.Text+import Distribution.Verbosity+import Distribution.Version+import System.Directory+import System.FilePath+import qualified Data.ByteString.Lazy.Char8 as BS+import qualified Distribution.Package as P+import System.Posix.Files+import System.Unix.Directory+import System.Process+import System.Exit+import System.IO++addCabal :: ReaderT Cmds IO ()+addCabal = do+    dbFn <- cfgGet dbFile+    db <- liftIO $ readDb dbFn+    pD <- cfgGet patchDir+    cbls <- cfgGet cbls+    dR <- cfgGet dryRun+    genPkgs <- liftIO $ mapM (\ c -> withTemporaryDirectory "/tmp/cblrepo." (readCabal pD c)) cbls+    let pkgNames = map ((\ (P.PackageName n) -> n ) . P.pkgName . package . packageDescription) genPkgs+    let tmpDb = filter (\ p -> not $ pkgName p `elem` pkgNames) db+    case doAddCabal tmpDb genPkgs of+        Left (unSats, brksOthrs) -> liftIO (mapM_ printUnSat unSats >> mapM_ printBrksOth brksOthrs)+        Right newDb -> liftIO $ unless dR $ saveDb newDb dbFn++doAddCabal db pkgs = let+        (succs, fails) = partition (canBeAdded db) pkgs+        newDb = foldl addPkg2 db (map (fromJust . finalizeToCblPkg db) succs)+        unSats = catMaybes $ map (finalizeToDeps db) fails+        genPkgName = ((\ (P.PackageName n) -> n ) . P.pkgName . package . packageDescription)+        genPkgVer = P.pkgVersion . package . packageDescription+        brksOthrs = filter (not . null . snd) $ map (\ p -> ((genPkgName p, genPkgVer p), checkDependants db (genPkgName p) (genPkgVer p))) fails+    in case (succs, fails) of+        (_, []) -> Right newDb+        ([], _) -> Left (unSats, brksOthrs)+        (_, _) -> doAddCabal newDb fails++canBeAdded db p = let+        finable = either (const False) (const True) (finalizePkg db p)+        n = ((\ (P.PackageName n) -> n ) . P.pkgName . package . packageDescription) p+        v = P.pkgVersion $ package $ packageDescription p+        depsOK = null $ checkDependants db n v+    in finable && depsOK++finalizeToCblPkg db p = case finalizePkg db p of+    Right (pd, _) -> Just $ createCblPkg pd+    _ -> Nothing++finalizeToDeps db p = case finalizePkg db p of+    Left ds -> Just $ (((\ (P.PackageName n) -> n ) . P.pkgName . package . packageDescription) p, ds)+    _ -> Nothing
+ src/BuildPkgs.hs view
@@ -0,0 +1,32 @@+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module BuildPkgs where++import PkgDB+import Util.Misc++import Control.Monad+import Control.Monad.Reader+import Data.List+import Data.Maybe+import System.FilePath++buildPkgs :: ReaderT Cmds IO ()+buildPkgs = do+    db <- cfgGet dbFile >>= liftIO . readDb+    pkgs <- cfgGet pkgs+    liftIO $ mapM_ putStrLn $ transitiveDependants db pkgs
+ src/BumpPkgs.hs view
@@ -0,0 +1,41 @@+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module BumpPkgs where++import PkgDB+import Util.Misc++import Control.Monad+import Control.Monad.Reader+import Data.List+import Data.Maybe+import System.FilePath++bumpPkgs :: ReaderT Cmds IO ()+bumpPkgs = do+    dbFn <- cfgGet dbFile+    db <- liftIO $ readDb dbFn+    dR <- cfgGet dryRun+    pkgs <- cfgGet pkgs+    incl <- cfgGet inclusive+    let bpkgs = transDependants db incl pkgs+    let newDb = foldl (\ db p -> bumpRelease db p) db bpkgs+    if dR+        then liftIO $ putStrLn "Would bump:" >> mapM_ putStrLn bpkgs+        else liftIO $ saveDb newDb dbFn++transDependants db i pkgs = filter ((||) i . (not . flip elem pkgs)) $ transitiveDependants db pkgs
+ src/IdxSync.hs view
@@ -0,0 +1,28 @@+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module IdxSync where++import Util.Misc++import Control.Monad.Reader+import System.FilePath+import Control.Monad.Error++idxSync :: ReaderT Cmds IO ()+idxSync = do+    aD <- cfgGet appDir+    liftIO $ getFromURL "http://hackage.haskell.org/packages/archive/00-index.tar.gz" (aD </> "00-index.tar.gz")
+ src/IdxVersion.hs view
@@ -0,0 +1,56 @@+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module IdxVersion where++import Util.Misc++import Codec.Archive.Tar as Tar+import Codec.Compression.GZip as GZip+import Control.Monad+import Control.Monad.Reader+import Data.List+import Data.Maybe+import Distribution.Text+import Distribution.Version+import System.FilePath+import qualified Data.ByteString.Lazy.Char8 as BS++idxVersion :: ReaderT Cmds IO ()+idxVersion = do+    aD <- cfgGet appDir+    pkgs <- cfgGet pkgs+    liftIO $ do+        es <- liftM (Tar.read . GZip.decompress) (BS.readFile $ aD </> "00-index.tar.gz")+        mapM_ (printVersions . findVersions es) pkgs++findVersions es p = (p, findV p es [])+    where+        findV pkgName (Next e es) acc = let+                eP = entryPath e+                (ePkg:v:_) = splitDirectories eP+            in if ('/' `elem` eP) && (pkgName == ePkg)+                then findV pkgName es (v:acc)+                else findV pkgName es acc++        findV _ Done acc = let+                vs :: [Version]+                vs = map (fromJust . simpleParse) acc+            in map display $ sort vs++printVersions (p, vs) = do+    putStr $ p ++ " : "+    putStrLn $ unwords vs
+ src/ListPkgs.hs view
@@ -0,0 +1,38 @@+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module ListPkgs where++import Util.Misc+import PkgDB++import Control.Monad+import Control.Monad.Reader+import Data.Maybe+import Distribution.Text+import System.FilePath++listPkgs :: ReaderT Cmds IO ()+listPkgs = do+    iB <- cfgGet incBase+    db <- cfgGet dbFile >>= liftIO . readDb+    let pkgs = if not iB+            then filter (not . isBasePkg) db+            else db+    liftIO $ mapM_ printCblPkgShort pkgs++printCblPkgShort (p, (v, _, r)) =+    putStrLn $ p ++ " ==" ++ (display v) ++ " (" ++ (show r) ++")"
+ src/Main.hs view
@@ -0,0 +1,135 @@+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module Main where++import AddBase+import AddCabal+import BuildPkgs+import BumpPkgs+import IdxSync+import IdxVersion+import ListPkgs+import Updates+import Util.Misc+import Urls+import PkgBuild++import Paths_cblrepo++import Control.Monad+import Control.Monad.Reader+import Distribution.Text+import System.Console.CmdArgs+import System.Directory+import System.FilePath++-- {{{1 command line arguments+argAppDir = appDir := def += explicit += name "appdir" += help "application data directory" += typDir+argDbFile = dbFile := "cblrepo.db" += explicit += name "db" += help "package database" += typFile+argDryRun = dryRun := False += explicit += name "n" += help "dry run"++cmdAddBasePkg = record defAddBasePkg+    [ argAppDir, argDbFile, argDryRun+    , pkgVers := def += args += typ "STRING,STRING"+    ] += name "addbasepkg" += help "add base packages" += details+        [ "The format for a package is <name>,<version>." ]++cmdAddPkg = record defAddPkg+    [ argAppDir, argDbFile+    , patchDir := "patches" += explicit += name "patchdir" += help "location of patches" += typDir+    , argDryRun+    , cbls := def += args += typ "CABAL"+    ] += name "add" += help "add a package from a Cabal file" += details+        [ "There are three ways to specify the location of the Cabal file:"+        , " 1. The filename of the Cabal file."+        , " 2. A URL where the Cabal can be found (for file:// URLs the full absolute path is required)."+        , " 3. A pair, <name>,<version>, will load the Cabal file out of an index file (see idxupdate)"+        , "All three format may be mixed on the command line."+        ]++cmdBumpPkgs = record defBumpPkgs+    [ argAppDir, argDbFile+    , argDryRun+    , inclusive := False += explicit += name "inclusive" += help "include listed packages"+    , pkgs := def += args += typ "PKG"+    ] += name "bump" += help "bump packages that need it after updating the named packages"++cmdBuildPkgs = record defBuildPkgs+    [ argAppDir, argDbFile+    , pkgs := def += args += typ "PKG"+    ] += name "build" += help "list packages that need rebuilding, in order"++cmdIdxSync = record defIdxSync [ argAppDir ] += help "update the index"++cmdIdxVersion = record defIdxVersion+    [ argAppDir+    , pkgs := def += args += typ "PKG"+    ] += help "list available versions"++cmdUpdates = record defUpdates [ argAppDir , argDbFile ] += name "updates" += help "check for availabale updates"++cmdListPkgs = record defListPkgs+    [ argAppDir, argDbFile+    , incBase := False += help "include base packages in listing"+    ] += name "list" += help "list packages in repo"++cmdUrls = record defUrls+    [ argAppDir+    , pkgVers := def += args += typ "STRING,STRING"+    ] += help "list urls of cabal files for the given packages" += details+        [ "The format for a package is <name>,<version>." ]++cmdPkgBuild = record defPkgBuild+    [ argAppDir, argDbFile+    , patchDir := "patches" += explicit += name "patchdir" += help "location of patches (patches)" += typDir+    , pkgs := def += args += typ "PKG"+    ] += help "create a PKGBUILD, and other files necessary for an Arch package"++cmds = cmdArgsMode_ $ modes_+    [ cmdAddBasePkg+    , cmdAddPkg+    , cmdBuildPkgs+    , cmdBumpPkgs+    , cmdIdxSync+    , cmdIdxVersion+    , cmdListPkgs+    , cmdUpdates+    , cmdUrls+    , cmdPkgBuild+    ]+    += program progName+    += summary (progName ++ " v" ++ (display version))+    += help "maintain a database of dependencies of CABAL packages"++-- {{{1 main+main = do+    defAppDir <- getAppUserDataDirectory progName+    cmdArgsRun cmds >>= \ c -> do+        let aD = if null (appDir c) then defAppDir else (appDir c)+        let c' = c { appDir = aD }+        createDirectoryIfMissing True (aD)+        case c' of+            AddBasePkg {} -> runReaderT addBase c'+            AddPkg {} -> runReaderT addCabal c'+            BuildPkgs {} -> runReaderT buildPkgs c'+            BumpPkgs {} -> runReaderT bumpPkgs c'+            IdxSync {} -> runReaderT idxSync c'+            IdxVersion {} -> runReaderT idxVersion c'+            ListPkgs {} -> runReaderT listPkgs c'+            Updates {} -> runReaderT updates c'+            Urls {} -> runReaderT urls c'+            PkgBuild {} -> runReaderT pkgBuild c'
+ src/PkgDB.hs view
@@ -0,0 +1,145 @@+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module PkgDB where++import Util.Misc++import Control.Exception as CE+import Data.List+import Data.Maybe+import Data.Maybe+import Distribution.PackageDescription+import Distribution.Text+import Distribution.Version+import System.IO.Error+import Text.JSON+import qualified Distribution.Package as P+import qualified Distribution.Version as V++type CblPkg = (String, (V.Version, [P.Dependency], Int))+type CblDB = [CblPkg]++pkgName :: CblPkg -> String+pkgName (n, _) = n++pkgVersion :: CblPkg -> V.Version+pkgVersion (_, (v, _, _)) = v++pkgDeps :: CblPkg -> [P.Dependency]+pkgDeps (_, (_, ds, _)) = ds++pkgRelease :: CblPkg -> Int+pkgRelease (_, (_, _, i)) = i++createCblPkg :: PackageDescription -> CblPkg+createCblPkg pd = (name, (version, deps, 1))+    where+        name = (\ (P.PackageName n) -> n) (P.pkgName $ package pd)+        version = P.pkgVersion $ package pd+        deps = buildDepends pd++getDependencyOn :: String -> CblPkg -> Maybe P.Dependency+getDependencyOn n p = find (\ d -> depName d == n) (pkgDeps p)++isBasePkg (_, (_, ds, _)) = null ds++emptyPkgDB :: CblDB+emptyPkgDB = []++addPkg :: CblDB -> String -> V.Version -> [P.Dependency] -> Int -> CblDB+addPkg db n v ds r = nubBy cmp newdb+    where+        cmp (n1, _) (n2, _) = n1 == n2+        newdb = (n, (v, ds, r)) : db++addPkg2 db (n, (v, ds, r)) = addPkg db n v ds r++addBasePkg db n v = addPkg db n v [] 0++delPkg :: CblDB -> String -> CblDB+delPkg db n = filter (\ p -> n /= pkgName p) db++lookupPkg :: CblDB -> String -> Maybe CblPkg+lookupPkg db n = maybe Nothing (\ s -> Just (n, s)) (lookup n db)++lookupDependencies :: CblDB -> String -> Maybe [String]+lookupDependencies db n =+    case lookupPkg db n of+        Nothing -> Nothing+        Just p -> let+                ds = pkgDeps p+            in Just $ map depName ds++lookupDependants :: CblDB -> String -> [String]+lookupDependants db n = map pkgName $ filter (\ p -> doesDependOn p n) db+    where+        doesDependOn p n = n `elem` (map depName $ pkgDeps p)++checkDependants db n v = let+        d1 = catMaybes $ map (lookupPkg db) (lookupDependants db n)+        d2 = map (\ p -> (pkgName p, getDependencyOn n p)) d1+        fails = filter (not . withinRange v . depVersionRange . fromJust . snd) d2+    in fails++transitiveDependants db pkgs = keepLast $ concat $ map transUsersOfOne pkgs+    where+        transUsersOfOne pkg = pkg : (keepLast $ concat $ map (transUsersOfOne) (lookupDependants db pkg))+        keepLast = reverse . nub . reverse++lookupRelease :: CblDB -> String -> Maybe Int+lookupRelease db n = lookupPkg db n >>= return . pkgRelease++bumpRelease db n = let+        bump (n', (v', d', r')) = (n', (v', d', r' + 1))+    in maybe db (addPkg2 db . bump) (lookupPkg db n)+readDb :: FilePath -> IO CblDB+readDb fp = (flip CE.catch)+    (\ e -> if isDoesNotExistError e+        then return emptyPkgDB+        else throwIO e)+    $ do+        r <- readFile fp >>= return . sequence . map decode . lines+        case r of+            Ok a -> return a+            Error s -> fail s++saveDb :: CblDB -> FilePath -> IO ()+saveDb db fp = writeFile fp s+    where+        s = unlines $ map (encode . showJSON) db++instance JSON V.Version where+    showJSON v = makeObj [ ("Version", showJSON $ display v) ]+    readJSON (JSObject o) = let+            jAssoc = fromJSObject o+            resultToMaybe (Ok a) = Just a+            resultToMaybe _ = Nothing+            version = lookup "Version" jAssoc >>= resultToMaybe . readJSON >>= simpleParse+        in+            maybe (fail "Not a version object") return version+    readJSON _ = fail "Not a version object"++instance JSON P.Dependency where+    showJSON d = makeObj [ ("Dependency", showJSON $ display d) ]+    readJSON (JSObject o) = let+            jAssoc = fromJSObject o+            resultToMaybe (Ok a) = Just a+            resultToMaybe _ = Nothing+            dependency = lookup "Dependency" jAssoc >>= resultToMaybe . readJSON >>= simpleParse+        in+            maybe (fail "Not a dependency object") return dependency+    readJSON _ = fail "Not a version object"
+ src/PkgDB.hs-boot view
@@ -0,0 +1,9 @@+module PkgDB where++import qualified Distribution.Package as P+import qualified Distribution.Version as V++type CblPkg = (String, (V.Version, [P.Dependency], Int))+type CblDB = [CblPkg]++lookupPkg :: CblDB -> String -> Maybe CblPkg
+ src/Updates.hs view
@@ -0,0 +1,70 @@+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module Updates where++import PkgDB+import Util.Misc++import Codec.Archive.Tar as Tar+import Codec.Compression.GZip as GZip+import Control.Monad+import Control.Monad.Reader+import Data.Maybe+import Distribution.Text+import Distribution.Version+import System.FilePath+import qualified Data.ByteString.Lazy.Char8 as BS++updates :: ReaderT Cmds IO ()+updates = do+    db <- cfgGet dbFile >>= liftIO . readDb+    aD <- cfgGet appDir+    entries <- liftIO $ liftM (Tar.read . GZip.decompress)+        (BS.readFile $ aD </> "00-index.tar.gz")+    let nonBasePkgs = filter (\ (_, (_, ds, _)) -> not $ null ds) db+    let pkgsNVers = map (\ (p, (v, _, _)) -> (p, v)) nonBasePkgs+    let availPkgs = catMaybes $ eMap extractPkgVer entries+    let outdated = filter+            (\ (p, v) -> maybe False (> v) (latestVer p availPkgs))+            pkgsNVers+    liftIO $ mapM_ (flip printOutdated availPkgs) outdated++type PkgVer = (String, Version)++extractPkgVer :: Entry -> Maybe PkgVer+extractPkgVer e = let+        ep = entryPath e+        isCabal = '/' `elem` ep+        (pkg:ver':_) = map dropTrailingPathSeparator $ splitPath ep+        ver = simpleParse ver'+    in if isCabal && isJust ver+        then Just $ (pkg, (fromJust ver))+        else Nothing++latestVer p pvs = let+        vs = map snd $ filter ((== p) . fst) pvs+    in if null vs+        then Nothing+        else Just $ maximum vs++eMap _ Done = []+eMap f (Next e es) = (f e):(eMap f es)++printOutdated (p, v) avail = let+        l = fromJust $ latestVer p avail+    in+        putStrLn $ p ++ ": " ++ (display v) ++ " (" ++ (display l) ++ ")"
+ src/Urls.hs view
@@ -0,0 +1,28 @@+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module Urls where++import Util.Misc++import Control.Monad.Reader++urls :: ReaderT Cmds IO ()+urls = do+    pkgs <- cfgGet pkgVers+    liftIO $ mapM_ (putStrLn . createUrl) pkgs++createUrl (pkg, ver) = "http://hackage.haskell.org/packages/archive/" ++ pkg ++ "/" ++ ver ++ "/" ++ pkg ++ ".cabal"
+ src/Util/Misc.hs view
@@ -0,0 +1,204 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module Util.Misc where++import {-# SOURCE #-} PkgDB++import Codec.Archive.Tar as Tar+import Codec.Compression.GZip as GZip+import Control.Concurrent+import Control.Monad+import Control.Monad.Error+import Control.Monad.Reader+import Data.Data+import Data.List+import Data.Typeable+import Distribution.Compiler+import Distribution.Package+import Distribution.PackageDescription+import Distribution.PackageDescription.Configuration+import Distribution.PackageDescription.Parse+import Distribution.System+import Distribution.Text+import Distribution.Verbosity+import Distribution.Version+import System.Directory+import System.Exit+import System.FilePath+import System.IO+import System.Posix.Files+import System.Process+import qualified Control.Exception as CE+import qualified Data.ByteString.Lazy.Char8 as BS++-- {{{1 Dependency+depName (Dependency (PackageName n) _) = n+depVersionRange (Dependency _ vr) = vr++-- {{{ print functions+printUnSat (n, ds) = do+    putStrLn $ "Failed to satisfy the following dependencies for " ++ n ++ ":"+    mapM_ (putStrLn . ("  " ++) . display) ds++printBrksOth  ((n, v), brks) = do+    putStrLn $ "Adding " ++ n ++ " " ++ (display v) ++ " would break:"+    mapM_ (\ (bN, (Just bD)) -> putStrLn $ "  " ++ bN ++ " : " ++ (display bD)) brks++-- {{{1 program variables+progName = "cblrepo"+dbName = progName ++ ".db"++-- {{{1 command line argument type+data Cmds+    = AddBasePkg { appDir :: FilePath, dbFile :: FilePath, dryRun :: Bool, pkgVers :: [(String, String)] }+    | AddPkg { appDir :: FilePath, dbFile :: FilePath, patchDir :: FilePath, dryRun :: Bool, cbls :: [FilePath] }+    | BuildPkgs { appDir :: FilePath, dbFile :: FilePath, pkgs :: [String] }+    | BumpPkgs { appDir :: FilePath, dbFile :: FilePath, dryRun :: Bool, inclusive :: Bool, pkgs :: [String] }+    | IdxSync { appDir :: FilePath }+    | IdxVersion { appDir :: FilePath, pkgs :: [String] }+    | ListPkgs { appDir :: FilePath, dbFile :: FilePath, incBase :: Bool }+    | Updates { appDir :: FilePath, dbFile :: FilePath }+    | Urls { appDir :: FilePath, pkgVers :: [(String, String)] }+    | PkgBuild { appDir :: FilePath, dbFile :: FilePath, patchDir :: FilePath, pkgs :: [String] }+    deriving (Show, Data, Typeable)++defAddBasePkg = AddBasePkg "" "" True []+defAddPkg = AddPkg "" "" "" True []+defBuildPkgs =  BuildPkgs "" "" []+defBumpPkgs =  BumpPkgs "" "" False False []+defIdxSync =  IdxSync ""+defIdxVersion =  IdxVersion "" []+defListPkgs =  ListPkgs "" "" True+defUpdates =  Updates "" ""+defUrls =  Urls "" []+defPkgBuild =  PkgBuild "" "" "" []++cfgGet f = liftM f ask++-- {{{1 getFromURL+getFromURL url fn = do+    (ec, _, er) <- readProcessWithExitCode "curl" ["-f", "-o", fn, url] ""+    case ec of+        ExitSuccess -> return ()+        ExitFailure _ -> do+            hPutStrLn stderr ("Failed downloading " ++ url)+            hPutStrLn stderr er+            exitFailure++-- {{{1 applyPatchIfExist+applyPatch origFilename patchFilename = do+    (ec, _, err) <- readProcessWithExitCode "patch" [origFilename, patchFilename] ""+    case ec of+        ExitSuccess -> return ()+        ExitFailure _ ->+            hPutStrLn stderr "Failed patching the .cabal file" >> exitFailure++applyPatchIfExist origFilename patchFilename =+    fileExist patchFilename >>= flip when (applyPatch origFilename patchFilename)++-- {{{1 package descriptions+-- {{{2 readCabal+data LocType = Url | Idx | File++-- | Read in a Cabal file.+readCabal patchDir loc tmpDir = let+        locType+            | isInfixOf "://" loc = Url+            | ',' `elem` loc = Idx+            | otherwise = File++        copyCabal tmpDir loc = copyFile loc fn >> return fn+            where fn = tmpDir </> takeFileName loc++        downloadCabal tmpDir loc = getFromURL loc fn >> return fn+            where+                fn = tmpDir </> takeFileName loc++        extractCabal tmpDir loc = let+                (p, (_: v)) = span (/= ',') loc+                path = p </> v </> p ++ ".cabal"+                pkgStr = p ++ " " ++ v+                fn = tmpDir </> (p ++ ".cabal")++                esFindEntry p (Next e es) = if p == (entryPath e)+                    then Just e+                    else esFindEntry p es+                esFindEntry _ _ = Nothing++                eGetContent e = let+                        ec = entryContent e+                    in case ec of+                        NormalFile c _ -> Just $ BS.unpack c+                        _ -> Nothing++            in do+                fp <- getAppUserDataDirectory "cblrepo"+                es <- liftM (Tar.read . GZip.decompress)+                    (BS.readFile $ fp </> "00-index.tar.gz")+                e <- maybe (error $ "No entry for " ++ pkgStr)+                    return+                    (esFindEntry path es)+                cbl <- maybe (error $ "Failed to extract contents for " ++ pkgStr)+                    return+                    (eGetContent e)+                writeFile fn cbl+                return fn++        extractName fn = liftM name $ readPackageDescription silent fn+            where+                packageName (PackageName s) = s+                name = packageName . pkgName . package . packageDescription++    in do+        cblFn <- case locType of+            File -> copyCabal tmpDir loc+            Idx -> extractCabal tmpDir loc+            Url -> downloadCabal tmpDir loc+        pn <- extractName cblFn+        let patchFn = patchDir </> pn <.> "cabal"+        return patchFn+        applyPatchIfExist cblFn patchFn+        readPackageDescription silent cblFn++-- {{{2 finalising+finalizePkg db = finalizePackageDescription+    [] -- no flags+    (checkAgainstDb db)+    (Platform X86_64 buildOS) -- platform+    (CompilerId GHC (Version [7,0,2] []))  -- compiler version+    [] -- no additional constraints++checkAgainstDb db dep = let+        dN = depName dep+        dVR = depVersionRange dep+    in case lookupPkg db dN of+        Nothing -> False+        Just (_, (v, _, _)) -> withinRange v dVR++-- {{{1 allPatches+allPatches pn patchDir = let+        cblPatch = patchDir </> pn <.> "cabal"+        pkgPatch = patchDir </> pn <.> "pkgbuild"+        bldPatch = patchDir </> pn <.> "build"+    in do+        cE <- fileExist cblPatch+        pE <- fileExist pkgPatch+        bE <- fileExist bldPatch+        return (if cE then Just cblPatch else Nothing,+            if pE then Just pkgPatch else Nothing,+            if bE then Just bldPatch else Nothing)
+ tst/TestPkgDB.hs view
@@ -0,0 +1,52 @@+{-# OPTIONS_GHC -XTemplateHaskell #-}+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module TestPkgDB+    ( testGroup+    ) where++import PkgDB++import Data.Maybe+import Distribution.Text+import Test.Framework.Providers.HUnit+import Test.Framework.TH+import Test.HUnit++-- {{{1 transitiveDependants+theDb :: CblDB+theDb =+    [ ("pkgA", (fromJust $ simpleParse "1.0", [])) -- nothing depends on this pkg+    -- a chain of two+    , ("pkgB", (fromJust $ simpleParse "1.0", []))+    , ("pkgC", (fromJust $ simpleParse "1.0", map (fromJust . simpleParse) ["pkgB"]))+    -- a chain of three+    , ("pkgD", (fromJust $ simpleParse "1.0", []))+    , ("pkgE", (fromJust $ simpleParse "1.0", map (fromJust . simpleParse) ["pkgD"]))+    , ("pkgF", (fromJust $ simpleParse "1.0", map (fromJust . simpleParse) ["pkgE"]))+    ]++case_TD_one = do transitiveDependants theDb ["pkgA"] @=? ["pkgA"]+case_TD_two = do transitiveDependants theDb ["pkgB"] @=? ["pkgB", "pkgC"]+case_TD_three = do transitiveDependants theDb ["pkgD"] @=? ["pkgD", "pkgE", "pkgF"]+-- pass in packages in correct order+case_TD_ordering1 = do transitiveDependants theDb ["pkgD", "pkgF"] @=? ["pkgD", "pkgE", "pkgF"]+-- pass in packages in reverse order+case_TD_ordering2 = do transitiveDependants theDb ["pkgF", "pkgD"] @=? ["pkgD", "pkgE", "pkgF"]++-- {{{1 testGroup+testGroup = $(testGroupGenerator)
+ tst/TestSystem.hs view
@@ -0,0 +1,31 @@+{-# OPTIONS_GHC -XTemplateHaskell #-}+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module TestSystem+    ( testGroup+    ) where++import Data.List+import Test.Framework.Providers.HUnit+import Test.Framework.TH+import Test.HUnit++-- {{{1 nub works as expected+case_nub_order = do nub [1,2,1,3,1] @=? [1,2,3]++-- {{{1 testGroup+testGroup = $(testGroupGenerator)
+ tst/tst.hs view
@@ -0,0 +1,28 @@+{-+ - Copyright 2011 Per Magnus Therning+ -+ - Licensed under the Apache License, Version 2.0 (the "License");+ - you may not use this file except in compliance with the License.+ - You may obtain a copy of the License at+ -+ -     http://www.apache.org/licenses/LICENSE-2.0+ -+ - Unless required by applicable law or agreed to in writing, software+ - distributed under the License is distributed on an "AS IS" BASIS,+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+ - See the License for the specific language governing permissions and+ - limitations under the License.+ -}++module Main where++import qualified TestPkgDB+import qualified TestSystem++import Test.Framework++main = defaultMain+    [ TestPkgDB.testGroup+    , TestSystem.testGroup+    ]+