├── .gitignore ├── Distribution └── ArchLinux │ ├── CabalTranslation.hs │ ├── HackageTranslation.hs │ ├── PkgBuild.hs │ ├── SrcRepo.hs │ └── SystemProvides.lhs ├── LICENSE ├── Setup.lhs ├── TODO ├── archlinux.cabal ├── scripts ├── recdeps.hs └── reverse_deps.hs └── tests ├── listconflicts.hs ├── readpkg.hs └── toposort.hs /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | *.hi 3 | *.o 4 | *~ 5 | 6 | -------------------------------------------------------------------------------- /Distribution/ArchLinux/CabalTranslation.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : Distribution.ArchLinux.CabalTranslation 3 | -- Copyright : (c) Don Stewart, 2008-2010, Rémy Oudompheng 2010 4 | -- License : BSD3 5 | -- 6 | -- Maintainer: Arch Haskell Team 7 | -- 8 | 9 | module Distribution.ArchLinux.CabalTranslation ( 10 | preprocessCabal, 11 | cabal2pkg, cabal2pkg', 12 | oldCabal2Arch, 13 | install_hook_name 14 | ) where 15 | -- Cabal modules 16 | import Distribution.Package 17 | import Distribution.PackageDescription 18 | import Distribution.PackageDescription.Configuration 19 | import Distribution.License 20 | import Distribution.Version 21 | import Distribution.Compiler 22 | import Distribution.System 23 | -- Archlinux modules 24 | import Distribution.ArchLinux.PkgBuild 25 | import Distribution.ArchLinux.SystemProvides 26 | -- Standard types 27 | import Distribution.Text 28 | import Data.Char 29 | import Data.List 30 | import qualified Data.Map as M 31 | import Data.Maybe 32 | import Data.Monoid 33 | import System.FilePath 34 | -- Debugging 35 | import Debug.Trace 36 | 37 | -- 38 | -- | Configure package for system 39 | -- 40 | preprocessCabal :: GenericPackageDescription -> SystemProvides -> Maybe PackageDescription 41 | preprocessCabal cabalsrc systemContext = 42 | case finalizePackageDescription 43 | [] 44 | (const True) -- could check against prefered pkgs.... 45 | (Platform X86_64 buildOS) -- linux/x86_64 46 | (CompilerInfo (CompilerId GHC (Version [7,0,3] [])) NoAbiTag Nothing Nothing Nothing) 47 | 48 | -- now constrain it to solve in the context of a modern ghc only 49 | (corePackages systemContext ++ platformPackages systemContext) 50 | cabalsrc 51 | of 52 | Left deps -> trace ("Unresolved dependencies: " ++show deps) Nothing 53 | Right (pkg,_) -> Just pkg { buildDepends = removeCoreFrom (buildDepends pkg) systemContext } 54 | 55 | -- attempt to filter out core packages we've already satisified 56 | -- not actually correct, since it doesn't take any version 57 | -- info into account. 58 | -- 59 | -- TODO this should use configDependency to find the precise 60 | -- versions we have available on Arch. 61 | -- 62 | removeCoreFrom :: [Dependency] -> SystemProvides -> [Dependency] 63 | removeCoreFrom [] _ = [] 64 | removeCoreFrom (x@(Dependency n vr):xs) systemContext = 65 | case find (\(Dependency k _) -> n == k) $ corePackages systemContext of 66 | -- haskell-parsec, haskell-quickcheck 67 | Just (Dependency (PackageName "base") _) 68 | -> removeCoreFrom xs systemContext 69 | 70 | Just (Dependency _ corevr) 71 | | isAnyVersion corevr -> removeCoreFrom xs systemContext 72 | | isJust isspef && (withinRange (fromJust isspef) vr) 73 | -> removeCoreFrom xs systemContext 74 | where isspef = isSpecificVersion corevr 75 | 76 | _ -> x : removeCoreFrom xs systemContext 77 | 78 | ------------------------------------------------------------------------------------ 79 | 80 | -- 81 | -- | Translate a generic cabal file into a PKGBUILD (using default 82 | -- values for pkgname and pkgrel). 83 | -- 84 | cabal2pkg :: PackageDescription -> SystemProvides -> (AnnotatedPkgBuild, Maybe String) 85 | cabal2pkg cabal systemContext = cabal2pkg' cabal archName 1 systemContext 86 | where 87 | archName = map toLower (if isLibrary then "haskell-" ++ display name else display name) 88 | name = pkgName (package cabal) 89 | isLibrary = isJust (library cabal) && map toLower (display name) `notElem` shouldNotBeLibraries 90 | 91 | -- 92 | -- | Translate a generic cabal file into a PKGBUILD, using the specified 93 | -- ArchLinux package name and package release. 94 | -- 95 | cabal2pkg' :: PackageDescription -> String -> Int -> SystemProvides -> (AnnotatedPkgBuild, Maybe String) 96 | cabal2pkg' cabal archName release systemContext 97 | 98 | -- TODO decide if it's a library or an executable, 99 | -- handle multipackages 100 | -- extract C dependencies 101 | 102 | -- = trace (show cabal) $ 103 | = ( emptyPkg { 104 | pkgHeader = [] 105 | , hkgName = display name 106 | , pkgBody = stub { 107 | arch_pkgname = archName 108 | , arch_pkgver = vers 109 | , arch_pkgrel = release 110 | , arch_pkgdesc = case synopsis cabal of 111 | [] -> take 80 (description cabal) 112 | s -> s 113 | , arch_license = 114 | ArchList . return $ 115 | case license cabal of 116 | x@GPL {} -> x 117 | x@LGPL {} -> x 118 | l -> UnknownLicense ("custom:"++ show l) 119 | , arch_package = (arch_package stub) ++ 120 | (if not (null (licenseFiles cabal)) && (case license cabal of GPL {} -> False; LGPL {} -> False; _ -> True) 121 | then 122 | [ "install -D -m644 " ++ unwords (licenseFiles cabal) ++ " ${pkgdir}/usr/share/licenses/${pkgname}/" 123 | , "rm -f ${pkgdir}/usr/share/doc/${pkgname}/LICENSE" 124 | ] 125 | else []) } 126 | }, if hasLibrary 127 | then Just (install_hook archName) 128 | else Nothing 129 | ) 130 | 131 | where 132 | stub = if hasLibrary 133 | then (stubPackageLibrary $ display name) { 134 | arch_depends = ( 135 | if not (isLibrary) 136 | then ArchList [ArchDep (Dependency (PackageName "gmp") anyVersion)] 137 | `mappend` anyClibraries 138 | -- libraries have 'register-time' dependencies on 139 | -- their dependent Haskell libraries. 140 | -- 141 | else ArchList []) `mappend` my_makedepends 142 | } 143 | else (stubPackageProgram $ display name) { 144 | -- isLibrary = False automatically 145 | arch_makedepends = my_makedepends 146 | , arch_depends = ArchList [ArchDep (Dependency (PackageName "gmp") anyVersion)] 147 | `mappend` anyClibraries 148 | } 149 | 150 | name = pkgName (package cabal) 151 | vers = pkgVersion (package cabal) 152 | 153 | -- build time dependencies 154 | my_makedepends = 155 | -- everything depends on ghc and Cabal 1.4.x 156 | ArchList 157 | [(ArchDep (Dependency (PackageName "ghc") anyVersion))] 158 | `mappend` 159 | -- Haskell libraries 160 | -- TODO: use a real package spec to compute these names 161 | -- based on what is in Arch. 162 | ArchList 163 | [ ArchDep (Dependency (PackageName $ 164 | if d `notElem` shouldNotBeLibraries 165 | then "haskell" <-> map toLower (display d) else display d) v) 166 | | Dependency (PackageName d) v <- gtk2hsIfy (buildDepends cabal) ] 167 | `mappend` 168 | anyClibraries 169 | `mappend` 170 | ArchList [ ArchDep d' | b <- allBuildInfo cabal 171 | , d@(Dependency n _) <- buildTools b 172 | , n /= PackageName "hsc2hs" 173 | , let d' | n `elem` gtkTools 174 | = Dependency (PackageName "gtk2hs-buildtools") anyVersion 175 | | otherwise = d 176 | ] 177 | 178 | gtkTools = map PackageName ["gtk2hsTypeGen" , "gtk2hsHookGenerator", "gtk2hsC2hs"] 179 | 180 | -- TODO: need a 'nub' in here. 181 | 182 | hasLibrary = isJust (library cabal) 183 | isLibrary = isJust (library cabal) -- && null (executables cabal) 184 | && map toLower (display name) `notElem` shouldNotBeLibraries 185 | 186 | anyClibraries | null libs = ArchList [] 187 | | otherwise = ArchList libs 188 | where 189 | libs = [ ArchDep (Dependency (PackageName s) anyVersion) | s <- nub (findCLibs cabal systemContext) ] 190 | 191 | (<->) :: String -> String -> String 192 | x <-> y = x ++ "-" ++ y 193 | 194 | -- 195 | -- | A PKGBUILD skeleton for Haskell libraries (hasLibrary = True) 196 | -- 197 | stubPackageLibrary :: String -> PkgBuild 198 | stubPackageLibrary _ = emptyPkgBuild { 199 | arch_url = "http://hackage.haskell.org/package/${_hkgname}" 200 | -- All Hackage packages depend on GHC at build time 201 | -- All Haskell libraries are prefixed with "haskell-" 202 | , arch_makedepends = ArchList [] -- makedepends should not duplicate depends 203 | -- Hackage programs only need their own source to build 204 | , arch_source = ArchList . return $ 205 | "http://hackage.haskell.org/packages/archive/${_hkgname}/${pkgver}/${_hkgname}-${pkgver}.tar.gz" 206 | , arch_build = 207 | [ "cd ${srcdir}/${_hkgname}-${pkgver}" 208 | , "runhaskell Setup configure -O ${PKGBUILD_HASKELL_ENABLE_PROFILING:+-p } --enable-split-objs --enable-shared \\" 209 | , " --prefix=/usr --docdir=/usr/share/doc/${pkgname} --libsubdir=\\$compiler/site-local/\\$pkgid" 210 | , "runhaskell Setup build" 211 | , "runhaskell Setup haddock" 212 | , "runhaskell Setup register --gen-script" 213 | , "runhaskell Setup unregister --gen-script" 214 | , "sed -i -r -e \"s|ghc-pkg.*unregister[^ ]* |&'--force' |\" unregister.sh" 215 | ] 216 | , arch_package = 217 | [ "cd ${srcdir}/${_hkgname}-${pkgver}" 218 | , "install -D -m744 register.sh ${pkgdir}/usr/share/haskell/${pkgname}/register.sh" 219 | , "install -m744 unregister.sh ${pkgdir}/usr/share/haskell/${pkgname}/unregister.sh" 220 | , "install -d -m755 ${pkgdir}/usr/share/doc/ghc/html/libraries" 221 | , "ln -s /usr/share/doc/${pkgname}/html ${pkgdir}/usr/share/doc/ghc/html/libraries/${_hkgname}" 222 | , "runhaskell Setup copy --destdir=${pkgdir}" 223 | ] 224 | -- if its a library: 225 | , arch_install = Just "${pkgname}.install" 226 | } 227 | 228 | -- 229 | -- | A PKGBUILD skeleton for Haskell programs (hasLibrary = False) 230 | -- 231 | stubPackageProgram :: String -> PkgBuild 232 | stubPackageProgram _ = emptyPkgBuild { 233 | arch_url = "http://hackage.haskell.org/package/${_hkgname}" 234 | -- Hackage programs only need their own source to build 235 | , arch_source = ArchList . return $ 236 | "http://hackage.haskell.org/packages/archive/${_hkgname}/${pkgver}/${_hkgname}-${pkgver}.tar.gz" 237 | , arch_build = 238 | [ "cd ${srcdir}/${_hkgname}-${pkgver}" 239 | , "runhaskell Setup configure --prefix=/usr --docdir=/usr/share/doc/${pkgname} -O" 240 | , "runhaskell Setup build" 241 | ] 242 | , arch_package = 243 | [ "cd ${srcdir}/${_hkgname}-${pkgver}" 244 | , "runhaskell Setup copy --destdir=${pkgdir}"] 245 | , arch_install = Nothing 246 | } 247 | 248 | -- 249 | -- post install, and pre-remove hooks to run, to sync up ghc-pkg 250 | -- 251 | install_hook_name :: String -> String 252 | install_hook_name pkgname = pkgname <.> "install" 253 | 254 | install_hook :: String -> String 255 | install_hook pkgname = unlines 256 | [ "HS_DIR=usr/share/haskell/" ++ pkgname 257 | , "post_install() {" 258 | , " ${HS_DIR}/register.sh" 259 | , " (cd usr/share/doc/ghc/html/libraries; ./gen_contents_index)" 260 | , "}" 261 | , "pre_upgrade() {" 262 | , " ${HS_DIR}/unregister.sh" 263 | , "}" 264 | , "post_upgrade() {" 265 | , " ${HS_DIR}/register.sh" 266 | , " (cd usr/share/doc/ghc/html/libraries; ./gen_contents_index)" 267 | , "}" 268 | , "pre_remove() {" 269 | , " ${HS_DIR}/unregister.sh" 270 | , "}" 271 | , "post_remove() {" 272 | , " (cd usr/share/doc/ghc/html/libraries; ./gen_contents_index)" 273 | , "}" ] 274 | 275 | findCLibs :: PackageDescription -> SystemProvides -> [String] 276 | findCLibs (PackageDescription { library = lib, executables = exe }) sysContext = 277 | -- warn for packages not in list. 278 | filter (not . null) $ map (canonicalise . map toLower) (some ++ rest) 279 | where 280 | some = concatMap (extraLibs.buildInfo) exe 281 | rest = case lib of 282 | Nothing -> [] 283 | Just l -> extraLibs (libBuildInfo l) ++ 284 | map (\(Dependency (PackageName n) _) -> 285 | if '-' `elem` n 286 | then reverse . drop 1 . dropWhile (/= '-') . reverse $ n 287 | else n) 288 | (pkgconfigDepends (libBuildInfo l)) 289 | 290 | canonicalise k = case M.lookup k (translationTable sysContext) of 291 | Nothing -> trace ("WARNING: this library depends on a C library we do not know the pacman name for (" ++ map toLower k ++ ") . Check the C library names in the generated PKGBUILD File") $ map toLower k 292 | Just s -> s 293 | 294 | shouldNotBeLibraries :: [String] 295 | shouldNotBeLibraries = 296 | ["xmonad" 297 | ,"gitit" 298 | ,"yavie" 299 | ,"berp" 300 | ,"l-seed" 301 | ,"hspresent" 302 | ,"haskell-platform" 303 | ,"xmonad-contrib" 304 | ,"xmonad-extras" 305 | ,"lambdabot" 306 | ,"piet" 307 | ,"hsffig" 308 | ,"yi" 309 | ,"haddock" 310 | ,"hscolour" 311 | ,"line2pdf" 312 | ,"distract" 313 | ,"derive" 314 | ,"Hedi" 315 | ,"conjure" 316 | ,"clevercss" 317 | ,"cpphs" 318 | ,"backdropper" 319 | ,"darcs-beta" 320 | ,"gtk2hs" 321 | ,"darcs" 322 | ,"greencard" 323 | -- the pandoc package doesnt' ship haskell-pandoc 324 | -- ,"pandoc" 325 | ,"pugs-drift" 326 | ,"wol" 327 | ,"timepiece" 328 | ,"hledger" 329 | ,"hp2any-graph" 330 | ,"hp2any-manager" 331 | ] 332 | 333 | -- translate some library dependencies to gtk names 334 | -- 335 | gtk2hsIfy :: [Dependency] -> [Dependency] 336 | gtk2hsIfy = id 337 | 338 | {- 339 | gtk2hsIfy [] = [] 340 | gtk2hsIfy xs | foundSome = Dependency (PackageName "gtk2hs") AnyVersion : 341 | [ v | v@(Dependency n _) <- xs 342 | , n `notElem` gtkLibs ] 343 | | otherwise = xs 344 | 345 | where 346 | foundSome = not . null $ filter (`elem` gtkLibs) (map unDep xs) 347 | unDep (Dependency n _) = n 348 | -} 349 | 350 | ------------------------------------------------------------------------ 351 | -- Checker 352 | -- 353 | 354 | -- 2010-10-29: This code is unused. Do we still need it? 355 | -- type Warnings = String 356 | 357 | -- Hard code the cabal2arch version 358 | recentCabal2ArchVersion :: Maybe Version 359 | recentCabal2ArchVersion = case simpleParse "0.7" of -- XXX 360 | Nothing -> error "Unable to parse cabal2arch version" 361 | Just v -> Just v 362 | 363 | -- | Look for problems in the PKGBUILD 364 | oldCabal2Arch :: AnnotatedPkgBuild -> Bool 365 | oldCabal2Arch s 366 | | isNothing (pkgBuiltWith s) 367 | = True 368 | 369 | | pkgBuiltWith s < recentCabal2ArchVersion 370 | = True -- ["Old version of cabal2arch: " ++ display (fromJust (pkgBuiltWith s))] 371 | 372 | | otherwise = False 373 | 374 | -------------------------------------------------------------------------------- /Distribution/ArchLinux/HackageTranslation.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : Distribution.ArchLinux.HackageTranslation 3 | -- Copyright : (c) Rémy Oudompheng 2010 4 | -- License : BSD3 5 | -- 6 | -- Maintainer: Arch Haskell Team 7 | -- 8 | 9 | module Distribution.ArchLinux.HackageTranslation 10 | ( getVersionConflicts 11 | , getLatestVersions 12 | ) 13 | where 14 | 15 | import Distribution.ArchLinux.CabalTranslation 16 | import Distribution.ArchLinux.SystemProvides 17 | -- Cabal modules 18 | import Distribution.Package 19 | import Distribution.Version 20 | import Distribution.PackageDescription 21 | -- Standard types 22 | import qualified Data.Map as M 23 | import Data.Maybe 24 | 25 | -- 26 | -- | Check for inconsistencies in version requirements 27 | -- returns a list of pairs (package, conflicting dep). 28 | -- 29 | getVersionConflicts :: [GenericPackageDescription] -> SystemProvides -> [(PackageDescription, Dependency)] 30 | getVersionConflicts packages sysProvides = concat $ map conflicts cabals 31 | where cabals = mapMaybe (\p -> preprocessCabal p sysProvides) packages 32 | versions = M.fromList $ map (\p -> (pkgName $ packageId p, pkgVersion $ packageId p)) cabals 33 | issatisfied (Dependency pkg range) = case M.lookup pkg versions of 34 | Nothing -> True 35 | Just v -> v `withinRange` range 36 | conflicts p = map (\d -> (p,d)) $ filter (not . issatisfied) (buildDepends p) 37 | 38 | -- 39 | -- | Returns the latest versions 40 | -- 41 | getLatestVersions :: [GenericPackageDescription] -> M.Map PackageName Version 42 | getLatestVersions packages = M.fromListWith max versions 43 | where versions = map (\p -> (pkgName $ packageId p, pkgVersion $ packageId p)) packages 44 | -------------------------------------------------------------------------------- /Distribution/ArchLinux/PkgBuild.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeSynonymInstances #-} 2 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} 3 | {-# LANGUAGE FlexibleInstances #-} 4 | -- | 5 | -- Module : Distribution.ArchLinux.PkgBuild 6 | -- Copyright : (c) Don Stewart, 2008-2010 7 | -- License : BSD3 8 | -- 9 | -- Maintainer: Don Stewart 10 | -- 11 | 12 | module Distribution.ArchLinux.PkgBuild ( 13 | PkgBuild(..), 14 | emptyPkgBuild, 15 | AnnotatedPkgBuild(..), 16 | emptyPkg, 17 | ArchList(..), 18 | ArchDep(..), 19 | pkgnameFromArchDep, 20 | decodePackage, 21 | pkg2doc 22 | ) where 23 | 24 | import Distribution.Text 25 | import Distribution.Version 26 | import Distribution.PackageDescription 27 | import Distribution.Package 28 | import Distribution.License 29 | 30 | import Text.PrettyPrint 31 | import Data.List 32 | import Data.Monoid hiding ((<>)) 33 | import Debug.Trace 34 | 35 | import Control.Applicative (Applicative(..)) 36 | import Control.Monad 37 | import Data.Char 38 | 39 | 40 | -- 41 | -- | A data type to represent PKGBUILD files 42 | -- 43 | data PkgBuild = 44 | PkgBuild 45 | { arch_pkgname :: String 46 | -- ^ 47 | -- The name of the package. This has be a unix-friendly name 48 | -- as it will be used in the package filename. 49 | , arch_pkgver :: Version 50 | -- ^ The version of the software as released from the authorii 51 | -- (e.g. ´2.7.1´). 52 | , arch_pkgrel :: !Int 53 | -- ^ 54 | -- This is the release number specific to the Arch Linux 55 | -- release. This allows package maintainers to make updates to 56 | -- the package´s configure flags, for example. A pkgrel of 1 57 | -- is typically used for each upstream software release and is 58 | -- incremented for intermediate PKGBUILD updates. 59 | , arch_pkgdesc :: String 60 | -- ^ 61 | -- This should be a brief description of the package and its 62 | -- functionality. Try to keep the description to one line of text. 63 | , arch_arch :: ArchList ArchArch 64 | -- ^ 65 | -- Defines on which architectures the given package is 66 | -- available (e.g. arch=(´i686´ ´x86_64´)). 67 | , arch_url :: String 68 | -- ^ 69 | -- This field contains a URL that is associated with the software 70 | -- being packaged. This is typically the project´s website. 71 | , arch_license :: ArchList License 72 | -- ^ 73 | -- This field specifies the license(s) that apply to the package. 74 | -- Commonly-used licenses are found in /usr/share/licenses/common. If 75 | -- you see the package´s license there, simply reference it in the 76 | -- license field (e.g. license=(´GPL´)). If the package provides a 77 | -- license not found in /usr/share/licenses/common, then you should 78 | -- include the license in the package itself and set 79 | -- license=(´custom´) or license=(´custom:LicenseName´). The license 80 | -- should be placed in $pkgdir/usr/share/licenses/$pkgname when 81 | -- building the package. If multiple licenses are applicable for a 82 | -- package, list all of them: license=(´GPL´ ´FDL´). 83 | , arch_makedepends :: ArchList ArchDep 84 | -- ^ 85 | -- An array of packages that this package depends on to build, but are 86 | -- not needed at runtime. Packages in this list follow the same format 87 | -- as depends. 88 | 89 | , arch_depends :: ArchList ArchDep 90 | -- ^ 91 | -- An array of packages that this package depends on to run. Packages 92 | -- in this list should be surrounded with single quotes and contain at 93 | -- least the package name. Entries can also include a version 94 | -- requirement of the form name<>version, where <> is one of five 95 | -- comparisons: >= (greater than or equal to), <= (less than or equal 96 | -- to), = (equal to), > (greater than), or < (less than). 97 | , arch_source :: ArchList String 98 | -- ^ 99 | -- An array of source files required to build the package. Source 100 | -- files must either reside in the same directory as the PKGBUILD 101 | -- file, or be a fully-qualified URL that makepkg will use to download 102 | -- the file. In order to make the PKGBUILD as useful as possible, use 103 | -- the $pkgname and $pkgver variables if possible when specifying the 104 | -- download location. Any files that are compressed will automatically 105 | -- be extracted, unless found in the noextract array listed below. 106 | , arch_md5sum :: ArchList String 107 | -- ^ 108 | -- This array contains an MD5 hash for every source file specified in 109 | -- the source array (in the same order). makepkg will use this to 110 | -- verify source file integrity during subsequent builds. To easily 111 | -- generate md5sums, run “makepkg -g >> PKGBUILD”. If desired, move 112 | -- the md5sums line to an appropriate location. NOTE: makepkg 113 | -- supports multiple integrity algorithms and their corresponding 114 | -- arrays (i.e. sha1sums for the SHA1 algorithm); however, official 115 | -- packages use only md5sums for the time being. 116 | , arch_build :: [String] 117 | -- ^ 118 | -- The build hook 119 | , arch_package :: [String] 120 | -- ^ 121 | -- The packaging hook 122 | , arch_install :: Maybe String 123 | -- ^ 124 | -- Specifies a special install script that is to be included in the package. This 125 | -- file should reside in the same directory as the PKGBUILD, and will be copied 126 | -- into the package by makepkg. It does not need to be included in the source 127 | -- array (e.g. install=pkgname.install). 128 | -- 129 | , arch_options :: ArchList ArchOptions 130 | -- ^ 131 | -- This array allows you to override some of makepkg´s default behavior when 132 | -- building packages. To set an option, just include the option name in the 133 | -- options array. To reverse the default behavior, place an “!” at the front of 134 | -- the option. Only specify the options you specifically want to override, the 135 | -- rest will be taken from makepkg.conf(5). NOTE: force is a special option only 136 | -- used in a PKGBUILD(5), do not use it unless you know what you are doing. 137 | 138 | } 139 | deriving (Show, Eq) 140 | 141 | data ArchOptions 142 | = Strip 143 | deriving (Show, Eq) 144 | 145 | -- 146 | -- | An empty PKGBUILD 147 | -- 148 | emptyPkgBuild :: PkgBuild 149 | emptyPkgBuild = 150 | PkgBuild 151 | { arch_pkgname = display $ pkgName (package e) 152 | , arch_pkgver = pkgVersion (package e) 153 | , arch_pkgrel = 1 154 | , arch_pkgdesc = synopsis e 155 | , arch_arch = ArchList [Arch_X86, Arch_X86_64] 156 | , arch_url = homepage e 157 | , arch_license = ArchList [license e] 158 | , arch_depends = ArchList [] 159 | , arch_makedepends = ArchList [] 160 | , arch_source = ArchList [] 161 | , arch_md5sum = ArchList [] 162 | -- sha1sums=('a08670e4c749850714205f425cb460ed5a0a56b2') 163 | , arch_build = [] 164 | , arch_package = [] 165 | , arch_install = Nothing -- executable 166 | , arch_options = ArchList [Strip] 167 | } 168 | where 169 | e = emptyPackageDescription 170 | 171 | ------------------------------------------------------------------------ 172 | -- Extra pretty printer instances and types 173 | 174 | newtype ArchDep = ArchDep Dependency 175 | deriving (Eq,Show) 176 | 177 | instance Text ArchOptions where 178 | disp Strip = text "strip" 179 | parse = undefined 180 | 181 | -- the PKGBUILD version spec is less expressive than cabal, we can 182 | -- only handle simple intervals like (0,v) or (v,+infty) 183 | mydisp :: VersionInterval -> Doc 184 | mydisp (LowerBound v t, NoUpperBound) = 185 | case t of 186 | InclusiveBound -> if v==zeroVersion then empty else text ">=" <> disp v 187 | ExclusiveBound -> text ">" <> disp v 188 | mydisp (LowerBound v1 _, UpperBound v2 t2) = text symbol <> disp v2 189 | where symbol | v1 == v2 = "=" 190 | | t2 == InclusiveBound = "<=" 191 | | t2 == ExclusiveBound = "<" 192 | 193 | zeroVersion :: Version 194 | zeroVersion = Version [0] [] 195 | 196 | instance Text ArchDep where 197 | disp (ArchDep (Dependency name ver)) = 198 | disp name <> mydisp (collapse intervals) 199 | where 200 | intervals = asVersionIntervals ver 201 | strName = display name 202 | -- >= (greater than or equal to), <= (less than or 203 | -- equal to), = (equal to), > (greater than), or < 204 | -- 205 | -- Reduce intervals to a single one 206 | collapse l | null l = trace ("WARNING: version requirement for " ++ 207 | strName ++ " is logically impossible.") 208 | (head $ asVersionIntervals anyVersion) 209 | | null $ tail l = head l 210 | -- If there are multiple possible ranges, take the interval that contains all 211 | | otherwise = trace ("WARNING: multiple version ranges specified for " ++ 212 | strName ++ ", using the extremal bounds instead.") 213 | (fst $ head l, snd $ last l) 214 | parse = undefined 215 | 216 | -- 217 | -- | Extract just the package name from ArchDep 218 | -- 219 | pkgnameFromArchDep :: ArchDep -> String 220 | pkgnameFromArchDep (ArchDep (Dependency (PackageName p) _)) = p 221 | 222 | -- 223 | -- | Valid linux platforms 224 | -- 225 | data ArchArch = Arch_X86 | Arch_X86_64 226 | deriving (Show, Eq) 227 | 228 | instance Text ArchArch where 229 | disp x = case x of 230 | Arch_X86 -> text "i686" 231 | Arch_X86_64 -> text "x86_64" 232 | parse = error "Text.parrse not defined for ArchList" 233 | 234 | -- Lists with quotes 235 | newtype ArchList a = ArchList [a] 236 | deriving (Show, Eq, Monoid, Functor) 237 | 238 | instance Text String where 239 | disp s = text s 240 | parse = error "Text.parse not defined for String" 241 | 242 | instance Text a => Text (ArchList a) where 243 | disp (ArchList xs) = 244 | parens (hcat 245 | (intersperse space 246 | (map (quotes . disp) xs))) 247 | parse = error "Text.parse not defined for ArchList" 248 | 249 | -- | Printing with no quotes 250 | dispNoQuotes :: Text a => ArchList a -> Doc 251 | dispNoQuotes (ArchList xs) = 252 | parens (hcat 253 | (intersperse space 254 | (map disp xs))) 255 | 256 | 257 | ------------------------------------------------------------------------ 258 | -- Support for parsing PKGBULIDs 259 | 260 | -- | A PKGBUILD data structure with additional metadata 261 | data AnnotatedPkgBuild = 262 | AnnotatedPkgBuild 263 | {pkgBuiltWith :: Maybe Version -- ^ version of cabal2arch used, if any 264 | ,pkgHeader :: String -- ^ header strings 265 | ,hkgName :: String -- ^ package name on Hackage 266 | ,pkgBody :: PkgBuild } -- ^ contents of pkgbuild file 267 | deriving (Eq, Show) 268 | 269 | -- | Empty state structure 270 | emptyPkg :: AnnotatedPkgBuild 271 | emptyPkg = AnnotatedPkgBuild 272 | { pkgBuiltWith = Nothing 273 | , pkgHeader = [] 274 | , hkgName = [] 275 | , pkgBody = emptyPkgBuild { arch_options = ArchList [] 276 | , arch_makedepends = ArchList [] 277 | } 278 | } 279 | 280 | -- | Result type for pkgbuild parsers 281 | type ResultP a = Either String a 282 | 283 | decodePackage :: String -> ResultP AnnotatedPkgBuild 284 | decodePackage s = runGetPKG (readPackage emptyPkg) s 285 | 286 | -- | The type of pkgbuild parsers for String 287 | newtype GetPKG a = GetPKG { un :: String -> Either String (a,String) } 288 | 289 | instance Functor GetPKG where fmap = liftM 290 | 291 | instance Applicative GetPKG where 292 | pure = return 293 | (<*>) = ap 294 | 295 | instance Monad GetPKG where 296 | return x = GetPKG (\s -> Right (x,s)) 297 | fail x = GetPKG (\_ -> Left x) 298 | GetPKG m >>= f = GetPKG (\s -> case m s of 299 | Left err -> Left err 300 | Right (a,s1) -> un (f a) s1) 301 | 302 | ------------------------------------------------------------------------ 303 | 304 | -- | Run a PKG reader on an input String, returning a PKGBUILD 305 | runGetPKG :: GetPKG a -> String -> ResultP a 306 | runGetPKG (GetPKG m) s = case m s of 307 | Left err -> Left err 308 | Right (a,t) -> case t of 309 | [] -> Right a 310 | _ -> Left $ "Invalid tokens at end of PKG string: "++ show (take 10 t) 311 | 312 | getInput :: GetPKG String 313 | getInput = GetPKG (\s -> Right (s,s)) 314 | 315 | setInput :: String -> GetPKG () 316 | setInput s = GetPKG (\_ -> Right ((),s)) 317 | 318 | -- 2010-10-29: This code is unused. Do we still need it? 319 | -- (<$>) :: Functor f => (a -> b) -> f a -> f b 320 | -- x <$> y = fmap x y 321 | 322 | ------------------------------------------------------------------------ 323 | 324 | -- read until end of line 325 | line :: String -> GetPKG String 326 | line s = case break (== '\n') s of 327 | (h , _ : rest) -> do 328 | setInput rest 329 | return h 330 | 331 | -- | Recursively parse the pkgbuild 332 | -- 333 | readPackage :: AnnotatedPkgBuild -> GetPKG AnnotatedPkgBuild 334 | readPackage st = do 335 | cs <- getInput 336 | 337 | case cs of 338 | _ | "# Maintainer" `isPrefixOf` cs -> do 339 | h <- line cs 340 | readPackage st { pkgHeader = h } 341 | 342 | | "# Package generated" `isPrefixOf` cs -> do 343 | h <- line cs 344 | let v = simpleParse 345 | . reverse 346 | . takeWhile (not . isSpace) 347 | . reverse $ h 348 | readPackage st { pkgBuiltWith = v } 349 | 350 | | "_hkgname=" `isPrefixOf` cs -> do 351 | h <- line cs 352 | let s = drop 9 h 353 | readPackage st { hkgName = s } 354 | 355 | | "pkgname=" `isPrefixOf` cs -> do 356 | h <- line cs 357 | let s = drop 8 h 358 | readPackage st { pkgBody = (pkgBody st) { arch_pkgname = s } } 359 | 360 | | "pkgrel=" `isPrefixOf` cs -> do 361 | h <- line cs 362 | let s = drop 7 h 363 | readPackage st { pkgBody = (pkgBody st) { arch_pkgrel = read s } } 364 | 365 | | "pkgver=" `isPrefixOf` cs -> do 366 | h <- line cs 367 | let s = drop 7 h 368 | case simpleParse s of 369 | Nothing -> fail $ "Unable to parse package version" 370 | Just v -> readPackage st { pkgBody = (pkgBody st) { arch_pkgver = v } } 371 | 372 | | "pkgdesc=" `isPrefixOf` cs -> do 373 | h <- line cs 374 | let s = drop 8 h 375 | readPackage st { pkgBody = (pkgBody st) { arch_pkgdesc = s } } 376 | 377 | | "url=" `isPrefixOf` cs -> do 378 | h <- line cs 379 | let s = drop 4 h 380 | readPackage st { pkgBody = (pkgBody st) { arch_url = s } } 381 | 382 | | "license=" `isPrefixOf` cs -> do 383 | h <- line cs 384 | let s = takeWhile (/= '\'') 385 | . drop 1 386 | . dropWhile (/= '\'') 387 | . drop 8 $ h 388 | s' | "custom:" `isPrefixOf` s = drop 7 s 389 | | otherwise = s 390 | 391 | case simpleParse s' of 392 | Nothing -> readPackage st { pkgBody = (pkgBody st) { arch_license = ArchList [UnknownLicense s'] } } 393 | Just l -> readPackage st { pkgBody = (pkgBody st) { arch_license = ArchList [l] } } 394 | 395 | | "depends=(" `isPrefixOf` cs -> do 396 | h <- line cs 397 | let s = drop 9 h 398 | readPackage st { pkgBody = (pkgBody st) { arch_depends = readDepends s } } 399 | 400 | | "makedepends=(" `isPrefixOf` cs -> do 401 | h <- line cs 402 | let s = drop 13 h 403 | readPackage st { pkgBody = (pkgBody st) { arch_makedepends = readDepends s } } 404 | 405 | -- do these later: 406 | 407 | | "arch=" `isPrefixOf` cs 408 | -> do _ <- line cs ; readPackage st 409 | | "options=" `isPrefixOf` cs 410 | -> do _ <- line cs ; readPackage st 411 | | "source=" `isPrefixOf` cs 412 | -> do _ <- line cs ; readPackage st 413 | | "install=" `isPrefixOf` cs 414 | -> do _ <- line cs ; readPackage st 415 | | "md5sums=" `isPrefixOf` cs 416 | -> do _ <- line cs ; readPackage st 417 | | "build()" `isPrefixOf` cs 418 | -> do setInput [] ; return st 419 | | "package()" `isPrefixOf` cs 420 | -> do setInput [] ; return st 421 | 422 | -- skip comments 423 | | "#" `isPrefixOf` cs 424 | -> do _ <- line cs ; readPackage st 425 | 426 | | otherwise -> fail $ "Malformed PKGBUILD: " ++ take 80 cs 427 | 428 | -- 429 | -- | Read a quoted list of depends 430 | -- 431 | readDepends :: String -> ArchList ArchDep 432 | readDepends s = 433 | let s1 = dropWhile (\x -> x `elem` "' )") s 434 | in case s1 of 435 | "" -> ArchList [] 436 | _ -> ArchList (d:ds) 437 | where dep = takeWhile (\x -> x `notElem` "' ") s1 438 | -- end of the dep field 439 | s2 = dropWhile (\x -> x `notElem` "' ") s1 440 | s3 = dropWhile (\x -> x `elem` "' ") s2 441 | d = str2archdep dep 442 | ArchList ds = readDepends s3 443 | 444 | -- TODO : read version spec 445 | str2archdep :: String -> ArchDep 446 | str2archdep s = case v of 447 | Nothing -> ArchDep (Dependency (PackageName name) anyVersion) 448 | Just w -> ArchDep (Dependency (PackageName name) w) 449 | where name = takeWhile (\x -> x `notElem` "<=>") s 450 | vspec = dropWhile (\x -> x `notElem` "<=>") s 451 | v = simpleParse vspec 452 | 453 | ------------------------------------------------------------------------ 454 | -- Pretty printing: 455 | 456 | -- 457 | -- | Translate an abstract PkgBuild file into a document structure 458 | -- 459 | 460 | (<=>) :: Doc -> Doc -> Doc 461 | x <=> y = x <> char '=' <> y 462 | 463 | -- 464 | -- Print a PKGBUILD without comments 465 | -- 466 | rawpkg2doc :: PkgBuild -> Doc 467 | rawpkg2doc pkg = vcat 468 | [ text "pkgname" 469 | <=> text (arch_pkgname pkg) 470 | , text "pkgver" 471 | <=> disp (arch_pkgver pkg) 472 | , text "pkgrel" 473 | <=> int (arch_pkgrel pkg) 474 | , text "pkgdesc" 475 | <=> doubleQuotes (text $ escapeForBash $ arch_pkgdesc pkg) 476 | , text "url" 477 | <=> doubleQuotes (text (arch_url pkg)) 478 | , text "license" 479 | <=> disp (arch_license pkg) 480 | , text "arch" 481 | <=> disp (arch_arch pkg) 482 | , text "makedepends" 483 | <=> disp (arch_makedepends pkg) 484 | , case arch_depends pkg of 485 | ArchList [] -> empty 486 | ArchList xs -> text "depends" <=> disp (ArchList (nub xs)) 487 | , text "options" <=> disp (arch_options pkg) 488 | , text "source" 489 | <=> dispNoQuotes (arch_source pkg) 490 | , case arch_install pkg of 491 | Nothing -> empty 492 | Just p -> text "install" <=> disp p 493 | , text "md5sums" 494 | <=> disp (arch_md5sum pkg) 495 | , hang 496 | (text "build() {") 4 497 | (vcat $ (map text) (arch_build pkg)) 498 | $$ char '}' 499 | , hang 500 | (text "package() {") 4 501 | (vcat $ (map text) (arch_package pkg)) 502 | $$ char '}' 503 | ] 504 | 505 | -- 506 | -- | Helper function to escape strings for PKGBUILDs 507 | -- 508 | escapeForBash :: String -> String 509 | escapeForBash = concatMap escapeCharForBash 510 | 511 | escapeCharForBash :: Char -> String 512 | escapeCharForBash c = case c of 513 | '$' -> "\\$" 514 | '`' -> "\\`" 515 | '"' -> "\\\"" 516 | '\\' -> "\\\\" 517 | '\n' -> " " 518 | x -> [x] 519 | 520 | instance Text PkgBuild where 521 | disp p = rawpkg2doc p 522 | parse = undefined 523 | 524 | -- 525 | -- Display a PKGBUILD with header 526 | -- 527 | instance Text AnnotatedPkgBuild where 528 | disp AnnotatedPkgBuild { 529 | pkgBuiltWith = ver, 530 | pkgHeader = header, 531 | hkgName = hkg, 532 | pkgBody = pkg 533 | } = vcat [ if null header then empty else text header 534 | , text "_hkgname" <=> text hkg 535 | , disp pkg ] 536 | parse = undefined 537 | 538 | -- 539 | -- Display a full PKGBUILD with Maintainer name. 540 | -- 541 | pkg2doc :: String -> AnnotatedPkgBuild -> Doc 542 | pkg2doc email pkg = text "# Maintainer:" <+> text email $$ disp pkg 543 | -------------------------------------------------------------------------------- /Distribution/ArchLinux/SrcRepo.hs: -------------------------------------------------------------------------------- 1 | -- | 2 | -- Module : Distribution.ArchLinux.SrcRepo 3 | -- Copyright : (c) Rémy Oudompheng, 2010 4 | -- License : BSD3 5 | -- 6 | -- This module makes a database out a directory ${repo} 7 | -- containing subdirs ${repo}/${package} corresponding to packages. 8 | -- It can output rebuild lists in reverse dependency order. 9 | 10 | module Distribution.ArchLinux.SrcRepo where 11 | 12 | import Distribution.ArchLinux.PkgBuild as PkgBuild 13 | 14 | import Distribution.Package 15 | import Distribution.Version 16 | 17 | import Data.List as L 18 | import Data.Map as M 19 | import Data.Maybe 20 | import System.FilePath 21 | import System.Directory as Dir 22 | import Control.Monad 23 | 24 | -- 25 | -- | Data type for source repositories 26 | -- 27 | data SrcRepo = SrcRepo 28 | { repo_path :: FilePath 29 | -- ^ 30 | -- The path to the repository 31 | , repo_contents :: M.Map String PkgBuild 32 | } 33 | deriving (Show) 34 | 35 | -- 36 | -- | Reads a directory into a package 37 | -- 38 | getPkgFromDir :: FilePath -> IO PkgBuild 39 | getPkgFromDir p = do 40 | pkg <- readFile (p "PKGBUILD") 41 | case decodePackage pkg of 42 | Left e -> fail ("cannot parse " ++ show p ++ ": " ++ show e) 43 | Right annot_pkg -> return (pkgBody annot_pkg) 44 | 45 | -- 46 | -- | Reads a specified path into a SrcRepo structure 47 | -- 48 | getRepoFromDir :: FilePath -> IO (Maybe SrcRepo) 49 | getRepoFromDir path = do 50 | valid <- Dir.doesDirectoryExist path 51 | if valid 52 | then do 53 | subthings' <- Dir.getDirectoryContents path 54 | let subthings = [ path x | x <- subthings', head x /= '.' ] 55 | -- Read PkgBuilds 56 | contents <- foldM insertpkg M.empty subthings 57 | let result = SrcRepo { repo_path = path , repo_contents = contents } 58 | return (Just result) 59 | else return Nothing 60 | 61 | insertpkg :: Map String PkgBuild -> FilePath -> IO (Map String PkgBuild) 62 | insertpkg m dir = do 63 | pkg <- getPkgFromDir dir 64 | return $ M.insert (takeBaseName dir) pkg m 65 | 66 | --------------------------------------------------------------------------- 67 | -- 68 | -- Only pure functions below 69 | 70 | -- 71 | -- | Dumps a topologically sorted list of packages 72 | -- starting with an optionally given key 73 | -- 74 | dumpContentsTopo :: SrcRepo -> [String] 75 | dumpContentsTopo repo 76 | | M.null m = [] 77 | | otherwise = leafNames ++ (dumpContentsTopo $ repo {repo_contents = notLeaves}) 78 | where -- find leaf packages 79 | m = repo_contents repo 80 | isLeaf pbuild = (trueDepends pbuild repo) == [] 81 | leafList = L.filter (isLeaf . snd) (M.toList m) 82 | leafNames = L.map fst leafList 83 | notLeaves = M.filterWithKey (\n -> \pkg -> n `notElem` leafNames) m 84 | 85 | --- We temporarily duplicate here the list of pseudo-dependencies 86 | archProvidedPkgs :: [String] 87 | archProvidedPkgs = 88 | [ "ghc" 89 | , "haskell-array", "haskell-bytestring", "haskell-cabal", "haskell-containers", "haskell-directory" 90 | , "haskell-extensible-exceptions", "haskell-filepath", "haskell-haskell98", "haskell-hpc", "haskell-old-locale" 91 | , "haskell-old-time", "haskell-pretty", "haskell-process", "haskell-random", "haskell-syb", "haskell-template-haskell", "haskell-time" 92 | , "haskell-unix" ] 93 | 94 | -- 95 | -- | Helper function 96 | -- 97 | isExternalDep :: String -> SrcRepo -> Bool 98 | isExternalDep name (SrcRepo {repo_contents = m}) = 99 | (name `notMember` m) || (name `elem` archProvidedPkgs) 100 | 101 | trueDepends :: PkgBuild -> SrcRepo -> [String] 102 | trueDepends p repo = L.filter (\p' -> not $ isExternalDep p' repo) (strDepends p) 103 | 104 | ------------------------------------------------------------ 105 | 106 | -- 107 | -- | Enumerate all build-time dependencies for a package 108 | -- 109 | strDepends :: PkgBuild -> [String] 110 | strDepends PkgBuild { arch_depends = ArchList deps 111 | , arch_makedepends = ArchList makedeps } 112 | = L.map pkgnameFromArchDep (deps ++ makedeps) 113 | 114 | -- 115 | -- | Output the recursive dependencies of a package in topological order 116 | -- 117 | getDependencies :: String -> SrcRepo -> [String] 118 | getDependencies pkg repo = dumpContentsTopo $ getDependencyRepo pkg repo 119 | 120 | -- 121 | -- | Extract the subrepository of recursive dependencies of a package 122 | -- 123 | getDependencyRepo :: String -> SrcRepo -> SrcRepo 124 | getDependencyRepo pkg repo = case M.lookup pkg $ repo_contents repo of 125 | Nothing -> repo { repo_contents = M.empty } 126 | Just p -> repo { repo_contents = M.insert pkg p (unions recDeps) } 127 | where trueDeps = trueDepends p repo 128 | recDeps = L.map (repo_contents . (\d -> getDependencyRepo d repo)) trueDeps 129 | 130 | -- 131 | -- | Output reverse dependencies of a list of packages in topological order 132 | -- 133 | getReverseDependencies :: [String] -> SrcRepo -> [String] 134 | getReverseDependencies pkg repo = dumpContentsTopo $ getReverseDependencyRepo pkg repo 135 | 136 | -- 137 | -- | Extract reverse dependencies of a list of packages 138 | -- 139 | getReverseDependencyRepo :: [String] -> SrcRepo -> SrcRepo 140 | getReverseDependencyRepo pkgs repo = repo { repo_contents = revdeps } 141 | where revdeps = M.filterWithKey (isarevdep) (repo_contents repo) 142 | isarevdep k _ = or $ L.map (\p -> M.member p (repo_contents $ getDependencyRepo k repo)) pkgs 143 | 144 | ---------------------------------------------------------------- 145 | -- 146 | -- Version checking 147 | 148 | -- 149 | -- | Find version inconsistencies in a repository 150 | -- 151 | isConflicting :: SrcRepo -> Bool 152 | isConflicting repo = and areConflicting 153 | where listOfPkgs = M.toList $ repo_contents repo 154 | areConflicting = L.map (\(_,pkg) -> pkg `isConflictingWith` repo) listOfPkgs 155 | 156 | listVersionConflicts :: SrcRepo -> [String] 157 | listVersionConflicts repo = L.map fst listConflicting 158 | where listOfPkgs = M.toList $ repo_contents repo 159 | listConflicting = L.filter (\(_,pkg) -> pkg `isConflictingWith` repo) listOfPkgs 160 | 161 | -- 162 | -- | Check package dependencies against the repo 163 | -- 164 | isConflictingWith :: PkgBuild -> SrcRepo -> Bool 165 | PkgBuild { arch_depends = ArchList deps 166 | , arch_makedepends = ArchList makedeps 167 | } `isConflictingWith` repo = not (and satisfied) 168 | where satisfied = Data.Maybe.mapMaybe (\dep -> isSatisfiedBy dep repo) (deps ++ makedeps) 169 | 170 | -- 171 | -- | check for existence of the right version is the repository 172 | -- (return Nothing if package not found) 173 | -- 174 | isSatisfiedBy :: ArchDep -> SrcRepo -> Maybe Bool 175 | ArchDep (Dependency (PackageName depname) vrange) `isSatisfiedBy` repo = case deppkg of 176 | Nothing -> Nothing 177 | Just pkgbuild -> Just ((arch_pkgver pkgbuild) `withinRange` vrange) 178 | where 179 | deppkg = M.lookup depname (repo_contents repo) 180 | -------------------------------------------------------------------------------- /Distribution/ArchLinux/SystemProvides.lhs: -------------------------------------------------------------------------------- 1 | Module : Distribution.ArchLinux.SystemProvides 2 | Copyright : (c) Rémy Oudompheng 2010 3 | License : BSD3 4 | 5 | Maintainer: Arch Haskell Team 6 | 7 | > module Distribution.ArchLinux.SystemProvides 8 | > ( SystemProvides(..) 9 | > , emptySystemProvides 10 | > , parseSystemProvides 11 | > ) where 12 | 13 | Cabal modules 14 | 15 | > import Distribution.Package 16 | 17 | Standard types 18 | 19 | > import Distribution.Text 20 | > import qualified Data.Map as M 21 | > import Data.Maybe 22 | 23 | A big structure holding data about ArchLinux 24 | 25 | > data SystemProvides = SystemProvides 26 | > { corePackages :: [Dependency] 27 | > -- ^ 28 | > -- A list of Dependencies which are automatically satified 29 | > -- when GHC is installed. 30 | > , platformPackages :: [Dependency] 31 | > -- ^ 32 | > -- A list of packages to preferably use (e.g. Haskell Platform) 33 | > , translationTable :: M.Map String String 34 | > -- ^ 35 | > -- A hash-map where keys are library names and values are 36 | > -- names of the corresponding ArchLinux package. 37 | > } 38 | > deriving (Show,Eq) 39 | 40 | Empty SystemProvides 41 | 42 | > emptySystemProvides :: SystemProvides 43 | > emptySystemProvides = SystemProvides [] [] M.empty 44 | 45 | Get SystemProvides from files. 46 | 47 | > parseSystemProvides :: String -> String -> String -> SystemProvides 48 | > parseSystemProvides sPkg sPlat sTranslation = 49 | > SystemProvides { corePackages = parseDeplist sPkg 50 | > , platformPackages = parseDeplist sPlat 51 | > , translationTable = parseTranslationTable sTranslation } 52 | 53 | Extract a list of dependency descriptions from a file 54 | 55 | > depstr2hs :: String -> Maybe Dependency 56 | > depstr2hs s | s == "" || head s == '#' = Nothing 57 | > | otherwise = simpleParse s 58 | 59 | > parseDeplist :: String -> [Dependency] 60 | > parseDeplist srcfile = mapMaybe depstr2hs $ lines srcfile 61 | 62 | Now we translate the "library-providers" file. Any line beginning with "# " 63 | or lines with something else than two words are discarded. Lines should have 64 | the form "libraryname packagename". 65 | 66 | > trstr2hs :: String -> Maybe (String, String) 67 | > trstr2hs s = case words s of 68 | > "#":_ -> Nothing 69 | > a:b:_ -> Just (a,b) 70 | > _ -> Nothing 71 | 72 | > parseTranslationTable :: String -> M.Map String String 73 | > parseTranslationTable srcfile2 = M.fromList $ mapMaybe trstr2hs $ lines srcfile2 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Don Stewart 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the author nor the names of his contributors 17 | may be used to endorse or promote products derived from this software 18 | without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 21 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR 24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /Setup.lhs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env runhaskell 2 | > import Distribution.Simple 3 | > main = defaultMain 4 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 2 | * lint checker 3 | -- for all our packages, check they are consistent 4 | 5 | * hackage version checker 6 | -- automatically check versions between hackage and AUR 7 | 8 | * cabal2arch version checker 9 | -- list all packages that were generated with which version of cabal2arch, 10 | then auto-rebuild 11 | 12 | * regression builds 13 | 14 | Oh,also, anyone know how I can get the full list of arch-haskell 15 | maintained packages from AUR programmatically? I've found only very 16 | sparse documentation for the JSON interface. 17 | 18 | If we could get the full package list, we can do regression builds. 19 | 20 | -------------------------------------------------------------------------------- /archlinux.cabal: -------------------------------------------------------------------------------- 1 | name: archlinux 2 | version: 1.3 3 | license: BSD3 4 | license-file: LICENSE 5 | author: Don Stewart , 6 | Rémy Oudompheng , 7 | Peter Simons , 8 | Magnus Therning , 9 | Linus Arver 10 | maintainer: ArchHaskell Team 11 | homepage: http://github.com/archhaskell/ 12 | category: Distribution 13 | synopsis: Support for working with Arch Linux packages. 14 | description: Support for working with Arch Linux packages. 15 | See also the cabal2arch tool 16 | for conversion between Hackage and AUR. 17 | build-type: Simple 18 | stability: stable 19 | cabal-version: >= 1.8 20 | 21 | source-repository head 22 | type: git 23 | location: git://github.com/archhaskell/archlinux.git 24 | 25 | library 26 | ghc-Options: -Wall 27 | build-depends: 28 | base >= 4 && < 6, 29 | Cabal >= 1.6, 30 | pretty, 31 | containers, 32 | directory, 33 | filepath 34 | 35 | exposed-modules: 36 | Distribution.ArchLinux.PkgBuild 37 | Distribution.ArchLinux.SrcRepo 38 | Distribution.ArchLinux.CabalTranslation 39 | Distribution.ArchLinux.SystemProvides 40 | Distribution.ArchLinux.HackageTranslation 41 | -------------------------------------------------------------------------------- /scripts/recdeps.hs: -------------------------------------------------------------------------------- 1 | -- 2 | -- | This test reads the current directory and dumps a topologically sorted package list 3 | -- 4 | 5 | module Main where 6 | 7 | import Distribution.ArchLinux.SrcRepo 8 | import System.IO 9 | import System.Directory 10 | import System.Environment 11 | import Control.Monad 12 | 13 | main = do 14 | [pkg] <- getArgs 15 | dot <- getCurrentDirectory 16 | repo <- getRepoFromDir dot 17 | case repo of 18 | Nothing -> return () 19 | Just r -> foldM (\a -> \s -> putStrLn s) () (getDependencies pkg r) 20 | -------------------------------------------------------------------------------- /scripts/reverse_deps.hs: -------------------------------------------------------------------------------- 1 | -- This script prints all reverse dependencies for the specified 2 | -- packages with respect to an ABS-like repository located at the 3 | -- path given as first command-line argument. 4 | 5 | module Main ( main ) where 6 | 7 | import System.Environment ( getArgs ) 8 | import Distribution.ArchLinux.SrcRepo ( getRepoFromDir, getReverseDependencies ) 9 | 10 | main :: IO () 11 | main = do 12 | habs:pkgs <- getArgs 13 | repo <- getRepoFromDir habs 14 | case repo of 15 | Nothing -> fail ("cannot load habs tree at " ++ show habs) 16 | Just r -> mapM_ putStrLn (filter (`notElem`pkgs) (getReverseDependencies pkgs r)) 17 | -------------------------------------------------------------------------------- /tests/listconflicts.hs: -------------------------------------------------------------------------------- 1 | -- 2 | -- | This test reads the current directory and prints the list of conflicting packages 3 | -- 4 | 5 | import Distribution.ArchLinux.SrcRepo 6 | import System.IO 7 | import System.Directory 8 | import Control.Monad 9 | 10 | main = do 11 | dot <- getCurrentDirectory 12 | repo <- getRepoFromDir dot 13 | case repo of 14 | Nothing -> return () 15 | Just r -> foldM (\a -> \s -> putStrLn s) () (listVersionConflicts r) 16 | -------------------------------------------------------------------------------- /tests/readpkg.hs: -------------------------------------------------------------------------------- 1 | import Distribution.ArchLinux.PkgBuild 2 | 3 | import System.IO 4 | import Text.PrettyPrint 5 | 6 | -- 7 | -- | This test parses the file "PKGBUILD" and prints back the data. 8 | -- 9 | 10 | main = do 11 | s <- readFile ("PKGBUILD") 12 | let t = decodePackage s 13 | case t of 14 | Left _ -> putStrLn "error" 15 | Right pkg -> putStr $ render $ pkg2doc "mail@example.org" pkg 16 | -------------------------------------------------------------------------------- /tests/toposort.hs: -------------------------------------------------------------------------------- 1 | -- 2 | -- | This test reads the current directory and dumps a topologically sorted package list 3 | -- 4 | 5 | import Distribution.ArchLinux.SrcRepo 6 | import System.IO 7 | import System.Directory 8 | import Control.Monad 9 | 10 | main = do 11 | dot <- getCurrentDirectory 12 | repo <- getRepoFromDir dot 13 | case repo of 14 | Nothing -> return () 15 | Just r -> foldM (\a -> \s -> putStrLn s) () (dumpContentsTopo r) 16 | --------------------------------------------------------------------------------