cabal2arch (empty) → 0.1
raw patch · 4 files changed
+489/−0 lines, 4 filesdep +Cabaldep +basedep +bytestringsetup-changed
Dependencies added: Cabal, base, bytestring, directory, filepath, pretty, process, pureMD5
Files
- LICENSE +30/−0
- Main.hs +420/−0
- Setup.lhs +3/−0
- cabal2arch.cabal +36/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2008 Don Stewart++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of his contributors+ may be used to endorse or promote products derived from this software+ without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
+ Main.hs view
@@ -0,0 +1,420 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE BangPatterns #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- |+-- Module : cabal2arch: convert cabal packages to Arch Linux PKGBUILD format+-- Copyright : (c) Don Stewart, 2008+-- License : BSD3+--+-- Maintainer: Don Stewart <dons@galois.com>+-- Stability : provisional+-- Portability:+--++import Distribution.PackageDescription+import Distribution.Simple.Utils hiding (die)+import Distribution.Verbosity+import Distribution.Version+import Distribution.Package+import Distribution.License+import Distribution.Text++import Data.Digest.MD5+import qualified Data.ByteString.Lazy as B++import Control.Exception+import Data.List+import Data.Monoid+import System.Environment+import Control.Concurrent+import qualified Control.Exception as C+import System.Process+import Text.PrettyPrint+import System.FilePath+import System.Exit+import System.IO+import Control.Monad+import System.Directory++main :: IO ()+main =+ bracket+ -- Set up a temp dir to work in+ (do cwd <- getCurrentDirectory+ etmp <- readProcess "mktemp" ["-d"] []+ case etmp of+ Left _ -> die "Unable to create temp directory"+ Right d -> do+ let dir = makeValid (init d) -- drop newline+ setCurrentDirectory dir+ return (dir, cwd))++ -- Always remember to clean up+ (\(d,cwd) -> do+ setCurrentDirectory cwd+ removeDirectoryRecursive d)++ -- Now, get to work:+ $ \(tmp,cwd) -> do++ do x <- getArgs+ case x of+ ["--help"] -> help+ ["-h"] -> help+ _ -> return ()+ email <- maybe "" id `fmap` getEnvMaybe "DARCS_EMAIL"++ cabalfile <- findCabalFile cwd tmp+ hPutStrLn stderr $ "Using " ++ cabalfile++ cabalsrc <- readPackageDescription normal cabalfile+ let pkgbuild' = cabal2pkg cabalsrc++ pkgbuild <- getMD5 pkgbuild'+ let doc = pkg2doc email pkgbuild+ putStrLn (render doc)++------------------------------------------------------------------------++-- | Given an abstract pkgbuild, download the source bundle,+-- and compute its md5, returning a modified PkgBuild with+-- the md5 set.+getMD5 :: PkgBuild -> IO PkgBuild+getMD5 pkg@(PkgBuild { arch_source = ArchList [url] }) = do+ hPutStrLn stderr $ "Fetching " ++ url+ hFlush stderr+ eres <- readProcess "wget" [url] []+ case eres of+ Left (_,s,_) -> do+ hPutStrLn stderr s+ die $ "Couldn't download package: " ++ show url+ Right _ -> do+ src <- B.readFile (takeBaseName url <.> "gz")+ let !md5sum = show (md5 src)+ return pkg { arch_md5sum = ArchList [md5sum] }+getMD5 _ = die "Malformed PkgBuild"++-- Return the path to a .cabal file.+-- If not arguments are specified, use ".",+-- if the argument looks like a url, download that+-- otherwise, assume its a directory+--+findCabalFile :: FilePath -> FilePath -> IO FilePath+findCabalFile cwd tmp = do+ args <- getArgs+ let epath | null args+ = Right cwd+ | "http://" `isPrefixOf` file+ = Left file+ | ".cabal" `isSuffixOf` file+ = Right (makeValid (joinPath [cwd,file]))+ | otherwise -- a directory path+ = Right file++ where file = head args++ -- download url to .cabal+ case epath of+ Left url -> do+ eres <- readProcess "wget" [url] []+ case eres of+ Left (_,s,_) -> do+ hPutStrLn stderr s+ die $ "Couldn't download .cabal file: " ++ show url+ Right _ ->+ findPackageDesc tmp -- tmp dir++ -- it might be a .cabal file+ Right f | ".cabal" `isSuffixOf` f -> do+ b <- doesFileExist f+ if not b+ then die $ ".cabal file doesn't exist: " ++ show f+ else return f++ -- or assume it is a dir to a file:+ Right dir -> do+ b <- doesDirectoryExist dir+ if not b+ then die $ "directory doesn't exist: " ++ show dir+ else findPackageDesc dir++------------------------------------------------------------------------+-- Parsing and pretty printing:++-- +-- | Translate an abstract PkgBuild file into a document structure+-- +pkg2doc :: String -> PkgBuild -> Doc+pkg2doc email pkg = vcat+ [ text "# Contributor:"+ <+> text email+ , text "pkgname"+ <=> text (arch_pkgname pkg)+ , text "pkgrel"+ <=> int (arch_pkgrel pkg)+ , text "pkgver"+ <=> disp (arch_pkgver pkg)+ , text "pkgdesc"+ <=> doubleQuotes (text (arch_pkgdesc pkg))+ , text "url"+ <=> doubleQuotes (text (arch_url pkg))+ , text "license"+ <=> disp (arch_license pkg)+ , text "arch"+ <=> disp (arch_arch pkg)+ , text "makedepends"+ <=> disp (arch_makedepends pkg)+ , text "source"+ <=> dispNoQuotes (arch_source pkg)+ , text "md5sum"+ <=> disp (arch_md5sum pkg)+ ]++--+-- | Tranlsate a generic cabal file into a PGKBUILD+--+cabal2pkg :: GenericPackageDescription -> PkgBuild+cabal2pkg GenericPackageDescription+ { packageDescription = cabal+ , genPackageFlags = _cabal_genPackageFlags+ , condLibrary = _cabal_condLibrary+ , condExecutables = _cabal_condExecutables+ }+ = emptyPkgBuild+ { arch_pkgname = name+ , arch_pkgver = vers+ , arch_url = homepage cabal+ , arch_pkgdesc = synopsis cabal+ , arch_license = ArchList [license cabal]++ -- All Hackage packages depend on GHC at build time+ , arch_makedepends = (arch_makedepends emptyPkgBuild)+ `mappend`+ (ArchList (buildDepends cabal))++ -- Hackage programs only need their own source to build+ , arch_source = ArchList . return $+ "http://hackage.haskell.org/packages/archive/"+ ++ (name </> display vers </> name <-> display vers <.> "tar.gz")+ }+ where+ name = pkgName (package cabal)+ vers = pkgVersion (package cabal)++-- +-- | A data type to represent PKGBUILD files+-- +data PkgBuild =+ PkgBuild+ { arch_pkgname :: String+ -- ^+ -- The name of the package. This has be a unix-friendly name+ -- as it will be used in the package filename.+ , arch_pkgver :: Version+ -- ^ The version of the software as released from the authorii+ -- (e.g. ´2.7.1´).+ , arch_pkgrel :: !Int+ -- ^+ -- This is the release number specific to the Arch Linux+ -- release. This allows package maintainers to make updates to+ -- the package´s configure flags, for example. A pkgrel of 1+ -- is typically used for each upstream software release and is+ -- incremented for intermediate PKGBUILD updates.+ , arch_pkgdesc :: String+ -- ^+ -- This should be a brief description of the package and its+ -- functionality. Try to keep the description to one line of text.+ , arch_arch :: ArchList Arch+ -- ^+ -- Defines on which architectures the given package is+ -- available (e.g. arch=(´i686´ ´x86_64´)).+ , arch_url :: String+ -- ^+ -- This field contains a URL that is associated with the software+ -- being packaged. This is typically the project´s website.+ , arch_license :: ArchList License+ -- ^+ -- This field specifies the license(s) that apply to the package.+ -- Commonly-used licenses are found in /usr/share/licenses/common. If+ -- you see the package´s license there, simply reference it in the+ -- license field (e.g. license=(´GPL´)). If the package provides a+ -- license not found in /usr/share/licenses/common, then you should+ -- include the license in the package itself and set+ -- license=(´custom´) or license=(´custom:LicenseName´). The license+ -- should be placed in $pkgdir/usr/share/licenses/$pkgname when+ -- building the package. If multiple licenses are applicable for a+ -- package, list all of them: license=(´GPL´ ´FDL´).+ , arch_makedepends :: ArchList Dependency+ -- ^+ -- An array of packages that this package depends on to build, but are+ -- not needed at runtime. Packages in this list follow the same format+ -- as depends.++ , arch_depends :: ArchList Dependency+ -- ^+ -- An array of packages that this package depends on to run. Packages+ -- in this list should be surrounded with single quotes and contain at+ -- least the package name. Entries can also include a version+ -- requirement of the form name<>version, where <> is one of five+ -- comparisons: >= (greater than or equal to), <= (less than or equal+ -- to), = (equal to), > (greater than), or < (less than).+ , arch_source :: ArchList String+ -- ^+ -- An array of source files required to build the package. Source+ -- files must either reside in the same directory as the PKGBUILD+ -- file, or be a fully-qualified URL that makepkg will use to download+ -- the file. In order to make the PKGBUILD as useful as possible, use+ -- the $pkgname and $pkgver variables if possible when specifying the+ -- download location. Any files that are compressed will automatically+ -- be extracted, unless found in the noextract array listed below.+ , arch_md5sum :: ArchList String+ -- ^+ -- This array contains an MD5 hash for every source file specified in+ -- the source array (in the same order). makepkg will use this to+ -- verify source file integrity during subsequent builds. To easily+ -- generate md5sums, run “makepkg -g >> PKGBUILD”. If desired, move+ -- the md5sums line to an appropriate location. NOTE: makepkg+ -- supports multiple integrity algorithms and their corresponding+ -- arrays (i.e. sha1sums for the SHA1 algorithm); however, official+ -- packages use only md5sums for the time being.+ }+ deriving (Show, Eq)++--+-- | An empty PKGBUILD+--+emptyPkgBuild :: PkgBuild+emptyPkgBuild =+ PkgBuild+ { arch_pkgname = pkgName (package e)+ , arch_pkgver = pkgVersion (package e)+ , arch_pkgrel = 1+ , arch_pkgdesc = synopsis e+ , arch_arch = ArchList [X86, X86_64]+ , arch_url = homepage e+ , arch_license = ArchList [license e]+ , arch_makedepends = ArchList [(Dependency "ghc" AnyVersion)]+ , arch_depends = ArchList []+ , arch_source = ArchList []+ , arch_md5sum = ArchList []+ }+ where+ e = emptyPackageDescription++------------------------------------------------------------------------+-- Extra pretty printer instances and types++--+-- | Valid linux platforms+--+data Arch = X86 | X86_64+ deriving (Show, Eq)++instance Text Arch where+ disp x = case x of+ X86 -> text "i686"+ X86_64 -> text "x86_64"+ parse = error "Text.parrse not defined for ArchList"++-- Lists with quotes+newtype ArchList a = ArchList [a]+ deriving (Show, Eq, Monoid, Functor)++instance Text String where+ disp s = text s+ parse = error "Text.parse not defined for String"++instance Text a => Text (ArchList a) where+ disp (ArchList xs) =+ parens (hcat+ (intersperse space+ (map (quotes . disp) xs)))+ parse = error "Text.parse not defined for ArchList"++-- | Printing with no quotes+dispNoQuotes :: Text a => ArchList a -> Doc+dispNoQuotes (ArchList xs) =+ parens (hcat+ (intersperse space+ (map disp xs)))++------------------------------------------------------------------------+-- Some extras+--++help :: IO a+help = do+ hPutStrLn stderr $ unlines+ [ "cabal2pkg: [-h|--help] [directory|url]"+ , ""+ , " Generate PKGBUILD to stdout for .cabal file in <directory> or at <url>"+ , ""+ , "Usage:"+ , " -h Display help message"+ , ""+ , "Arguments: <directory>"+ , " Look for .cabal file in <directory>"+ , " If directory is empty, use pwd"+ , " <file.cabal>"+ , " Use .cabal file as source"+ , " <url>"+ , " Download .cabal file from <url>"+ ]+ exitWith ExitSuccess++------------------------------------------------------------------------++die :: String -> IO a+die s = do+ hPutStrLn stderr $ "cabal2pkg:\n" ++ s+ exitWith (ExitFailure 1)++(<=>) :: Doc -> Doc -> Doc+x <=> y = x <> char '=' <> y++(<->) :: String -> String -> String+x <-> y = x ++ "-" ++ y++-- Safe wrapper for getEnv+getEnvMaybe :: String -> IO (Maybe String)+getEnvMaybe name = handle (const $ return Nothing) (Just `fmap` getEnv name)++------------------------------------------------------------------------++--+-- Strict process reading +--+readProcess :: FilePath -- ^ command to run+ -> [String] -- ^ any arguments+ -> String -- ^ standard input+ -> IO (Either (ExitCode,String,String) String) -- ^ either the stdout, or an exitcode and any output++readProcess cmd args input = C.handle (return . handler) $ do+ (inh,outh,errh,pid) <- runInteractiveProcess cmd args Nothing Nothing++ output <- hGetContents outh+ outMVar <- newEmptyMVar+ forkIO $ (C.evaluate (length output) >> putMVar outMVar ())++ errput <- hGetContents errh+ errMVar <- newEmptyMVar+ forkIO $ (C.evaluate (length errput) >> putMVar errMVar ())++ when (not (null input)) $ hPutStr inh input+ takeMVar outMVar+ takeMVar errMVar+ ex <- C.catch (waitForProcess pid) (\_e -> return ExitSuccess)+ hClose outh+ hClose inh -- done with stdin+ hClose errh -- ignore stderr++ return $ case ex of+ ExitSuccess -> Right output+ ExitFailure _ -> Left (ex, errput, output)++ where+ handler (C.ExitException e) = Left (e,"","")+ handler e = Left (ExitFailure 1, show e, "")+
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ cabal2arch.cabal view
@@ -0,0 +1,36 @@+name: cabal2arch+version: 0.1+homepage: http://code.haskell.org/~dons/code/cabal2arch+synopsis: Create Arch Linux packages from Cabal packages+description: Create Arch Linux packages from Cabal packages+category: Distribution+license: BSD3+license-file: LICENSE+author: Don Stewart+maintainer: dons@galois.com+cabal-version: >= 1.2+build-type: Simple++flag small_base+ description: Choose the new smaller, split-up base package.++executable cabal2arch+ main-is: Main.hs+ ghc-options: -Wall++ if flag(small_base)+ build-depends: + base >= 3,+ pretty,+ process,+ directory,+ bytestring+ else+ build-depends: + base < 3++ build-depends:+ Cabal >= 1.3.11,+ filepath,+ pureMD5+