packages feed

hackport-0.3: Portage/EBuild.hs

module Portage.EBuild
        ( EBuild(..)
        , ebuildTemplate
        , src_uri
        ) where

import Distribution.Text ( Text(..), display )
import qualified Text.PrettyPrint as Disp

import Portage.Dependency

import Distribution.License as Cabal

import Data.String.Utils
import Data.Version(Version(..))
import qualified Paths_hackport(version)

data EBuild = EBuild {
    name :: String,
    hackage_name :: String, -- might differ a bit (we mangle case)
    version :: String,
    hackportVersion :: String,
    description :: String,
    long_desc :: String,
    homepage :: String,
    license :: Cabal.License,
    slot :: String,
    keywords :: [String],
    iuse :: [String],
    depend :: [Dependency],
    depend_extra :: [String],
    rdepend :: [Dependency],
    rdepend_extra :: [String],
    features :: [String],
    my_pn :: Maybe String -- ^ Just 'myOldName' if the package name contains upper characters
  }

getHackportVersion :: Version -> String
getHackportVersion Version {versionBranch=(x:s)} = foldl (\y z -> y ++ "." ++ (show z)) (show x) s
getHackportVersion Version {versionBranch=[]} = ""

ebuildTemplate :: EBuild
ebuildTemplate = EBuild {
    name = "foobar",
    hackage_name = "FooBar",
    version = "0.1",
    hackportVersion = getHackportVersion Paths_hackport.version,
    description = "",
    long_desc = "",
    homepage = "http://hackage.haskell.org/package/${HACKAGE_N}",
    license = Cabal.UnknownLicense "xxx UNKNOWN xxx",
    slot = "0",
    keywords = ["~amd64","~x86"],
    iuse = [],
    depend = [],
    depend_extra = [],
    rdepend = [],
    rdepend_extra = [],
    features = [],
    my_pn = Nothing
  }

instance Text EBuild where
  disp = Disp.text . showEBuild

-- | Given an EBuild, give the URI to the tarball of the source code.
-- Assumes that the server is always hackage.haskell.org.
src_uri :: EBuild -> String
src_uri e = 
  case my_pn e of
    -- use standard address given that the package name has no upper
    -- characters
    Nothing -> "http://hackage.haskell.org/packages/archive/${PN}/${PV}/${P}.tar.gz"
    -- use MY_X variables (defined in showEBuild) as we've renamed the
    -- package
    Just _  -> "http://hackage.haskell.org/packages/archive/${MY_PN}/${PV}/${MY_P}.tar.gz"

showEBuild :: EBuild -> String
showEBuild ebuild =
  ss "# Copyright 1999-2012 Gentoo Foundation". nl.
  ss "# Distributed under the terms of the GNU General Public License v2". nl.
  ss "# $Header: $". nl.
  nl.
  ss "EAPI=5". nl.
  nl.
  ss ("# ebuild generated by hackport " ++ hackportVersion ebuild). nl.
  nl.
  ss "CABAL_FEATURES=". quote' (sepBy " " $ features ebuild). nl.
  ss "inherit haskell-cabal". nl.
  nl.
  (case my_pn ebuild of
     Nothing -> id
     Just pn -> ss "MY_PN=". quote pn. nl.
                ss "MY_P=". quote "${MY_PN}-${PV}". nl. nl).
  ss "DESCRIPTION=". quote (description ebuild). nl.
  ss "HOMEPAGE=". quote (expandVars (homepage ebuild)). nl.
  ss "SRC_URI=". quote (toMirror $ src_uri ebuild). nl.
  nl.
  ss "LICENSE=". quote (convertLicense . license $ ebuild).
     (if null (licenseComment . license $ ebuild) then id
         else ss "\t#". ss (licenseComment . license $ ebuild)). nl.
  ss "SLOT=". quote (slot ebuild). nl.
  ss "KEYWORDS=". quote' (sepBy " " $ keywords ebuild).nl.
  ss "IUSE=". quote' (sepBy ", " $ iuse ebuild). nl.
  nl.
  dep_str "RDEPEND" (rdepend_extra ebuild) (rdepend ebuild).
  dep_str "DEPEND"  ( depend_extra ebuild) ( depend ebuild).
  (case my_pn ebuild of
     Nothing -> id
     Just _ -> nl. ss "S=". quote ("${WORKDIR}/${MY_P}"). nl)
  $ []
  where expandVars = replaceMultiVars [ (        name ebuild, "${PN}")
                                      , (hackage_name ebuild, "${HACKAGE_N}")
                                      ]
        toMirror = replace "http://hackage.haskell.org/" "mirror://hackage/" 

ss :: String -> String -> String
ss = showString

sc :: Char -> String -> String
sc = showChar

nl :: String -> String
nl = sc '\n'

dep_str :: String -> [String] -> [Dependency] -> (String -> String)
dep_str var extra deps = ss var. sc '='. quote' (sepBy "\n\t\t" $ extra ++ map display deps). nl

quote :: String -> String -> String
quote str = sc '"'. ss (esc str). sc '"'
  where
  esc = concatMap esc'
  esc' '"' = "\""
  esc' c = [c]

quote' :: (String -> String) -> String -> String
quote' str = sc '"'. str. sc '"'

sepBy :: String -> [String] -> ShowS
sepBy _ []     = id
sepBy _ [x]    = ss x
sepBy s (x:xs) = ss x. ss s. sepBy s xs

getRestIfPrefix ::
	String ->	-- ^ the prefix
	String ->	-- ^ the string
	Maybe String
getRestIfPrefix (p:ps) (x:xs) = if p==x then getRestIfPrefix ps xs else Nothing
getRestIfPrefix [] rest = Just rest
getRestIfPrefix _ [] = Nothing

subStr ::
	String ->	-- ^ the search string
	String ->	-- ^ the string to be searched
	Maybe (String,String)  -- ^ Just (pre,post) if string is found
subStr sstr str = case getRestIfPrefix sstr str of
	Nothing -> if null str then Nothing else case subStr sstr (tail str) of
		Nothing -> Nothing
		Just (pre,post) -> Just (head str:pre,post)
	Just rest -> Just ([],rest)

replaceMultiVars ::
	[(String,String)] ->	-- ^ pairs of variable name and content
	String ->		-- ^ string to be searched
	String 			-- ^ the result
replaceMultiVars [] str = str
replaceMultiVars whole@((pname,cont):rest) str = case subStr cont str of
	Nothing -> replaceMultiVars rest str
	Just (pre,post) -> (replaceMultiVars rest pre)++pname++(replaceMultiVars whole post)

-- map the cabal license type to the gentoo license string format
convertLicense :: Cabal.License -> String
convertLicense (Cabal.GPL mv)     = "GPL-" ++ (maybe "2" display mv)  -- almost certainly version 2
convertLicense (Cabal.LGPL mv)    = "LGPL-" ++ (maybe "2.1" display mv) -- probably version 2.1
convertLicense Cabal.BSD3         = "BSD"
convertLicense Cabal.BSD4         = "BSD-4"
convertLicense Cabal.PublicDomain = "public-domain"
convertLicense Cabal.AllRightsReserved = ""
convertLicense Cabal.MIT          = "MIT"
convertLicense _                  = ""

licenseComment :: Cabal.License -> String
licenseComment Cabal.AllRightsReserved =
  "Note: packages without a license cannot be included in portage"
licenseComment Cabal.OtherLicense =
  "Fixme: \"OtherLicense\", please fill in manually"
licenseComment (Cabal.UnknownLicense _) = "Fixme: license unknown to cabal"
licenseComment _ = ""