cabal-debian 1.25 → 3.0.3
raw patch · 42 files changed
+6410/−2069 lines, 42 filesdep +Diffdep +HUnitdep +ansi-wl-pprintdep −prettydep ~Cabaldep ~basedep ~debiansetup-changednew-component:exe:cabal-debian-tests
Dependencies added: Diff, HUnit, ansi-wl-pprint, applicative-extras, cabal-debian, data-lens, hsemail, network, pureMD5, syb, text
Dependencies removed: pretty
Dependency ranges changed: Cabal, base, debian
Files
- CabalDebian.hs +0/−20
- Distribution/Package/Debian.hs +0/−752
- Distribution/Package/Debian/Bundled.hs +0/−323
- Distribution/Package/Debian/Dependencies.hs +0/−327
- Distribution/Package/Debian/Interspersed.hs +0/−54
- Distribution/Package/Debian/Main.hs +0/−28
- Distribution/Package/Debian/Relations.hs +0/−135
- Distribution/Package/Debian/Setup.hs +0/−220
- Setup.hs +17/−1
- cabal-debian.cabal +76/−33
- debian/Debianize.hs +49/−0
- debian/cabal-debian.1 +0/−120
- debian/cabal-debian.manpages +0/−1
- debian/changelog +278/−0
- debian/compat +0/−1
- debian/control +0/−40
- debian/rules +0/−14
- src/CabalDebian.hs +11/−0
- src/Data/Algorithm/Diff/Context.hs +54/−0
- src/Data/Algorithm/Diff/Pretty.hs +17/−0
- src/Debian/Debianize.hs +73/−0
- src/Debian/Debianize/Atoms.hs +1207/−0
- src/Debian/Debianize/Bundled.hs +388/−0
- src/Debian/Debianize/ControlFile.hs +206/−0
- src/Debian/Debianize/Debianize.hs +304/−0
- src/Debian/Debianize/Dependencies.hs +431/−0
- src/Debian/Debianize/Files.hs +199/−0
- src/Debian/Debianize/Finalize.hs +301/−0
- src/Debian/Debianize/Generic.hs +108/−0
- src/Debian/Debianize/Goodies.hs +376/−0
- src/Debian/Debianize/Input.hs +315/−0
- src/Debian/Debianize/Interspersed.hs +61/−0
- src/Debian/Debianize/Options.hs +135/−0
- src/Debian/Debianize/SubstVars.hs +138/−0
- src/Debian/Debianize/Tests.hs +611/−0
- src/Debian/Debianize/Types.hs +66/−0
- src/Debian/Debianize/Utility.hs +231/−0
- src/Debian/Orphans.hs +172/−0
- src/Debian/Policy.hs +233/−0
- src/Distribution/Version/Invert.hs +40/−0
- src/Tests.hs +7/−0
- src/Triplets.hs +306/−0
− CabalDebian.hs
@@ -1,20 +0,0 @@--- |--- Module : CabalDebian--- Copyright : David Fox 2008------ Maintainer : David Fox <dsf@seereason.com>--- Stability : alpha--- Portability : portable------ Explanation: Main entry point for generating Debianizations from Cabal data.---- This software may be used and distributed according to the terms of--- the GNU General Public License, incorporated herein by reference.--module Main where--import qualified Distribution.Package.Debian.Main as Debian--main :: IO ()--main = Debian.main
− Distribution/Package/Debian.hs
@@ -1,752 +0,0 @@-{-# LANGUAGE CPP, ScopedTypeVariables, TupleSections, TypeSynonymInstances #-}-{-# OPTIONS -Wall -fno-warn-name-shadowing #-}---- |--- Module : Distribution.Package.Debian--- Copyright : David Fox 2008------ Maintainer : David Fox <dsf@seereason.com>--- Stability : alpha--- Portability : portable------ Explanation: Support for generating Debianization from Cabal data.---- This software may be used and distributed according to the terms of--- the GNU General Public License, incorporated herein by reference.--module Distribution.Package.Debian- ( debian- ) where---- import Debug.Trace--import Codec.Binary.UTF8.String (decodeString)-import Control.Arrow (second)-import Control.Exception (SomeException, try, bracket, IOException)-import Control.Monad (when,mplus)-import Control.Monad.Reader (ReaderT(runReaderT), ask)-import Control.Monad.Trans (lift)-import Data.Char (isSpace)-import Data.Either (partitionEithers)-import Data.List-import qualified Data.Map as Map-import Data.Maybe-import qualified Data.Set as Set-import Data.Version (showVersion)-import Debian.Control-import qualified Debian.Relation as D-import Debian.Release (parseReleaseName)-import Debian.Changes (ChangeLogEntry(..), prettyEntry, parseLog)-import Debian.Time (getCurrentLocalRFC822Time)-import Debian.Version (DebianVersion, prettyDebianVersion)-import Debian.Version.String-import System.Cmd (system)-import System.Directory-import System.Exit (ExitCode(..))-import System.FilePath ((</>), dropExtension)-import System.IO (IOMode (ReadMode), hGetContents, hPutStrLn, hSetBinaryMode, openFile, stderr, withFile)-import System.IO.Error (ioeGetFileName, isDoesNotExistError)-import System.Posix.Files (setFileCreationMask)-import System.Environment--import Distribution.Text (display)-import Distribution.Simple.Compiler (CompilerFlavor(..), compilerFlavor, Compiler(..), CompilerId(..))-import Distribution.System (Platform(..), buildOS, buildArch)-import Distribution.License (License(..))-import Distribution.Package (Package(..), PackageIdentifier(..), PackageName(..), Dependency(..))-import Distribution.Simple.Program (defaultProgramConfiguration)-import Distribution.Simple.Configure (configCompiler)-import Distribution.Simple.Utils (die, setupMessage)-import Distribution.PackageDescription (GenericPackageDescription(..), PackageDescription(..), exeName)-import Distribution.PackageDescription.Configuration (finalizePackageDescription)---import Distribution.ParseUtils (parseQuoted)-import Distribution.Verbosity (Verbosity)-import Distribution.Package.Debian.Dependencies (PackageType(..), debianExtraPackageName, debianUtilsPackageName, debianSourcePackageName, debianDocPackageName,- {-DebianBinPackageName,-} debianDevPackageName, debianProfPackageName)-import Distribution.Package.Debian.Relations (versionSplits)-import Distribution.Package.Debian.Setup (Flags(..), DebAction(..), DebType(..))---import qualified Distribution.Compat.ReadP as ReadP---import Distribution.Text ( Text(parse) )-import Text.PrettyPrint.HughesPJ--import Distribution.Package.Debian.Relations (buildDependencies, docDependencies, allBuildDepends, cabalDependencies)--{--_parsePackageId' :: ReadP.ReadP PackageIdentifier PackageIdentifier-_parsePackageId' = parseQuoted parse ReadP.<++ parse--}--type DebMap = Map.Map D.BinPkgName (Maybe DebianVersion)--buildDebVersionMap :: IO DebMap-buildDebVersionMap =- readFile "/var/lib/dpkg/status" >>=- return . either (const []) unControl . parseControl "/var/lib/dpkg/status" >>=- mapM (\ p -> case (lookupP "Package" p, lookupP "Version" p) of- (Just (Field (_, name)), Just (Field (_, version))) ->- return (Just (D.BinPkgName (D.PkgName (stripWS name)), Just (parseDebianVersion (stripWS version))))- _ -> return Nothing) >>=- return . Map.fromList . catMaybes--(!) :: DebMap -> D.BinPkgName -> DebianVersion-m ! k = maybe (error ("No version number for " ++ (show . D.prettyBinPkgName $ k) ++ " in " ++ show (Map.map (maybe Nothing (Just . prettyDebianVersion)) m))) id (Map.findWithDefault Nothing k m)--trim :: String -> String-trim = dropWhile isSpace--simplePackageDescription :: GenericPackageDescription -> Flags- -> IO (Compiler, PackageDescription)-simplePackageDescription genPkgDesc flags = do- (compiler', _) <- {- fchroot (buildRoot flags) -} (configCompiler (Just (rpmCompiler flags)) Nothing Nothing- defaultProgramConfiguration- (rpmVerbosity flags))- let compiler = case (rpmCompilerVersion flags, rpmCompiler flags) of- (Just v, ghc) -> compiler' {compilerId = CompilerId ghc v}- _ -> compiler'- --installed <- installedPackages- case finalizePackageDescription (rpmConfigurationsFlags flags)- (const True) (Platform buildArch buildOS) (compilerId compiler)- {- (Nothing :: Maybe PackageIndex) -}- [] genPkgDesc of- Left e -> die $ "finalize failed: " ++ show e- Right (pd, _) -> return (compiler, pd)- -debian :: GenericPackageDescription -- ^ info from the .cabal file- -> Flags -- ^ command line flags- -> IO ()--debian genPkgDesc flags =- case rpmCompiler flags of- GHC ->- do (compiler, pkgDesc) <- simplePackageDescription genPkgDesc flags- let verbose = rpmVerbosity flags- createDirectoryIfMissing True (debOutputDir flags)- --lbi <- localBuildInfo pkgDesc flags- debVersions <- buildDebVersionMap- cabalPackages <- libPaths compiler debVersions >>= return . Map.fromList . map (\ p -> (cabalName p, p))- bracket (setFileCreationMask 0o022) setFileCreationMask $ \ _ -> do- autoreconf verbose pkgDesc- case debAction flags of- SubstVar name ->- do control <- readFile "debian/control" >>= either (error . show) return . parseControl "debian/control"- substvars flags pkgDesc compiler debVersions control cabalPackages name- Debianize ->- debianize True pkgDesc flags compiler (debOutputDir flags)- UpdateDebianization ->- updateDebianization True pkgDesc flags compiler (debOutputDir flags)- Usage ->- error "Unexpected debAction: usage"- c -> die ("the " ++ show c ++ " compiler is not yet supported")--autoreconf :: Verbosity -> PackageDescription -> IO ()--autoreconf verbose pkgDesc = do- ac <- doesFileExist "configure.ac"- when ac $ do- c <- doesFileExist "configure"- when (not c) $ do- setupMessage verbose "Running autoreconf" (packageId pkgDesc)- ret <- system "autoreconf"- case ret of- ExitSuccess -> return ()- ExitFailure n -> die ("autoreconf failed with status " ++ show n)--data PackageInfo = PackageInfo { libDir :: FilePath- , cabalName :: String- , cabalVersion :: String- , devDeb :: Maybe (D.BinPkgName, DebianVersion)- , profDeb :: Maybe (D.BinPkgName, DebianVersion)- , docDeb :: Maybe (D.BinPkgName, DebianVersion) } ---- |Each cabal package corresponds to a directory <name>-<version>,--- either in /usr/lib or in /usr/lib/haskell-packages/ghc/lib.--- In that directory is a compiler subdirectory such as ghc-6.8.2.--- In the ghc subdirectory is one or two library files of the form--- libHS<name>-<version>.a and libHS<name>-<version>_p.a. We can--- determine the debian package names by running dpkg -S on these--- names, or examining the /var/lib/dpkg/info/\*.list files. From--- these we can determine the source package name, and from that--- the documentation package name.-substvars :: Flags- -> PackageDescription -- ^info from the .cabal file- -> Compiler -- ^compiler details- -> DebMap- -> Control -- ^The debian/control file- -> Map.Map String PackageInfo -- ^The list of installed cabal packages- -> DebType -- ^The type of deb we want to write substvars for- -> IO ()-substvars flags pkgDesc _compiler _debVersions control cabalPackages debType =- case (missingBuildDeps, path) of- -- There should already be a .substvars file produced by dh_haskell_prep,- -- keep the relations listed there. They will contain something like this:- -- libghc-cabal-debian-prof.substvars:- -- haskell:Depends=ghc-prof (<< 6.8.2-999), ghc-prof (>= 6.8.2), libghc-cabal-debian-dev (= 0.4)- -- libghc-cabal-debian-dev.substvars:- -- haskell:Depends=ghc (<< 6.8.2-999), ghc (>= 6.8.2)- -- haskell-cabal-debian-doc.substvars:- -- haskell:Depends=ghc-doc, haddock (>= 2.1.0), haddock (<< 2.1.0-999)- ([], Just path') ->- do old <- try (readFile path') >>= return . either (\ (_ :: SomeException) -> "") id- let new = addDeps old- hPutStrLn stderr (if new /= old- then ("cabal-debian - Updated " ++ show path' ++ ":\n " ++ old ++ "\n ->\n " ++ new)- else ("cabal-debian - No updates found for " ++ show path'))- maybe (return ()) (\ _x -> replaceFile path' new) name- ([], Nothing) -> return ()- (missing, _) -> - die ("These debian packages need to be added to the build dependency list so the required cabal packages are available:\n " ++ intercalate "\n " (map (show . D.prettyBinPkgName . fst) missing) ++- "\nIf this is an obsolete package you may need to withdraw the old versions from the\n" ++- "upstream repository, and uninstall and purge it from your local system.")- where- addDeps old =- case partition (isPrefixOf "haskell:Depends=") (lines old) of- ([], other) -> unlines (("haskell:Depends=" ++ showDeps deps) : other)- (hdeps, more) ->- case deps of- [] -> unlines (hdeps ++ more)- _ -> unlines (map (++ (", " ++ showDeps deps)) hdeps ++ more)- path = fmap (\ (D.BinPkgName (D.PkgName x)) -> "debian/" ++ x ++ ".substvars") name- name = case debType of Dev -> devDebName; Prof -> profDebName; Doc -> docDebName- deps = case debType of Dev -> devDeps; Prof -> profDeps; Doc -> docDeps- -- We must have build dependencies on the profiling and documentation packages- -- of all the cabal packages.- missingBuildDeps =- let requiredDebs =- concat (map (\ (Dependency (PackageName name) _) ->- case Map.lookup name cabalPackages :: Maybe PackageInfo of- Just info ->- let prof = maybe (devDeb info) Just (profDeb info) in- let doc = docDeb info in- catMaybes [prof, doc]- Nothing -> []) (cabalDependencies flags pkgDesc)) in- filter (not . (`elem` buildDepNames) . fst) requiredDebs- -- Make a list of the debian devel packages corresponding to cabal packages- -- which are build dependencies- devDeps :: D.Relations- devDeps =- catMaybes (map (\ (Dependency (PackageName name) _) ->- case Map.lookup name cabalPackages :: Maybe PackageInfo of- Just package -> maybe Nothing (\ (s, v) -> Just [D.Rel s (Just (D.GRE v)) Nothing]) (devDeb package)- Nothing -> Nothing) (cabalDependencies flags pkgDesc))- profDeps :: D.Relations- profDeps =- maybe [] (\ name -> [[D.Rel name Nothing Nothing]]) devDebName ++- catMaybes (map (\ (Dependency (PackageName name) _) ->- case Map.lookup name cabalPackages :: Maybe PackageInfo of- Just package -> maybe Nothing (\ (s, v) -> Just [D.Rel s (Just (D.GRE v)) Nothing]) (profDeb package)- Nothing -> Nothing) (cabalDependencies flags pkgDesc))- docDeps :: D.Relations- docDeps =- catMaybes (map (\ (Dependency (PackageName name) _) ->- case Map.lookup name cabalPackages :: Maybe PackageInfo of- Just package -> maybe Nothing (\ (s, v) -> Just [D.Rel s (Just (D.GRE v)) Nothing]) (docDeb package)- Nothing -> Nothing) (cabalDependencies flags pkgDesc))- buildDepNames :: [D.BinPkgName]- buildDepNames = concat (map (map (\ (D.Rel s _ _) -> s)) buildDeps)- buildDeps :: D.Relations- buildDeps = (either (error . show) id . D.parseRelations $ bd) ++ (either (error . show) id . D.parseRelations $ bdi)- --sourceName = maybe (error "Invalid control file") (\ (Field (_, s)) -> stripWS s) (lookupP "Source" (head (unControl control)))- devDebName = fmap (D.BinPkgName . D.PkgName) $ listToMaybe (filter (isSuffixOf "-dev") debNames)- profDebName = fmap (D.BinPkgName . D.PkgName) $ listToMaybe (filter (isSuffixOf "-prof") debNames)- docDebName = fmap (D.BinPkgName . D.PkgName) $ listToMaybe (filter (isSuffixOf "-doc") debNames)- debNames = map (\ (Field (_, s)) -> stripWS s) (catMaybes (map (lookupP "Package") (tail (unControl control))))- bd = maybe "" (\ (Field (_a, b)) -> stripWS b) . lookupP "Build-Depends" . head . unControl $ control- bdi = maybe "" (\ (Field (_a, b)) -> stripWS b) . lookupP "Build-Depends-Indep" . head . unControl $ control---- |Write a file which we might still be reading from in--- order to compute the text argument.-replaceFile :: FilePath -> String -> IO ()-replaceFile path text =- try (removeFile back) >>= either chk1 return >> -- This may not exist- try (renameFile path back) >>= either chk2 return >> -- This may not exist- writeFile path text -- This must succeed- where- back = path ++ "~"- chk1 :: IOException -> IO ()- chk1 e =- if ioeGetFileName e == Just back && isDoesNotExistError e- then return ()- else ioError e- chk2 :: IOException -> IO ()- chk2 e =- if ioeGetFileName e == Just path && isDoesNotExistError e- then return ()- else ioError e--libPaths :: Compiler -> DebMap -> IO [PackageInfo]-libPaths compiler debVersions- | compilerFlavor compiler == GHC =- do a <- getDirPaths "/usr/lib"- b <- getDirPaths "/usr/lib/haskell-packages/ghc/lib"- -- Build a map from names of installed debs to version numbers- dpkgFileMap >>= runReaderT (mapM (packageInfo compiler debVersions) (a ++ b)) >>= return . catMaybes- | True = error $ "Can't handle compiler flavor: " ++ show (compilerFlavor compiler)- where- getDirPaths path = try (getDirectoryContents path) >>= return . map (\ x -> (path, x)) . either (\ (_ :: SomeException) -> []) id--packageInfo :: Compiler -> DebMap -> (FilePath, String) -> ReaderT (Map.Map FilePath (Set.Set D.BinPkgName)) IO (Maybe PackageInfo)-packageInfo compiler debVersions (d, f) =- case parseNameVersion f of- Nothing -> return Nothing- Just (p, v) -> lift (doesDirectoryExist (d </> f </> cdir)) >>= cond (return Nothing) (info (d, p, v))- where- cdir = display (compilerId compiler)- info (d, p, v) = - do dev <- debOfFile ("^" ++ d </> p ++ "-" ++ v </> cdir </> "libHS" ++ p ++ "-" ++ v ++ ".a$")- prof <- debOfFile ("^" ++ d </> p ++ "-" ++ v </> cdir </> "libHS" ++ p ++ "-" ++ v ++ "_p.a$")- doc <- debOfFile ("/" ++ p ++ ".haddock$")- return (Just (PackageInfo { libDir = d- , cabalName = p- , cabalVersion = v- , devDeb = maybe Nothing (\ x -> Just (x, debVersions ! x)) dev- , profDeb = maybe Nothing (\ x -> Just (x, debVersions ! x)) prof- , docDeb = maybe Nothing (\ x -> Just (x, debVersions ! x)) doc }))- parseNameVersion s =- case (break (== '-') (reverse s)) of- (_a, "") -> Nothing- (a, b) -> Just (reverse (tail b), reverse a) ---- |Create a map from pathname to the names of the packages that contains that pathname.--- We need to make sure we consume all the files, so -dpkgFileMap :: IO (Map.Map FilePath (Set.Set D.BinPkgName))-dpkgFileMap =- do- let fp = "/var/lib/dpkg/info"- names <- getDirectoryContents fp >>= return . filter (isSuffixOf ".list")- let paths = map (fp </>) names- files <- mapM (strictReadF lines) paths- return $ Map.fromList $ zip (map dropExtension names) (map (Set.fromList . map (D.BinPkgName . D.PkgName)) $ files)--strictReadF :: (String -> r) -> FilePath -> IO r-strictReadF f path = withFile path ReadMode (\h -> hGetContents h >>= (\x -> return $! f x))--- strictRead = strictReadF id---- |Given a path, return the name of the package that owns it.-debOfFile :: FilePath -> ReaderT (Map.Map FilePath (Set.Set D.BinPkgName)) IO (Maybe D.BinPkgName)-debOfFile path =- do mp <- ask- return $ testPath (Map.lookup path mp)- where- -- testPath :: Maybe (Set.Set FilePath) -> Maybe FilePath- testPath Nothing = Nothing- testPath (Just s) =- case Set.size s of- 1 -> Just (Set.findMin s)- _ -> Nothing--cond :: t -> t -> Bool -> t-cond ifF _ifT False = ifF-cond _ifF ifT True = ifT--debianize :: Bool -> PackageDescription -> Flags -> Compiler -> FilePath -> IO ()-debianize force pkgDesc flags compiler tgtPfx =- mapM_ removeIfExists ["debian/control"] >>- updateDebianization force pkgDesc flags compiler tgtPfx--removeFileIfExists :: FilePath -> IO ()-removeFileIfExists x = doesFileExist x >>= (`when` (removeFile x))--removeDirectoryIfExists :: FilePath -> IO ()-removeDirectoryIfExists x = doesDirectoryExist x >>= (`when` (removeDirectory x))--removeIfExists :: FilePath -> IO ()-removeIfExists x = removeFileIfExists x >> removeDirectoryIfExists x--updateDebianization :: Bool -- ^whether to forcibly create file- -> PackageDescription -- ^info from the .cabal file- -> Flags -- ^command line flags- -> Compiler -- ^compiler details- -> FilePath -- ^directory in which to create files- -> IO ()-updateDebianization _force pkgDesc flags compiler tgtPfx =- do createDirectoryIfMissing True "debian"- createDirectoryIfMissing True "debian/source"- date <- getCurrentLocalRFC822Time- copyright <- try (readFile' (licenseFile pkgDesc)) >>=- return . either (\ (_ :: SomeException) -> showLicense . license $ pkgDesc) id- debianMaintainer <- getDebianMaintainer flags >>= maybe (error "Missing value for --maintainer") return- controlUpdate (tgtPfx </> "control") flags compiler debianMaintainer pkgDesc- changelogUpdate flags (tgtPfx </> "changelog") debianMaintainer pkgDesc date- replaceFile (tgtPfx </> "rules") (cdbsRules pkgDesc)- getPermissions "debian/rules" >>= setPermissions "debian/rules" . (\ p -> p {executable = True})- replaceFile (tgtPfx </> "compat") "7" -- should this be hardcoded, or automatically read from /var/lib/dpkg/status?- replaceFile (tgtPfx </> "copyright") copyright- replaceFile (tgtPfx </> "source/format") (sourceFormat flags)- replaceFile (tgtPfx </> "watch") ("version=3\nopts=\"downloadurlmangle=s|archive/([\\w\\d_-]+)/([\\d\\.]+)/|archive/$1/$2/$1-$2.tar.gz|,\\\nfilenamemangle=s|(.*)/$|" ++ pkgname ++ "-$1.tar.gz|\" \\\n http://hackage.haskell.org/packages/archive/" ++ pkgname ++ " \\\n ([\\d\\.]*\\d)/\n")- -- The dev postinst and prerm files are generated by haskell-devscripts via cdbs.- return ()- where- PackageName pkgname = pkgName . package $ pkgDesc--readFile' :: FilePath -> IO String-readFile' path- = do- file <- openFile path ReadMode- hSetBinaryMode file True- hGetContents file--{--Create a debian maintainer field from the environment variables:-- DEBFULLNAME (preferred) or NAME- DEBEMAIL (preferred) or EMAIL--More work could be done to match dch, but this is sufficient for-now. Here is what the man page for dch has to say:-- If the environment variable DEBFULLNAME is set, this will be used for- the maintainer full name; if not, then NAME will be checked. If the- environment variable DEBEMAIL is set, this will be used for the email- address. If this variable has the form "name <email>", then the- maintainer name will also be taken from here if neither DEBFULLNAME- nor NAME is set. If this variable is not set, the same test is- performed on the environment variable EMAIL. Next, if the full name- has still not been determined, then use getpwuid(3) to determine the- name from the pass‐word file. If this fails, use the previous- changelog entry. For the email address, if it has not been set from- DEBEMAIL or EMAIL, then look in /etc/mailname, then attempt to build- it from the username and FQDN, otherwise use the email address in the- previous changelog entry. In other words, it’s a good idea to set- DEBEMAIL and DEBFULLNAME when using this script.---}-getDebianMaintainer :: Flags -> IO (Maybe String)-getDebianMaintainer flags =- case debMaintainer flags of- Nothing -> envMaintainer- maint -> return maint- where- envMaintainer :: IO (Maybe String)- envMaintainer =- do env <- map (second decodeString) `fmap` getEnvironment- return $ do fullname <- lookup "DEBFULLNAME" env `mplus` lookup "NAME" env- email <- lookup "DEBEMAIL" env `mplus` lookup "EMAIL" env- return (fullname ++ " <" ++ email ++ ">")--cdbsRules :: PackageDescription -> String-cdbsRules pkgDesc =- unlines (intercalate [""] ([header, execs, comments] {- ++ devrules ++ profrules -} ))- where- header =- ["#!/usr/bin/make -f",- "",- "DEB_CABAL_PACKAGE = " ++ (show (D.prettyBinPkgName (debianExtraPackageName versionSplits (pkgName (package pkgDesc)) (Just (D.EEQ (parseDebianVersion (showVersion (pkgVersion (package pkgDesc))))))))),- "",- "include /usr/share/cdbs/1/rules/debhelper.mk",- "include /usr/share/cdbs/1/class/hlibrary.mk"]- execs =- case map exeName (executables pkgDesc) ++ dataFiles pkgDesc of- [] -> []-{-- [e] ->- let exe = exeName e- src = "dist-ghc/build/" ++ exe ++ "/" ++ exe- dst = "debian/" ++ exeDeb e ++ "/usr/bin/" ++ exe in- [ -- Magic rule required to get binaries to build in packages that have no libraries- "build/" ++ exeDeb e ++ ":: build-ghc-stamp"- , "binary-fixup/" ++ exeDeb e ++ "::"- , "\tinstall -m 755 -s -D " ++ src ++ " " ++ dst ++ " || true"]--}- _ ->- [ -- Magic rule required to get binaries to build in packages that have no libraries- "build/" ++ (show . D.prettyBinPkgName $ utilsDeb) ++ ":: build-ghc-stamp"- , "binary-fixup/" ++ (show . D.prettyBinPkgName $ utilsDeb) ++ "::" ] ++- map (\ exe ->- let src = "dist-ghc/build/" ++ exe ++ "/" ++ exe- dst = "debian/" ++ (show . D.prettyBinPkgName $ utilsDeb) ++ "/usr/bin/" ++ exe in- "\tinstall -m 755 -s -D " ++ src ++ " " ++ dst ++ " || true") (map exeName (executables pkgDesc)) ++- map (\ file ->- let dst = "debian" </> (show . D.prettyBinPkgName $ utilsDeb) </> "usr/share" </> libDir </> file in- "\tinstall -m 644 -D " ++ file ++ " " ++ dst ++ " || true") (dataFiles pkgDesc)- comments =- ["# How to install an extra file into the documentation package",- "#binary-fixup/" ++ (show . D.prettyBinPkgName $ docDeb) ++ "::",- "#\techo \"Some informative text\" > debian/" ++ (show . D.prettyBinPkgName $ docDeb) ++ "/usr/share/doc/" ++ (show . D.prettyBinPkgName $ docDeb) ++ "/AnExtraDocFile"]- p = pkgName . package $ pkgDesc- v = pkgVersion . package $ pkgDesc- libDir = unPackageName p ++ "-" ++ showVersion v- docDeb = debianDocPackageName versionSplits (pkgName (package pkgDesc)) (Just (D.EEQ (parseDebianVersion (showVersion (pkgVersion (package pkgDesc))))))- utilsDeb = debianUtilsPackageName versionSplits (pkgName (package pkgDesc)) (Just (D.EEQ (parseDebianVersion (showVersion (pkgVersion (package pkgDesc))))))- --exeDeb e = debianExtraPackageName (PackageName (exeName e)) Nothing--list :: b -> ([a] -> b) -> [a] -> b-list d f l = case l of [] -> d; _ -> f l--controlUpdate :: FilePath -> Flags -> Compiler -> String -> PackageDescription -> IO ()-controlUpdate path flags compiler debianMaintainer pkgDesc =- try (readFile path) >>=- either (\ (_ :: SomeException) -> writeFile path (show newCtl)) (\ s -> writeFile (path ++ ".new") $! show (merge newCtl (oldCtl s)))- where- newCtl = control flags compiler debianMaintainer pkgDesc- oldCtl s = either (const (Control [])) id (parseControl "debian/control" s)- merge (Control new) (Control old) =- case (new, old) of- (_newSource : _new', []) -> Control new- (newSource : new', oldSource : old') ->- Control (mergeParagraphs newSource oldSource : mergeOther new' old')- -- Merge a list of binary package paragraphs- mergeOther new old =- map mergePackages allNames- where- mergePackages name =- case (findPackage name new, findPackage name old) of- (Just x, Nothing) -> x- (Nothing, Just x) -> x- (Just x, Just y) -> mergeParagraphs x y- findPackage name paras = listToMaybe (filter (hasName name) paras)- where hasName name para = lookupP "Package" para == Just name- allNames = newNames ++ (oldNames \\ newNames)- newNames = catMaybes $ map (lookupP "Package") new- oldNames = catMaybes $ map (lookupP "Package") old--mergeParagraphs :: Paragraph' String -> Paragraph' String -> Paragraph' String-mergeParagraphs new@(Paragraph newFields) old@(Paragraph oldFields) =- Paragraph (map mergeField fieldNames)- where- fieldNames = map fieldName oldFields ++ (map fieldName newFields \\ map fieldName oldFields)- fieldName (Field (name, _)) = name- mergeField :: String -> Field- mergeField name =- case (lookupP name new, lookupP name old) of- (Just (Field (_, x)), Nothing) -> Field (name, x)- (Nothing, Just (Field (_, x))) -> Field (name, x)- (Just (Field (_, x)), Just (Field (_, y))) -> Field (name, mergeValues name x y)- _ -> error $ "Internal error"- mergeValues :: String -> String -> String -> String- mergeValues "Build-Depends" x y =- " " ++ (showDeps' "Build-Depends:" $ mergeDeps (parseDeps x) (parseDeps y))- mergeValues "Depends" x y =- " " ++ (showDeps' "Depends:" $ mergeDeps (parseDeps x) (parseDeps y))- mergeValues _ x _ = x- parseDeps s = either (error . show) id (D.parseRelations s)--mergeDeps :: D.Relations -> D.Relations -> D.Relations-mergeDeps x y = - -- foldr :: (a -> b -> b) -> b -> [a] -> b- nub $ foldr insertDep x y- where- insertDep :: [D.Relation] -> D.Relations -> D.Relations- insertDep ys xss =- case depPackageNames ys of- [name] -> case break (\ xs -> depPackageNames xs == [name]) xss of- (a, b : c) -> a ++ [b, ys] ++ c- (a, []) -> a ++ [ys]- _ -> xss ++ [ys]- depPackageNames xs = nub (map depPackageName xs)- depPackageName (D.Rel x _ _) = x--control :: Flags -> Compiler -> String -> PackageDescription -> Control-control flags compiler debianMaintainer pkgDesc =- -- trace ("allBuildDepends " ++ show flags ++ " -> " ++ show (allBuildDepends flags pkgDesc)) $- Control {unControl =- ([sourceSpec] ++- develLibrarySpecs ++- profileLibrarySpecs ++ - docLibrarySpecs ++- (case map exeName (executables pkgDesc) ++ dataFiles pkgDesc of- [] -> []- -- [e] -> [utilsSpec (debianName Extra (PackageName (exeName e)) Nothing)]- _ -> [utilsSpec (show . D.prettyBinPkgName $ debianUtilsPackageName versionSplits (pkgName (package pkgDesc)) (Just (D.EEQ (parseDebianVersion (showVersion (pkgVersion (package pkgDesc)))))))]))}- where- --buildDepsIndep = ""- sourceSpec =- Paragraph- ([Field ("Source", " " ++ (show . D.prettySrcPkgName $ debianSourcePackageName versionSplits (pkgName . package $ pkgDesc) (Just (D.EEQ (parseDebianVersion (showVersion (pkgVersion (package pkgDesc)))))))),- Field ("Priority", " " ++ "extra"),- Field ("Section", " " ++ "haskell"),- Field ("Maintainer", " " ++ debianMaintainer),- Field ("Build-Depends", " " ++ showDeps' "Build-Depends:" (debianBuildDeps ++ map rel (buildDeps flags))),- Field ("Build-Depends-Indep", " " ++ showDeps' "Build-Depends-Indep:" debianBuildDepsIndep),- --Field ("Build-Depends-Indep", " " ++ buildDepsIndep),- Field ("Standards-Version", " " ++ "3.9.3"),- Field ("Homepage",- " " ++- if homepage pkgDesc == ""- then "http://hackage.haskell.org/package/" ++- unPackageName (pkgName $ package pkgDesc)- else homepage pkgDesc)])- rel x = [D.Rel (D.BinPkgName (D.PkgName x)) Nothing Nothing]- utilsSpec p =- Paragraph- [Field ("Package", " " ++ p),- Field ("Architecture", " " ++ "any"),- Field ("Section", " " ++ "misc"),- -- No telling what the dependencies of an executable might- -- be. The developer will have to fill them in- Field ("Depends", " " ++ showDeps [[D.Rel (D.BinPkgName (D.PkgName "${shlibs:Depends}")) Nothing Nothing], - [D.Rel (D.BinPkgName (D.PkgName "${haskell:Depends}")) Nothing Nothing],- [D.Rel (D.BinPkgName (D.PkgName "${misc:Depends}")) Nothing Nothing]]),- Field ("Description", " " ++ maybe debianDescription (const executableDescription) (library pkgDesc))]- develLibrarySpecs = if isJust (library pkgDesc) then [librarySpec "any" Development debianDevPackageName] else []- profileLibrarySpecs = if debLibProf flags && isJust (library pkgDesc) then [librarySpec "any" Profiling debianProfPackageName] else []- docLibrarySpecs = if isJust (library pkgDesc) && rpmHaddock flags then [docSpecsParagraph] else []- docSpecsParagraph =- Paragraph- [Field ("Package", " " ++ (show . D.prettyBinPkgName $ debianDocPackageName versionSplits (pkgName (package pkgDesc)) (Just (D.EEQ (parseDebianVersion (showVersion (pkgVersion (package pkgDesc)))))))),- Field ("Architecture", " " ++ "all"),- Field ("Section", " " ++ "doc"),- Field ("Depends", " " ++ showDeps' "Depends:" [[D.Rel (D.BinPkgName (D.PkgName "${haskell:Depends}")) Nothing Nothing],- [D.Rel (D.BinPkgName (D.PkgName "${misc:Depends}")) Nothing Nothing]]),- Field ("Recommends", " " ++ "${haskell:Recommends}"),- Field ("Suggests", " " ++ "${haskell:Suggests}"),- Field ("Description", " " ++ libraryDescription Documentation)]- librarySpec arch typ debianName =- Paragraph- [Field ("Package", " " ++ (show . D.prettyBinPkgName $ debianName versionSplits (pkgName (package pkgDesc)) (Just (D.EEQ (parseDebianVersion (showVersion (pkgVersion (package pkgDesc)))))))),- Field ("Architecture", " " ++ arch),- Field ("Depends", " " ++ showDeps' "Depends:" (- (if typ == Development- then [[D.Rel (D.BinPkgName (D.PkgName "${shlibs:Depends}")) Nothing Nothing]] ++ map (\ x -> [D.Rel (D.BinPkgName (D.PkgName x)) Nothing Nothing]) (extraDevDeps flags)- else []) ++- [[D.Rel (D.BinPkgName (D.PkgName "${haskell:Depends}")) Nothing Nothing],- [D.Rel (D.BinPkgName (D.PkgName "${misc:Depends}")) Nothing Nothing]])),- Field ("Recommends", " " ++ "${haskell:Recommends}"),- Field ("Suggests", " " ++ "${haskell:Suggests}"),- Field ("Provides", " " ++ "${haskell:Provides}"),- Field ("Description", " " ++ libraryDescription typ)]- -- The haskell-cdbs package contains the hlibrary.mk file with- -- the rules for building haskell packages.- debianBuildDeps :: D.Relations- debianBuildDeps = - nub $- [[D.Rel (D.BinPkgName (D.PkgName "debhelper")) (Just (D.GRE (parseDebianVersion "7.0"))) Nothing],- [D.Rel (D.BinPkgName (D.PkgName "haskell-devscripts")) (Just (D.GRE (parseDebianVersion "0.8"))) Nothing],- [D.Rel (D.BinPkgName (D.PkgName "cdbs")) Nothing Nothing],- [D.Rel (D.BinPkgName (D.PkgName "ghc")) Nothing Nothing]] ++- (if debLibProf flags then [[D.Rel (D.BinPkgName (D.PkgName "ghc-prof")) Nothing Nothing]] else []) ++- (concat . map (buildDependencies flags compiler) . allBuildDepends flags $ pkgDesc)- debianBuildDepsIndep :: D.Relations- debianBuildDepsIndep =- nub $- [[D.Rel (D.BinPkgName (D.PkgName "ghc-doc")) Nothing Nothing]] ++- (concat . map (docDependencies flags compiler) . allBuildDepends flags $ pkgDesc)- debianDescription = - (unwords . words . synopsis $ pkgDesc) ++- case description pkgDesc of- "" -> ""- text ->- let text' = text ++ "\n" ++- list "" ("\n Author: " ++) (author pkgDesc) ++- list "" ("\n Upstream-Maintainer: " ++) (maintainer pkgDesc) ++- list "" ("\n Url: " ++) (pkgUrl pkgDesc) in- "\n " ++ (trim . intercalate "\n " . map addDot . lines $ text')- addDot line = if all (flip elem " \t") line then "." else line- executableDescription = " " ++ "An executable built with the " ++ display (package pkgDesc) ++ " library."- libraryDescription Profiling = debianDescription ++ "\n .\n This package contains the libraries compiled with profiling enabled."- libraryDescription Development = debianDescription ++ "\n .\n This package contains the normal library files."- libraryDescription Documentation = debianDescription ++ "\n .\n This package contains the documentation files."- libraryDescription x = error $ "Unexpected library package name suffix: " ++ show x--showDeps :: [[D.Relation]] -> String-showDeps xss = intercalate ", " (map (intercalate " | " . map (show . D.prettyRelation)) xss)--{--showDeps' :: [a] -> [[D.Relation]] -> String-showDeps' prefix xss =- intercalate (",\n " ++ prefix') (map (intercalate " | " . map (show . D.prettyRelation)) xss)- where prefix' = map (\ _ -> ' ') prefix--}--showDeps' :: [a] -> [[D.Relation]] -> String-showDeps' prefix xss =- intercalate ("\n" ++ prefix' ++ " , ") (map (intercalate " | " . map (show . D.prettyRelation)) xss)- where prefix' = map (\ _ -> ' ') prefix--changelogUpdate :: Flags -> FilePath -> String -> PackageDescription -> String -> IO ()-changelogUpdate flags path debianMaintainer pkgDesc date =- read >>= replace- where- read :: IO ([[String]], [ChangeLogEntry])- read = try (readFile path) >>= return . either (\ (e :: SomeException) -> ([[show e]], [])) (partitionEithers . parseLog)- replace :: ([[String]], [ChangeLogEntry]) -> IO ()- replace (errs, entries) =- do when (not (null errs)) (hPutStrLn stderr (intercalate "\n " ("Errors reading changelog:" : concat errs)))- let entries' = dropWhile (\ entry -> logVersion entry >= logVersion log) entries- replaceFile path (intercalate "\n" (map (render . prettyEntry) (log : entries')))- log = changelog flags debianMaintainer pkgDesc date--{--changelog :: Flags -> String -> PackageDescription -> String -> String-changelog flags debianMaintainer pkgDesc date =- render . prettyEntry $ changelog' flags debianMaintainer pkgDesc date--}--changelog :: Flags -> String -> PackageDescription -> String -> ChangeLogEntry-changelog flags debianMaintainer pkgDesc date =- Entry { logPackage = (show . D.prettySrcPkgName $ debianSourcePackageName versionSplits (pkgName . package $ pkgDesc) (Just (D.EEQ (parseDebianVersion (showVersion (pkgVersion (package pkgDesc)))))))- , logVersion = updateOriginal f $ debianVersionNumber pkgDesc- , logDists = [parseReleaseName "unstable"]- , logUrgency = "low"- , logComments = " * Debianization generated by cabal-debian\n\n"- , logWho = debianMaintainer- , logDate = date }- where- f s = maybe (g s) (\ d -> if older d s then error ("Version from --deb-version (" ++ show d ++ ") is older than hackage version (" ++ show s ++ "), maybe you need to unpin this package?") else d) (debVersion flags)- g s = maybe "" (\ n -> show n ++ ":") (Map.lookup (pkgName (package pkgDesc)) (epochMap flags)) ++ s ++ revision flags- older debv cabv = parseDebianVersion debv < parseDebianVersion cabv--updateOriginal :: (String -> String) -> DebianVersion -> DebianVersion---updateOriginal f (DebianVersion str dv) = DebianVersion (f str) dv-updateOriginal f v = parseDebianVersion . f . show . prettyDebianVersion $ v--unPackageName :: PackageName -> String-unPackageName (PackageName s) = s----debianDevelPackageName' (Dependency (PackageName name) _) = debianDevelPackageName name---- debianPackageName prefix name suffix = prefix ++ (map toLower name) ++ suffix--debianVersionNumber :: PackageDescription -> DebianVersion-debianVersionNumber pkgDesc = parseDebianVersion . showVersion . pkgVersion . package $ pkgDesc---- generated with:--- apt-cache show ghc \--- | grep ^Provides: \--- | cut -d\ -f2---- | sed 's/, /\n/g' \--- | grep libghc- \--- | cut -d- -f2- \--- | grep dev$ \--- | sed 's/-dev//;s/$/",/;s/^/"/'--{--base :: Set String-base- = Data.Set.fromList- ["array",- "base",- "bin-package-db",- "bytestring",- "cabal",- "containers",- "directory",- "extensible-exceptions",- "filepath",- "ghc-binary",- "ghc-prim",- "haskell2010",- "haskell98",- "hpc",- "integer-gmp",- "old-locale",- "old-time",- "pretty",- "process",- "random",- "rts",- "template-haskell",- "time",- "unix"]--}---- | Convert from license to RPM-friendly description. The strings are--- taken from TagsCheck.py in the rpmlint distribution.--showLicense :: License -> String-showLicense (GPL _) = "GPL"-showLicense (LGPL _) = "LGPL"-showLicense BSD3 = "BSD"-showLicense BSD4 = "BSD-like"-showLicense PublicDomain = "Public Domain"-showLicense AllRightsReserved = "Proprietary"-showLicense OtherLicense = "Non-distributable"
− Distribution/Package/Debian/Bundled.hs
@@ -1,323 +0,0 @@--- |--- Module : Distribution.Package.Debian.Bundled--- Copyright : David Fox 2008------ Maintainer : David Fox <dsf@seereason.com>--- Stability : alpha--- Portability : portable------ Determine whether a specific version of a Haskell package is--- bundled with into this particular version of the given compiler.---- This software may be used and distributed according to the terms of--- the GNU General Public License, incorporated herein by reference.-{-# LANGUAGE StandaloneDeriving #-}-module Distribution.Package.Debian.Bundled- ( ghcBuiltIn- ) where--import qualified Data.Map as Map-import Data.Set (fromList, member)-import Data.Version (Version(..))-import Debian.Relation.ByteString()-import Distribution.Simple.Compiler (Compiler(..), CompilerId(..), CompilerFlavor(..), {-PackageDB(GlobalPackageDB), compilerFlavor-})-import Distribution.Package (PackageIdentifier(..), PackageName(..) {-, Dependency(..)-})-import System.Exit (ExitCode(..))--type Bundled = (CompilerFlavor, Version, [PackageIdentifier])---- |Return a list of built in packages for the compiler in an environment.--- ghcBuiltIns :: FilePath -> IO [PackageIdentifier]--- ghcBuiltIns root =--- fchroot root (lazyProcess "ghc-pkg" ["list", "--simple-output"] Nothing Nothing empty) >>=--- return . map parsePackageIdentifier . words . unpack . fst . collectStdout--- where--- parsePackageIdentifier s =--- let (v', n') = break (== '-') (reverse s)--- (v, n) = (reverse (tail n'), reverse v') in--- PackageIdentifier (PackageName n) (Version (map read (filter (/= ".") (groupBy (\ a b -> (a == '.') == (b == '.')) v))) [])--ghcBuiltIns :: Compiler -> Bundled-ghcBuiltIns (Compiler {compilerId = CompilerId GHC compilerVersion}) =- case Map.lookup compilerVersion- (Map.fromList [ (Version [7,4,1] [], (GHC, Version [7,4,1] [], ghc741BuiltIns))- , (Version [7,4,0,20111219] [], (GHC, Version [7,4,0,20111219] [], ghc740BuiltIns))- , (Version [7,4,0,20120108] [], (GHC, Version [7,4,0,20120108] [], ghc740BuiltIns))- , (Version [7,2,2] [], (GHC, Version [7,2,2] [], ghc721BuiltIns))- , (Version [7,2,1] [], (GHC, Version [7,2,1] [], ghc721BuiltIns))- , (Version [7,0,4] [], (GHC, Version [7,0,4] [], ghc701BuiltIns))- , (Version [7,0,3] [], (GHC, Version [7,0,3] [], ghc701BuiltIns))- , (Version [7,0,1] [], (GHC, Version [7,0,1] [], ghc701BuiltIns))- , (Version [6,8,3] [], (GHC, Version [6,8,3] [], ghc683BuiltIns))- , (Version [6,8,2] [], (GHC, Version [6,8,2] [], ghc682BuiltIns))- , (Version [6,8,1] [], (GHC, Version [6,8,1] [], ghc681BuiltIns))- , (Version [6,6,1] [], (GHC, Version [6,6,1] [], ghc661BuiltIns))- , (Version [6,6] [], (GHC, Version [6,6] [], ghc66BuiltIns)) ]) of- Nothing -> error $ "cabal-debian: No bundled package list for ghc " ++ show compilerVersion- Just x -> x--ghcBuiltIn :: Compiler -> PackageName -> Bool-ghcBuiltIn compiler package =- Data.Set.member- package- (Data.Set.fromList- (let {- (Just (_, _, xs)) = unsafePerformIO (ghc6BuiltIns compiler) -}- (_, _, xs) = ghcBuiltIns compiler in map pkgName xs))--v :: String -> [Int] -> PackageIdentifier-v n x = PackageIdentifier (PackageName n) (Version x [])---- | Packages bundled with 7.4.0.20111219-2.-ghc741BuiltIns :: [PackageIdentifier]-ghc741BuiltIns = [- v "Cabal" [1,14,0],- v "array" [0,4,0,0],- v "base" [4,5,0,0],- v "bin-package-db" [0,0,0,0],- v "binary" [0,5,1,0],- v "bytestring" [0,9,2,1],- v "containers" [0,4,2,1],- v "deepseq" [1,3,0,0],- v "directory" [1,1,0,2],- v "extensible-exceptions" [0,1,1,4],- v "filepath" [1,3,0,0],- v "ghc" [7,4,1],- v "ghc-prim" [0,2,0,0],- v "haskell2010" [1,1,0,1],- v "haskell98" [2,0,0,1],- v "hoopl" [3,8,7,2],- v "hpc" [0,5,1,1],- v "integer-gmp" [0,4,0,0],- v "old-locale" [1,0,0,4],- v "old-time" [1,1,0,0],- v "pretty" [1,1,1,0],- v "process" [1,1,0,1], - v "rts" [1,0],- v "template-haskell" [2,7,0,0],- v "time" [1,4],- v "unix" [2,5,1,0] ]---- | Packages bundled with 7.4.0.20111219-2.-ghc740BuiltIns :: [PackageIdentifier]-ghc740BuiltIns = [- v "Cabal" [1,14,0],- v "array" [0,4,0,0],- v "base" [4,5,0,0],- v "bin-package-db" [0,0,0,0],- v "binary" [0,5,1,0],- v "bytestring" [0,9,2,1],- v "containers" [0,4,2,1],- v "deepseq" [1,3,0,0],- v "directory" [1,1,0,2],- v "extensible-exceptions" [0,1,1,4],- v "filepath" [1,3,0,0],- v "ghc" [7,4,0,20111219],- v "ghc-prim" [0,2,0,0],- v "haskell2010" [1,1,0,1],- v "haskell98" [2,0,0,1],- v "hoopl" [3,8,7,2],- v "hpc" [0,5,1,1],- v "integer-gmp" [0,4,0,0],- v "old-locale" [1,0,0,4],- v "old-time" [1,1,0,0],- v "pretty" [1,1,1,0],- v "process" [1,1,0,1], - v "rts" [1,0],- v "template-haskell" [2,7,0,0],- v "time" [1,4],- v "unix" [2,5,1,0] ]--ghc721BuiltIns :: [PackageIdentifier]-ghc721BuiltIns = [- v "Cabal" [1,12,0],- v "array" [0,3,0,3],- v "base" [4,4,0,0],- v "bin-package-db" [0,0,0,0],- v "binary" [0,5,0,2],- v "bytestring" [0,9,2,0],- v "containers" [0,4,1,0],- v "directory" [1,1,0,1],- v "extensible-exceptions" [0,1,1,3],- v "filepath" [1,2,0,1],- v "ghc" [7,2,1],- -- ghc-binary renamed to binary- v "ghc-prim" [0,2,0,0],- v "haskell2010" [1,1,0,0],- v "haskell98" [2,0,0,0],- v "hoopl" [3,8,7,1], -- new- v "hpc" [0,5,1,0],- v "integer-gmp" [0,3,0,0],- v "old-locale" [1,0,0,3],- v "old-time" [1,0,0,7],- v "pretty" [1,1,0,0],- v "process" [1,1,0,0], - -- random removed- v "rts" [1,0],- v "template-haskell" [2,6,0,0],- v "time" [1,2,0,5],- v "unix" [2,5,0,0] ]--ghc701BuiltIns :: [PackageIdentifier]-ghc701BuiltIns = [- v "Cabal" [1,10,0,0],- v "array" [0,3,0,2],- v "base" [4,3,0,0],- v "bin-package-db" [0,0,0,0],- v "bytestring" [0,9,1,8],- v "containers" [0,4,0,0],- v "directory" [1,1,0,0],- v "extensible-exceptions" [0,1,1,2],- v "filepath" [1,2,0,0],- v "ghc" [7,0,1],- v "ghc-binary" [0,5,0,2],- v "ghc-prim" [0,2,0,0],- v "haskell2010" [1,0,0,0],- v "haskell98" [1,1,0,0],- v "hpc" [0,5,0,6],- v "integer-gmp" [0,2,0,2],- v "old-locale" [1,0,0,2],- v "old-time" [1,0,0,6],- v "pretty" [1,0,1,2],- v "process" [1,0,1,4],- v "random" [1,0,0,3],- v "rts" [1,0],- v "template-haskell" [2,5,0,0],- v "time" [1,2,0,3],- v "unix" [2,4,1,0]- ]--ghc683BuiltIns :: [PackageIdentifier]-ghc683BuiltIns = ghc682BuiltIns--ghc682BuiltIns :: [PackageIdentifier]-ghc682BuiltIns = [- v "Cabal" [1,2,3,0],- v "array" [0,1,0,0],- v "base" [3,0,1,0],- v "bytestring" [0,9,0,1],- v "containers" [0,1,0,1],- v "directory" [1,0,0,0],- v "filepath" [1,1,0,0],- v "ghc" [6,8,2,0],- v "haskell98" [1,0,1,0],- v "hpc" [0,5,0,0],- v "old-locale" [1,0,0,0],- v "old-time" [1,0,0,0],- v "packedstring" [0,1,0,0],- v "pretty" [1,0,0,0],- v "process" [1,0,0,0],- v "random" [1,0,0,0],- v "readline" [1,0,1,0],- v "template-haskell" [2,2,0,0],- v "unix" [2,3,0,0]- ]--ghc681BuiltIns :: [PackageIdentifier]-ghc681BuiltIns = [- v "base" [3,0,0,0],- v "Cabal" [1,2,2,0],- v "GLUT" [2,1,1,1],- v "HGL" [3,2,0,0],- v "HUnit" [1,2,0,0],- v "OpenAL" [1,3,1,1],- v "OpenGL" [2,2,1,1],- v "QuickCheck" [1,1,0,0],- v "X11" [1,2,3,1],- v "array" [0,1,0,0],- v "bytestring" [0,9,0,1],- v "cgi" [3001,1,5,1],- v "containers" [0,1,0,0],- v "directory" [1,0,0,0],- v "fgl" [5,4,1,1],- v "filepatch" [1,1,0,0],- v "ghc" [6,8,1,0],- v "haskell-src" [1,0,1,1],- v "haskell98" [1,0,1,0],- v "hpc" [0,5,0,0],- v "html" [1,0,1,1],- v "mtl" [1,1,0,0],- v "network" [2,1,0,0],- v "old-locale" [1,0,0,0],- v "old-time" [1,0,0,0],- v "packedstring" [0,1,0,0],- v "parallel" [1,0,0,0],- v "parsec" [2,1,0,0],- v "pretty" [1,0,0,0],- v "process" [1,0,0,0],- v "random" [1,0,0,0],- v "readline" [1,0,1,0],- v "regex-base" [0,72,0,1],- v "regex-compat" [0,71,0,1],- v "regex-posix" [0,72,0,1],- v "stm" [2,1,1,0],- v "template-haskell" [2,2,0,0],- v "time" [1,1,2,0],- v "unix" [2,2,0,0],- v "xhtml" [3000,0,2,1]- ]--ghc661BuiltIns :: [PackageIdentifier]-ghc661BuiltIns = [- v "base" [2,1,1],- v "Cabal" [1,1,6,2],- v "cgi" [3001,1,1],- v "fgl" [5,4,1],- v "filepath" [1,0],- v "ghc" [6,6,1],- v "GLUT" [2,1,1],- v "haskell98" [1,0],- v "haskell-src" [1,0,1],- v "HGL" [3,1,1],- v "html" [1,0,1],- v "HUnit" [1,1,1],- v "mtl" [1,0,1],- v "network" [2,0,1],- v "OpenAL" [1,3,1],- v "OpenGL" [2,2,1],- v "parsec" [2,0],- v "QuickCheck" [1,0,1],- v "readline" [1,0],- v "regex-base" [0,72],- v "regex-compat" [0,71],- v "regex-posix" [0,71],- v "rts" [1,0],- v "stm" [2,0],- v "template-haskell" [2,1],- v "time" [1,1,1],- v "unix" [2,1],- v "X11" [1,2,1],- v "xhtml" [3000,0,2]- ]--ghc66BuiltIns :: [PackageIdentifier]-ghc66BuiltIns = [- v "base" [2,0],- v "Cabal" [1,1,6],- v "cgi" [2006,9,6],- v "fgl" [5,2],- v "ghc" [6,6],- v "GLUT" [2,0],- v "haskell98" [1,0],- v "haskell-src" [1,0],- v "HGL" [3,1],- v "html" [1,0],- v "HTTP" [2006,7,7],- v "HUnit" [1,1],- v "mtl" [1,0],- v "network" [2,0],- v "OpenAL" [1,3],- v "OpenGL" [2,1],- v "parsec" [2,0],- v "QuickCheck" [1,0],- v "readline" [1,0],- v "regex-base" [0,71],- v "regex-compat" [0,71],- v "regex-posix" [0,71],- v "rts" [1,0],- v "stm" [2,0],- v "template-haskell" [2,0],- v "time" [1,0],- v "unix" [1,0],- v "X11" [1,1],- v "xhtml" [2006,9,13]- ]
− Distribution/Package/Debian/Dependencies.hs
@@ -1,327 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses, StandaloneDeriving #-}-{-# OPTIONS -Wall -Wwarn -fno-warn-name-shadowing -fno-warn-orphans #-}-module Distribution.Package.Debian.Dependencies- ( PackageType(..)- , VersionSplits(..)- , dependencies- , mkPkgName- , invertVersionRange- -- , debianName- , debianSourcePackageName- , DebianBinPackageName- , debianDevPackageName- , debianProfPackageName- , debianDocPackageName- , debianExtraPackageName- , debianUtilsPackageName- ) where--import Data.Char (toLower)-import Data.Function (on)-import Data.List (intersperse, minimumBy)-import qualified Data.Map as Map-import Data.Maybe (catMaybes)-import Data.Version (showVersion)-import Debian.Relation (Relations, Relation, BinPkgName(BinPkgName), PkgName(PkgName), VersionReq(..), SrcPkgName(..))-import qualified Debian.Relation as D-import Debian.Version (DebianVersion, parseDebianVersion, prettyDebianVersion)-import Distribution.Package (PackageName(PackageName))-import Distribution.Package.Debian.Bundled (ghcBuiltIn)-import Distribution.Package.Debian.Interspersed (Interspersed(..))-import Distribution.Package.Debian.Setup (Flags(..))-import Distribution.Simple.Compiler (Compiler(..))-import Distribution.Version (Version(..), VersionRange(..), anyVersion, foldVersionRange', intersectVersionRanges, unionVersionRanges,- laterVersion, orLaterVersion, earlierVersion, orEarlierVersion, fromVersionIntervals, toVersionIntervals, withinVersion,- isNoVersion, asVersionIntervals, mkVersionIntervals, LowerBound(..), UpperBound(..), Bound(..))-import Text.PrettyPrint (Doc, text, hcat , (<>), empty)--data PackageType = Source | Development | Profiling | Documentation | Utilities | Extra deriving (Eq, Show)--data VersionSplits- = VersionSplits {- packageName :: PackageName- , oldestPackage :: BinPkgName- , splits :: [(Version, BinPkgName)] -- Assumed to be in version number order- }--instance Interspersed VersionSplits BinPkgName Version where- leftmost (VersionSplits {splits = []}) = error "Empty Interspersed instance"- leftmost (VersionSplits {oldestPackage = p}) = p- pairs (VersionSplits {splits = xs}) = xs---- | Turn a cabal dependency into debian dependencies. The result--- needs to correspond to a single debian package to be installed,--- so we will return just an OrRelation.-dependencies :: Flags -> Compiler -> (PackageType -> [VersionSplits]) -> PackageType -> Either BinPkgName PackageName -> VersionRange -> Relations-dependencies flags compiler versionSplits typ (Left name) cabalRange = [[D.Rel name Nothing Nothing]]-dependencies flags compiler versionSplits typ (Right name@(PackageName string)) cabalRange =- map doBundled $ convert' (canonical (Or (catMaybes (map convert alts))))- where-- -- Compute a list of alternative debian dependencies for- -- satisfying a cabal dependency. The only caveat is that- -- we may need to distribute any "and" dependencies implied- -- by a version range over these "or" dependences.- alts :: [(BinPkgName, VersionRange)]- alts = case Map.lookup name (packageSplits versionSplits typ) of- -- If there are no splits for this package just return the single dependency for the package- Nothing -> [(mkPkgName string typ, cabalRange')]- -- If there are splits create a list of (debian package name, VersionRange) pairs- Just splits -> packageRangesFromVersionSplits splits-- convert :: (BinPkgName, VersionRange) -> Maybe (Rels Relation)- convert (dname, range) =- if isNoVersion range'''- then Nothing- else Just $- foldVersionRange'- (Rel (D.Rel dname Nothing Nothing))- (\ v -> Rel (D.Rel dname (Just (D.EEQ (dv v))) Nothing))- (\ v -> Rel (D.Rel dname (Just (D.SGR (dv v))) Nothing))- (\ v -> Rel (D.Rel dname (Just (D.SLT (dv v))) Nothing))- (\ v -> Rel (D.Rel dname (Just (D.GRE (dv v))) Nothing))- (\ v -> Rel (D.Rel dname (Just (D.LTE (dv v))) Nothing))- (\ x y -> And [Rel (D.Rel dname (Just (D.GRE (dv x))) Nothing), Rel (D.Rel dname (Just (D.SLT (dv y))) Nothing)])- (\ x y -> Or [x, y])- (\ x y -> And [x, y])- id- range'''- where - -- Choose the simpler of the two- range''' = canon (simpler range' range'')- -- Unrestrict the range for versions that we know don't exist for this debian package- range'' = canon (unionVersionRanges range' (invertVersionRange range))- -- Restrict the range to the versions specified for this debian package- range' = intersectVersionRanges cabalRange' range- -- When we see a cabal equals dependency we need to turn it into- -- a wildcard because the resulting debian version numbers have- -- various suffixes added.- cabalRange' =- foldVersionRange'- anyVersion- withinVersion -- <- Here we are turning equals into wildcard- laterVersion- earlierVersion- orLaterVersion- orEarlierVersion- (\ lb ub -> intersectVersionRanges (orLaterVersion lb) (earlierVersion ub))- unionVersionRanges- intersectVersionRanges- id- cabalRange- -- Convert a cabal version to a debian version, adding an epoch number if requested- dv v = parseDebianVersion (maybe "" (\ n -> show n ++ ":") (Map.lookup name (epochMap flags)) ++ showVersion v)- simpler v1 v2 = minimumBy (compare `on` (length . asVersionIntervals)) [v1, v2]- -- Simplify a VersionRange- canon = fromVersionIntervals . toVersionIntervals-- -- If a package is bundled with the compiler we make the- -- compiler a substitute for that package. If we were to- -- specify the virtual package (e.g. libghc-base-dev) we would- -- have to make sure not to specify a version number.- doBundled :: [D.Relation] -> [D.Relation]- doBundled rels | ghcBuiltIn compiler name = rels ++ [D.Rel (compilerPackageName typ) Nothing Nothing]- doBundled rels = rels-- compilerPackageName Documentation = D.BinPkgName (D.PkgName "ghc-doc")- compilerPackageName Profiling = D.BinPkgName (D.PkgName "ghc-prof")- compilerPackageName Development = D.BinPkgName (D.PkgName "ghc")- compilerPackageName _ = D.BinPkgName (D.PkgName "ghc") -- whatevs--data Rels a = And {unAnd :: [Rels a]} | Or {unOr :: [Rels a]} | Rel {unRel :: a} deriving Show---- | The intent of this class is to be similar to Show, but only one--- way, with no corresponding Read class. To put something in a--- pretty printing class implies that there is only one way to pretty--- print it, which is not an assumption made by Text.PrettyPrint. But--- in practice this is often good enough.-class Pretty x where- pretty :: x -> Doc---- | return and of ors of rel-canonical :: Rels a -> Rels a-canonical (Rel rel) = And [Or [Rel rel]]-canonical (And rels) = And $ concatMap (unAnd . canonical) rels-canonical (Or rels) = And . map Or $ sequence $ map (concat . map unOr . unAnd . canonical) $ rels--convert' :: Rels a -> [[a]]-convert' = map (map unRel . unOr) . unAnd . canonical--packageSplits :: (PackageType -> [VersionSplits]) -> PackageType -> Map.Map PackageName VersionSplits-packageSplits versionSplits typ =- foldr (\ splits mp -> Map.insertWith multipleSplitsError (packageName splits) splits mp)- Map.empty- (versionSplits typ)- where- multipleSplitsError (VersionSplits {packageName = PackageName p}) _s2 =- error ("Multiple splits for package " ++ show p)--packageRangesFromVersionSplits :: VersionSplits -> [(BinPkgName, VersionRange)]-packageRangesFromVersionSplits splits =- foldInverted (\ older dname newer more ->- (dname, intersectVersionRanges (maybe anyVersion orLaterVersion older) (maybe anyVersion earlierVersion newer)) : more)- []- splits---- | Build a debian package name from a cabal package name and a--- debian package type.-mkPkgName :: String -> PackageType -> BinPkgName-mkPkgName base typ =- BinPkgName . PkgName $ prefix typ ++ map toLower base ++ suffix typ- where- suffix Source = ""- suffix Documentation = "-doc"- suffix Development = "-dev"- suffix Profiling = "-prof"- suffix Utilities = "-utils"- suffix Extra = ""-- prefix Source = "haskell-"- prefix Documentation = "libghc-"- prefix Development = "libghc-"- prefix Profiling = "libghc-"- prefix Utilities = "haskell-"- prefix Extra = ""--instance Pretty VersionRange where- pretty range =- foldVersionRange'- (text "*")- (\ v -> text "=" <> pretty v)- (\ v -> text ">" <> pretty v)- (\ v -> text "<" <> pretty v)- (\ v -> text ">=" <> pretty v)- (\ v -> text "<=" <> pretty v)- (\ x _ -> text "=" <> pretty x <> text ".*") -- not exactly right- (\ x y -> text "(" <> x <> text " || " <> y <> text ")")- (\ x y -> text "(" <> x <> text " && " <> y <> text ")")- (\ x -> text "(" <> x <> text ")")- range--instance Pretty Version where- pretty = text . showVersion--instance Pretty a => Pretty [a] where- pretty xs = text "[" <> hcat (intersperse (text ", ") (map pretty xs)) <> text "]"--instance (Pretty a, Pretty b) => Pretty (a, b) where- pretty (a, b) = text "(" <> pretty a <> text ", " <> pretty b <> text ")"--instance Pretty D.BinPkgName where- pretty (D.BinPkgName p) = text "deb:" <> (pretty p)--instance Pretty D.PkgName where- pretty (D.PkgName p) = text p--instance Pretty D.Relation where- pretty (D.Rel name ver arch) =- pretty name <> maybe empty pretty ver <> maybe empty pretty arch--instance Pretty D.VersionReq where- pretty (D.EEQ v) = text "=" <> pretty v- pretty (D.SLT v) = text "<" <> pretty v- pretty (D.LTE v) = text "<=" <> pretty v- pretty (D.GRE v) = text ">=" <> pretty v- pretty (D.SGR v) = text ">" <> pretty v--instance Pretty D.ArchitectureReq where- pretty (D.ArchOnly ss) = text "[" <> hcat (intersperse (text ",") (map text ss)) <> text "]"- pretty (D.ArchExcept ss) = text "[!" <> hcat (intersperse (text ",") (map text ss)) <> text "]"--instance Pretty DebianVersion where- pretty = text . show--instance Show D.Relation where- show = show . pretty-instance Show D.ArchitectureReq where- show = show . pretty--invertVersionRange :: VersionRange -> VersionRange-invertVersionRange = fromVersionIntervals . maybe (error "invertVersionRange") id . mkVersionIntervals . invertVersionIntervals . asVersionIntervals--invertVersionIntervals :: [(LowerBound, UpperBound)] -> [(LowerBound, UpperBound)]-invertVersionIntervals xs =- case xs of- [] -> [(lb0, NoUpperBound)]- ((LowerBound (Version {versionBranch = [0], versionTags = []}) InclusiveBound, ub) : more) ->- invertVersionIntervals' ub more- ((lb, ub) : more) ->- (lb0, invertLowerBound lb) : invertVersionIntervals' ub more- where- invertVersionIntervals' :: UpperBound -> [(LowerBound, UpperBound)] -> [(LowerBound, UpperBound)]- invertVersionIntervals' NoUpperBound [] = []- invertVersionIntervals' ub0 [] = [(invertUpperBound ub0, NoUpperBound)]- invertVersionIntervals' ub0 [(lb, NoUpperBound)] = [(invertUpperBound ub0, invertLowerBound lb)]- invertVersionIntervals' ub0 ((lb, ub1) : more) = (invertUpperBound ub0, invertLowerBound lb) : invertVersionIntervals' ub1 more-- invertLowerBound :: LowerBound -> UpperBound- invertLowerBound (LowerBound v b) = UpperBound v (invertBound b)-- invertUpperBound :: UpperBound -> LowerBound- invertUpperBound (UpperBound v b) = LowerBound v (invertBound b)- invertUpperBound NoUpperBound = error "NoUpperBound: unexpected"-- invertBound :: Bound -> Bound- invertBound ExclusiveBound = InclusiveBound- invertBound InclusiveBound = ExclusiveBound-- lb0 :: LowerBound- lb0 = LowerBound (Version {versionBranch = [0], versionTags = []}) InclusiveBound--deriving instance Show VersionReq-instance Show DebianVersion where- show = show . prettyDebianVersion--debianSourcePackageName :: (PackageType -> [VersionSplits]) -> PackageName -> Maybe VersionReq -> SrcPkgName-debianSourcePackageName versionSplits name version = SrcPkgName (D.unBinPkgName (debianName Source versionSplits name version))--debianDevPackageName :: (PackageType -> [VersionSplits]) -> PackageName -> Maybe VersionReq -> BinPkgName-debianDevPackageName versionSplits name version = debianName Development versionSplits name version--debianProfPackageName :: (PackageType -> [VersionSplits]) -> PackageName -> Maybe VersionReq -> BinPkgName-debianProfPackageName versionSplits name version = debianName Profiling versionSplits name version--debianDocPackageName :: (PackageType -> [VersionSplits]) -> PackageName -> Maybe VersionReq -> BinPkgName-debianDocPackageName versionSplits name version = debianName Documentation versionSplits name version--type DebianBinPackageName = PackageName -> Maybe VersionReq -> BinPkgName--debianExtraPackageName :: (PackageType -> [VersionSplits]) -> PackageName -> Maybe VersionReq -> BinPkgName-debianExtraPackageName versionSplits name version = debianName Extra versionSplits name version--debianUtilsPackageName :: (PackageType -> [VersionSplits]) -> PackageName -> Maybe VersionReq -> BinPkgName-debianUtilsPackageName versionSplits name version = debianName Utilities versionSplits name version---- | Return the basename of the debian package for a given version--- relation. If the version split happens at v, this will return the--- ltName is < v and the geName if the relation is >= v. It also handles--- a special case for the name of the haskell-src-exts package.-debianName :: PackageType -> (PackageType -> [VersionSplits]) -> PackageName -> Maybe VersionReq -> BinPkgName-debianName typ versionSplits pname@(PackageName name) version =- case filter (\ x -> pname == packageName x) (versionSplits typ) of- [] -> def- [splits] ->- foldTriples' (\ ltName v geName debName ->- if pname /= packageName splits- then debName- else let split = parseDebianVersion (showVersion v) in- case version of- Nothing -> geName- Just (SLT v') | v' <= split -> ltName- -- Otherwise use ltName only when the split is below v'- Just (EEQ v') | v' < split -> ltName- Just (LTE v') | v' < split -> ltName- Just (GRE v') | v' < split -> ltName- Just (SGR v') | v' < split -> ltName- _ -> geName)- def- splits- _ -> error $ "Multiple splits for cabal package " ++ name- where- foldTriples' :: (BinPkgName -> Version -> BinPkgName -> BinPkgName -> BinPkgName) -> BinPkgName -> VersionSplits -> BinPkgName- foldTriples' = foldTriples- def = mkPkgName (map fixChar name) typ--fixChar :: Char -> Char-fixChar '_' = '-'-fixChar c = toLower c
− Distribution/Package/Debian/Interspersed.hs
@@ -1,54 +0,0 @@-{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, StandaloneDeriving, TypeSynonymInstances #-}-{-# OPTIONS_GHC -Wall -Werror #-}-module Distribution.Package.Debian.Interspersed- ( Interspersed(..)- ) where--import Debug.Trace---- | A class of Bs insterspersed with Cs. Minimum implementation is a--- method to return the leftmost B, and another to return the--- following (C,B) pairs. Its unfortunate to require lists in the--- implementation, a fold function would be better (though I find--- implementing such folds to be a pain in the you-know-what.)--- --- The class provides implementations of three folds, each of which--- exposes slightly different views of the data.-class Interspersed t around between | t -> around, t -> between where- leftmost :: t -> around- pairs :: t -> [(between, around)]-- foldTriples :: (around -> between -> around -> r -> r) -> r -> t -> r- foldTriples f r0 x = snd $ foldl (\ (b1, r) (c, b2) -> (b2, f b1 c b2 r)) (leftmost x, r0) (pairs x)-- -- Treat the b's as the centers and the c's as the things to their- -- left and right. Use Maybe to make up for the missing c's at the- -- ends.- foldInverted :: (Maybe between -> around -> Maybe between -> r -> r) -> r -> t -> r- foldInverted f r0 x =- (\ (bn, an, r) -> f bn an Nothing r) $- foldl g (Nothing, leftmost x, r0) (pairs x)- where- g (b1, a1, r) (b2, a2) = (Just b2, a2, f b1 a1 (Just b2) r)-- foldArounds :: (around -> around -> r -> r) -> r -> t -> r- foldArounds f r0 x = snd $ foldl (\ (a1, r) (_, a2) -> (a2, f a1 a2 r)) (leftmost x, r0) (pairs x)-- foldBetweens :: (between -> r -> r) -> r -> t -> r- foldBetweens f r0 x = foldl (\ r (b, _) -> (f b r)) r0 (pairs x)---- | An example-data Splits = Splits Double [(String, Double)] deriving Show--instance Interspersed Splits Double String where- leftmost (Splits x _) = x- pairs (Splits _ x) = x--_splits :: Splits-_splits = Splits 1.0 [("between 1 and 2", 2.0), ("between 2 and 3", 3.0)]--_test1 :: ()-_test1 = foldTriples (\ l s r () -> trace ("l=" ++ show l ++ " s=" ++ show s ++ " r=" ++ show r) ()) () _splits--_test2 :: ()-_test2 = foldInverted (\ sl f sr () -> trace ("sl=" ++ show sl ++ " f=" ++ show f ++ " sr=" ++ show sr) ()) () _splits
− Distribution/Package/Debian/Main.hs
@@ -1,28 +0,0 @@--- |--- Module : Distribution.Package.Debian.Main--- Copyright : David Fox 2008------ Maintainer : David Fox <dsf@seereason.com>--- Stability : alpha--- Portability : portable------ Explanation: Main entry point for Debianizer of Cabal packages.---- This software may be used and distributed according to the terms of--- the GNU General Public License, incorporated herein by reference.--module Distribution.Package.Debian.Main where--import Distribution.PackageDescription.Parse (readPackageDescription)-import Distribution.Package.Debian (debian)-import Distribution.Package.Debian.Setup (Flags (..), parseArgs)-import Distribution.Simple.Utils (defaultPackageDesc)-import System.Environment (getArgs)--main :: IO ()--main = do opts <- getArgs >>= parseArgs- let verbosity = rpmVerbosity opts- descPath <- defaultPackageDesc verbosity- pkgDesc <- readPackageDescription verbosity descPath- debian pkgDesc opts
− Distribution/Package/Debian/Relations.hs
@@ -1,135 +0,0 @@-{-# LANGUAGE CPP, FlexibleInstances, MultiParamTypeClasses, PackageImports, ScopedTypeVariables,- StandaloneDeriving, TupleSections, TypeSynonymInstances #-}-{-# OPTIONS -Wall -fno-warn-orphans #-}---- |--- Module : Distribution.Package.Debian--- Copyright : David Fox 2008------ Maintainer : David Fox <dsf@seereason.com>--- Stability : alpha--- Portability : portable------ Explanation: Support for generating Debianization from Cabal data.---- This software may be used and distributed according to the terms of--- the GNU General Public License, incorporated herein by reference.--module Distribution.Package.Debian.Relations- ( allBuildDepends- , buildDependencies- , docDependencies- , cabalDependencies- , versionSplits- ) where--import Data.Char (isSpace)-import Data.List-import qualified Data.Map as Map-import Data.Maybe-import Data.Version (Version(Version))-import qualified Debian.Relation as D-import Distribution.Simple.Compiler (Compiler(..))-import Distribution.Package (PackageName(..), Dependency(..))-import Distribution.PackageDescription (PackageDescription(..),- allBuildInfo, buildTools, pkgconfigDepends,- extraLibs)-import Distribution.Version (anyVersion)-import Distribution.Package.Debian.Setup (Flags(..))-import Distribution.Package.Debian.Dependencies (PackageType(..), VersionSplits(..), dependencies, mkPkgName)-import System.Exit (ExitCode(ExitSuccess))-import System.IO.Unsafe (unsafePerformIO)-import System.Process (readProcessWithExitCode)--cabalDependencies :: Flags -> PackageDescription -> [Dependency]-cabalDependencies flags pkgDesc = catMaybes $ map unboxDependency $ allBuildDepends flags pkgDesc---- |Debian packages don't have per binary package build dependencies,--- so we just gather them all up here.-allBuildDepends :: Flags -> PackageDescription -> [Dependency_]-allBuildDepends flags pkgDesc =- nub $ map BuildDepends (buildDepends pkgDesc) ++- concat (map (map BuildTools . buildTools) (allBuildInfo pkgDesc) ++- map- (map PkgConfigDepends . pkgconfigDepends)- (allBuildInfo pkgDesc) ++- map (map ExtraLibs . (fixDeps . extraLibs)) (allBuildInfo pkgDesc))- where- fixDeps :: [String] -> [D.BinPkgName]- fixDeps xs = concatMap (\ cab -> fromMaybe [D.BinPkgName (D.PkgName ("lib" ++ cab ++ "-dev"))] (Map.lookup cab (depMap flags))) xs---- The build dependencies for a package include the profiling--- libraries and the documentation packages, used for creating cross--- references.-buildDependencies :: Flags -> Compiler -> Dependency_ -> D.Relations-buildDependencies flags compiler (BuildDepends (Dependency name ranges)) =- dependencies flags compiler versionSplits Development (Right name) ranges ++ dependencies flags compiler versionSplits Profiling (Right name) ranges-buildDependencies flags compiler dep@(ExtraLibs _) =- concat (map (\ x -> dependencies flags compiler versionSplits Extra (Left x) anyVersion) $ adapt flags dep)-buildDependencies flags compiler dep =- case unboxDependency dep of- Just (Dependency _name ranges) ->- concat (map (\ x -> dependencies flags compiler versionSplits Extra (Left x) ranges) $ adapt flags dep)- Nothing ->- []--adapt :: Flags -> Dependency_ -> [D.BinPkgName]-adapt flags (PkgConfigDepends (Dependency (PackageName pkg) _)) =- maybe (aptFile pkg) (: []) (Map.lookup pkg (execMap flags))-adapt flags (BuildTools (Dependency (PackageName pkg) _)) =- maybe (aptFile pkg) (: []) (Map.lookup pkg (execMap flags))-adapt _flags (ExtraLibs x) = [x]-{-- maybe (error ("No mapping from library " ++ x ++ " to debian binary package name"))- (map (\ s -> PackageName ("lib" ++ s ++ "-dev"))) (Map.lookup x (depMap flags))--}-adapt _flags (BuildDepends (Dependency (PackageName pkg) _)) = [D.BinPkgName (D.PkgName pkg)]---- |There are two reasons this may not work, or may work--- incorrectly: (1) the build environment may be a different--- distribution than the parent environment (the environment the--- autobuilder was run from), so the packages in that--- environment might have different names, and (2) the package--- we are looking for may not be installed in the parent--- environment.-aptFile :: String -> [D.BinPkgName] -- Maybe would probably be more correct-aptFile pkg =- unsafePerformIO $- do ret <- readProcessWithExitCode "apt-file" ["-l", "search", pkg ++ ".pc"] ""- return $ case ret of- (ExitSuccess, out, _) -> [D.BinPkgName (D.PkgName (takeWhile (not . isSpace) out))]- _ -> []---- The documentation dependencies for a package include the documentation--- package for any libraries which are build dependencies, so we have access--- to all the cross references.-docDependencies :: Flags -> Compiler -> Dependency_ -> D.Relations-docDependencies flags compiler (BuildDepends (Dependency name ranges)) =- dependencies flags compiler versionSplits Documentation (Right name) ranges-docDependencies _ _ _ = []--data Dependency_- = BuildDepends Dependency- | BuildTools Dependency- | PkgConfigDepends Dependency- | ExtraLibs D.BinPkgName- deriving (Eq, Show)--unboxDependency :: Dependency_ -> Maybe Dependency-unboxDependency (BuildDepends d) = Just d-unboxDependency (BuildTools d) = Just d-unboxDependency (PkgConfigDepends d) = Just d-unboxDependency (ExtraLibs _) = Nothing -- Dependency (PackageName d) anyVersion---- | These are the instances of debian names changing that we know about.-versionSplits :: PackageType -> [VersionSplits]-versionSplits typ =- [ VersionSplits {- packageName = PackageName "parsec"- , oldestPackage = mkPkgName "parsec2" typ- , splits = [(Version [3] [], mkPkgName "parsec3" typ)] }- , VersionSplits {- packageName = PackageName "QuickCheck"- , oldestPackage = mkPkgName "quickcheck1" typ- , splits = [(Version [2] [], mkPkgName "quickcheck2" typ)] }- ]
− Distribution/Package/Debian/Setup.hs
@@ -1,220 +0,0 @@--- |--- Module : Distribution.Package.Debian.Setup--- Copyright : David Fox 2008------ Maintainer : David Fox <dsf@seereason.com>--- Stability : alpha--- Portability : portable------ Explanation: Command line option processing for building RPM--- packages.---- This software may be used and distributed according to the terms of--- the GNU General Public License, incorporated herein by reference.--module Distribution.Package.Debian.Setup (- Flags(..)- , DebAction(..)- , DebType(..)- , parseArgs- ) where--import Control.Monad (when)-import Data.Char (toLower, isDigit, ord)-import qualified Data.Map as Map-import Data.Version (Version, parseVersion)-import Debian.Relation (PkgName(..), BinPkgName(..))-import Distribution.Compiler (CompilerFlavor(..))-import Distribution.ReadE (readEOrFail)-import Distribution.PackageDescription (FlagName(..))-import Distribution.Package (PackageName(..))-import Distribution.Verbosity (Verbosity, flagToVerbosity, normal)-import System.Console.GetOpt (ArgDescr (..), ArgOrder (..), OptDescr (..),- usageInfo, getOpt')-import System.Environment (getProgName)-import System.Exit (exitWith, ExitCode (..))-import System.IO (Handle, hPutStrLn, stderr, stdout)-import Text.ParserCombinators.ReadP (readP_to_S)--data Flags = Flags- {- rpmPrefix :: FilePath- , rpmCompiler :: CompilerFlavor- , rpmCompilerVersion :: Maybe Version- , rpmConfigurationsFlags :: [(FlagName, Bool)]- , rpmHaddock :: Bool- , rpmHelp :: Bool- , debLibProf :: Bool- , rpmName :: Maybe String- , rpmOptimisation :: Bool- , rpmRelease :: Maybe String- , rpmSplitObjs :: Bool- , debOutputDir :: FilePath- , buildRoot :: FilePath- , rpmVerbosity :: Verbosity- , rpmVersion :: Maybe String- , debMaintainer :: Maybe String- , debAction :: DebAction- , buildDeps :: [String]- , extraDevDeps :: [String]- -- , debName :: Maybe String- , debVersion :: Maybe String- , depMap :: Map.Map String [BinPkgName]- , epochMap :: Map.Map PackageName Int- , revision :: String- , execMap :: Map.Map String BinPkgName- , omitLTDeps :: Bool- , sourceFormat :: String- }- deriving (Eq, Show)--data DebType = Dev| Prof | Doc deriving (Eq, Read, Show)--data DebAction = Usage | Debianize | SubstVar DebType | UpdateDebianization deriving (Eq, Show)--emptyFlags :: Flags--emptyFlags = Flags- {- rpmPrefix = "/usr/lib/haskell-packages/ghc6"- , rpmCompiler = GHC- , rpmCompilerVersion = Nothing- , rpmConfigurationsFlags = []- , rpmHaddock = True- , rpmHelp = False- , debLibProf = True- , rpmName = Nothing- , rpmOptimisation = True- , rpmRelease = Nothing- , rpmSplitObjs = True- , debOutputDir = "./debian"- , buildRoot = "/"- , rpmVerbosity = normal- , rpmVersion = Nothing- , debMaintainer = Nothing- , debAction = Usage- , buildDeps = []- , extraDevDeps = []- , depMap = Map.empty- , epochMap = Map.empty- -- , debName = Nothing- , debVersion = Nothing- , revision = "-1~hackage1"- , execMap = Map.empty- , omitLTDeps = False- , sourceFormat = "3.0 (native)"- }--options :: [OptDescr (Flags -> Flags)]--options =- [- Option "" ["prefix"] (ReqArg (\ path x -> x { rpmPrefix = path }) "PATH")- "Pass this prefix if we need to configure the package",- Option "" ["ghc"] (NoArg (\x -> x { rpmCompiler = GHC }))- "Compile with GHC",- Option "" ["hugs"] (NoArg (\x -> x { rpmCompiler = Hugs }))- "Compile with Hugs",- Option "" ["jhc"] (NoArg (\x -> x { rpmCompiler = JHC }))- "Compile with JHC",- Option "" ["nhc"] (NoArg (\x -> x { rpmCompiler = NHC }))- "Compile with NHC",- Option "h?" ["help"] (NoArg (\x -> x { rpmHelp = True }))- "Show this help text",- Option "" ["ghc-version"] (ReqArg (\ ver x -> x { rpmCompilerVersion = Just (last (map fst (readP_to_S parseVersion ver)))}) "VERSION")- "Version of GHC in build environment",- Option "" ["name"] (ReqArg (\name x -> x { rpmName = Just name }) "NAME")- "Override the default package name",- Option "" ["disable-haddock"] (NoArg (\x -> x { rpmHaddock = False }))- "Don't generate API docs",- Option "" ["disable-library-profiling"] (NoArg (\x -> x { debLibProf = False }))- "Don't generate profiling libraries",- Option "" ["disable-optimization"] (NoArg (\x -> x { rpmOptimisation = False }))- "Don't generate optimised code",- Option "" ["disable-split-objs"] (NoArg (\x -> x { rpmSplitObjs = False }))- "Don't split object files to save space",- Option "f" ["flags"] (ReqArg (\flags x -> x { rpmConfigurationsFlags = rpmConfigurationsFlags x ++ flagList flags }) "FLAGS")- "Set given flags in Cabal conditionals",- Option "" ["release"] (ReqArg (\rel x -> x { rpmRelease = Just rel }) "RELEASE")- "Override the default package release",- Option "" ["debdir"] (ReqArg (\path x -> x { debOutputDir = path }) "DEBDIR")- ("Override the default output directory (" ++ show (debOutputDir emptyFlags) ++ ")"),- Option "" ["root"] (ReqArg (\ path x -> x { buildRoot = path }) "BUILDROOT")- "Use the compiler information in the given build environment.",- Option "v" ["verbose"] (ReqArg (\verb x -> x { rpmVerbosity = readEOrFail flagToVerbosity verb }) "n")- "Change build verbosity",- Option "" ["version"] (ReqArg (\vers x -> x { rpmVersion = Just vers }) "VERSION")- "Override the default package version",- Option "" ["maintainer"] (ReqArg (\maint x -> x { debMaintainer = Just maint }) "Maintainer Name <email addr>")- "Override the Maintainer name and email in $DEBEMAIL/$EMAIL/$DEBFULLNAME/$FULLNAME",- Option "" ["debianize"] (NoArg (\x -> x {debAction = Debianize}))- "Generate a new debianization, replacing any existing one. One of --debianize, --substvar, or --update-debianization is required.",- Option "" ["build-dep"] (ReqArg (\ name x -> x {buildDeps = name : (buildDeps x)}) "Debian binary package name")- "Specify a package to add to the build dependency list in debian/control, e.g. '--build-dep libglib2.0-dev'.",- Option "" ["dev-dep"] (ReqArg (\ name x -> x {extraDevDeps = name : (extraDevDeps x)}) "Debian binary package name")- "Specify a package to add to the Depends: list of the -dev package, e.g. '--build-dep libssl-dev'.",- Option "" ["map-dep"] (ReqArg (\ pair x -> x {depMap = case break (== '=') pair of- (cab, (_ : deb)) -> Map.insertWith (++) cab [BinPkgName (PkgName deb)] (depMap x)- (_, "") -> error "usage: --dep-map CABALNAME=DEBIANNAME"}) "CABALNAME=DEBIANNAME")- "Specify a mapping from the name appearing in the Extra-Library field of the cabal file to a debian binary package name, e.g. --dep-map cryptopp=libcrypto-dev",- -- Option "" ["deb-name"] (ReqArg (\ name x -> x {debName = Just name}) "NAME")- -- "Specify the base name of the debian package, the part between 'libghc-' and '-dev'. Normally this is the downcased cabal name.",- Option "" ["deb-version"] (ReqArg (\ version x -> x {debVersion = Just version}) "VERSION")- "Specify the version number for the debian package. This will pin the version and should be considered dangerous.",- Option "" ["revision"] (ReqArg (\ rev x -> x {revision = rev}) "REVISION")- "Add this string to the cabal version to get the debian version number. By default this is '-1~hackage1'. Debian policy says this must either be empty (--revision '') or begin with a dash.",- Option "" ["epoch-map"] (ReqArg (\ pair x -> x {epochMap =- case break (== '=') pair of- (_, (_ : ['0'])) -> epochMap x- (cab, (_ : [d])) | isDigit d -> Map.insert (PackageName cab) (ord d - ord '0') (epochMap x)- _ -> error "usage: --epoch-map CABALNAME=DIGIT"}) "CABALNAME=DIGIT")- "Specify a mapping from the cabal package name to a digit to use as the debian package epoch number, e.g. --epoch-map HTTP=1",- Option "" ["substvar"] (ReqArg (\ name x -> x {debAction = SubstVar (read name)}) "Doc, Prof, or Dev")- (unlines ["Write out the list of dependencies required for the dev, prof or doc package depending",- "on the argument. This value can be added to the appropriate substvars file."]),- Option "" ["update-debianization"] (NoArg (\x -> x {debAction = UpdateDebianization}))- "Update an existing debianization, or generate a new one.",- Option "" ["exec-map"] (ReqArg (\ s x -> x {execMap = case break (== '=') s of- (cab, (_ : deb)) -> Map.insert cab (BinPkgName (PkgName deb)) (execMap x)- _ -> error "usage: --exec-map CABALNAME=DEBNAME"}) "EXECNAME=DEBIANNAME")- "Specify a mapping from the name appearing in the Build-Tool field of the cabal file to a debian binary package name, e.g. --exec-map trhsx=haskell-hsx-utils",- Option "" ["omit-lt-deps"] (NoArg (\x -> x { omitLTDeps = True }))- "Don't generate the << dependency when we see a cabal equals dependency.",- Option "" ["quilt"] (NoArg (\ x -> x {sourceFormat = "3.0 (quilt)"}))- "The package has an upstream tarball, write '3.0 (quilt)' into source/format."- ]---- Lifted from Distribution.Simple.Setup, since it's not exported.-flagList :: String -> [(FlagName, Bool)]-flagList = map tagWithValue . words- where tagWithValue ('-':name) = (FlagName (map toLower name), False)- tagWithValue name = (FlagName (map toLower name), True)--printHelp :: Handle -> IO ()--printHelp h = do- progName <- getProgName- let info = "Usage: " ++ progName ++ " [FLAGS]\n"- hPutStrLn h (usageInfo info options)--parseArgs :: [String] -> IO Flags--parseArgs args = do- let (os, args', unknown, errs) = getOpt' RequireOrder options args- opts = foldl (flip ($)) emptyFlags os- when (rpmHelp opts || debAction opts == Usage) $ do- printHelp stdout- exitWith ExitSuccess- when (not (null errs)) $ do- hPutStrLn stderr "Errors:"- mapM_ (hPutStrLn stderr) errs- exitWith (ExitFailure 1)- when (not (null unknown)) $ do- hPutStrLn stderr "Unrecognised options:"- mapM_ (hPutStrLn stderr) unknown- exitWith (ExitFailure 1)- when (not (null args')) $ do- hPutStrLn stderr "Unrecognised arguments:"- mapM_ (hPutStrLn stderr) args'- exitWith (ExitFailure 1)- return opts
Setup.hs view
@@ -1,6 +1,22 @@ #!/usr/bin/runhaskell import Distribution.Simple+import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(buildDir)) import Distribution.Simple.Program+import System.Cmd+import System.Exit -main = defaultMainWithHooks simpleUserHooks+main = defaultMainWithHooks simpleUserHooks {+ postBuild =+ \ _ _ _ lbi ->+ case buildDir lbi of+ "dist-ghc/build" -> return ()+ _ -> runTestScript lbi+ , runTests = \ _ _ _ lbi -> runTestScript lbi+ }++runTestScript lbi =+ system (buildDir lbi ++ "/cabal-debian-tests/cabal-debian-tests") >>= \ code ->+ if code == ExitSuccess then return () else error "unit test failure"++
cabal-debian.cabal view
@@ -1,5 +1,5 @@ Name: cabal-debian-Version: 1.25+Version: 3.0.3 License: BSD3 License-File: debian/copyright Author: David Fox <dsf@seereason.com>@@ -8,42 +8,85 @@ Homepage: http://src.seereason.com/cabal-debian Build-Type: Simple Synopsis: Create a debianization for a cabal package-Cabal-Version: >= 1.6-Description:- A program which creates a debian subdirectory containing the required- files to build a deb.-Data-Files:- debian/cabal-debian.1,- debian/cabal-debian.manpages,+Cabal-Version: >= 1.8+Extra-Source-Files: debian/changelog,- debian/compat,- debian/control,- debian/copyright,- debian/rules+ debian/Debianize.hs+Description:+ Tool for creating debianizations of Haskell packages based on the .cabal+ file. If apt-file is installed it will use it to discover what is the+ debian package name of a C library. Source-Repository head type: darcs- location: http://src.seereason.com/cabal-debian+ location: http://src.seereason.com/debian-tools -Flag cabal19- Description: True if Cabal >= 1.9 is available+Library+ Hs-Source-Dirs: src+ GHC-Options: -threaded -Wall -O2+ Build-Depends:+ ansi-wl-pprint,+ applicative-extras,+ base < 5,+ bytestring,+ Cabal >= 1.9,+ containers,+ data-lens,+ debian >= 3.70,+ Diff,+ directory,+ filepath,+ hsemail,+ HUnit,+ mtl,+ network,+ parsec >= 3,+ process,+ pureMD5,+ regex-tdfa,+ syb,+ text,+ unix,+ Unixutils >= 1.50,+ utf8-string+ Exposed-Modules:+ Data.Algorithm.Diff.Context+ Data.Algorithm.Diff.Pretty+ Debian.Debianize+ Debian.Debianize.Atoms+ Debian.Debianize.Bundled+ Debian.Debianize.ControlFile+ Debian.Debianize.Debianize+ Debian.Debianize.Dependencies+ Debian.Debianize.Files+ Debian.Debianize.Finalize+ Debian.Debianize.Generic+ Debian.Debianize.Goodies+ Debian.Debianize.Interspersed+ Debian.Debianize.Input+ Debian.Debianize.Options+ Debian.Debianize.SubstVars+ Debian.Debianize.Tests+ Debian.Debianize.Types+ Debian.Debianize.Utility+ Debian.Orphans+ Debian.Policy+ Distribution.Version.Invert+ Triplets Executable cabal-debian- Main-is: CabalDebian.hs- ghc-options: -threaded -W- Build-Depends: base < 5, bytestring, Cabal >= 1.6.0.1, containers, debian >= 3.63, directory, filepath, mtl, parsec >= 3, pretty, process, regex-tdfa, unix, Unixutils >= 1.50, utf8-string- Extensions: ExistentialQuantification CPP- if flag(cabal19)- build-depends: Cabal >= 1.9- cpp-options: -DCABAL19- else- build-depends: Cabal >= 1.8- cpp-options: -DCABAL18- Other-Modules:- Distribution.Package.Debian,- Distribution.Package.Debian.Bundled,- Distribution.Package.Debian.Dependencies,- Distribution.Package.Debian.Interspersed,- Distribution.Package.Debian.Main,- Distribution.Package.Debian.Relations,- Distribution.Package.Debian.Setup+ Main-is: src/CabalDebian.hs+ ghc-options: -threaded -Wall -O2+ Build-Depends:+ base,+ cabal-debian,+ containers++Executable cabal-debian-tests+ Main-Is: src/Tests.hs+ GHC-Options: -Wall -O2 -threaded -rtsopts+ Build-Depends:+ base,+ cabal-debian,+ Diff,+ HUnit
+ debian/Debianize.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE OverloadedStrings #-}+import Debian.Debianize+import Data.Lens.Lazy+import Debian.Changes (ChangeLog(..), ChangeLogEntry(..))+import Debian.Relation (BinPkgName(BinPkgName), Relation(Rel), VersionReq(SLT))+import Debian.Version (parseDebianVersion)+import Data.Map as Map (insertWith)+import Data.Maybe (fromMaybe)+import Data.Set as Set (insert, union, singleton)+import Data.Text as Text (intercalate)++main :: IO ()+main =+ do new <- debianization "."+ (modL control (\ y -> y {homepage = Just "http://src.seereason.com/cabal-debian"}) $+ setL changelog (Just log) $+ setL compat (Just 7) $+ setL standards (Just (StandardsVersion 3 9 3 Nothing)) $+ setL sourceFormat (Just Native3) $+ setL utilsPackageName (Just (BinPkgName "cabal-debian")) $+ modL depends (Map.insertWith union (BinPkgName "cabal-debian") (singleton (Rel (BinPkgName "apt-file") Nothing Nothing))) $+ modL conflicts (Map.insertWith union (BinPkgName "cabal-debian")+ (singleton (Rel (BinPkgName "haskell-debian-utils") (Just (SLT (parseDebianVersion ("3.59" :: String)))) Nothing))) $+ modL description (Map.insertWith (error "test7") (BinPkgName "cabal-debian")+ (Text.intercalate "\n"+ [ "Create a debianization for a cabal package"+ , " Tool for creating debianizations of Haskell packages based on the .cabal"+ , " file. If apt-file is installed it will use it to discover what is the"+ , " debian package name of a C library."+ , " ."+ , " Author: David Fox <dsf@seereason.com>"+ , " Upstream-Maintainer: David Fox <dsf@seereason.com>" ])) $+ defaultAtoms)+ inputDebianization "." defaultAtoms >>= \ old -> case compareDebianization old (copyFirstLogEntry old new) of+ "" -> return ()+ s -> error $ "Debianization mismatch:\n" ++ s+ -- This would overwrite the existing debianization rather than+ -- just make sure it matches:+ -- writeDebianization "." new++-- | This copies the first log entry of deb1 into deb2. Because the+-- debianization process updates that log entry, we need to undo that+-- update in order to get a clean comparison.+copyFirstLogEntry :: Atoms -> Atoms -> Atoms+copyFirstLogEntry deb1 deb2 =+ modL changelog (const (Just (ChangeLog (hd1 : tl2)))) deb2+ where+ ChangeLog (hd1 : _) = fromMaybe (error "Missing debian/changelog") (getL changelog deb1)+ ChangeLog (_ : tl2) = fromMaybe (error "Missing debian/changelog") (getL changelog deb2)
− debian/cabal-debian.1
@@ -1,120 +0,0 @@-.\"Original page generated by txt2man and then hacked extensively.-.TH CABAL-DEBIAN 1 "May 10, 2010"--.SH NAME-cabal-debian \- create Debian package meta data from a Haskell cabal file.--.SH SYNOPSIS-.B cabal-debian-.RI [flags]--.SH DESCRIPTION-cabal-debian will generated the Debian meta data for a Debian package from the-cabal file of a Haskell package. The program expects to find the package's-<pkgname>.cabal file in the directory from which it was run.--.TP-.B-\fB--prefix=PATH\fP-Pass this prefix if we need to configure the package--.TP-.B-\fB--ghc\fP-Compile with GHC.--.TP-.B-\fB--hugs\fP-Compile with HUGS.--.TP-.B-\fB--jhc\fP-Compile with JHC.--.TP-.B-\fB--nhc\fP-Compile with NHC.--.TP-.B-\fB-h, -?, \-\-help\fP-Show this help text.--.TP-.B-\fB--name\fP=NAME-Override the default package name.-.TP-.B-\fB--disable-haddock\fP-Don't generate API docs.-.TP-.B-\fB--disable-library-profiling\fP-Don't generate profiling libraries.-.TP-.B-\fB--disable-optimization\fP-Don't generate optimised code.--.TP-.B-\fB--disable-split-objs\fP-Don't split object files to save space.--.TP-.B-\fB-f\fP FLAGS, \fB--flags\fP=FLAGS-Set given flags in Cabal conditionals.--.TP-.B-\fB--release\fP=RELEASE-Override the default package release.--.TP-.B-\fB--debdir\fP=DEBDIR-Override the default output directory ("./debian").--.TP-.B-\fB-v n,q \fB--verbose=n\fP-Change build verbosity.--.TP-.B-\fB--version\fP=VERSION-Override the default package version.--.TP-.B-\fB--maintainer\fP=Maintainer Name <email addr>-Override the Maintainer name and email in $DEBEMAIL/$EMAIL/$DEBFULLNAME/$FULLNAME.--.TP-.B-\fB--debianize\fP-Generate a new debianization, replacing any existing one. One of-\fB--debianize\fP, \fB--substvar\fP, or \fB--update-debianization\fP is-required.--.TP-.B-\fB--substvar\fP=Doc, Prof, or Dev-Write out the list of dependencies required for the dev, prof or doc package-depending on the argument. This value can be added to the appropriate substvars-file.--.TP-.B-\fB--update-debianization\fP-Update an existing debianization.--.SH AUTHOR-This manual page was originally written by Erik de Castro Lopo -<erikd@mega-nerd.com> for the Debian GNU/Linux system (but may be used by -others).
− debian/cabal-debian.manpages
@@ -1,1 +0,0 @@-debian/*.1
debian/changelog view
@@ -1,3 +1,281 @@+haskell-cabal-debian (3.0.3) unstable; urgency=low++ * Due to a typo, the noDocumentationLibrary lens was turning off+ profiling rather than documentation.++ -- David Fox <dsf@seereason.com> Fri, 08 Feb 2013 17:14:09 -0800++haskell-cabal-debian (3.0.2) unstable; urgency=low++ * Fix argument and exception handling in cabal-debian+ * Make Standards-Version field non-mandatory+ * Make sure every binary deb paragraph has a non-empty description++ -- David Fox <dsf@seereason.com> Thu, 07 Feb 2013 10:03:25 -0800++haskell-cabal-debian (3.0.1) unstable; urgency=low++ * Don't build Debian version numbers with revision (Just "").+ * Output the descriptions of the binary packages.++ -- David Fox <dsf@seereason.com> Tue, 05 Feb 2013 14:48:33 -0800++haskell-cabal-debian (3.0) unstable; urgency=low++ * Moved the Distribution.Debian modules to Debian.Cabal and+ Debian.Debianize.+ * Refactored the debianize function for easier testing+ * Added test cases.+ * Add a Debianization type that intends to fully describe a debian+ package, with functions to read, build, modify, and write a+ Debianization.++ -- David Fox <dsf@seereason.com> Wed, 26 Dec 2012 05:45:35 -0800++haskell-cabal-debian (2.6.3) unstable; urgency=low++ * Fix pretty printing of Relations (i.e. dependency lists.) There+ is an instance for printing lists in ansi-wl-pprint which prevents+ us from writing customized Pretty instances for type aliases like+ Relations, AndRelation, and OrRelation.++ -- David Fox <dsf@seereason.com> Fri, 04 Jan 2013 09:30:48 -0800++haskell-cabal-debian (2.6.2) unstable; urgency=low++ * Fix a bug constructing the destination pathnames that was dropping+ files that were supposed to be installed into packages.++ -- David Fox <dsf@seereason.com> Thu, 20 Dec 2012 06:49:25 -0800++haskell-cabal-debian (2.6.1) unstable; urgency=low++ * Remove the modifyAtoms field from the Flags record, we want to+ be able to create instances like Read and Show for this type.+ The modifyAtoms function is now passed separately to debianize.+ * The flags field of Server was renamed serverFlags because the+ newly exported Config record has a flags field.++ -- David Fox <dsf@seereason.com> Wed, 19 Dec 2012 09:45:22 -0800++haskell-cabal-debian (2.5.10) unstable; urgency=low++ * Filter cabal self dependencies out before generating+ Build-Depends-Indep, just as we added code to filter them out+ of Build-Depends in version 2.5.7.++ -- David Fox <dsf@seereason.com> Tue, 18 Dec 2012 13:23:39 -0800++haskell-cabal-debian (2.5.9) unstable; urgency=low++ * Always add +RTS -IO -RTS to server flags.++ -- David Fox <dsf@seereason.com> Sun, 16 Dec 2012 10:40:52 -0800++haskell-cabal-debian (2.5.8) unstable; urgency=low++ * Add a builtin list for ghc-7.6.1.++ -- David Fox <dsf@seereason.com> Sat, 15 Dec 2012 07:04:49 -0800++haskell-cabal-debian (2.5.7) unstable; urgency=low++ * Filter out cabal self-dependencies before building the debian+ dependencies. In cabal a self dependency means you need the library+ to build an executable, while in debian it means you need an older+ version installed to build the current version.++ -- David Fox <dsf@seereason.com> Thu, 29 Nov 2012 08:42:30 -0800++haskell-cabal-debian (2.5.6) unstable; urgency=low++ * Don't add --base-uri and --http-port arguments automatically, they can be+ computed by calling the oldClckwrksFlags function and adding the value to+ the flags field. Clckwrks-0.3 no longer needs the --base-uri argument.++ -- David Fox <dsf@seereason.com> Tue, 27 Nov 2012 13:34:31 -0800++haskell-cabal-debian (2.5.5) unstable; urgency=low++ * Have the debianize function return False if there is no debian/Debianize.hs file,+ but throw an exception if running it failed, so we notice bad debianization code.++ -- David Fox <dsf@seereason.com> Tue, 27 Nov 2012 07:34:51 -0800++haskell-cabal-debian (2.5.4) unstable; urgency=low++ * Insert "SetEnv proxy-sendcl 1" line into Apache config.++ -- David Fox <dsf@seereason.com> Tue, 20 Nov 2012 13:43:54 -0800++haskell-cabal-debian (2.5.3) unstable; urgency=low++ * Remove extra copy of binary from the executable debs+ * Add a sourcePackageName field to Flags, and a --source-package-name+ command line option.++ -- David Fox <dsf@seereason.com> Sat, 17 Nov 2012 00:16:21 -0800++haskell-cabal-debian (2.5.2) unstable; urgency=low++ * Fix the path to where the DHInstallTo and DHInstallCabalExecTo+ DebAtoms put their files.++ -- David Fox <dsf@seereason.com> Fri, 16 Nov 2012 18:11:45 -0800++haskell-cabal-debian (2.5.1) unstable; urgency=low++ * Add a destName field to Executable so we can give installed+ executables a different name than they had in the build.++ -- David Fox <dsf@seereason.com> Fri, 16 Nov 2012 15:37:16 -0800++haskell-cabal-debian (2.5) unstable; urgency=low++ * Add a debName field to the Executable record, before the deb+ package name had to equal the executable name.++ -- David Fox <dsf@seereason.com> Fri, 16 Nov 2012 12:32:39 -0800++haskell-cabal-debian (2.4.2) unstable; urgency=low++ * Move location of cabal install files from dist/build/install to+ debian/cabalInstall, the dist directory was getting wiped at bad+ moments.+ * Split the autobuilder function autobuilderDebianize into two+ new functions in cabal-debian: runDebianize and callDebianize.+ * Custom debianization code now goes in debian/Debianize.hs rather than+ in setup, so we can distinguish it failing from it not existing more+ easily.++ -- David Fox <dsf@seereason.com> Thu, 15 Nov 2012 11:00:08 -0800++haskell-cabal-debian (2.4.1) unstable; urgency=low++ * We need to verify that debian/compat was created after running the+ debianize function, because ghc still exits with ExitSuccess++ -- David Fox <dsf@seereason.com> Thu, 15 Nov 2012 06:34:02 -0800++haskell-cabal-debian (2.4.0) unstable; urgency=low++ * You can run a function in Setup.hs other than main using ghc -e, so we+ will use this trick to run the debianize function directly rather than+ running main.+ * Eliminate the autobuilderDebianize function.++ -- David Fox <dsf@seereason.com> Thu, 15 Nov 2012 04:05:49 -0800++haskell-cabal-debian (2.3.4) unstable; urgency=low++ * Fix the builddir used when running the cabal-debian standalone+ executable - it was dist-cabal/build, so the resulting debianization+ had files in places where cabal didn't expect them.++ -- David Fox <dsf@seereason.com> Tue, 13 Nov 2012 06:20:51 -0800++haskell-cabal-debian (2.3.3) unstable; urgency=low++ * Eliminate class MonadBuild and the BuildT monad.++ -- David Fox <dsf@seereason.com> Sun, 11 Nov 2012 17:46:31 -0800++haskell-cabal-debian (2.3.2) unstable; urgency=low++ * Fix exception that was keeping changelogs from being preserved.++ -- David Fox <dsf@seereason.com> Sat, 10 Nov 2012 10:07:50 -0800++haskell-cabal-debian (2.3.1) unstable; urgency=low++ * Fix the extension of the debhelper links files+ * Add a general mechanism for installing a file into a deb when+ we have the file's text in a String (rather than in a file.)++ -- David Fox <dsf@seereason.com> Sat, 10 Nov 2012 07:35:09 -0800++haskell-cabal-debian (2.3) unstable; urgency=low++ * Add MonadBuild.++ -- David Fox <dsf@seereason.com> Fri, 09 Nov 2012 12:21:14 -0800++haskell-cabal-debian (2.2.1) unstable; urgency=low++ * Add a modifyAtoms function to Flags that is applied to final list of+ DebAtom before writing the debianization.+ * Add DHApacheSite and DHInstallCabalExec atoms so atoms don't depend on+ the build directory+ * Add #DEBHELPER# and exit 0 to default web server postinst.++ -- David Fox <dsf@seereason.com> Fri, 09 Nov 2012 10:25:32 -0800++haskell-cabal-debian (2.2.0) unstable; urgency=low++ * Append a trailing slash to the --base-uri argument passed to the+ server. This is required by Web.Routes.Site.runSite.++ -- David Fox <dsf@seereason.com> Thu, 08 Nov 2012 04:40:08 -0800++haskell-cabal-debian (2.1.4) unstable; urgency=low++ * Merge the Executable and Script constructors of the Executable type+ * Add a destDir field to Executable to specify the destination.++ -- David Fox <dsf@seereason.com> Tue, 06 Nov 2012 13:24:25 -0800++haskell-cabal-debian (2.1.3) unstable; urgency=low++ * Don't append a slash to the base-uri.+ * Construct the name of the data directory in /usr/share from the cabal+ package name rather than the debian source package name.+ * Add a --self-depend flag to include a build dependency on this library+ in all generated debianizations.++ -- David Fox <dsf@seereason.com> Tue, 06 Nov 2012 07:07:57 -0800++haskell-cabal-debian (2.1.2) unstable; urgency=low++ * Output the server support files.++ -- David Fox <dsf@seereason.com> Tue, 06 Nov 2012 06:37:18 -0800++haskell-cabal-debian (2.1.1) unstable; urgency=low++ * Restore code that checks for version number match when validating+ a debianization. The autobuilder can now pass the version number+ to cabal-debian, so it should match.++ -- David Fox <dsf@seereason.com> Mon, 05 Nov 2012 17:42:32 -0800++haskell-cabal-debian (2.1.0) unstable; urgency=low++ * Enable processing of Script, Server and WebSite executables.++ -- David Fox <dsf@seereason.com> Mon, 05 Nov 2012 12:45:42 -0800++haskell-cabal-debian (2.0.9) unstable; urgency=low++ * Add a Library section, export all the modules.++ -- David Fox <dsf@seereason.com> Mon, 05 Nov 2012 06:41:25 -0800++haskell-cabal-debian (2.0.8) unstable; urgency=low++ * Bypass abandoned versions.++ -- David Fox <dsf@seereason.com> Sat, 03 Nov 2012 06:13:27 -0700++haskell-cabal-debian (1.26) unstable; urgency=low++ * Don't try to update the existing debianization, except for the+ changelog where we retain entries that look older than the one+ we generate.+ * Use .install files instead of adding rules to debian/rules+ * Add --depends and --conflicts options++ -- David Fox <dsf@seereason.com> Thu, 25 Oct 2012 12:03:49 -0700+ haskell-cabal-debian (1.25) unstable; urgency=low * If the --disable-haddock flag is given omit the doc package from the
− debian/compat
@@ -1,1 +0,0 @@-7
− debian/control
@@ -1,40 +0,0 @@-Source: haskell-cabal-debian-Priority: extra-Section: haskell-Maintainer: David Fox <dsf@seereason.com>-Build-Depends: debhelper (>= 7.0),- haskell-devscripts (>= 0.8),- cdbs,- ghc,- ghc-prof,- libghc-unixutils-dev (>= 1.50),- libghc-unixutils-prof (>= 1.50),- libghc-debian-dev (>= 3.63),- libghc-debian-prof (>= 3.63),- libghc-mtl-dev,- libghc-mtl-prof,- libghc-parsec3-dev (>= 3),- libghc-parsec3-prof (>= 3),- libghc-regex-tdfa-dev,- libghc-regex-tdfa-prof,- libghc-utf8-string-dev,- libghc-utf8-string-prof-Build-Depends-Indep: ghc-doc,- libghc-unixutils-doc (>= 1.50),- libghc-debian-doc (>= 3.63),- libghc-mtl-doc,- libghc-parsec3-doc (>= 3),- libghc-regex-tdfa-doc,- libghc-utf8-string-doc-Standards-Version: 3.9.1-Homepage: http://src.seereason.com/cabal-debian--Package: cabal-debian-Architecture: any-Section: misc-Depends: ${shlibs:Depends}, ${haskell:Depends}, ${misc:Depends}, apt-file-Conflicts: haskell-debian-utils (<< 3.59)-Description: Create a debianization for a cabal package- Tool for creating debianizations of Haskell packages based on the .cabal- file. If apt-file is installed it will use it to discover what is the- debian package name of a C library.
− debian/rules
@@ -1,14 +0,0 @@-#!/usr/bin/make -f--DEB_CABAL_PACKAGE = cabal-debian--include /usr/share/cdbs/1/rules/debhelper.mk-include /usr/share/cdbs/1/class/hlibrary.mk--build/cabal-debian:: build-ghc-stamp-binary-fixup/cabal-debian::- install -m 755 -s -D dist-ghc/build/cabal-debian/cabal-debian debian/cabal-debian/usr/bin/cabal-debian || true--# How to install an extra file into the documentation package-#binary-fixup/libghc-cabal-debian-doc::-# echo "Some informative text" > debian/libghc-cabal-debian-doc/usr/share/doc/libghc-cabal-debian-doc/AnExtraDocFile
+ src/CabalDebian.hs view
@@ -0,0 +1,11 @@+-- | This is the main function of the cabal-debian executable. This+-- is generally run by the autobuilder to debianize packages that+-- don't have any custom debianization code in Setup.hs. This is a+-- less flexible and powerful method than calling the debianize+-- function directly, many sophisticated configuration options cannot+-- be accessed using the command line interface.++import Debian.Debianize.Debianize (cabalDebian)++main :: IO ()+main = cabalDebian
+ src/Data/Algorithm/Diff/Context.hs view
@@ -0,0 +1,54 @@+module Data.Algorithm.Diff.Context+ ( contextDiff+ , groups+ ) where++import Data.Algorithm.Diff (Diff(..), getGroupedDiff)++-- | Do a grouped diff and then turn it into a list of hunks, where+-- each hunk is a grouped diff with at most N elements of common+-- context around each one.+contextDiff :: Eq a => Int -> [a] -> [a] -> [[Diff [a]]]+contextDiff context a b =+ group $ swap $ trimTail $ trimHead $ concatMap split $ getGroupedDiff a b+ where+ -- Split common runs longer than 2N elements, keeping first and+ -- last N lines.+ split (Both xs ys) =+ case length xs of+ n | n > (2 * context) -> [Both (take context xs) (take context ys), Both (drop (n - context) xs) (drop (n - context) ys)]+ _ -> [Both xs ys]+ split x = [x]+ -- If split created a a pair of Both's at the beginning or end+ -- of the diff, remove the outermost.+ trimHead [] = []+ trimHead [Both _ _] = []+ trimHead [Both _ _, Both _ _] = []+ trimHead (Both _ _ : x@(Both _ _) : more) = x : more+ trimHead xs = trimTail xs+ trimTail [x@(Both _ _), Both _ _] = [x]+ trimTail (x : more) = x : trimTail more+ trimTail [] = []+ -- If we see Second before First swap them so that the deletions+ -- appear before the additions.+ swap (x@(Second _) : y@(First _) : xs) = y : x : swap xs+ swap (x : xs) = x : swap xs+ swap [] = []+ -- Split the list wherever we see adjacent Both constructors+ group xs =+ groups (\ x y -> not (isBoth x && isBoth y)) xs+ where+ isBoth (Both _ _) = True+ isBoth _ = False++-- | Group the elements whose adjacent pairs satisfy the predicate.+-- Differs from groupBy because the predicate does not have to define+-- a total ordering.+groups :: Eq a => (a -> a -> Bool) -> [a] -> [[a]]+groups f xs =+ filter (/= []) $ reverse (groups' [[]] xs)+ where+ groups' (r : rs) (x : y : xs') | f x y = groups' ((x : r) : rs) (y : xs')+ groups' (r : rs) (x : y : xs') = groups' ([y] : reverse (x : r) : rs) xs'+ groups' (r : rs) [y] = reverse (y : r) : rs+ groups' rs [] = rs
+ src/Data/Algorithm/Diff/Pretty.hs view
@@ -0,0 +1,17 @@+module Data.Algorithm.Diff.Pretty+ ( prettyDiff+ ) where++import Data.Algorithm.Diff (Diff(..))+import Data.Monoid (mconcat, (<>))+import Text.PrettyPrint.ANSI.Leijen (Pretty(pretty), Doc, text, empty)++prettyDiff :: (Pretty a, Pretty b, Pretty c) => a -> b -> [[Diff [c]]] -> Doc+prettyDiff _ _ [] = empty+prettyDiff old new hunks =+ text "--- " <> pretty old <> text "\n+++ " <> pretty new <> text "\n" <> mconcat (map (\ hunk -> text "@@\n" <> p hunk) hunks)+ where+ p (Both ts _ : more) = mconcat (map (\ l -> text " " <> pretty l <> text "\n") ts) <> p more+ p (First ts : more) = mconcat (map (\ l -> text "-" <> pretty l <> text "\n") ts) <> p more+ p (Second ts : more) = mconcat (map (\ l -> text "+" <> pretty l <> text "\n") ts) <> p more+ p [] = empty
+ src/Debian/Debianize.hs view
@@ -0,0 +1,73 @@+-- | QUICK START: You can either run the cabal-debian executable, or+-- for more power and flexibility you can construct a+-- 'Debian.Debianize.Atoms' value and pass it to the+-- 'Debian.Debianize.debianize' function. The+-- 'Debian.Debianize.callDebianize' function retrieves extra arguments+-- from the @CABALDEBIAN@ environment variable and calls+-- 'Debian.Debianize.debianize' with the build directory set as it+-- would be when the packages is built by @dpkg-buildpackage@.+-- +-- To see what your debianization would produce, or how it differs+-- from the debianization already present:+-- +-- > % cabal-debian --debianize -n+-- +-- This is equivalent to the library call+-- +-- > % ghc -e 'Debian.Debianize.callDebianize ["-n"]'+-- +-- To actually create the debianization and then build the debs,+-- +-- > % ghc -e 'Debian.Debianize.callDebianize []'+-- > % sudo dpkg-buildpackage+-- +-- At this point you may need to modify Cabal.defaultFlags to achieve+-- specific packaging goals. Create a module for this in debian/Debianize.hs:+-- +-- > import Distribution.Debian (Flags(..), defaultFlags)+-- > main = debianize (defaultFlags { extraDevDeps = "haskell-hsx-utils" : extraDevDeps defaultFlags})+-- +-- Then to test it,+-- +-- > % CABALDEBIAN='["-n"]' runhaskell debian/Debianize.hs+-- +-- and to run it+-- +-- > % runhaskell debian/Debianize.hs+module Debian.Debianize+ ( module Debian.Debianize.Atoms+ , module Debian.Debianize.Bundled+ , module Debian.Debianize.ControlFile+ , module Debian.Debianize.Debianize+ , module Debian.Debianize.Dependencies+ , module Debian.Debianize.Files+ , module Debian.Debianize.Finalize+ , module Debian.Debianize.Generic+ , module Debian.Debianize.Goodies+ , module Debian.Debianize.Input+ , module Debian.Debianize.Interspersed+ , module Debian.Debianize.Options+ , module Debian.Debianize.SubstVars+ , module Debian.Debianize.Tests+ , module Debian.Debianize.Types+ , module Debian.Debianize.Utility+ , module Debian.Policy+ ) where++import Debian.Debianize.Atoms+import Debian.Debianize.Bundled+import Debian.Debianize.Debianize+import Debian.Debianize.Dependencies+import Debian.Debianize.Files+import Debian.Debianize.Finalize+import Debian.Debianize.Generic+import Debian.Debianize.Goodies+import Debian.Debianize.Input+import Debian.Debianize.Interspersed+import Debian.Debianize.Options+import Debian.Debianize.SubstVars+import Debian.Debianize.Tests+import Debian.Debianize.Types+import Debian.Debianize.ControlFile hiding (depends, conflicts, maintainer, description, section)+import Debian.Debianize.Utility+import Debian.Policy
+ src/Debian/Debianize/Atoms.hs view
@@ -0,0 +1,1207 @@+{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, OverloadedStrings, ScopedTypeVariables, TupleSections #-}+module Debian.Debianize.Atoms+ ( Atoms+ -- * Modes of operation+ , verbosity+ , dryRun+ , validate+ , debAction+ , flags+ , warning+ -- * Cabal info+ , compilerVersion+ , packageDescription+ , buildDir+ , dataDir+ , compiler+ , extraLibMap+ , execMap+ , cabalFlagAssignments+ -- * Global debian info+ , versionSplits+ , epochMap+ -- * High level information about the debianization+ , description+ , executable+ , serverInfo+ , website+ , backups+ , apacheSite+ , missingDependencies+ , utilsPackageName+ , sourcePackageName+ , revision+ , debVersion+ , maintainer+ , packageInfo+ , omitLTDeps+ , noProfilingLibrary+ , noDocumentationLibrary+ , copyright+ , sourceArchitecture+ , binaryArchitectures+ , sourcePriority+ , binaryPriorities+ , sourceSection+ , binarySections+ , buildDeps+ , buildDepsIndep+ , depends+ , conflicts+ , extraDevDeps+ -- * Debianization files and file fragments+ , rulesHead+ , rulesFragments+ , postInst+ , postRm+ , preInst+ , preRm+ , compat+ , sourceFormat+ , watch+ , changelog+ , comments+ , control+ , standards+ , logrotateStanza+ , link+ , install+ , installTo+ , installData+ , file+ , installCabalExec+ , installCabalExecTo+ , installDir+ , installInit+ , intermediateFiles+ ) where++import Data.Generics (Data, Typeable)+import Data.Lens.Lazy (Lens, lens, getL, modL)+import Data.Map as Map (Map, fold, foldWithKey, insertWith, empty, insert)+import Data.Maybe (fromMaybe)+import Data.Monoid (Monoid(..))+import Data.Set as Set (Set, maxView, empty, union, singleton, fold, insert)+import Data.Text (Text)+import Data.Version (Version, showVersion)+import Debian.Changes (ChangeLog)+import Debian.Debianize.ControlFile (SourceDebDescription(standardsVersion), newSourceDebDescription)+import Debian.Debianize.Types (PackageInfo(..), Site(..), Server(..), InstallFile(..), VersionSplits(..), DebAction(..))+import Debian.Orphans ()+import Debian.Policy (PackageArchitectures, SourceFormat, PackagePriority, Section, StandardsVersion)+import Debian.Relation (SrcPkgName, BinPkgName, Relation(..))+import Debian.Version (DebianVersion)+import Distribution.License (License)+import Distribution.Package (PackageName(PackageName), PackageIdentifier(..))+import Distribution.PackageDescription as Cabal (PackageDescription(package), FlagName, PackageDescription)+import Distribution.Simple.Compiler (Compiler)+import Prelude hiding (init, unlines, log)+import System.FilePath ((</>))+import Text.ParserCombinators.Parsec.Rfc2822 (NameAddr)++-- All the internals of this module is a steaming pile of poo, except+-- for the stuff that is exported.++data DebAtomKey+ = Source+ | Binary BinPkgName+ deriving (Eq, Ord, Data, Typeable, Show)++-- | The smallest pieces of debhelper information. Some of these are+-- converted directly into files in the debian directory, others+-- become fragments of those files, and others are first converted+-- into different DebAtom values as new information becomes available.+data DebAtom+ = NoDocumentationLibrary+ -- ^ Do not produce a libghc-foo-doc package.+ | NoProfilingLibrary+ -- ^ Do not produce a libghc-foo-prof package.+ | CompilerVersion Version+ -- ^ Specify the version number of the GHC compiler in the build+ -- environment. The default is to assume that version is the same+ -- as the one in the environment where cabal-debian is running.+ -- This is used to look up hard coded lists of packages bundled+ -- with the compiler and their version numbers. (This could+ -- certainly be done in a more beautiful way.)+ | DHPackageDescription PackageDescription+ -- ^ The cabal package description record+ | DHCompiler Compiler+ -- ^ The Compiler value returned with the Cabal+ -- PackageDescription, then used to determine what libraries+ -- (i.e. dependencies) are provided by the compiler.+ | BuildDir FilePath+ -- ^ The build directory used by cabal, typically dist/build when+ -- building manually or dist-ghc/build when building using GHC and+ -- haskell-devscripts. This value is used to locate files+ -- produced by cabal so we can move them into the deb. Note that+ -- the --builddir option of runhaskell Setup appends the "/build"+ -- to the value it receives, so, yes, try not to get confused.+ | DataDir FilePath+ -- ^ the pathname of the package's data directory, generally the+ -- value of the dataDirectory field in the PackageDescription.+ | DebSourceFormat SourceFormat -- ^ Write debian/source/format+ | DebWatch Text -- ^ Write debian/watch+ | DHIntermediate FilePath Text -- ^ Put this text into a file with the given name in the debianization.+ | DebRulesHead Text -- ^ The header of the debian/rules file. The remainder is assembled+ -- from DebRulesFragment values in the atom list.+ | DebRulesFragment Text -- ^ A Fragment of debian/rules+ | Warning Text -- ^ A warning to be reported later+ | UtilsPackageName BinPkgName -- ^ Name to give the package for left-over data files and executables+ | DebChangeLog ChangeLog -- ^ The changelog, first entry contains the source package name and version+ | DebLogComments [[Text]] -- ^ Each element is a comment to be added to the changelog, where the+ -- element's text elements are the lines of the comment.+ | SourcePackageName SrcPkgName -- ^ Name to give to debian source package. If not supplied name is constructed+ -- from the cabal package name.+ | DHMaintainer NameAddr -- ^ Value for the maintainer field in the control file. Note that+ -- the cabal maintainer field can have multiple addresses, but debian+ -- only one. If this is not explicitly set, it is obtained from the+ -- cabal file, and if it is not there then from the environment. As a+ -- last resort, there is a hard coded string in here somewhere.+ | DHCabalFlagAssignments (Set (FlagName, Bool)) -- ^ Flags to pass to Cabal function finalizePackageDescription, this+ -- can be used to control the flags in the cabal file.+ | DHFlags Flags -- ^ Information regarding mode of operation - verbosity, dry-run, usage, etc+ | DebRevision String -- ^ Specify the revision string to use when converting the cabal+ -- version to debian.++ | OmitLTDeps -- ^ If present, don't generate the << dependency when we see a cabal+ -- equals dependency. (The implementation of this was somehow lost.)+ | DebVersion DebianVersion -- ^ Specify the exact debian version of the resulting package,+ -- including epoch. One use case is to work around the the+ -- "buildN" versions that are often uploaded to the debian and+ -- ubuntu repositories. Say the latest cabal version of+ -- transformers is 0.3.0.0, but the debian repository contains+ -- version 0.3.0.0-1build3, we need to specify+ -- debVersion="0.3.0.0-1build3" or the version we produce will+ -- look older than the one already available upstream.+ | DebVersionSplits [VersionSplits] -- ^ Instances where the debian package name is different (for+ -- some range of version numbers) from the default constructed+ -- by mkPkgName.+ | BuildDep BinPkgName -- ^ Add a build dependency (FIXME: should be a Rel, or an OrRelation, not a BinPkgName)+ | BuildDepIndep BinPkgName -- ^ Add an arch independent build dependency+ | MissingDependency BinPkgName -- ^ Lets cabal-debian know that a package it might expect to exist+ -- actually does not, so omit all uses in resulting debianization.+ | ExtraLibMapping String BinPkgName -- ^ Map a cabal Extra-Library name to a debian binary package name,+ -- e.g. @ExtraLibMapping extraLibMap "cryptopp" "libcrypto-dev"@ adds a+ -- build dependency on @libcrypto-dev@ to any package that has @cryptopp@+ -- in its cabal Extra-Library list.+ | ExecMapping String BinPkgName -- ^ Map a cabal Build-Tool name to a debian binary package name,+ -- e.g. @ExecMapping "trhsx" "haskell-hsx-utils"@ adds a build+ -- dependency on @haskell-hsx-utils@ to any package that has @trhsx@ in its+ -- cabal build-tool list.+ | EpochMapping PackageName Int -- ^ Specify epoch numbers for the debian package generated from a+ -- cabal package. Example: @EpochMapping (PackageName "HTTP") 1@.+ | DebPackageInfo PackageInfo -- ^ Supply some info about a cabal package.+ | DebCompat Int -- ^ The debhelper compatibility level, from debian/compat.+ | DebCopyright (Either License Text) -- ^ Copyright information, either as a Cabal License value or+ -- the full text.+ | DebControl SourceDebDescription -- ^ The parsed contents of the control file++ -- From here down are atoms to be associated with a Debian binary+ -- package. This could be done with more type safety, separate+ -- maps for the Source atoms and the Binary atoms.+ | DHApacheSite String FilePath Text -- ^ Have Apache configure a site using PACKAGE, DOMAIN, LOGDIR, and APACHECONFIGFILE+ | DHLogrotateStanza Text -- ^ Add a stanza of a logrotate file to the binary package+ | DHLink FilePath FilePath -- ^ Create a symbolic link in the binary package+ | DHPostInst Text -- ^ Script to run after install, should contain #DEBHELPER# line before exit 0+ | DHPostRm Text -- ^ Script to run after remove, should contain #DEBHELPER# line before exit 0+ | DHPreInst Text -- ^ Script to run before install, should contain #DEBHELPER# line before exit 0+ | DHPreRm Text -- ^ Script to run before remove, should contain #DEBHELPER# line before exit 0+ | DHArch PackageArchitectures -- ^ Set the Architecture field of source or binary+ | DHPriority PackagePriority -- ^ Set the Priority field of source or binary+ | DHSection Section -- ^ Set the Section field of source or binary+ | DHDescription Text -- ^ Set the description of source or binary+ | DHInstall FilePath FilePath -- ^ Install a build file into the binary package+ | DHInstallTo FilePath FilePath -- ^ Install a build file into the binary package at an exact location+ | DHInstallData FilePath FilePath -- ^ DHInstallTo somewhere relative to DataDir (see above)+ | DHFile FilePath Text -- ^ Create a file with the given text at the given path+ | DHInstallCabalExec String FilePath -- ^ Install a cabal executable into the binary package+ | DHInstallCabalExecTo String FilePath -- ^ Install a cabal executable into the binary package at an exact location+ | DHInstallDir FilePath -- ^ Create a directory in the binary package+ | DHInstallInit Text -- ^ Add an init.d file to the binary package+ | DHExecutable InstallFile -- ^ Create a binary package to hold a cabal executable+ | DHServer Server -- ^ Like DHExecutable, but configure the executable as a server process+ | DHWebsite Site -- ^ Like DHServer, but configure the server as a web server+ | DHBackups String -- ^ Configure the executable to do incremental backups+ | Depends Relation -- ^ Says that the debian package should have this relation in Depends+ | Conflicts Relation -- ^ Says that the debian package should have this relation in Conflicts+ | DevDepends BinPkgName -- ^ Limited version of Depends, put a dependency on the dev library package. The only+ -- reason to use this is because we don't yet know the name of the dev library package.+ deriving (Eq, Ord, Show, Typeable)++-- | This record supplies information about the task we want done -+-- debianization, validataion, help message, etc.+data Flags = Flags+ {+ -------------------------+ -- Modes of Operation ---+ -------------------------+ verbosity_ :: Int+ -- ^ Run with progress messages at the given level of verboseness.+ , dryRun_ :: Bool+ -- ^ Don't write any files or create any directories, just explain+ -- what would have been done.+ , validate_ :: Bool+ -- ^ Fail if the debianization already present doesn't match the+ -- one we are going to generate closely enough that it is safe to+ -- debianize during the run of dpkg-buildpackage, when Setup+ -- configure is run. Specifically, the version number in the top+ -- changelog entry must match, and the sets of package names in+ -- the control file must match.+ , debAction_ :: DebAction+ -- ^ What to do - Usage, Debianize or Substvar+ } deriving (Eq, Ord, Show)++-- | Bits and pieces of information about the mapping from cabal package+-- names and versions to debian package names and versions. In essence,+-- an 'Atoms' value represents a package's debianization. The lenses in+-- this module are used to get and set the values hidden in this Atoms+-- value. Many of the values should be left alone to be set when the+-- debianization is finalized.+newtype Atoms = Atoms (Map DebAtomKey (Set DebAtom)) deriving (Eq, Show)++instance Monoid Atoms where+ -- We need mempty to actually be an empty map because we test for+ -- this in the expandAtoms recursion.+ mempty = Atoms mempty -- defaultAtoms+ mappend a b = foldAtoms insertAtom a b++-- Lenses to access values in the Atoms type. This is an old+-- design which I plan to make private and turn into something+-- nicer, so these will remain ugly and repetitive for now.++-- | Set how much progress messages get generated.+verbosity :: Lens Atoms Int+verbosity = lens (\ a -> verbosity_ (getL flags a)) (\ b a -> modL flags (\ x -> x {verbosity_ = b}) a)++-- | Don't write anything, just output a description of what would have happened+dryRun :: Lens Atoms Bool+dryRun = lens (\ a -> dryRun_ (getL flags a)) (\ b a -> modL flags (\ x -> x {dryRun_ = b}) a)++-- | Make sure the version number and package names of the generated debianization match the+validate :: Lens Atoms Bool+validate = lens (\ a -> validate_ (getL flags a)) (\ b a -> modL flags (\ x -> x {validate_ = b}) a)++-- | Debianize, SubstVars, or Usage. I'm no longer sure what SubstVars does, but someone+-- may still be using it.+debAction :: Lens Atoms DebAction+debAction = lens (\ a -> debAction_ (getL flags a)) (\ b a -> modL flags (\ x -> x {debAction_ = b}) a)++-- | Obsolete record containing verbosity, dryRun, validate, and debAction.+flags :: Lens Atoms Flags+flags = lens g s+ where+ g atoms = fromMaybe defaultFlags $ foldAtoms from Nothing atoms+ where+ from Source (DHFlags x') (Just x) | x /= x' = error $ "Conflicting control values:" ++ show (x, x')+ from Source (DHFlags x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const ((singleton . (Source,) . DHFlags) x)) atoms+ where+ f Source (DHFlags y) = Just y+ f _ _ = Nothing++-- | Unused+warning :: Lens Atoms (Set Text)+warning = lens g s+ where+ g atoms = foldAtoms from Set.empty atoms+ where+ from Source (Warning t) x = Set.insert t x+ from _ _ x = x+ s x atoms = Set.fold (\ text atoms' -> insertAtom Source (Warning text) atoms') (deleteAtoms p atoms) x+ where+ p Source (Warning _) = True+ p _ _ = False+++-- | Set the compiler version, this is used when loading the cabal file to+compilerVersion :: Lens Atoms (Maybe Version)+compilerVersion = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (CompilerVersion x') (Just x) | x /= x' = error $ "Conflicting compat values:" ++ show (x, x')+ from Source (CompilerVersion x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . CompilerVersion) x)) atoms+ where+ f Source (CompilerVersion y) = Just y+ f _ _ = Nothing++-- | The information loaded from the cabal file.+packageDescription :: Lens Atoms (Maybe PackageDescription)+packageDescription = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DHPackageDescription x') (Just x) | x /= x' = error $ "Conflicting rulesHead values:" ++ show (x, x')+ from Source (DHPackageDescription x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DHPackageDescription) x)) atoms+ where+ f Source (DHPackageDescription y) = Just y+ f _ _ = Nothing++-- | The build directory. This can be set by an argument to the @Setup@ script.+-- When @Setup@ is run manually it is just @dist@, when it is run by+-- @dpkg-buildpackage@ the compiler name is appended, so it is typically+-- @dist-ghc@. Cabal-debian needs the correct value of buildDir to find+-- the build results.+buildDir :: Lens Atoms (Maybe FilePath)+buildDir = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (BuildDir x') (Just x) | x /= x' = error $ "Conflicting rulesHead values:" ++ show (x, x')+ from Source (BuildDir x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . BuildDir) x)) atoms+ where+ f Source (BuildDir y) = Just y+ f _ _ = Nothing++-- | The data directory for the package, generated from the packageDescription+dataDir :: Lens Atoms (Maybe FilePath)+dataDir = lens g s+ where+ g atoms =+ fmap (\ p -> let PackageName pkgname = pkgName. package $ p in+ "usr/share" </> (pkgname ++ "-" ++ (showVersion . pkgVersion . package $ p))) (getL packageDescription atoms)+ s _ _ = error "setL dataDir"+++-- | The Compiler value returned when the cabal file was loaded.+compiler :: Lens Atoms (Maybe Compiler)+compiler = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DHCompiler x') (Just x) | x /= x' = error $ "Conflicting compat values:" ++ show (x, x')+ from Source (DHCompiler x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DHCompiler) x)) atoms+ where+ f Source (DHCompiler y) = Just y+ f _ _ = Nothing++-- | Map from cabal Extra-Lib names to debian binary package names.+extraLibMap :: Lens Atoms (Map String (Set BinPkgName))+extraLibMap = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from Source (ExtraLibMapping cabal debian) x = Map.insertWith union cabal (singleton debian) x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ cabal debian atoms' -> Set.fold (\ debian' atoms'' -> insertAtom Source (ExtraLibMapping cabal debian') atoms'') atoms' debian) (deleteAtoms p atoms) x+ where+ p Source (ExtraLibMapping _ _) = True+ p _ _ = False++-- | Map from cabal Build-Tool names to debian binary package names.+execMap :: Lens Atoms (Map String BinPkgName)+execMap = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from Source (ExecMapping cabal debian) x = Map.insertWith (error "Conflict in execMap") cabal debian x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ cabal debian atoms' -> insertAtom Source (ExecMapping cabal debian) atoms') (deleteAtoms p atoms) x+ where+ p Source (ExecMapping _ _) = True+ p _ _ = False++-- | Cabal flag assignments to use when loading the cabal file.+cabalFlagAssignments :: Lens Atoms (Set (FlagName, Bool))+cabalFlagAssignments = lens g s+ where+ g atoms = foldAtoms from Set.empty atoms+ where+ from Source (DHCabalFlagAssignments pairs) x = union pairs x+ from _ _ x = x+ s x atoms = insertAtom Source (DHCabalFlagAssignments x) (deleteAtoms p atoms)+ where+ p Source (DHCabalFlagAssignments _) = True+ p _ _ = False++-- | Map from cabal version number ranges to debian package names. This is a+-- result of the fact that only one version of a debian package can be+-- installed at a given time, while multiple versions of a cabal packages can.+versionSplits :: Lens Atoms [VersionSplits]+versionSplits = lens g s+ where+ g atoms = foldAtoms from [] atoms+ where+ from Source (DebVersionSplits xs') xs = xs ++ xs'+ from _ _ xs = xs+ s x atoms = insertAtom Source (DebVersionSplits x) (deleteAtoms p atoms)+ where+ p Source (DebVersionSplits _) = True+ p _ _ = False++-- | Map of Debian epoch numbers assigned to cabal packages.+epochMap :: Lens Atoms (Map PackageName Int)+epochMap = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from Source (EpochMapping name epoch) x = Map.insertWith (error "Conflicting Epochs") name epoch x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ name epoch atoms' -> insertAtom Source (EpochMapping name epoch) atoms') (deleteAtoms p atoms) x+ where+ p Source (EpochMapping _ _) = True+ p _ _ = False++-- | Map of binary deb descriptions.+description :: Lens Atoms (Map BinPkgName Text)+description = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHDescription d) x = Map.insertWith (error "description") b d x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b y atoms'-> insertAtom (Binary b) (DHDescription y) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHDescription _) = True+ p _ _ = False++-- | Create a package to hold a cabal executable+executable :: Lens Atoms (Map BinPkgName InstallFile)+executable = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHExecutable f) x = Map.insertWith (error "executable") b f x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b y atoms'-> insertAtom (Binary b) (DHExecutable y) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHExecutable _) = True+ p _ _ = False++-- | Create a package for an operating service using the given executable+serverInfo :: Lens Atoms (Map BinPkgName Server)+serverInfo = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHServer s) x = Map.insertWith (error "server") b s x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b y atoms'-> insertAtom (Binary b) (DHServer y) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHServer _) = True+ p _ _ = False++-- | Create a package for a website using the given executable as the server+website :: Lens Atoms (Map BinPkgName Site)+website = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHWebsite s) x = Map.insertWith (error "website") b s x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b y atoms'-> insertAtom (Binary b) (DHWebsite y) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHWebsite _) = True+ p _ _ = False++-- | Generate a backups package using the given cabal executable+backups :: Lens Atoms (Map BinPkgName String)+backups = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHBackups s) x = Map.insertWith (error "backups") b s x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b y atoms'-> insertAtom (Binary b) (DHBackups y) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHBackups _) = True+ p _ _ = False++-- | Create an apache configuration file with the given+-- (domain, logdir, filetext). This is called when expanding+-- the result of the website lens above.+apacheSite :: Lens Atoms (Map BinPkgName (String, FilePath, Text))+apacheSite = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHApacheSite dom log text) x = Map.insertWith (error "backups") b (dom, log, text) x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b (dom, log, text) atoms' -> insertAtom (Binary b) (DHApacheSite dom log text) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHApacheSite _ _ _) = True+ p _ _ = False++-- * Lower level hints about the debianization+++-- | List if packages that should be omitted from any+-- dependency list - e.g. a profiling package missing due+-- to use of noProfilingPackage lens elsewhere.+missingDependencies :: Lens Atoms (Set BinPkgName)+missingDependencies = lens g s+ where+ g atoms = foldAtoms from Set.empty atoms+ where+ from Source (MissingDependency b) x = Set.insert b x+ from _ _ x = x+ s x atoms = Set.fold (\ b atoms' -> insertAtom Source (MissingDependency b) atoms') (deleteAtoms p atoms) x+ where+ p Source (MissingDependency _) = True+ p _ _ = False++-- | Override the package name used to hold left over data files and executables+utilsPackageName :: Lens Atoms (Maybe BinPkgName)+utilsPackageName = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (UtilsPackageName x') (Just x) | x /= x' = error $ "Conflicting compat values:" ++ show (x, x')+ from Source (UtilsPackageName x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . UtilsPackageName) x)) atoms+ where+ f Source (UtilsPackageName y) = Just y+ f _ _ = Nothing++-- | Override the debian source package name constructed from the cabal name+sourcePackageName :: Lens Atoms (Maybe SrcPkgName)+sourcePackageName = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (SourcePackageName x') (Just x) | x /= x' = error $ "Conflicting compat values:" ++ show (x, x')+ from Source (SourcePackageName x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . SourcePackageName) x)) atoms+ where+ f Source (SourcePackageName y) = Just y+ f _ _ = Nothing++-- | Revision string used in constructing the debian verison number from the cabal version+revision :: Lens Atoms (Maybe String)+revision = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DebRevision x') (Just x) | x /= x' = error $ "Conflicting rulesHead values:" ++ show (x, x')+ from Source (DebRevision x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DebRevision) x)) atoms+ where+ f Source (DebRevision y) = Just y+ f _ _ = Nothing++-- | Exact debian version number, overrides the version generated from the cabal version+debVersion :: Lens Atoms (Maybe DebianVersion)+debVersion = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DebVersion x') (Just x) | x /= x' = error $ "Conflicting rulesHead values:" ++ show (x, x')+ from Source (DebVersion x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DebVersion) x)) atoms+ where+ f Source (DebVersion y) = Just y+ f _ _ = Nothing++-- | Maintainer field. Overrides any value found in the cabal file, or+-- in the DEBIANMAINTAINER environment variable.+maintainer :: Lens Atoms (Maybe NameAddr)+maintainer = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DHMaintainer x') (Just x) | x /= x' = error $ "Conflicting rulesHead values:" ++ show (x, x')+ from Source (DHMaintainer x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DHMaintainer) x)) atoms+ where+ f Source (DHMaintainer y) = Just y+ f _ _ = Nothing++-- | No longer sure what the purpose of this lens is.+packageInfo :: Lens Atoms (Map PackageName PackageInfo)+packageInfo = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from Source (DebPackageInfo i) x = Map.insert (cabalName i) i x+ from _ _ x = x+ s x atoms =+ Map.fold (\ i atoms' -> insertAtom Source (DebPackageInfo i) atoms') (deleteAtoms p atoms) x+ where+ p Source (DebPackageInfo _) = True+ p _ _ = False++-- | Set this to filter any less-than dependencies out of the generated debian+-- dependencies. (Not sure if this is implemented.)+omitLTDeps :: Lens Atoms Bool+omitLTDeps = lens g s+ where+ g atoms = foldAtoms from False atoms+ where+ from Source OmitLTDeps _ = True+ from _ _ x = x+ s x atoms = (if x then insertAtom Source OmitLTDeps else id) (deleteAtoms p atoms)+ where+ p Source OmitLTDeps = True+ p _ _ = False++-- | Set this to omit the prof library deb.+noProfilingLibrary :: Lens Atoms Bool+noProfilingLibrary = lens g s+ where+ g atoms = foldAtoms from False atoms+ where+ from Source NoProfilingLibrary _ = True+ from _ _ x = x+ s x atoms = (if x then insertAtom Source NoProfilingLibrary else id) (deleteAtoms p atoms)+ where+ p Source NoProfilingLibrary = True+ p _ _ = False++-- | Set this to omit the doc library deb.+noDocumentationLibrary :: Lens Atoms Bool+noDocumentationLibrary = lens g s+ where+ g atoms = foldAtoms from False atoms+ where+ from Source NoDocumentationLibrary _ = True+ from _ _ x = x+ s x atoms = (if x then insertAtom Source NoDocumentationLibrary else id) (deleteAtoms p atoms)+ where+ p Source NoDocumentationLibrary = True+ p _ _ = False++-- | The copyright information+copyright :: Lens Atoms (Maybe (Either License Text))+copyright = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DebCopyright x') (Just x) | x /= x' = error $ "Conflicting rulesHead values:" ++ show (x, x')+ from Source (DebCopyright x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DebCopyright) x)) atoms+ where+ f Source (DebCopyright y) = Just y+ f _ _ = Nothing++-- | The source package architecture - @Any@, @All@, or some list of specific architectures.+sourceArchitecture :: Lens Atoms (Maybe PackageArchitectures)+sourceArchitecture = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DHArch x') (Just x) | x /= x' = error $ "Conflicting rulesHead values:" ++ show (x, x')+ from Source (DHArch x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DHArch) x)) atoms+ where+ f Source (DHArch y) = Just y+ f _ _ = Nothing++-- | Map of the binary package architectures+binaryArchitectures :: Lens Atoms (Map BinPkgName PackageArchitectures)+binaryArchitectures = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHArch x) m = Map.insert b x m+ from _ _ m = m+ s x atoms = Map.foldWithKey (\ b a atoms' -> insertAtom (Binary b) (DHArch a) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHArch _) = True+ p _ _ = False++-- | The source package priority+sourcePriority :: Lens Atoms (Maybe PackagePriority)+sourcePriority = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DHPriority x') (Just x) | x /= x' = error $ "Conflicting rulesHead values:" ++ show (x, x')+ from Source (DHPriority x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DHPriority) x)) atoms+ where+ f Source (DHPriority y) = Just y+ f _ _ = Nothing++-- | Map of the binary package priorities+binaryPriorities :: Lens Atoms (Map BinPkgName PackagePriority)+binaryPriorities = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHPriority p) x = Map.insertWith (error "priorities") b p x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b p atoms'-> insertAtom (Binary b) (DHPriority p) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHPriority _) = True+ p _ _ = False++-- | The source package's section assignment+sourceSection :: Lens Atoms (Maybe Section)+sourceSection = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DHSection x') (Just x) | x /= x' = error $ "Conflicting rulesHead values:" ++ show (x, x')+ from Source (DHSection x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DHSection) x)) atoms+ where+ f Source (DHSection y) = Just y+ f _ _ = Nothing++-- | Map of the binary deb section assignments+binarySections :: Lens Atoms (Map BinPkgName Section)+binarySections = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHSection p) x = Map.insertWith (error "sections") b p x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b p atoms'-> insertAtom (Binary b) (DHSection p) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHSection _) = True+ p _ _ = False++-- * Debian dependency info++-- | Build dependencies+buildDeps :: Lens Atoms (Set BinPkgName)+buildDeps = lens g s+ where+ g atoms = foldAtoms from Set.empty atoms+ where+ from Source (BuildDep d) x = Set.insert d x+ from _ _ x = x+ s x atoms = Set.fold (\ d atoms' -> insertAtom Source (BuildDep d) atoms') (deleteAtoms p atoms) x+ where+ p Source (BuildDep _) = True+ p _ _ = False++-- | Architecture independent+buildDepsIndep :: Lens Atoms (Set BinPkgName)+buildDepsIndep = lens g s+ where+ g atoms = foldAtoms from Set.empty atoms+ where+ from Source (BuildDepIndep d) x = Set.insert d x+ from _ _ x = x+ s x atoms = Set.fold (\ d atoms' -> insertAtom Source (BuildDepIndep d) atoms') (deleteAtoms p atoms) x+ where+ p Source (BuildDepIndep _) = True+ p _ _ = False++-- | Map of extra install dependencies for the package's binary debs.+-- This should be [[Relation]] for full generality, or Set (Set Relation)+depends :: Lens Atoms (Map BinPkgName (Set Relation))+depends = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (Depends rel) x = Map.insertWith union b (singleton rel) x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b rels atoms' -> Set.fold (\ rel atoms'' -> insertAtom (Binary b) (Depends rel) atoms'') atoms' rels) (deleteAtoms p atoms) x+ where+ p (Binary _) (Depends _) = True+ p _ _ = False++-- | Map of extra install conflicts for the package's binary debs.+-- We should support all the other dependency fields - provides, replaces, etc.+conflicts :: Lens Atoms (Map BinPkgName (Set Relation))+conflicts = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (Conflicts rel) x = Map.insertWith union b (singleton rel) x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b rels atoms' -> Set.fold (\ rel atoms'' -> insertAtom (Binary b) (Conflicts rel) atoms'') atoms' rels) (deleteAtoms p atoms) x+ where+ p (Binary _) (Conflicts _) = True+ p _ _ = False++-- | Extra install dependencies for the devel library. Redundant+-- with depends, but kept for backwards compatibility. Also, I+-- think maybe this is or was needed because it can be set before+-- the exact name of the library package is known.+extraDevDeps :: Lens Atoms (Set BinPkgName)+extraDevDeps = lens g s+ where+ g atoms = foldAtoms from Set.empty atoms+ where+ from Source (DevDepends b) x = Set.insert b x+ from _ _ x = x+ s x atoms = Set.fold (\ d atoms' -> insertAtom Source (DevDepends d) atoms') (deleteAtoms p atoms) x+ where+ p Source (DevDepends _) = True+ p _ _ = False++-- | The beginning of the rules file+rulesHead :: Lens Atoms (Maybe Text)+rulesHead = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DebRulesHead x') (Just x) | x /= x' = error $ "Conflicting rulesHead values:" ++ show (x, x')+ from Source (DebRulesHead x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DebRulesHead) x)) atoms+ where+ f Source (DebRulesHead y) = Just y+ f _ _ = Nothing++-- | Additional fragments of the rules file+rulesFragments :: Lens Atoms (Set Text)+rulesFragments = lens g s+ where+ g atoms = foldAtoms from Set.empty atoms+ where+ from Source (DebRulesFragment t) x = Set.insert t x+ from _ _ x = x+ s x atoms = Set.fold (\ text atoms' -> insertAtom Source (DebRulesFragment text) atoms') (deleteAtoms p atoms) x+ where+ p Source (DebRulesFragment _) = True+ p _ _ = False++-- | Map of @debian/postinst@ scripts+postInst :: Lens Atoms (Map BinPkgName Text)+postInst = lens g s+ where+ g atoms = foldAtoms from mempty atoms+ where+ from :: DebAtomKey -> DebAtom -> Map BinPkgName Text -> Map BinPkgName Text+ from (Binary b) (DHPostInst t) x = Map.insertWith (error "Conflicting postInsts") b t x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b t atoms' -> insertAtom (Binary b) (DHPostInst t) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHPostInst _) = True+ p _ _ = False++-- | Map of @debian/postrm@ scripts+postRm :: Lens Atoms (Map BinPkgName Text)+postRm = lens g s+ where+ g atoms = foldAtoms from mempty atoms+ where+ from :: DebAtomKey -> DebAtom -> Map BinPkgName Text -> Map BinPkgName Text+ from (Binary b) (DHPostRm t) m = Map.insertWith (error "Conflicting postRms") b t m+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b t atoms' -> insertAtom (Binary b) (DHPostRm t) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHPostRm _) = True+ p _ _ = False++-- | Map of @debian/preinst@ scripts+preInst :: Lens Atoms (Map BinPkgName Text)+preInst = lens g s+ where+ g atoms = foldAtoms from mempty atoms+ where+ from :: DebAtomKey -> DebAtom -> Map BinPkgName Text -> Map BinPkgName Text+ from (Binary b) (DHPreInst t) m = Map.insertWith (error "Conflicting preInsts") b t m+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b t atoms' -> insertAtom (Binary b) (DHPreInst t) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHPreInst _) = True+ p _ _ = False++-- | Map of @debian/prerm@ scripts+preRm :: Lens Atoms (Map BinPkgName Text)+preRm = lens g s+ where+ g atoms = foldAtoms from mempty atoms+ where+ from :: DebAtomKey -> DebAtom -> Map BinPkgName Text -> Map BinPkgName Text+ from (Binary b) (DHPreRm t) m = Map.insertWith (error "Conflicting preRms") b t m+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b t atoms' -> insertAtom (Binary b) (DHPreRm t) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHPreRm _) = True+ p _ _ = False++-- | The @debian/compat@ file, contains the minimum compatible version of the @debhelper@ package+compat :: Lens Atoms (Maybe Int)+compat = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DebCompat x') (Just x) | x /= x' = error $ "Conflicting compat values:" ++ show (x, x')+ from Source (DebCompat x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DebCompat) x)) atoms+ where+ f Source (DebCompat y) = Just y+ f _ _ = Nothing++-- | The @debian/source/format@ file.+sourceFormat :: Lens Atoms (Maybe SourceFormat)+sourceFormat = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DebSourceFormat x') (Just x) | x /= x' = error $ "Conflicting compat values:" ++ show (x, x')+ from Source (DebSourceFormat x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DebSourceFormat) x)) atoms+ where+ f Source (DebSourceFormat y) = Just y+ f _ _ = Nothing++-- | the @debian/watch@ file+watch :: Lens Atoms (Maybe Text)+watch = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DebWatch x') (Just x) | x /= x' = error $ "Conflicting watch values:" ++ show (x, x')+ from Source (DebWatch x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DebWatch) x)) atoms+ where+ f Source (DebWatch y) = Just y+ f _ _ = Nothing++-- | the @debian/changelog@ file+changelog :: Lens Atoms (Maybe ChangeLog)+changelog = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DebChangeLog x') (Just x) | x /= x' = error $ "Conflicting compat values:" ++ show (x, x')+ from Source (DebChangeLog x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DebChangeLog) x)) atoms+ where+ f Source (DebChangeLog y) = Just y+ f _ _ = Nothing++-- | Comment entries for the latest changelog entry (DebLogComments [[Text]])+comments :: Lens Atoms (Maybe [[Text]])+comments = lens g s+ where+ g atoms = foldAtoms from Nothing atoms+ where+ from Source (DebLogComments xss') (Just xss) | xss == xss' = error $ "Conflicting log comments: " ++ show (xss, xss')+ from Source (DebLogComments xss) _ = Just xss+ from _ _ x = x+ s x atoms = modifyAtoms' f (const (maybe Set.empty (singleton . (Source,) . DebLogComments) x)) atoms+ where+ f Source (DebLogComments y) = Just y+ f _ _ = Nothing++-- | The @debian/control@ file.+control :: Lens Atoms SourceDebDescription+control = lens g s+ where+ g atoms = fromMaybe newSourceDebDescription $ foldAtoms from Nothing atoms+ where+ from Source (DebControl x') (Just x) | x /= x' = error $ "Conflicting control values:" ++ show (x, x')+ from Source (DebControl x) _ = Just x+ from _ _ x = x+ s x atoms = modifyAtoms' f (const ((singleton . (Source,) . DebControl) x)) atoms+ where+ f Source (DebControl y) = Just y+ f _ _ = Nothing++-- | The @Standards-Version@ field of the @debian/control@ file+standards :: Lens Atoms (Maybe StandardsVersion)+standards = lens (\ a -> standardsVersion (getL control a)) (\ b a -> modL control (\ x -> x {standardsVersion = b}) a)+logrotateStanza :: Lens Atoms (Map BinPkgName (Set Text))+logrotateStanza = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHLogrotateStanza r) x = Map.insertWith Set.union b (singleton r) x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b ts atoms'-> Set.fold (\ t atoms'' -> insertAtom (Binary b) (DHLogrotateStanza t) atoms'') atoms' ts) (deleteAtoms p atoms) x+ where+ p (Binary _) (DHLogrotateStanza _) = True+ p _ _ = False+link :: Lens Atoms (Map BinPkgName (Set (FilePath, FilePath)))+link = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHLink loc txt) x = Map.insertWith Set.union b (singleton (loc, txt)) x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b pairs atoms'-> Set.fold (\ (loc, txt) atoms'' -> insertAtom (Binary b) (DHLink loc txt) atoms'') atoms' pairs) (deleteAtoms p atoms) x+ where+ p (Binary _) (DHLink _ _) = True+ p _ _ = False++-- | Install files into directories+install :: Lens Atoms (Map BinPkgName (Set (FilePath, FilePath)))+install = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHInstall src dst) x = Map.insertWith Set.union b (singleton (src, dst)) x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b pairs atoms'-> Set.fold (\ (src, dst) atoms'' -> insertAtom (Binary b) (DHInstall src dst) atoms'') atoms' pairs) (deleteAtoms p atoms) x+ where+ p (Binary _) (DHInstall _ _) = True+ p _ _ = False++-- | Rename and install files+installTo :: Lens Atoms (Map BinPkgName (Set (FilePath, FilePath)))+installTo = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHInstallTo src dst) x = Map.insertWith Set.union b (singleton (src, dst)) x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b pairs atoms'-> Set.fold (\ (src, dst) atoms'' -> insertAtom (Binary b) (DHInstallTo src dst) atoms'') atoms' pairs) (deleteAtoms p atoms) x+ where+ p (Binary _) (DHInstallTo _ _) = True+ p _ _ = False++-- | Install files into the package data directory+installData :: Lens Atoms (Map BinPkgName (Set (FilePath, FilePath)))+installData = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHInstallData src dst) x = Map.insertWith Set.union b (singleton (src, dst)) x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b pairs atoms'-> Set.fold (\ (src, dst) atoms'' -> insertAtom (Binary b) (DHInstallData src dst) atoms'') atoms' pairs) (deleteAtoms p atoms) x+ where+ p (Binary _) (DHInstallData _ _) = True+ p _ _ = False++-- | Create a file in the deb with the given text+file :: Lens Atoms (Map BinPkgName (Set (FilePath, Text)))+file = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHFile path text) x = Map.insertWith Set.union b (singleton (path, text)) x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b pairs atoms'-> Set.fold (\ (path, text) atoms'' -> insertAtom (Binary b) (DHFile path text) atoms'') atoms' pairs) (deleteAtoms p atoms) x+ where+ p (Binary _) (DHFile _ _) = True+ p _ _ = False++-- | Install a cabal executable+installCabalExec :: Lens Atoms (Map BinPkgName (Set (String, FilePath)))+installCabalExec = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHInstallCabalExec name dst) x = Map.insertWith Set.union b (singleton (name, dst)) x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b pairs atoms'-> Set.fold (\ (name, dst) atoms'' -> insertAtom (Binary b) (DHInstallCabalExec name dst) atoms'') atoms' pairs) (deleteAtoms p atoms) x+ where+ p (Binary _) (DHInstallCabalExec _ _) = True+ p _ _ = False++-- | Rename and install a cabal executable+installCabalExecTo :: Lens Atoms (Map BinPkgName (Set (String, FilePath)))+installCabalExecTo = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHInstallCabalExecTo name dst) x = Map.insertWith Set.union b (singleton (name, dst)) x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b pairs atoms'-> Set.fold (\ (name, dst) atoms'' -> insertAtom (Binary b) (DHInstallCabalExecTo name dst) atoms'') atoms' pairs) (deleteAtoms p atoms) x+ where+ p (Binary _) (DHInstallCabalExecTo _ _) = True+ p _ _ = False++-- | Create directories in the package+installDir :: Lens Atoms (Map BinPkgName (Set FilePath))+installDir = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHInstallDir path) x = Map.insertWith Set.union b (singleton path) x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b paths atoms'-> Set.fold (\ path atoms'' -> insertAtom (Binary b) (DHInstallDir path) atoms'') atoms' paths) (deleteAtoms p atoms) x+ where+ p (Binary _) (DHInstallDir _) = True+ p _ _ = False++-- | Create an /etc/init.d file in the package+installInit :: Lens Atoms (Map BinPkgName Text)+installInit = lens g s+ where+ g atoms = foldAtoms from Map.empty atoms+ where+ from (Binary b) (DHInstallInit text) x = Map.insertWith (error "installInit") b text x+ from _ _ x = x+ s x atoms = Map.foldWithKey (\ b text atoms'-> insertAtom (Binary b) (DHInstallInit text) atoms') (deleteAtoms p atoms) x+ where+ p (Binary _) (DHInstallInit _) = True+ p _ _ = False++-- | Create a file in the debianization. This is used to implement the file lens above.+intermediateFiles :: Lens Atoms (Set (FilePath, Text))+intermediateFiles = lens g s+ where+ g atoms = foldAtoms from Set.empty atoms+ where+ from Source (DHIntermediate path text) x = Set.insert (path, text) x+ from _ _ x = x+ s x atoms = Set.fold (\ (path, text) atoms' -> insertAtom Source (DHIntermediate path text) atoms') (deleteAtoms p atoms) x+ where+ p Source (DHIntermediate _ _) = True+ p _ _ = False++defaultFlags :: Flags+defaultFlags =+ Flags {+ verbosity_ = 1+ , debAction_ = Usage+ , dryRun_ = False+ , validate_ = False+ }++insertAtom :: DebAtomKey -> DebAtom -> Atoms -> Atoms+insertAtom mbin atom (Atoms x) = Atoms (insertWith union mbin (singleton atom) x)++insertAtoms :: Set (DebAtomKey, DebAtom) -> Atoms -> Atoms+insertAtoms s atoms =+ case maxView s of+ Nothing -> atoms+ Just ((k, a), s') -> insertAtoms s' (insertAtom k a atoms)++foldAtoms :: (DebAtomKey -> DebAtom -> r -> r) -> r -> Atoms -> r+foldAtoms f r0 (Atoms xs) = Map.foldWithKey (\ k s r -> Set.fold (f k) r s) r0 xs++-- | Split atoms out of an Atoms by predicate.+partitionAtoms :: (DebAtomKey -> DebAtom -> Bool) -> Atoms -> (Set (DebAtomKey, DebAtom), Atoms)+partitionAtoms f deb =+ foldAtoms g (mempty, Atoms mempty) deb+ where+ g k atom (atoms, deb') =+ case f k atom of+ True -> (Set.insert (k, atom) atoms, deb')+ False -> (atoms, insertAtom k atom deb')++deleteAtoms :: (DebAtomKey -> DebAtom -> Bool) -> Atoms -> Atoms+deleteAtoms p atoms = snd (partitionAtoms p atoms)++-- | Split atoms out of a Atoms by predicate.+partitionAtoms' :: (Ord a) => (DebAtomKey -> DebAtom -> Maybe a) -> Atoms -> (Set a, Atoms)+partitionAtoms' f deb =+ foldAtoms g (mempty, Atoms mempty) deb+ where+ g k atom (xs, deb') =+ case f k atom of+ Just x -> (Set.insert x xs, deb')+ Nothing -> (xs, insertAtom k atom deb')++-- | Like modifyAtoms, but...+modifyAtoms' :: (Ord a) =>+ (DebAtomKey -> DebAtom -> Maybe a)+ -> (Set a -> Set (DebAtomKey, DebAtom))+ -> Atoms+ -> Atoms+modifyAtoms' f g atoms =+ insertAtoms (g s) atoms'+ where+ (s, atoms') = partitionAtoms' f atoms
+ src/Debian/Debianize/Bundled.hs view
@@ -0,0 +1,388 @@+-- | Determine whether a specific version of a Haskell package is+-- bundled with into this particular version of the given compiler.++{-# LANGUAGE StandaloneDeriving #-}+module Debian.Debianize.Bundled+ ( ghcBuiltIn+ ) where++import qualified Data.Map as Map+import Data.Set (fromList, member)+import Data.Version (Version(..))+import Debian.Relation.ByteString()+import Distribution.Simple.Compiler (Compiler(..), CompilerId(..), CompilerFlavor(..), {-PackageDB(GlobalPackageDB), compilerFlavor-})+import Distribution.Package (PackageIdentifier(..), PackageName(..) {-, Dependency(..)-})++type Bundled = (CompilerFlavor, Version, [PackageIdentifier])++-- |Return a list of built in packages for the compiler in an environment.+-- ghcBuiltIns :: FilePath -> IO [PackageIdentifier]+-- ghcBuiltIns root =+-- fchroot root (lazyProcess "ghc-pkg" ["list", "--simple-output"] Nothing Nothing empty) >>=+-- return . map parsePackageIdentifier . words . unpack . fst . collectStdout+-- where+-- parsePackageIdentifier s =+-- let (v', n') = break (== '-') (reverse s)+-- (v, n) = (reverse (tail n'), reverse v') in+-- PackageIdentifier (PackageName n) (Version (map read (filter (/= ".") (groupBy (\ a b -> (a == '.') == (b == '.')) v))) [])++ghcBuiltIns :: Compiler -> Bundled+ghcBuiltIns (Compiler {compilerId = CompilerId GHC compilerVersion}) =+ case Map.lookup compilerVersion+ (Map.fromList (map (\ (cmp, ver, lst) -> (ver, (cmp, ver, lst)))+ [ (GHC, Version [7,6,1] [], ghc761BuiltIns)+ , (GHC, Version [7,6,1,20121207] [], ghc761BuiltIns)+ , (GHC, Version [7,4,1] [], ghc741BuiltIns)+ , (GHC, Version [7,4,0,20111219] [], ghc740BuiltIns)+ , (GHC, Version [7,4,0,20120108] [], ghc740BuiltIns)+ , (GHC, Version [7,2,2] [], ghc721BuiltIns)+ , (GHC, Version [7,2,1] [], ghc721BuiltIns)+ , (GHC, Version [7,0,4] [], ghc701BuiltIns)+ , (GHC, Version [7,0,3] [], ghc701BuiltIns)+ , (GHC, Version [7,0,1] [], ghc701BuiltIns)+ , (GHC, Version [6,8,3] [], ghc683BuiltIns)+ , (GHC, Version [6,8,2] [], ghc682BuiltIns)+ , (GHC, Version [6,8,1] [], ghc681BuiltIns)+ , (GHC, Version [6,6,1] [], ghc661BuiltIns)+ , (GHC, Version [6,6] [], ghc66BuiltIns) ])) of+ Nothing -> error $ "cabal-debian: No bundled package list for ghc " ++ show compilerVersion+ Just x -> x+ghcBuiltIns (Compiler {compilerId = _}) = error "ghcBuiltIns: Only GHC is supported"++ghcBuiltIn :: Compiler -> PackageName -> Bool+ghcBuiltIn compiler package =+ Data.Set.member+ package+ (Data.Set.fromList+ (let {- (Just (_, _, xs)) = unsafePerformIO (ghc6BuiltIns compiler) -}+ (_, _, xs) = ghcBuiltIns compiler in map pkgName xs))++v :: String -> [Int] -> PackageIdentifier+v n x = PackageIdentifier (PackageName n) (Version x [])++-- Removed: rts, extensible-exceptions+ghc761BuiltIns :: [PackageIdentifier]+ghc761BuiltIns = [+ v "array" [0,4,0,1],+ v "base" [4,6,0,1],+ v "binary" [0,5,1,1],+ v "bin-package-db" [0,0,0,0],+ v "bytestring" [0,10,0,2],+ v "Cabal" [1,16,0],+ v "containers" [0,5,0,0],+ v "deepseq" [1,3,0,1],+ v "directory" [1,2,0,1],+ v "filepath" [1,3,0,1],+ v "ghc" [7,6,1,20121207],+ v "ghc-prim" [0,3,0,0],+ v "haskell2010" [1,1,1,0],+ v "haskell98" [2,0,0,2],+ v "hoopl" [3,9,0,0],+ v "hpc" [0,6,0,0],+ v "integer-gmp" [0,5,0,0],+ v "old-locale" [1,0,0,5],+ v "old-time" [1,1,0,1],+ v "pretty" [1,1,1,0],+ v "process" [1,1,0,2],+ v "template-haskell" [2,8,0,0],+ v "time" [1,4,0,1],+ v "unix" [2,6,0,1]+ ]++-- | Packages bundled with 7.4.0.20111219-2.+ghc741BuiltIns :: [PackageIdentifier]+ghc741BuiltIns = [+ v "Cabal" [1,14,0],+ v "array" [0,4,0,0],+ v "base" [4,5,0,0],+ v "bin-package-db" [0,0,0,0],+ v "binary" [0,5,1,0],+ v "bytestring" [0,9,2,1],+ v "containers" [0,4,2,1],+ v "deepseq" [1,3,0,0],+ v "directory" [1,1,0,2],+ v "extensible-exceptions" [0,1,1,4],+ v "filepath" [1,3,0,0],+ v "ghc" [7,4,1],+ v "ghc-prim" [0,2,0,0],+ v "haskell2010" [1,1,0,1],+ v "haskell98" [2,0,0,1],+ v "hoopl" [3,8,7,2],+ v "hpc" [0,5,1,1],+ v "integer-gmp" [0,4,0,0],+ v "old-locale" [1,0,0,4],+ v "old-time" [1,1,0,0],+ v "pretty" [1,1,1,0],+ v "process" [1,1,0,1], + v "rts" [1,0],+ v "template-haskell" [2,7,0,0],+ v "time" [1,4],+ v "unix" [2,5,1,0] ]++-- | Packages bundled with 7.4.0.20111219-2.+ghc740BuiltIns :: [PackageIdentifier]+ghc740BuiltIns = [+ v "Cabal" [1,14,0],+ v "array" [0,4,0,0],+ v "base" [4,5,0,0],+ v "bin-package-db" [0,0,0,0],+ v "binary" [0,5,1,0],+ v "bytestring" [0,9,2,1],+ v "containers" [0,4,2,1],+ v "deepseq" [1,3,0,0],+ v "directory" [1,1,0,2],+ v "extensible-exceptions" [0,1,1,4],+ v "filepath" [1,3,0,0],+ v "ghc" [7,4,0,20111219],+ v "ghc-prim" [0,2,0,0],+ v "haskell2010" [1,1,0,1],+ v "haskell98" [2,0,0,1],+ v "hoopl" [3,8,7,2],+ v "hpc" [0,5,1,1],+ v "integer-gmp" [0,4,0,0],+ v "old-locale" [1,0,0,4],+ v "old-time" [1,1,0,0],+ v "pretty" [1,1,1,0],+ v "process" [1,1,0,1], + v "rts" [1,0],+ v "template-haskell" [2,7,0,0],+ v "time" [1,4],+ v "unix" [2,5,1,0] ]++ghc721BuiltIns :: [PackageIdentifier]+ghc721BuiltIns = [+ v "Cabal" [1,12,0],+ v "array" [0,3,0,3],+ v "base" [4,4,0,0],+ v "bin-package-db" [0,0,0,0],+ v "binary" [0,5,0,2],+ v "bytestring" [0,9,2,0],+ v "containers" [0,4,1,0],+ v "directory" [1,1,0,1],+ v "extensible-exceptions" [0,1,1,3],+ v "filepath" [1,2,0,1],+ v "ghc" [7,2,1],+ -- ghc-binary renamed to binary+ v "ghc-prim" [0,2,0,0],+ v "haskell2010" [1,1,0,0],+ v "haskell98" [2,0,0,0],+ v "hoopl" [3,8,7,1], -- new+ v "hpc" [0,5,1,0],+ v "integer-gmp" [0,3,0,0],+ v "old-locale" [1,0,0,3],+ v "old-time" [1,0,0,7],+ v "pretty" [1,1,0,0],+ v "process" [1,1,0,0], + -- random removed+ v "rts" [1,0],+ v "template-haskell" [2,6,0,0],+ v "time" [1,2,0,5],+ v "unix" [2,5,0,0] ]++ghc701BuiltIns :: [PackageIdentifier]+ghc701BuiltIns = [+ v "Cabal" [1,10,0,0],+ v "array" [0,3,0,2],+ v "base" [4,3,0,0],+ v "bin-package-db" [0,0,0,0],+ v "bytestring" [0,9,1,8],+ v "containers" [0,4,0,0],+ v "directory" [1,1,0,0],+ v "extensible-exceptions" [0,1,1,2],+ v "filepath" [1,2,0,0],+ v "ghc" [7,0,1],+ v "ghc-binary" [0,5,0,2],+ v "ghc-prim" [0,2,0,0],+ v "haskell2010" [1,0,0,0],+ v "haskell98" [1,1,0,0],+ v "hpc" [0,5,0,6],+ v "integer-gmp" [0,2,0,2],+ v "old-locale" [1,0,0,2],+ v "old-time" [1,0,0,6],+ v "pretty" [1,0,1,2],+ v "process" [1,0,1,4],+ v "random" [1,0,0,3],+ v "rts" [1,0],+ v "template-haskell" [2,5,0,0],+ v "time" [1,2,0,3],+ v "unix" [2,4,1,0]+ ]++ghc683BuiltIns :: [PackageIdentifier]+ghc683BuiltIns = ghc682BuiltIns++ghc682BuiltIns :: [PackageIdentifier]+ghc682BuiltIns = [+ v "Cabal" [1,2,3,0],+ v "array" [0,1,0,0],+ v "base" [3,0,1,0],+ v "bytestring" [0,9,0,1],+ v "containers" [0,1,0,1],+ v "directory" [1,0,0,0],+ v "filepath" [1,1,0,0],+ v "ghc" [6,8,2,0],+ v "haskell98" [1,0,1,0],+ v "hpc" [0,5,0,0],+ v "old-locale" [1,0,0,0],+ v "old-time" [1,0,0,0],+ v "packedstring" [0,1,0,0],+ v "pretty" [1,0,0,0],+ v "process" [1,0,0,0],+ v "random" [1,0,0,0],+ v "readline" [1,0,1,0],+ v "template-haskell" [2,2,0,0],+ v "unix" [2,3,0,0]+ ]++ghc681BuiltIns :: [PackageIdentifier]+ghc681BuiltIns = [+ v "base" [3,0,0,0],+ v "Cabal" [1,2,2,0],+ v "GLUT" [2,1,1,1],+ v "HGL" [3,2,0,0],+ v "HUnit" [1,2,0,0],+ v "OpenAL" [1,3,1,1],+ v "OpenGL" [2,2,1,1],+ v "QuickCheck" [1,1,0,0],+ v "X11" [1,2,3,1],+ v "array" [0,1,0,0],+ v "bytestring" [0,9,0,1],+ v "cgi" [3001,1,5,1],+ v "containers" [0,1,0,0],+ v "directory" [1,0,0,0],+ v "fgl" [5,4,1,1],+ v "filepatch" [1,1,0,0],+ v "ghc" [6,8,1,0],+ v "haskell-src" [1,0,1,1],+ v "haskell98" [1,0,1,0],+ v "hpc" [0,5,0,0],+ v "html" [1,0,1,1],+ v "mtl" [1,1,0,0],+ v "network" [2,1,0,0],+ v "old-locale" [1,0,0,0],+ v "old-time" [1,0,0,0],+ v "packedstring" [0,1,0,0],+ v "parallel" [1,0,0,0],+ v "parsec" [2,1,0,0],+ v "pretty" [1,0,0,0],+ v "process" [1,0,0,0],+ v "random" [1,0,0,0],+ v "readline" [1,0,1,0],+ v "regex-base" [0,72,0,1],+ v "regex-compat" [0,71,0,1],+ v "regex-posix" [0,72,0,1],+ v "stm" [2,1,1,0],+ v "template-haskell" [2,2,0,0],+ v "time" [1,1,2,0],+ v "unix" [2,2,0,0],+ v "xhtml" [3000,0,2,1]+ ]++ghc661BuiltIns :: [PackageIdentifier]+ghc661BuiltIns = [+ v "base" [2,1,1],+ v "Cabal" [1,1,6,2],+ v "cgi" [3001,1,1],+ v "fgl" [5,4,1],+ v "filepath" [1,0],+ v "ghc" [6,6,1],+ v "GLUT" [2,1,1],+ v "haskell98" [1,0],+ v "haskell-src" [1,0,1],+ v "HGL" [3,1,1],+ v "html" [1,0,1],+ v "HUnit" [1,1,1],+ v "mtl" [1,0,1],+ v "network" [2,0,1],+ v "OpenAL" [1,3,1],+ v "OpenGL" [2,2,1],+ v "parsec" [2,0],+ v "QuickCheck" [1,0,1],+ v "readline" [1,0],+ v "regex-base" [0,72],+ v "regex-compat" [0,71],+ v "regex-posix" [0,71],+ v "rts" [1,0],+ v "stm" [2,0],+ v "template-haskell" [2,1],+ v "time" [1,1,1],+ v "unix" [2,1],+ v "X11" [1,2,1],+ v "xhtml" [3000,0,2]+ ]++ghc66BuiltIns :: [PackageIdentifier]+ghc66BuiltIns = [+ v "base" [2,0],+ v "Cabal" [1,1,6],+ v "cgi" [2006,9,6],+ v "fgl" [5,2],+ v "ghc" [6,6],+ v "GLUT" [2,0],+ v "haskell98" [1,0],+ v "haskell-src" [1,0],+ v "HGL" [3,1],+ v "html" [1,0],+ v "HTTP" [2006,7,7],+ v "HUnit" [1,1],+ v "mtl" [1,0],+ v "network" [2,0],+ v "OpenAL" [1,3],+ v "OpenGL" [2,1],+ v "parsec" [2,0],+ v "QuickCheck" [1,0],+ v "readline" [1,0],+ v "regex-base" [0,71],+ v "regex-compat" [0,71],+ v "regex-posix" [0,71],+ v "rts" [1,0],+ v "stm" [2,0],+ v "template-haskell" [2,0],+ v "time" [1,0],+ v "unix" [1,0],+ v "X11" [1,1],+ v "xhtml" [2006,9,13]+ ]++-- Script to output a list of the libraries in the ghc package+-- provides line. This could be run inside the build environment+-- instead of having these hard coded lists.+--+-- apt-cache show ghc \+-- | grep ^Provides: \+-- | cut -d\ -f2-+-- | sed 's/, /\n/g' \+-- | grep libghc- \+-- | cut -d- -f2- \+-- | grep dev$ \+-- | sort -u \+-- | sed 's/-dev//;s/$/",/;s/^/"/'+--+-- base :: Set String+-- base+-- = Data.Set.fromList+-- [ "array",+-- "base",+-- "binary",+-- "bin-package-db",+-- "bytestring",+-- "cabal",+-- "containers",+-- "deepseq",+-- "directory",+-- "extensible-exceptions",+-- "filepath",+-- "ghc-prim",+-- "haskell2010",+-- "haskell98",+-- "hoopl",+-- "hpc",+-- "integer-gmp",+-- "old-locale",+-- "old-time",+-- "pretty",+-- "process",+-- "rts",+-- "template-haskell",+-- "time",+-- "unix" ]
+ src/Debian/Debianize/ControlFile.hs view
@@ -0,0 +1,206 @@+-- | Preliminary.+{-# LANGUAGE DeriveDataTypeable, FlexibleInstances #-}+module Debian.Debianize.ControlFile+ ( SourceDebDescription(..)+ , newSourceDebDescription+ , newSourceDebDescription'+ , VersionControlSpec(..)+ , XField(..)+ , XFieldDest(..)+ , BinaryDebDescription(..)+ , newBinaryDebDescription+ , modifyBinaryDeb+ -- , modifyBinaryDescription+ , PackageRelations(..)+ , PackageType(..)+ , packageArch+ ) where++import Data.Generics (Data, Typeable)+import Data.Monoid (mempty)+import Data.Set as Set (Set, empty)+import Data.Text (Text)+import Debian.Orphans ()+import Debian.Policy (StandardsVersion, PackagePriority, PackageArchitectures(..), Section)+import Debian.Relation (Relations, SrcPkgName(..), BinPkgName)+import Prelude hiding (init, log)+import Text.ParserCombinators.Parsec.Rfc2822 (NameAddr)++-- | This type represents the debian/control file, which is the core+-- of the source package debianization. It includes the information+-- that goes in the first, or source, section, and then a list of the+-- succeeding binary package sections.+data SourceDebDescription+ = SourceDebDescription+ { source :: Maybe SrcPkgName+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Source>+ , maintainer :: Maybe NameAddr+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Maintainer>+ , changedBy :: Maybe NameAddr+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Changed-By>+ , uploaders :: [NameAddr]+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Uploaders>+ , dmUploadAllowed :: Bool+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-DM-Upload-Allowed>+ , priority :: Maybe PackagePriority+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Priority>+ , section :: Maybe Section+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Section>+ , standardsVersion :: Maybe StandardsVersion+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Standards-Version>+ , homepage :: Maybe Text+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Homepage>+ , vcsFields :: Set VersionControlSpec+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-VCS-fields>+ , xFields :: Set XField+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s5.7>+ , buildDepends :: Relations+ , buildConflicts :: Relations+ , buildDependsIndep :: Relations+ , buildConflictsIndep :: Relations+ , binaryPackages :: [BinaryDebDescription] -- This should perhaps be a set, or a map+ } deriving (Eq, Ord, Show, Data, Typeable)++newSourceDebDescription :: SourceDebDescription+newSourceDebDescription =+ SourceDebDescription+ { source = Nothing+ , maintainer = Nothing+ , changedBy = Nothing+ , uploaders = []+ , dmUploadAllowed = False+ , priority = Nothing+ , section = Nothing+ , buildDepends = []+ , buildConflicts = []+ , buildDependsIndep = []+ , buildConflictsIndep = []+ , standardsVersion = Nothing+ , homepage = Nothing+ , vcsFields = Set.empty+ , xFields = Set.empty+ , binaryPackages = [] }++newSourceDebDescription' :: SrcPkgName -> NameAddr -> SourceDebDescription+newSourceDebDescription' src who =+ newSourceDebDescription+ { source = Just src+ , maintainer = Just who }++data VersionControlSpec+ = VCSBrowser Text+ | VCSArch Text+ | VCSBzr Text+ | VCSCvs Text+ | VCSDarcs Text+ | VCSGit Text+ | VCSHg Text+ | VCSMtn Text+ | VCSSvn Text+ deriving (Eq, Ord, Show, Data, Typeable)++-- | User defined fields. Parse the line "XBS-Comment: I stand+-- between the candle and the star." to get XField (fromList "BS")+-- "Comment" " I stand between the candle and the star."+data XField+ = XField (Set XFieldDest) Text Text+ deriving (Eq, Ord, Show, Data, Typeable)++data XFieldDest+ = B -- ^ Field will be copied to the binary packgae control files+ | S -- ^ Field will be copied to the source packgae control files+ | C -- ^ Field will be copied to the upload control (.changes) file+ deriving (Eq, Ord, Read, Show, Data, Typeable)++-- | This type represents a section of the control file other than the+-- first, which in turn represent one of the binary packages or debs+-- produced by this debianization.+data BinaryDebDescription+ = BinaryDebDescription+ { package :: BinPkgName+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Package>+ , architecture :: PackageArchitectures+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Architecture>+ , binarySection :: Maybe Section+ , binaryPriority :: Maybe PackagePriority+ , essential :: Bool+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Essential>+ , description :: Text+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Description>+ , relations :: PackageRelations+ -- ^ <http://www.debian.org/doc/debian-policy/ch-controlfields.html#s5.6.10>+ } deriving (Eq, Ord, Read, Show, Data, Typeable)++newBinaryDebDescription :: BinPkgName -> PackageArchitectures -> BinaryDebDescription+newBinaryDebDescription name arch =+ BinaryDebDescription+ { package = name -- mkPkgName base typ+ , architecture = arch -- packageArch typ+ , binarySection = Nothing+ , binaryPriority = Nothing+ , essential = False+ , description = mempty+ , relations = newPackageRelations }++-- | Modify the description of one of the binary debs without changing+-- the package order.+modifyBinaryDeb :: BinPkgName -> (Maybe BinaryDebDescription -> BinaryDebDescription) -> SourceDebDescription -> SourceDebDescription+modifyBinaryDeb bin f deb =+ deb {binaryPackages = bins'}+ where+ bins' = if any (\ x -> package x == bin) bins+ then map g (binaryPackages deb)+ else binaryPackages deb ++ [f Nothing]+ g x = if package x == bin then f (Just x) else x+ bins = binaryPackages deb++-- ^ Package interrelationship information.+data PackageRelations+ = PackageRelations+ { depends :: Relations+ , recommends :: Relations+ , suggests :: Relations+ , preDepends :: Relations+ , breaks :: Relations+ , conflicts :: Relations+ , provides :: Relations+ , replaces :: Relations+ , builtUsing :: Relations+ } deriving (Eq, Ord, Read, Show, Data, Typeable)++newPackageRelations :: PackageRelations+newPackageRelations =+ PackageRelations+ { depends = []+ , recommends = []+ , suggests = []+ , preDepends = []+ , breaks = []+ , conflicts = []+ , provides = []+ , replaces = []+ , builtUsing = [] }++-- ^ The different types of binary debs we can produce from a haskell package+data PackageType+ = Development -- ^ The libghc-foo-dev package.+ | Profiling -- ^ The libghc-foo-prof package.+ | Documentation -- ^ The libghc-foo-doc package.+ | Exec -- ^ A package related to a particular executable, perhaps+ -- but not necessarily a server.+ | Utilities -- ^ A package that holds the package's data files+ -- and any executables not assigned to other+ -- packages.+ | Source' -- ^ The source package (not a binary deb actually.)+ | Cabal -- ^ This is used to construct the value for+ -- DEB_CABAL_PACKAGE in the rules file+ deriving (Eq, Show)++packageArch :: PackageType -> PackageArchitectures+packageArch Development = Any+packageArch Profiling = Any+packageArch Documentation = All+packageArch Utilities = All+packageArch Exec = Any+packageArch Cabal = undefined+packageArch Source' = undefined
+ src/Debian/Debianize/Debianize.hs view
@@ -0,0 +1,304 @@+{-# LANGUAGE FlexibleInstances, OverloadedStrings, ScopedTypeVariables, StandaloneDeriving, TupleSections, TypeSynonymInstances #-}+{-# OPTIONS -Wall -fno-warn-name-shadowing -fno-warn-orphans #-}++-- | Generate a package Debianization from Cabal data and command line+-- options.++module Debian.Debianize.Debianize+ ( cabalDebian+ , callDebianize+ , runDebianize+ , debianize+ , debianization+ , writeDebianization+ , describeDebianization+ , compareDebianization+ , validateDebianization+ ) where++import Control.Applicative ((<$>), (<*>), pure)+import Control.Exception (catch)+import Data.Algorithm.Diff.Context (contextDiff)+import Data.Algorithm.Diff.Pretty (prettyDiff)+import Data.Lens.Lazy (getL, setL, modL)+import Data.List as List (unlines, intercalate, nub)+import Data.Map as Map (lookup, toList, elems)+import Data.Maybe+import Data.Monoid ((<>))+import Data.Set as Set (toList)+import Data.Text as Text (Text, unpack, split)+import Data.Version (Version)+import Debian.Changes (ChangeLog(..), ChangeLogEntry(..))+import Debian.Debianize.Atoms (Atoms, packageDescription, compat, watch, control, copyright, changelog, comments,+ sourcePriority, sourceSection, debAction, validate, dryRun, debVersion, revision,+ sourcePackageName, epochMap, extraLibMap)+import Debian.Debianize.ControlFile as Debian (SourceDebDescription(..), BinaryDebDescription(..), PackageRelations(..), PackageType(..))+import Debian.Debianize.Dependencies (debianName)+import Debian.Debianize.Files (toFileMap)+import Debian.Debianize.Finalize (finalizeDebianization)+import Debian.Debianize.Goodies (defaultAtoms, watchAtom)+import Debian.Debianize.Input (inputDebianization, inputCabalization, inputCopyright, inputMaintainer, inputChangeLog)+import Debian.Debianize.Options (options, compileArgs)+import Debian.Debianize.SubstVars (substvars)+import Debian.Debianize.Types (DebAction(..))+import Debian.Debianize.Utility (withCurrentDirectory, foldEmpty, replaceFile, zipMaps, indent)+import Debian.Policy (PackagePriority(Optional), Section(MainSection), getDebhelperCompatLevel)+import Debian.Relation (SrcPkgName(..), BinPkgName(BinPkgName), Relation(Rel))+import Debian.Release (parseReleaseName)+import Debian.Version (DebianVersion, parseDebianVersion, buildDebianVersion)+import Debian.Time (getCurrentLocalRFC822Time)+import Distribution.Package (PackageIdentifier(..))+import qualified Distribution.PackageDescription as Cabal+import Prelude hiding (writeFile, unlines)+import System.Console.GetOpt (usageInfo)+import System.Directory (doesFileExist, Permissions(executable), getPermissions, setPermissions, createDirectoryIfMissing)+import System.Environment (getArgs, getEnv, getProgName)+import System.Exit (ExitCode(ExitSuccess))+import System.FilePath ((</>), takeDirectory)+import System.IO.Error (catchIOError)+import System.Posix.Env (setEnv)+import System.Process (readProcessWithExitCode)+import Text.ParserCombinators.Parsec.Rfc2822 (NameAddr)+import Text.PrettyPrint.ANSI.Leijen (Pretty(pretty))++{-+import Debian.Debianize.ControlFile as Debian (SourceDebDescription(source, binaryPackages), BinaryDebDescription(package))+import Debian.Debianize.Files (toFileMap)+import Debian.Debianize.Goodies (defaultAtoms)+import Debian.Debianize.Input (inputDebianization)+import Debian.Debianize.Utility (withCurrentDirectory)+import System.Directory (Permissions(executable), getPermissions, setPermissions, createDirectoryIfMissing)+import Text.PrettyPrint.ANSI.Leijen (pretty)+-}++-- | The main function for the cabal-debian executable.+cabalDebian :: IO ()+cabalDebian =+ compileAllArgs defaultAtoms >>= \ atoms ->+ case getL debAction atoms of+ SubstVar debType -> substvars atoms debType+ Debianize -> debianize "." atoms+ Usage -> do+ progName <- getProgName+ let info = "Usage: " ++ progName ++ " [FLAGS]\n"+ putStrLn (usageInfo info options)++-- | Compile the given arguments into an Atoms value and run the+-- debianize function. This is basically equivalent to @cabal-debian+-- --debianize@, except that the command line arguments come from the+-- function parameter.+callDebianize :: [String] -> IO ()+callDebianize args = compileEnvironmentArgs defaultAtoms >>= debianize "." . compileArgs args++-- | Put an argument list into the @CABALDEBIAN@ environment variable+-- and then run the script in debian/Debianize.hs. If this exists and+-- succeeds the return value is True, it may be assumed that a+-- debianization was created in the debian subdirectory of the current+-- directory. This is used to create customized debianizations that+-- are to sophisticated for the command line argument interface+-- available to the cabal-debian executable.+runDebianize :: [String] -> IO Bool+runDebianize args =+ doesFileExist "debian/Debianize.hs" >>= \ exists ->+ case exists of+ False -> return False+ True ->+ putEnvironmentArgs args >> readProcessWithExitCode "runhaskell" ("debian/Debianize.hs" : args) "" >>= \ result ->+ case result of+ (ExitSuccess, _, _) -> return True+ (code, out, err) ->+ error ("runDebianize failed with " ++ show code ++ ":\n stdout: " ++ show out ++"\n stderr: " ++ show err)++-- | Generate a debianization, and then either validate, describe, or+-- write it out dependeing on the command line arguments.+debianize :: FilePath -> Atoms -> IO ()+debianize top atoms =+ debianization top atoms >>= \ atoms' ->+ if getL validate atoms'+ then inputDebianization top >>= \ old -> either error return (validateDebianization old atoms')+ else if getL dryRun atoms'+ then inputDebianization top >>= \ old ->+ putStr . ("Debianization (dry run):\n" ++) $ compareDebianization old atoms'+ else writeDebianization top atoms'++-- | Given an Atoms value, get any additional configuration+-- information from the environment, read the cabal package+-- description and possibly the debian/changelog file, then generate+-- and return the new debianization (along with the data directory+-- computed from the cabal package description.)+debianization :: FilePath -> Atoms -> IO Atoms+debianization top atoms =+ do log <- (Just <$> inputChangeLog "debian") `catch` (\ (_ :: IOError) -> return Nothing)+ atoms' <- inputCabalization top atoms+ date <- getCurrentLocalRFC822Time+ maint <- inputMaintainer atoms' >>= maybe (error "Missing value for --maintainer") return+ level <- getDebhelperCompatLevel+ copyright <- withCurrentDirectory top $ inputCopyright (fromMaybe (error $ "cabalToDebianization: Failed to read cabal file in " ++ show top)+ (getL packageDescription atoms'))+ return $ debianization' date copyright maint level log atoms'++debianization' :: String -- ^ current date+ -> Text -- ^ copyright+ -> NameAddr -- ^ maintainer+ -> Int -- ^ Default standards version+ -> Maybe ChangeLog+ -> Atoms -- ^ Debianization specification+ -> Atoms -- ^ New debianization+debianization' date copyright' maint level log deb =+ finalizeDebianization $+ modL compat (maybe (Just level) Just) $+ modL changelog (maybe log Just) $+ setL sourcePriority (Just Optional) $+ setL sourceSection (Just (MainSection "haskell")) $+ setL watch (Just (watchAtom (pkgName $ Cabal.package $ pkgDesc))) $+ setL copyright (Just (Right copyright')) $+ versionInfo maint date $+ addExtraLibDependencies $+ -- Do we want to replace the atoms in the old deb, or add these?+ -- Or should we delete even more information from the original,+ -- keeping only the changelog? Probably the latter. So this is+ -- somewhat wrong.+ deb+ where+ pkgDesc = fromMaybe (error "debianization") $ getL packageDescription deb++-- | Set the debianization's version info - everything that goes into+-- the new changelog entry, source package name, exact debian version,+-- log comments, maintainer name, revision date.+versionInfo :: NameAddr -> String -> Atoms -> Atoms+versionInfo debianMaintainer date deb =+ modL changelog (const (Just newLog)) $+ modL control (\ y -> y { source = Just sourceName, Debian.maintainer = Just debianMaintainer }) deb+ where+ newLog =+ case getL changelog deb of+ Nothing -> ChangeLog [newEntry]+ Just (ChangeLog oldEntries) ->+ case dropWhile (\ entry -> logVersion entry > logVersion newEntry) oldEntries of+ -- If the new package version number matches the old, merge the new and existing log entries+ entry@(Entry {logVersion = d}) : older | d == logVersion newEntry -> ChangeLog (merge entry newEntry : older)+ -- Otherwise prepend the new entry+ entries -> ChangeLog (newEntry : entries)+ newEntry = Entry { logPackage = show (pretty sourceName)+ , logVersion = convertVersion debinfo (pkgVersion pkgId)+ , logDists = [parseReleaseName "unstable"]+ , logUrgency = "low"+ , logComments = List.unlines $ (map ((" * " <>) . List.intercalate "\n " . map unpack)) (fromMaybe [["Debianization generated by cabal-debian"]] (getL comments deb))+ , logWho = show (pretty debianMaintainer)+ , logDate = date }+ -- Get the source package name, either from the SourcePackageName+ -- atom or construct it from the cabal package name.+ sourceName :: SrcPkgName+ sourceName = maybe (debianName deb Source' pkgId) id (getL sourcePackageName deb)+ merge :: ChangeLogEntry -> ChangeLogEntry -> ChangeLogEntry+ merge old new =+ old { logComments = logComments old ++ logComments new+ , logDate = date }+ debinfo = maybe (Right (epoch, fromMaybe "" (getL revision deb))) Left (getL debVersion deb)+ epoch = Map.lookup (pkgName pkgId) (getL epochMap deb)+ pkgId = Cabal.package pkgDesc+ pkgDesc = fromMaybe (error "versionInfo: no PackageDescription") $ getL packageDescription deb++-- | Combine various bits of information to produce the debian version+-- which will be used for the debian package. If the override+-- parameter is provided this exact version will be used, but an error+-- will be thrown if that version is unusably old - i.e. older than+-- the cabal version of the package. Otherwise, the cabal version is+-- combined with the given epoch number and revision string to create+-- a version.+convertVersion :: Either DebianVersion (Maybe Int, String) -> Version -> DebianVersion+convertVersion debinfo cabalVersion =+ case debinfo of+ Left override | override >= parseDebianVersion (show (pretty cabalVersion)) -> override+ Left override -> error ("Version from --deb-version (" ++ show (pretty override) +++ ") is older than hackage version (" ++ show (pretty cabalVersion) +++ "), maybe you need to unpin this package?")+ Right (debianEpoch, debianRevision) ->+ buildDebianVersion debianEpoch+ (show (pretty cabalVersion))+ (foldEmpty Nothing Just debianRevision)++-- | Convert the extraLibs field of the cabal build info into debian+-- binary package names and make them dependendencies of the debian+-- devel package (if there is one.)+addExtraLibDependencies :: Atoms -> Atoms+addExtraLibDependencies deb =+ modL control (\ y -> y {binaryPackages = map f (binaryPackages (getL control deb))}) deb+ where+ f :: BinaryDebDescription -> BinaryDebDescription+ f bin+ | debianName deb Development (Cabal.package pkgDesc) == Debian.package bin+ = bin { relations = g (relations bin) }+ f bin = bin+ g :: Debian.PackageRelations -> Debian.PackageRelations+ g rels = rels { Debian.depends = Debian.depends rels +++ map anyrel' (concatMap (\ cab -> maybe [BinPkgName ("lib" ++ cab ++ "-dev")] Set.toList (Map.lookup cab (getL extraLibMap deb)))+ (nub $ concatMap Cabal.extraLibs $ Cabal.allBuildInfo $ pkgDesc)) }+ pkgDesc = fromMaybe (error "addExtraLibDependencies: no PackageDescription") $ getL packageDescription deb++anyrel' :: BinPkgName -> [Relation]+anyrel' x = [Rel x Nothing Nothing]++compileAllArgs :: Atoms -> IO Atoms+compileAllArgs atoms0 = compileEnvironmentArgs atoms0 >>= compileCommandlineArgs++compileEnvironmentArgs :: Atoms -> IO Atoms+compileEnvironmentArgs atoms0 = (compileArgs <$> (read <$> getEnv "CABALDEBIAN") <*> pure atoms0) `catchIOError` const (return atoms0)++compileCommandlineArgs :: Atoms -> IO Atoms+compileCommandlineArgs atoms0 = compileArgs <$> getArgs <*> pure atoms0++-- | Insert a value for CABALDEBIAN into the environment that the+-- withEnvironment* functions above will find and use. E.g.+-- putEnvironmentFlags ["--dry-run", "--validate"] (debianize defaultFlags)+putEnvironmentArgs :: [String] -> IO ()+putEnvironmentArgs fs = setEnv "CABALDEBIAN" (show fs) True++-- | Write the files of the debianization @d@ to the directory @top@.+writeDebianization :: FilePath -> Atoms -> IO ()+writeDebianization top d =+ withCurrentDirectory top $+ mapM_ (\ (path, text) ->+ createDirectoryIfMissing True (takeDirectory path) >>+ replaceFile path (unpack text))+ (Map.toList (toFileMap d)) >>+ getPermissions "debian/rules" >>= setPermissions "debian/rules" . (\ p -> p {executable = True})++describeDebianization :: Atoms -> String+describeDebianization atoms =+ concatMap (\ (path, text) -> path ++ ":\n" ++ indent " > " (unpack text)) (Map.toList (toFileMap atoms))++-- | Compare the existing debianization in @top@ to the generated one+-- @new@, returning a string describing the differences.+compareDebianization :: Atoms -> Atoms -> String+compareDebianization old new =+ concat . Map.elems $ zipMaps doFile (toFileMap old) (toFileMap new)+ where+ doFile :: FilePath -> Maybe Text -> Maybe Text -> Maybe String+ doFile path (Just _) Nothing = Just (path ++ ": Deleted\n")+ doFile path Nothing (Just n) = Just (path ++ ": Created\n" ++ indent " | " (unpack n))+ doFile path (Just o) (Just n) =+ if o == n+ then Nothing -- Just (path ++ ": Unchanged\n")+ else Just (show (prettyDiff ("old" </> path) ("new" </> path) (contextDiff 2 (split (== '\n') o) (split (== '\n') n))))+ doFile _path Nothing Nothing = error "Internal error in zipMaps"++-- | Don't change anything, just make sure the new debianization+-- matches the existing debianization in several particulars -+-- specifically, version number, and source and binary package names.+validateDebianization :: Atoms -> Atoms -> Either String ()+validateDebianization old new =+ case () of+ _ | oldVersion /= newVersion -> Left ("Version mismatch, expected " ++ show (pretty oldVersion) ++ ", found " ++ show (pretty newVersion))+ | oldSource /= newSource -> Left ("Source mismatch, expected " ++ show (pretty oldSource) ++ ", found " ++ show (pretty newSource))+ | oldPackages /= newPackages -> Left ("Package mismatch, expected " ++ show (pretty oldPackages) ++ ", found " ++ show (pretty newPackages))+ | True -> Right ()+ where+ oldVersion = logVersion (head (unChangeLog (fromMaybe (error "Missing changelog") (getL changelog old))))+ newVersion = logVersion (head (unChangeLog (fromMaybe (error "Missing changelog") (getL changelog new))))+ oldSource = source . getL control $ old+ newSource = source . getL control $ new+ oldPackages = map Debian.package . binaryPackages . getL control $ old+ newPackages = map Debian.package . binaryPackages . getL control $ new+ unChangeLog :: ChangeLog -> [ChangeLogEntry]+ unChangeLog (ChangeLog x) = x
+ src/Debian/Debianize/Dependencies.hs view
@@ -0,0 +1,431 @@+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, OverloadedStrings, RankNTypes, ScopedTypeVariables, StandaloneDeriving, TypeFamilies #-}+{-# OPTIONS -Wall -Wwarn -fno-warn-name-shadowing -fno-warn-orphans #-}+module Debian.Debianize.Dependencies+ ( cabalDependencies -- Debian.Cabal.SubstVars+ , selfDependency -- Debian.Debianize.Combinators+ , allBuildDepends+ , debDeps+ , putBuildDeps+ , dependencies+ , debianName+ , debNameFromType+ , getRulesHead+ , filterMissing+ , binaryPackageDeps+ , binaryPackageConflicts+ ) where++import Data.Char (isSpace, toLower)+import Data.Function (on)+import Data.Lens.Lazy (getL, modL)+import Data.List as List (nub, minimumBy, isSuffixOf, map)+import Data.Map as Map (Map, lookup, insertWith, empty)+import Data.Maybe (fromMaybe, catMaybes, listToMaybe)+import Data.Monoid ((<>))+import qualified Data.Set as Set+import Data.Text as Text (Text, pack, unlines)+import Data.Version (Version, showVersion)+import Debian.Control+import Debian.Debianize.Atoms (Atoms, packageDescription, rulesHead, compiler, noProfilingLibrary, noDocumentationLibrary,+ missingDependencies, versionSplits, extraLibMap, buildDeps, buildDepsIndep, execMap, epochMap,+ packageInfo, depends, conflicts, control)+import Debian.Debianize.Bundled (ghcBuiltIn)+import Debian.Debianize.ControlFile as Debian (PackageType(..), SourceDebDescription(..))+import Debian.Debianize.Interspersed (Interspersed(leftmost, pairs, foldInverted), foldTriples)+import Debian.Debianize.Types (PackageInfo(devDeb, profDeb, docDeb), VersionSplits(..), DebType(..))+import Debian.Orphans ()+import qualified Debian.Relation as D+import Debian.Relation (Relations, Relation, BinPkgName(BinPkgName), PkgName(pkgNameFromString))+import Debian.Version (parseDebianVersion)+import Distribution.Package (PackageName(PackageName), PackageIdentifier(..), Dependency(..))+import Distribution.PackageDescription as Cabal (PackageDescription(..), allBuildInfo, buildTools, pkgconfigDepends, extraLibs)+import Distribution.Version (VersionRange, anyVersion, foldVersionRange', intersectVersionRanges, unionVersionRanges,+ laterVersion, orLaterVersion, earlierVersion, orEarlierVersion, fromVersionIntervals, toVersionIntervals, withinVersion,+ isNoVersion, asVersionIntervals)+import Distribution.Version.Invert (invertVersionRange)+import Prelude hiding (unlines)+import System.Exit (ExitCode(ExitSuccess))+import System.IO.Unsafe (unsafePerformIO)+import System.Process (readProcessWithExitCode)+import Text.PrettyPrint.ANSI.Leijen (Pretty(pretty))++data Dependency_+ = BuildDepends Dependency+ | BuildTools Dependency+ | PkgConfigDepends Dependency+ | ExtraLibs D.BinPkgName+ deriving (Eq, Show)++-- | In cabal a self dependency probably means the library is needed+-- while building the executables. In debian it would mean that the+-- package needs an earlier version of itself to build, so we use this+-- to filter such dependencies out.+selfDependency :: PackageIdentifier -> Dependency_ -> Bool+selfDependency pkgId (BuildDepends (Dependency name _)) = name == pkgName pkgId+selfDependency _ _ = False++unboxDependency :: Dependency_ -> Maybe Dependency+unboxDependency (BuildDepends d) = Just d+unboxDependency (BuildTools d) = Just d+unboxDependency (PkgConfigDepends d) = Just d+unboxDependency (ExtraLibs _) = Nothing -- Dependency (PackageName d) anyVersion++-- Make a list of the debian devel packages corresponding to cabal packages+-- which are build dependencies+debDeps :: DebType -> Atoms -> Control' String -> D.Relations+debDeps debType atoms control =+ interdependencies ++ otherdependencies+ where+ interdependencies =+ case debType of+ Prof -> maybe [] (\ name -> [[D.Rel name Nothing Nothing]]) (debNameFromType control Dev)+ _ -> []+ otherdependencies =+ catMaybes (map (\ (Dependency name _) ->+ case Map.lookup name (getL packageInfo atoms) of+ Just p -> maybe Nothing (\ (s, v) -> Just [D.Rel s (Just (D.GRE v)) Nothing]) (case debType of+ Dev -> devDeb p+ Prof -> profDeb p+ Doc -> docDeb p)+ Nothing -> Nothing) (cabalDependencies atoms))++cabalDependencies :: Atoms -> [Dependency]+cabalDependencies atoms =+ catMaybes $ map unboxDependency $ allBuildDepends atoms+ (Cabal.buildDepends (fromMaybe (error "cabalDependencies") $ getL packageDescription atoms))+ (concatMap buildTools . allBuildInfo . fromMaybe (error "cabalDependencies") $ getL packageDescription atoms)+ (concatMap pkgconfigDepends . allBuildInfo . fromMaybe (error "cabalDependencies") $ getL packageDescription atoms)+ (concatMap extraLibs . allBuildInfo . fromMaybe (error "cabalDependencies") $ getL packageDescription atoms)++-- |Debian packages don't have per binary package build dependencies,+-- so we just gather them all up here.+allBuildDepends :: Atoms -> [Dependency] -> [Dependency] -> [Dependency] -> [String] -> [Dependency_]+allBuildDepends atoms buildDepends buildTools pkgconfigDepends extraLibs =+ nub $ map BuildDepends buildDepends +++ map BuildTools buildTools +++ map PkgConfigDepends pkgconfigDepends +++ map ExtraLibs (fixDeps extraLibs)+ where+ fixDeps :: [String] -> [D.BinPkgName]+ fixDeps xs = concatMap (\ cab -> maybe [D.BinPkgName ("lib" ++ cab ++ "-dev")] Set.toList (Map.lookup cab (getL extraLibMap atoms))) xs++putBuildDeps :: Atoms -> Atoms+putBuildDeps deb =+ modL control (\ y -> y { Debian.buildDepends = debianBuildDeps deb, buildDependsIndep = debianBuildDepsIndep deb }) deb++-- The haskell-cdbs package contains the hlibrary.mk file with+-- the rules for building haskell packages.+debianBuildDeps :: Atoms -> D.Relations+debianBuildDeps deb =+ filterMissing deb $+ nub $ [[D.Rel (D.BinPkgName "debhelper") (Just (D.GRE (parseDebianVersion ("7.0" :: String)))) Nothing],+ [D.Rel (D.BinPkgName "haskell-devscripts") (Just (D.GRE (parseDebianVersion ("0.8" :: String)))) Nothing],+ anyrel "cdbs",+ anyrel "ghc"] +++ (map anyrel' (Set.toList (getL buildDeps deb))) +++ (if getL noProfilingLibrary deb then [] else [anyrel "ghc-prof"]) +++ cabalDeps (getL packageDescription deb)+ where+ cabalDeps Nothing = []+ cabalDeps (Just pkgDesc) =+ (concat $ map (buildDependencies deb)+ $ filter (not . selfDependency (Cabal.package pkgDesc))+ $ allBuildDepends+ deb (Cabal.buildDepends pkgDesc) (concatMap buildTools . allBuildInfo $ pkgDesc)+ (concatMap pkgconfigDepends . allBuildInfo $ pkgDesc)+ (concatMap extraLibs . allBuildInfo $ pkgDesc))++debianBuildDepsIndep :: Atoms -> D.Relations+debianBuildDepsIndep deb =+ filterMissing deb $+ if getL noDocumentationLibrary deb+ then []+ else nub $ [anyrel "ghc-doc"] +++ (map anyrel' (Set.toList (getL buildDepsIndep deb))) +++ cabalDeps (getL packageDescription deb)+ where+ cabalDeps Nothing = []+ cabalDeps (Just pkgDesc) =+ (concat . map (docDependencies deb)+ $ filter (not . selfDependency (Cabal.package pkgDesc))+ $ allBuildDepends+ deb (Cabal.buildDepends pkgDesc) (concatMap buildTools . allBuildInfo $ pkgDesc)+ (concatMap pkgconfigDepends . allBuildInfo $ pkgDesc) (concatMap extraLibs . allBuildInfo $ pkgDesc))++-- | The documentation dependencies for a package include the+-- documentation package for any libraries which are build+-- dependencies, so we have access to all the cross references.+docDependencies :: Atoms -> Dependency_ -> D.Relations+docDependencies atoms (BuildDepends (Dependency name ranges)) =+ dependencies atoms Documentation name ranges+docDependencies _ _ = []++-- | The Debian build dependencies for a package include the profiling+-- libraries and the documentation packages, used for creating cross+-- references. Also the packages associated with extra libraries.+buildDependencies :: Atoms -> Dependency_ -> D.Relations+buildDependencies atoms (BuildDepends (Dependency name ranges)) =+ dependencies atoms Development name ranges +++ dependencies atoms Profiling name ranges+buildDependencies atoms dep@(ExtraLibs _) =+ concat (map dependency $ adapt (getL execMap atoms) dep)+buildDependencies atoms dep =+ case unboxDependency dep of+ Just (Dependency _name _ranges) ->+ concat (map dependency $ adapt (getL execMap atoms) dep)+ Nothing ->+ []++dependency :: D.BinPkgName -> D.Relations+dependency name = [[D.Rel name Nothing Nothing]]++adapt :: Map.Map String D.BinPkgName -> Dependency_ -> [D.BinPkgName]+adapt execMap (PkgConfigDepends (Dependency (PackageName pkg) _)) =+ maybe (aptFile pkg) (: []) (Map.lookup pkg execMap)+adapt execMap (BuildTools (Dependency (PackageName pkg) _)) =+ maybe (aptFile pkg) (: []) (Map.lookup pkg execMap)+adapt _flags (ExtraLibs x) = [x]+adapt _flags (BuildDepends (Dependency (PackageName pkg) _)) = [D.BinPkgName pkg]++-- There are two reasons this may not work, or may work+-- incorrectly: (1) the build environment may be a different+-- distribution than the parent environment (the environment the+-- autobuilder was run from), so the packages in that+-- environment might have different names, and (2) the package+-- we are looking for may not be installed in the parent+-- environment.+aptFile :: String -> [D.BinPkgName] -- Maybe would probably be more correct+aptFile pkg =+ unsafePerformIO $+ do ret <- readProcessWithExitCode "apt-file" ["-l", "search", pkg ++ ".pc"] ""+ return $ case ret of+ (ExitSuccess, out, _) ->+ case takeWhile (not . isSpace) out of+ "" -> error $ "Unable to locate a package containing " ++ pkg ++ ", try using --exec-map " ++ pkg ++ "=<debname> or Map.insert " ++ show pkg ++ " (BinPkgName \"<debname>\") (execMap flags)"+ s -> [D.BinPkgName s]+ _ -> []++anyrel :: String -> [D.Relation]+anyrel x = anyrel' (D.BinPkgName x)++anyrel' :: D.BinPkgName -> [D.Relation]+anyrel' x = [D.Rel x Nothing Nothing]++-- | Turn a cabal dependency into debian dependencies. The result+-- needs to correspond to a single debian package to be installed,+-- so we will return just an OrRelation.+dependencies :: Atoms -> PackageType -> PackageName -> VersionRange -> Relations+dependencies atoms typ name cabalRange =+ map doBundled $ convert' (canonical (Or (catMaybes (map convert alts))))+ where++ -- Compute a list of alternative debian dependencies for+ -- satisfying a cabal dependency. The only caveat is that+ -- we may need to distribute any "and" dependencies implied+ -- by a version range over these "or" dependences.+ alts :: [(BinPkgName, VersionRange)]+ alts = case Map.lookup name (packageSplits (getL versionSplits atoms)) of+ -- If there are no splits for this package just return the single dependency for the package+ Nothing -> [(mkPkgName name typ, cabalRange')]+ -- If there are splits create a list of (debian package name, VersionRange) pairs+ Just splits' -> map (\ (n, r) -> (mkPkgName n typ, r)) (packageRangesFromVersionSplits splits')++ convert :: (BinPkgName, VersionRange) -> Maybe (Rels Relation)+ convert (dname, range) =+ if isNoVersion range'''+ then Nothing+ else Just $+ foldVersionRange'+ (Rel (D.Rel dname Nothing Nothing))+ (\ v -> Rel (D.Rel dname (Just (D.EEQ (dv v))) Nothing))+ (\ v -> Rel (D.Rel dname (Just (D.SGR (dv v))) Nothing))+ (\ v -> Rel (D.Rel dname (Just (D.SLT (dv v))) Nothing))+ (\ v -> Rel (D.Rel dname (Just (D.GRE (dv v))) Nothing))+ (\ v -> Rel (D.Rel dname (Just (D.LTE (dv v))) Nothing))+ (\ x y -> And [Rel (D.Rel dname (Just (D.GRE (dv x))) Nothing), Rel (D.Rel dname (Just (D.SLT (dv y))) Nothing)])+ (\ x y -> Or [x, y])+ (\ x y -> And [x, y])+ id+ range'''+ where + -- Choose the simpler of the two+ range''' = canon (simpler range' range'')+ -- Unrestrict the range for versions that we know don't exist for this debian package+ range'' = canon (unionVersionRanges range' (invertVersionRange range))+ -- Restrict the range to the versions specified for this debian package+ range' = intersectVersionRanges cabalRange' range+ -- When we see a cabal equals dependency we need to turn it into+ -- a wildcard because the resulting debian version numbers have+ -- various suffixes added.+ cabalRange' =+ foldVersionRange'+ anyVersion+ withinVersion -- <- Here we are turning equals into wildcard+ laterVersion+ earlierVersion+ orLaterVersion+ orEarlierVersion+ (\ lb ub -> intersectVersionRanges (orLaterVersion lb) (earlierVersion ub))+ unionVersionRanges+ intersectVersionRanges+ id+ cabalRange+ -- Convert a cabal version to a debian version, adding an epoch number if requested+ dv v = parseDebianVersion (maybe "" (\ n -> show n ++ ":") (Map.lookup name (getL epochMap atoms)) ++ showVersion v)+ simpler v1 v2 = minimumBy (compare `on` (length . asVersionIntervals)) [v1, v2]+ -- Simplify a VersionRange+ canon = fromVersionIntervals . toVersionIntervals++ -- If a package is bundled with the compiler we make the+ -- compiler a substitute for that package. If we were to+ -- specify the virtual package (e.g. libghc-base-dev) we would+ -- have to make sure not to specify a version number.+ doBundled :: [D.Relation] -> [D.Relation]+ doBundled rels | ghcBuiltIn (fromMaybe (error "dependencies") $ getL compiler atoms) name = rels ++ [D.Rel (compilerPackageName typ) Nothing Nothing]+ doBundled rels = rels++ compilerPackageName Documentation = D.BinPkgName "ghc-doc"+ compilerPackageName Profiling = D.BinPkgName "ghc-prof"+ compilerPackageName Development = D.BinPkgName "ghc"+ compilerPackageName _ = D.BinPkgName "ghc" -- whatevs++data Rels a = And {unAnd :: [Rels a]} | Or {unOr :: [Rels a]} | Rel {unRel :: a} deriving Show++convert' :: Rels a -> [[a]]+convert' = map (map unRel . unOr) . unAnd . canonical++-- | return and of ors of rel+canonical :: Rels a -> Rels a+canonical (Rel rel) = And [Or [Rel rel]]+canonical (And rels) = And $ concatMap (unAnd . canonical) rels+canonical (Or rels) = And . map Or $ sequence $ map (concat . map unOr . unAnd . canonical) $ rels++-- | Function that applies the mapping from cabal names to debian+-- names based on version numbers. If a version split happens at v,+-- this will return the ltName if < v, and the geName if the relation+-- is >= v.+debianName :: (PkgName name) => Atoms -> PackageType -> PackageIdentifier -> name+debianName atoms typ pkgDesc =+ (\ pname -> mkPkgName pname typ) $+ case filter (\ x -> pname == packageName x) (getL versionSplits atoms) of+ [] -> pname+ [splits] ->+ foldTriples' (\ ltName v geName debName ->+ if pname /= packageName splits+ then debName+ else let split = parseDebianVersion (showVersion v) in+ case version of+ Nothing -> geName+ Just (D.SLT v') | v' <= split -> ltName+ -- Otherwise use ltName only when the split is below v'+ Just (D.EEQ v') | v' < split -> ltName+ Just (D.LTE v') | v' < split -> ltName+ Just (D.GRE v') | v' < split -> ltName+ Just (D.SGR v') | v' < split -> ltName+ _ -> geName)+ pname+ splits+ _ -> error $ "Multiple splits for cabal package " ++ string+ where+ foldTriples' :: (PackageName -> Version -> PackageName -> PackageName -> PackageName) -> PackageName -> VersionSplits -> PackageName+ foldTriples' = foldTriples+ -- def = mkPkgName pname typ+ pname@(PackageName string) = pkgName pkgDesc+ version = (Just (D.EEQ (parseDebianVersion (showVersion (pkgVersion pkgDesc)))))++-- | Given a control file and a DebType, look for the binary deb with+-- the corresponding suffix and return its name.+debNameFromType :: Control' String -> DebType -> Maybe BinPkgName+debNameFromType control debType =+ case debType of+ Dev -> fmap BinPkgName $ listToMaybe (filter (isSuffixOf "-dev") debNames)+ Prof -> fmap BinPkgName $ listToMaybe (filter (isSuffixOf "-prof") debNames)+ Doc -> fmap BinPkgName $ listToMaybe (filter (isSuffixOf "-doc") debNames)+ where+ debNames = map (\ (Field (_, s)) -> stripWS s) (catMaybes (map (lookupP "Package") (tail (unControl control))))++-- | Build a debian package name from a cabal package name and a+-- debian package type. Unfortunately, this does not enforce the+-- correspondence between the PackageType value and the name type, so+-- it can return nonsense like (SrcPkgName "libghc-debian-dev").+mkPkgName :: PkgName name => PackageName -> PackageType -> name+mkPkgName (PackageName name) typ =+ pkgNameFromString $+ case typ of+ Documentation -> "libghc-" ++ base ++ "-doc"+ Development -> "libghc-" ++ base ++ "-dev"+ Profiling -> "libghc-" ++ base ++ "-prof"+ Utilities -> "haskell-" ++ base ++ "-utils"+ Exec -> base+ Source' -> "haskell-" ++ base ++ ""+ Cabal -> base+ where+ base = map (fixChar . toLower) name+ -- Underscore is prohibited in debian package names.+ fixChar :: Char -> Char+ fixChar '_' = '-'+ fixChar c = toLower c++instance Interspersed VersionSplits PackageName Version where+ leftmost (VersionSplits {splits = []}) = error "Empty Interspersed instance"+ leftmost (VersionSplits {oldestPackage = p}) = p+ pairs (VersionSplits {splits = xs}) = xs++packageSplits :: [VersionSplits] -> Map PackageName VersionSplits+packageSplits splits =+ foldr (\ splits' mp -> Map.insertWith multipleSplitsError (packageName splits') splits' mp)+ Map.empty+ splits+ where+ multipleSplitsError :: VersionSplits -> a -> b+ multipleSplitsError (VersionSplits {packageName = PackageName p}) _s2 =+ error ("Multiple splits for package " ++ show p)++packageRangesFromVersionSplits :: VersionSplits -> [(PackageName, VersionRange)]+packageRangesFromVersionSplits splits =+ foldInverted (\ older dname newer more ->+ (dname, intersectVersionRanges (maybe anyVersion orLaterVersion older) (maybe anyVersion earlierVersion newer)) : more)+ []+ splits++{-+-- | Generate the head of the debian/rules file.+cdbsRules :: PackageIdentifier -> Atoms -> Atoms+cdbsRules pkgId deb =+ setL rulesHead+ (Just . unlines $+ ["#!/usr/bin/make -f",+ "",+ "DEB_CABAL_PACKAGE = " <> name,+ "",+ "include /usr/share/cdbs/1/rules/debhelper.mk",+ "include /usr/share/cdbs/1/class/hlibrary.mk" ])+ deb+ where+ -- The name is based on the cabal package, but it may need to be+ -- modified to avoid violating Debian rules - no underscores, no+ -- capital letters.+ name = pack (show (pretty (debianName deb Cabal pkgId :: BinPkgName)))+-}++getRulesHead :: Atoms -> Text+getRulesHead atoms =+ fromMaybe computeRulesHead (getL rulesHead atoms)+ where+ computeRulesHead =+ unlines $+ ["#!/usr/bin/make -f", ""] +++ maybe [] (\ x -> ["DEB_CABAL_PACKAGE = " <> x, ""]) (fmap name (getL packageDescription atoms)) +++ ["include /usr/share/cdbs/1/rules/debhelper.mk",+ "include /usr/share/cdbs/1/class/hlibrary.mk"]+ name pkgDesc = pack (show (pretty (debianName atoms Cabal (Cabal.package pkgDesc) :: BinPkgName)))++filterMissing :: Atoms -> [[Relation]] -> [[Relation]]+filterMissing atoms rels =+ filter (/= []) (List.map (filter (\ (D.Rel name _ _) -> not (Set.member name (getL missingDependencies atoms)))) rels)++binaryPackageDeps :: BinPkgName -> Atoms -> [[Relation]]+binaryPackageDeps b atoms = maybe [] (map (: []) . Set.toList) (Map.lookup b (getL depends atoms))++binaryPackageConflicts :: BinPkgName -> Atoms -> [[Relation]]+binaryPackageConflicts b atoms = maybe [] (map (: []) . Set.toList) (Map.lookup b (getL conflicts atoms))
+ src/Debian/Debianize/Files.hs view
@@ -0,0 +1,199 @@+-- | Convert a Debianization into a list of files that can then be+-- written out.+{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, TupleSections #-}+module Debian.Debianize.Files+ ( toFileMap+ ) where++import Data.Lens.Lazy (getL)+import Data.List as List (map, unlines)+import Data.Map as Map (Map, toList, fromListWithKey, mapKeys)+import Data.Maybe+import Data.Monoid ((<>))+import Data.Set as Set (toList, member)+import Data.Text as Text (Text, pack, unpack, lines, unlines, strip, null)+import Debian.Control (Control'(Control, unControl), Paragraph'(Paragraph), Field'(Field))+import Debian.Debianize.Atoms (Atoms, compat, sourceFormat, watch, changelog, control, postInst, postRm, preInst, preRm,+ intermediateFiles, install, installDir, installInit, logrotateStanza, link,+ rulesHead, rulesFragments, copyright)+import Debian.Debianize.ControlFile as Debian (SourceDebDescription(..), BinaryDebDescription(..), PackageRelations(..),+ VersionControlSpec(..), XField(..), XFieldDest(..))+import Debian.Debianize.Dependencies (getRulesHead)+import Debian.Debianize.Utility (showDeps')+import Debian.Relation (Relations, BinPkgName(BinPkgName))+import Prelude hiding (init, unlines, writeFile)+import System.FilePath ((</>))+import Text.PrettyPrint.ANSI.Leijen (pretty)++sourceFormatFiles :: Atoms -> [(FilePath, Text)]+sourceFormatFiles deb = maybe [] (\ x -> [("debian/source/format", pack (show (pretty x)))]) (getL sourceFormat deb)++watchFile :: Atoms -> [(FilePath, Text)]+watchFile deb = maybe [] (\ x -> [("debian/watch", x)]) (getL watch deb)++intermediates :: Atoms -> [(FilePath, Text)]+intermediates deb = Set.toList $ getL intermediateFiles deb++installs :: Atoms -> [(FilePath, Text)]+installs deb =+ map (\ (path, pairs) -> (path, pack (List.unlines (map (\ (src, dst) -> src <> " " <> dst) (Set.toList pairs))))) $+ Map.toList $+ mapKeys pathf $+ getL install deb+ where+ pathf name = "debian" </> show (pretty name) ++ ".install"++dirs :: Atoms -> [(FilePath, Text)]+dirs deb =+ map (\ (path, dirs) -> (path, pack (List.unlines (Set.toList dirs)))) $ Map.toList $ mapKeys pathf $ getL installDir deb+ where+ pathf name = "debian" </> show (pretty name) ++ ".dirs"++init :: Atoms -> [(FilePath, Text)]+init deb =+ Map.toList $ mapKeys pathf $ getL installInit deb+ where+ pathf name = "debian" </> show (pretty name) ++ ".init"++-- FIXME - use a map and insertWith, check for multiple entries+logrotate :: Atoms -> [(FilePath, Text)]+logrotate deb =+ map (\ (path, stanzas) -> (path, Text.unlines (Set.toList stanzas))) $ Map.toList $ mapKeys pathf $ getL logrotateStanza deb+ where+ pathf name = "debian" </> show (pretty name) ++ ".logrotate"++-- | Assemble all the links by package and output one file each+links :: Atoms -> [(FilePath, Text)]+links deb =+ map (\ (path, pairs) -> (path, pack (List.unlines (map (\ (loc, txt) -> loc ++ " " ++ txt) (Set.toList pairs))))) $+ Map.toList $+ mapKeys pathf $+ getL link deb+ where+ pathf name = "debian" </> show (pretty name) ++ ".links"++postinstFiles :: Atoms -> [(FilePath, Text)]+postinstFiles deb =+ Map.toList $ mapKeys pathf $ getL postInst deb+ where+ pathf (BinPkgName name) = "debian" </> show (pretty name) ++ ".postinst"++postrmFiles :: Atoms -> [(FilePath, Text)]+postrmFiles deb =+ Map.toList $ mapKeys pathf $ getL postRm deb+ where+ pathf name = "debian" </> show (pretty name) ++ ".postrm"++preinstFiles :: Atoms -> [(FilePath, Text)]+preinstFiles deb =+ Map.toList $ mapKeys pathf $ getL preInst deb+ where+ pathf name = "debian" </> show (pretty name) ++ ".preinst"++prermFiles :: Atoms -> [(FilePath, Text)]+prermFiles deb =+ Map.toList $ mapKeys pathf $ getL preRm deb+ where+ pathf name = "debian" </> show (pretty name) ++ ".prerm"++-- | Turn the Debianization into a list of files, making sure the text+-- associated with each path is unique. Assumes that+-- finalizeDebianization has already been called. (Yes, I'm+-- considering building one into the other, but it is handy to look at+-- the Debianization produced by finalizeDebianization in the unit+-- tests.)+toFileMap :: Atoms -> Map FilePath Text+toFileMap atoms =+ Map.fromListWithKey (\ k a b -> error $ "Multiple values for " ++ k ++ ":\n " ++ show a ++ "\n" ++ show b) $+ [("debian/control", pack (show (pretty (controlFile d)))),+ ("debian/changelog", pack (show (pretty (fromMaybe (error "Missing debian/changelog") (getL changelog atoms))))),+ ("debian/rules", rules atoms),+ ("debian/compat", pack (show (fromMaybe (error "Missing DebCompat atom") $ getL compat atoms) <> "\n")),+ ("debian/copyright", either (\ x -> pack (show x) <> "\n") id (fromMaybe (error "No DebCopyright atom") $ getL copyright atoms))] +++ sourceFormatFiles atoms +++ watchFile atoms +++ installs atoms +++ dirs atoms +++ init atoms +++ logrotate atoms +++ links atoms +++ postinstFiles atoms +++ postrmFiles atoms +++ preinstFiles atoms +++ prermFiles atoms +++ intermediates atoms+ where d = getL control atoms++rules :: Atoms -> Text+rules deb = Text.unlines (maybe (getRulesHead deb) id (getL rulesHead deb) : reverse (Set.toList (getL rulesFragments deb)))++controlFile :: SourceDebDescription -> Control' String+controlFile src =+ Control+ { unControl =+ (Paragraph+ ([Field ("Source", " " ++ show (pretty (source src))),+ Field ("Maintainer", " " <> show (pretty (maintainer src)))] +++ lField "Uploaders" (uploaders src) +++ (case dmUploadAllowed src of True -> [Field ("DM-Upload-Allowed", " yes")]; False -> []) +++ mField "Priority" (priority src) +++ mField "Section" (section src) +++ depField "Build-Depends" (buildDepends src) +++ depField "Build-Depends-Indep" (buildDependsIndep src) +++ depField "Build-Conflicts" (buildConflicts src) +++ depField "Build-Conflicts-Indep" (buildConflictsIndep src) +++ [Field ("Standards-Version", " " <> show (pretty (standardsVersion src)))] +++ mField "Homepage" (homepage src) +++ List.map vcsField (Set.toList (vcsFields src)) +++ List.map xField (Set.toList (xFields src))) :+ List.map binary (binaryPackages src))+ }+ where+ binary :: BinaryDebDescription -> Paragraph' String+ binary bin =+ Paragraph+ ([Field ("Package", " " ++ show (pretty (package bin))),+ Field ("Architecture", " " ++ show (pretty (architecture bin)))] +++ mField "Section" (binarySection bin) +++ mField "Priority" (binaryPriority bin) +++ bField "Essential" (essential bin) +++ relFields (relations bin) +++ [Field ("Description", " " ++ unpack (ensureDescription (description bin)))])+ where+ ensureDescription t =+ case Text.lines t of+ [] -> "No description available."+ (short : long) | Text.null (strip short) -> Text.unlines ("No short description available" : long)+ _ -> t+ mField tag = maybe [] (\ x -> [Field (tag, " " <> show (pretty x))])+ bField tag flag = if flag then [Field (tag, " yes")] else []+ lField _ [] = []+ lField tag xs = [Field (tag, " " <> show (pretty xs))]+ vcsField (VCSBrowser t) = Field ("Vcs-Browser", " " ++ unpack t)+ vcsField (VCSArch t) = Field ("Vcs-Arch", " " ++ unpack t)+ vcsField (VCSBzr t) = Field ("Vcs-Bzr", " " ++ unpack t)+ vcsField (VCSCvs t) = Field ("Vcs-Cvs", " " ++ unpack t)+ vcsField (VCSDarcs t) = Field ("Vcs-Darcs", " " ++ unpack t)+ vcsField (VCSGit t) = Field ("Vcs-Git", " " ++ unpack t)+ vcsField (VCSHg t) = Field ("Vcs-Hg", " " ++ unpack t)+ vcsField (VCSMtn t) = Field ("Vcs-Mtn", " " ++ unpack t)+ vcsField (VCSSvn t) = Field ("Vcs-Svn", " " ++ unpack t)+ xField (XField dests tag t) = Field (unpack ("X" <> showDests dests <> "-" <> tag), unpack (" " <> t))+ showDests s = if member B s then "B" else "" <>+ if member S s then "S" else "" <>+ if member C s then "C" else ""++relFields :: PackageRelations -> [Field' [Char]]+relFields rels =+ depField "Depends" (depends rels) +++ depField "Recommends" (recommends rels) +++ depField "Suggests" (suggests rels) +++ depField "Pre-Depends" (preDepends rels) +++ depField "Breaks" (breaks rels) +++ depField "Conflicts" (conflicts rels) +++ depField "Provides" (provides rels) +++ depField "Replaces" (replaces rels) +++ depField "Built-Using" (builtUsing rels)++depField :: [Char] -> Relations -> [Field' [Char]]+depField tag rels = case rels of [] -> []; _ -> [Field (tag, " " ++ showDeps' (tag ++ ":") rels)]
+ src/Debian/Debianize/Finalize.hs view
@@ -0,0 +1,301 @@+-- | Convert a Debianization into a list of files that can then be+-- written out.+{-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}+module Debian.Debianize.Finalize+ ( finalizeDebianization+ ) where++import Data.ByteString.Lazy.UTF8 (fromString)+import Data.Char (toLower)+import Data.Digest.Pure.MD5 (md5)+import Data.Lens.Lazy (setL, getL, modL)+import Data.List as List (map)+import Data.Map as Map (insertWith, foldWithKey, elems)+import Data.Maybe+import Data.Monoid (mempty, (<>))+import Data.Set as Set (Set, difference, fromList, null, insert, toList, filter, fold, map, union, singleton)+import Data.Text as Text (pack, unlines, unpack)+import Debian.Debianize.Atoms as Atoms+ (Atoms, packageDescription, control, binaryArchitectures, rulesFragments, website, serverInfo, link,+ backups, executable, sourcePriority, sourceSection, binaryPriorities, binarySections, description,+ install, installTo, installData, installCabalExecTo, noProfilingLibrary, noDocumentationLibrary,+ utilsPackageName, extraDevDeps, installData, installCabalExec, file, apacheSite, installDir, buildDir,+ dataDir, intermediateFiles)+import Debian.Debianize.ControlFile as Debian (SourceDebDescription(..), BinaryDebDescription(..), PackageRelations(..),+ newBinaryDebDescription, modifyBinaryDeb,+ PackageType(Exec, Development, Profiling, Documentation, Utilities))+import Debian.Debianize.Dependencies (debianName, binaryPackageDeps, binaryPackageConflicts, putBuildDeps)+import Debian.Debianize.Goodies (describe, siteAtoms, serverAtoms, backupAtoms, execAtoms)+import Debian.Debianize.Types (InstallFile(..))+import Debian.Policy (PackageArchitectures(Any, All), Section(..))+import Debian.Relation (Relation(Rel), BinPkgName(BinPkgName))+import Distribution.Package (PackageName(PackageName), PackageIdentifier(..))+import qualified Distribution.PackageDescription as Cabal+import Prelude hiding (init, unlines, writeFile, map)+import System.FilePath ((</>), (<.>), makeRelative, splitFileName, takeDirectory, takeFileName)+import Text.PrettyPrint.ANSI.Leijen (pretty)++-- | Now that we know the build and data directories, we can expand+-- some atoms into sets of simpler atoms which can eventually be+-- turned into the files of the debianization. The original atoms are+-- not removed from the list because they may contribute to the+-- debianization in other ways, so be careful not to do this twice,+-- this function is not idempotent. (Exported for use in unit tests.)+finalizeDebianization :: Atoms -> Atoms+finalizeDebianization deb0 =+ deb'''''+ where+ -- Fixme - makeUtilsPackage does stuff that needs to go through foldAtomsFinalized+ deb' = finalizeAtoms deb0+ deb'' = f deb'+ deb''' = makeUtilsPackage $ librarySpecs $ putBuildDeps $ deb''+ deb'''' = finalizeAtoms deb'''+ deb''''' = g deb'''' -- Apply tweaks to the debianization++ -- Create the binary packages+ f :: Atoms -> Atoms+ f atoms = (\ atoms' -> Map.foldWithKey (\ b _ atoms'' -> cabalExecBinaryPackage b atoms'') atoms' (getL website atoms)) .+ (\ atoms' -> Map.foldWithKey (\ b _ atoms'' -> cabalExecBinaryPackage b atoms'') atoms' (getL serverInfo atoms)) .+ (\ atoms' -> Map.foldWithKey (\ b _ atoms'' -> modL binaryArchitectures (Map.insertWith (flip const) b Any) . cabalExecBinaryPackage b $ atoms'') atoms' (getL backups atoms)) .+ (\ atoms' -> Map.foldWithKey (\ b _ atoms'' -> cabalExecBinaryPackage b atoms'') atoms' (getL executable atoms)) $ atoms+ -- Apply the hints in the atoms to the debianization+ g :: Atoms -> Atoms+ g atoms = (\ atoms' -> maybe atoms' (\ x -> modL control (\ y -> y {priority = Just x}) atoms') (getL sourcePriority atoms)) .+ (\ atoms' -> maybe atoms' (\ x -> modL control (\ y -> y {section = Just x}) atoms') (getL sourceSection atoms)) .+ (\ atoms' -> Map.foldWithKey (\ b x atoms'' -> modL control (\ y -> modifyBinaryDeb b ((\ bin -> bin {architecture = x}) . fromMaybe (newBinaryDebDescription b Any)) y) atoms'') atoms' (getL binaryArchitectures atoms)) .+ (\ atoms' -> Map.foldWithKey (\ b x atoms'' -> modL control (\ y -> modifyBinaryDeb b ((\ bin -> bin {binaryPriority = Just x}) . fromMaybe (newBinaryDebDescription b Any)) y) atoms'') atoms' (getL binaryPriorities atoms)) .+ (\ atoms' -> Map.foldWithKey (\ b x atoms'' -> modL control (\ y -> modifyBinaryDeb b ((\ bin -> bin {binarySection = Just x}) . fromMaybe (newBinaryDebDescription b Any)) y) atoms'') atoms' (getL binarySections atoms)) .+ (\ atoms' -> Map.foldWithKey (\ b x atoms'' -> modL control (\ y -> modifyBinaryDeb b ((\ bin -> bin {Debian.description = x}) . fromMaybe (newBinaryDebDescription b Any)) y) atoms'') atoms' (getL Atoms.description atoms)) $ atoms++cabalExecBinaryPackage :: BinPkgName -> Atoms -> Atoms+cabalExecBinaryPackage b deb =+ modL control (\ y -> y {binaryPackages = bin : binaryPackages y}) deb+ where+ bin = BinaryDebDescription+ { Debian.package = b+ , architecture = Any+ , binarySection = Just (MainSection "misc")+ , binaryPriority = Nothing+ , essential = False+ , Debian.description = describe deb Exec (Cabal.package pkgDesc)+ , relations = binaryPackageRelations b Exec deb+ }+ pkgDesc = fromMaybe (error "cabalExecBinaryPackage: no PackageDescription") $ getL packageDescription deb++binaryPackageRelations :: BinPkgName -> PackageType -> Atoms -> PackageRelations+binaryPackageRelations b typ deb =+ PackageRelations+ { Debian.depends = [anyrel "${shlibs:Depends}", anyrel "${haskell:Depends}", anyrel "${misc:Depends}"] +++ (if typ == Development then List.map anyrel' (toList (getL extraDevDeps deb)) else []) +++ binaryPackageDeps b deb+ , recommends = [anyrel "${haskell:Recommends}"]+ , suggests = [anyrel "${haskell:Suggests}"]+ , preDepends = []+ , breaks = []+ , conflicts = [anyrel "${haskell:Conflicts}"] ++ binaryPackageConflicts b deb+ , provides = [anyrel "${haskell:Provides}"]+ , replaces = []+ , builtUsing = []+ }++-- debLibProf haddock binaryPackageDeps extraDevDeps extraLibMap+librarySpecs :: Atoms -> Atoms+librarySpecs deb | isNothing (getL packageDescription deb) = deb+librarySpecs deb =+ (if doc+ then modL link (Map.insertWith Set.union debName (singleton ("/usr/share/doc" </> show (pretty debName) </> "html" </> cabal <.> "txt", "/usr/lib/ghc-doc/hoogle" </> hoogle <.> "txt")))+ else id) $+ modL control+ (\ y -> y { binaryPackages =+ (if dev then [librarySpec deb Any Development (Cabal.package pkgDesc)] else []) +++ (if prof then [librarySpec deb Any Profiling (Cabal.package pkgDesc)] else []) +++ (if doc then [docSpecsParagraph deb (Cabal.package pkgDesc)] else []) +++ (binaryPackages y) })+ deb+ where+ doc = dev && not (getL noDocumentationLibrary deb)+ prof = dev && not (getL noProfilingLibrary deb)+ dev = isJust (Cabal.library pkgDesc)+ pkgDesc = fromMaybe (error "librarySpecs: no PackageDescription") $ getL packageDescription deb+ PackageName cabal = pkgName (Cabal.package pkgDesc)+ debName :: BinPkgName+ debName = debianName deb Documentation (Cabal.package pkgDesc)+ hoogle = List.map toLower cabal++docSpecsParagraph :: Atoms -> PackageIdentifier -> BinaryDebDescription+docSpecsParagraph atoms pkgId =+ BinaryDebDescription+ { Debian.package = debianName atoms Documentation pkgId+ , architecture = All+ , binarySection = Just (MainSection "doc")+ , binaryPriority = Nothing+ , essential = False+ , Debian.description = describe atoms Documentation pkgId+ , relations = binaryPackageRelations (debianName atoms Documentation pkgId) Development atoms+ }++librarySpec :: Atoms -> PackageArchitectures -> PackageType -> PackageIdentifier -> BinaryDebDescription+librarySpec atoms arch typ pkgId =+ BinaryDebDescription+ { Debian.package = debianName atoms typ pkgId+ , architecture = arch+ , binarySection = Nothing+ , binaryPriority = Nothing+ , essential = False+ , Debian.description = describe atoms typ pkgId+ , relations = binaryPackageRelations (debianName atoms typ pkgId) Development atoms+ }++-- | Create a package to hold any executables and data files not+-- assigned to some other package.+makeUtilsPackage :: Atoms -> Atoms+makeUtilsPackage deb | isNothing (getL packageDescription deb) = deb+makeUtilsPackage deb =+ case (Set.difference availableData installedData, Set.difference availableExec installedExec) of+ (datas, execs) | Set.null datas && Set.null execs -> deb+ (datas, execs) ->+ let p = fromMaybe (debianName deb Utilities (Cabal.package pkgDesc)) (getL utilsPackageName deb)+ deb' = setL packageDescription (Just pkgDesc) . makeUtilsAtoms p datas execs $ deb in+ modL control (\ y -> modifyBinaryDeb p (f deb' p (if Set.null execs then All else Any)) y) deb'+ where+ f _ _ _ (Just bin) = bin+ f deb' p arch Nothing =+ let bin = newBinaryDebDescription p arch in+ bin {binarySection = Just (MainSection "misc"),+ relations = binaryPackageRelations p Utilities deb'}+ pkgDesc = fromMaybe (error "makeUtilsPackage: no PackageDescription") $ getL packageDescription deb+ availableData = Set.fromList (Cabal.dataFiles pkgDesc)+ availableExec = Set.map Cabal.exeName (Set.filter (Cabal.buildable . Cabal.buildInfo) (Set.fromList (Cabal.executables pkgDesc)))+ installedData :: Set FilePath+ installedData = Set.fromList ((List.map fst . concat . List.map toList . elems $ getL install deb) <>+ (List.map fst . concat . List.map toList . elems $ getL installTo deb) <>+ (List.map fst . concat . List.map toList . elems $ getL installData deb))+ installedExec :: Set String+ installedExec = Set.fromList ((List.map fst . concat . List.map toList . elems $ getL installCabalExec deb) <>+ (List.map fst . concat . List.map toList . elems $ getL installCabalExecTo deb) <>+ (List.map ename . elems $ getL executable deb))+ where ename i =+ case sourceDir i of+ (Nothing) -> execName i+ (Just s) -> s </> execName i+ -- installedExec = foldCabalExecs (Set.insert :: String -> Set String -> Set String) (Set.empty :: Set String) deb++makeUtilsAtoms :: BinPkgName -> Set FilePath -> Set String -> Atoms -> Atoms+makeUtilsAtoms p datas execs atoms0 =+ if Set.null datas && Set.null execs+ then atoms0+ else modL rulesFragments (Set.insert (pack ("build" </> show (pretty p) ++ ":: build-ghc-stamp\n"))) . g $ atoms0+ where+ g :: Atoms -> Atoms+ g atoms = Set.fold execAtom (Set.fold dataAtom atoms datas) execs+ dataAtom path atoms = modL installData (insertWith union p (singleton (path, path))) atoms+ execAtom name atoms = modL installCabalExec (insertWith union p (singleton (name, "usr/bin"))) atoms++anyrel :: String -> [Relation]+anyrel x = anyrel' (BinPkgName x)++anyrel' :: BinPkgName -> [Relation]+anyrel' x = [Rel x Nothing Nothing]++finalizeAtoms :: Atoms -> Atoms+finalizeAtoms atoms | atoms == mempty = atoms+finalizeAtoms atoms = atoms <> finalizeAtoms (expandAtoms atoms)++expandAtoms :: Atoms -> Atoms+expandAtoms old =+ expandApacheSite .+ expandInstallCabalExec .+ expandInstallCabalExecTo .+ expandInstallData .+ expandInstallTo .+ expandFile .+ expandWebsite .+ expandServer .+ expandBackups .+ expandExecutable $+ mempty+ where+ expandApacheSite :: Atoms -> Atoms+ expandApacheSite new =+ foldWithKey (\ b (dom, log, text) atoms ->+ modL link (Map.insertWith Set.union b (singleton ("/etc/apache2/sites-available/" ++ dom, "/etc/apache2/sites-enabled/" ++ dom))) .+ modL installDir (Map.insertWith Set.union b (singleton log)) .+ modL file (Map.insertWith Set.union b (singleton ("/etc/apache2/sites-available" </> dom, text))) $+ atoms)+ new+ (getL apacheSite old)++ expandInstallCabalExec :: Atoms -> Atoms+ expandInstallCabalExec new =+ foldWithKey (\ b pairs atoms -> Set.fold (\ (name, dst) atoms' -> modL install (Map.insertWith Set.union b (singleton (builddir </> name </> name, dst))) atoms')+ atoms+ pairs)+ new+ (getL installCabalExec old)+ where+ builddir = fromMaybe {-(error "finalizeAtoms: no buildDir")-} "dist-ghc/build" (getL buildDir old)++ expandInstallCabalExecTo :: Atoms -> Atoms+ expandInstallCabalExecTo new =+ foldWithKey (\ b pairs atoms ->+ Set.fold (\ (n, d) atoms' ->+ modL rulesFragments (Set.insert (Text.unlines+ [ pack ("binary-fixup" </> show (pretty b)) <> "::"+ , "\tinstall -Dp " <> pack (builddir </> n </> n) <> " " <> pack ("debian" </> show (pretty b) </> makeRelative "/" d) ])) atoms')+ atoms+ pairs)+ new+ (getL installCabalExecTo old)+ where+ builddir = fromMaybe {-(error "finalizeAtoms: no buildDir")-} "dist-ghc/build" (getL buildDir old)++ expandInstallData :: Atoms -> Atoms+ expandInstallData new =+ foldWithKey (\ b pairs atoms ->+ Set.fold (\ (s, d) atoms' ->+ if takeFileName s == takeFileName d+ then modL install (Map.insertWith Set.union b (singleton (s, datadir </> makeRelative "/" (takeDirectory d)))) atoms'+ else modL installTo (Map.insertWith Set.union b (singleton (s, datadir </> makeRelative "/" d))) atoms')+ atoms+ pairs)+ new+ (getL installData old)+ where+ datadir = fromMaybe (error "finalizeAtoms: no dataDir") $ getL dataDir old++ expandInstallTo :: Atoms -> Atoms+ expandInstallTo new =+ foldWithKey (\ p pairs atoms ->+ Set.fold (\ (s, d) atoms' ->+ modL rulesFragments (Set.insert (Text.unlines+ [ pack ("binary-fixup" </> show (pretty p)) <> "::"+ , "\tinstall -Dp " <> pack s <> " " <> pack ("debian" </> show (pretty p) </> makeRelative "/" d) ])) atoms') atoms pairs)+ new+ (getL installTo old)++ expandFile :: Atoms -> Atoms+ expandFile new =+ foldWithKey (\ p pairs atoms ->+ Set.fold (\ (path, s) atoms' ->+ let (destDir', destName') = splitFileName path+ tmpDir = "debian/cabalInstall" </> show (md5 (fromString (unpack s)))+ tmpPath = tmpDir </> destName' in+ modL intermediateFiles (Set.insert (tmpPath, s)) .+ modL install (Map.insertWith Set.union p (singleton (tmpPath, destDir'))) $+ atoms')+ atoms+ pairs)+ new+ (getL file old)++ expandWebsite :: Atoms -> Atoms+ expandWebsite new = foldWithKey siteAtoms new (getL website old)++ expandServer :: Atoms -> Atoms+ expandServer new = foldWithKey (\ b x atoms -> serverAtoms b x False atoms) new (getL serverInfo old)++ expandBackups :: Atoms -> Atoms+ expandBackups new = foldWithKey backupAtoms new (getL backups old)++ expandExecutable :: Atoms -> Atoms+ expandExecutable new = foldWithKey execAtoms new (getL executable old)
+ src/Debian/Debianize/Generic.hs view
@@ -0,0 +1,108 @@+-- | Some generic operations with specializations to avoid broken Data+-- instances in types like Text and Set.+{-# LANGUAGE DeriveDataTypeable, RankNTypes, StandaloneDeriving #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+module Debian.Debianize.Generic+ ( geq+ , gdiff+ , gshow+ ) where++import Prelude hiding (GT)+import Data.Generics (Data, Typeable, GenericQ, toConstr, showConstr, gzipWithQ, extQ, ext1Q, gmapQ, Constr {- , ext2Q, Typeable2, dataTypeName, dataTypeOf-})+import Data.List (sort)+import qualified Data.Text as T+import Data.Set as Set (Set, toList, fromList, difference)+import Debian.Debianize.Atoms (Atoms)+import Debian.Debianize.ControlFile (VersionControlSpec, XField)+import Debian.Debianize.Utility (showDeps)+import Debian.Relation (Relation)+import Triplets (mkQ2, extQ2)++-- These instances are only used here, to create debugging messages.+deriving instance Typeable Atoms++-- deriving instance Data Debianization+-- deriving instance Data DebAtom++-- ext2Q' :: (Data d, Typeable2 t) => (d -> q) -> (forall d1 d2. (Data d1, Data d2) => t d1 d2 -> q) -> d -> q+-- ext2Q' = ext2Q++geq :: GenericQ (GenericQ Bool)+geq x y =+ (geq' `mkQ2` stringEq `extQ2` textEq `extQ2` setEq1 `extQ2` setEq2 `extQ2` mapEq1) x y+ where+ -- If the specialized eqs don't work, use the generic. This+ -- will throw an exception if it encounters something with a+ -- NoRep type.+ geq' :: (Data a, Data b) => a -> b -> Bool+ geq' x' y' = (toConstr x' == toConstr y') && and (gzipWithQ geq x' y')+ stringEq :: String -> String -> Bool+ stringEq a b = (a == b)+ textEq :: T.Text -> T.Text -> Bool+ textEq a b = (a == b)+ setEq1 :: Set VersionControlSpec -> Set VersionControlSpec -> Bool+ setEq1 a b = toList a == toList b+ setEq2 :: Set XField -> Set XField -> Bool+ setEq2 a b = toList a == toList b+ mapEq1 :: Atoms -> Atoms -> Bool+ mapEq1 a b = (a == b)++data Diff+ = Diff { stack :: [Constr], expected :: String, actual :: String }+ | SetDiff { stack :: [Constr], expected :: String, missing :: String, extra :: String }+ deriving (Eq, Show)++gdiff :: GenericQ (GenericQ [Diff])+gdiff x y =+ (gdiff' `mkQ2` stringEq `extQ2` textEq `extQ2` setEq1 `extQ2` setEq2 `extQ2` mapEq1 `extQ2` relEq) x y+ where+ -- If the specialized eqs don't work, use the generic. This+ -- will throw an exception if it encounters something with a+ -- NoRep type.+ gdiff' :: (Data a, Data b) => a -> b -> [Diff]+ gdiff' x' y' =+ if toConstr x' == toConstr y'+ then map (\ diff -> diff {stack = toConstr x' : stack diff}) (concat (gzipWithQ gdiff x' y'))+ else [Diff {stack = [], expected = gshow x', actual = gshow y'}]+ stringEq :: String -> String -> [Diff]+ stringEq a b = if (a == b) then [] else [Diff {stack = [], expected = show a, actual = show b}]+ textEq :: T.Text -> T.Text -> [Diff]+ textEq a b = if a == b then [] else [Diff {stack = [], expected = show a, actual = show b}]+ setEq1 :: Set VersionControlSpec -> Set VersionControlSpec -> [Diff]+ setEq1 a b = if a == b then [] else [Diff {stack = [], expected = show a, actual = show b}]+ setEq2 :: Set XField -> Set XField -> [Diff]+ setEq2 a b = if a == b then [] else [Diff {stack = [], expected = show a, actual = show b}]+ mapEq1 :: Atoms -> Atoms -> [Diff]+ mapEq1 a b = if a == b then [] else [Diff {stack = [], expected = show a, actual = show b}]+ relEq :: [[Relation]] -> [[Relation]] -> [Diff]+ relEq a b = if Set.fromList a == Set.fromList b+ then []+ else [SetDiff {stack = [],+ expected = show [a, b],+ missing = showDeps (sort (Set.toList (Set.difference (Set.fromList a) (Set.fromList b)))),+ extra = showDeps (sort (Set.toList (Set.difference (Set.fromList b) (Set.fromList a))))}]++{-+gshow' :: Data a => a -> String+gshow' x =+ (gshow `extQ` (show :: T.Text -> String) `extQ` (show :: Maybe T.Text -> String)) x+-}++-- | Generic show: an alternative to \"deriving Show\"+gshow :: Data a => a -> String+gshow x = gshows x ""++-- | Generic shows+gshows :: Data a => a -> ShowS++-- This is a prefix-show using surrounding "(" and ")",+-- where we recurse into subterms with gmapQ.+gshows = ( \t ->+ showChar '('+ . (showString . showConstr . toConstr $ t)+ . (foldr (.) id . gmapQ ((showChar ' ' .) . gshows) $ t)+ . showChar ')'+ ) `extQ` (shows :: String -> ShowS)+ `extQ` ((shows . T.unpack) :: T.Text -> ShowS)+ `ext1Q` (\ s -> gshows (toList s))
+ src/Debian/Debianize/Goodies.hs view
@@ -0,0 +1,376 @@+-- | Things that seem like they could be clients of this library, but+-- are instead included as part of the library.+{-# LANGUAGE OverloadedStrings #-}+module Debian.Debianize.Goodies+ ( defaultAtoms+ , tightDependencyFixup+ , doServer+ , doWebsite+ , doBackups+ , doExecutable+ , debianDescription+ , describe+ , watchAtom+ , oldClckwrksSiteFlags+ , oldClckwrksServerFlags+ , siteAtoms+ , serverAtoms+ , backupAtoms+ , execAtoms+ ) where++import Data.Lens.Lazy (getL, setL, modL)+import Data.List as List (map, intersperse, intercalate)+import Data.Map as Map (Map, fromList, insertWith)+import Data.Maybe (fromMaybe)+import Data.Monoid (mempty, (<>))+import Data.Set as Set (insert, union, singleton)+import Data.Text as Text (Text, pack, unlines, intercalate)+import Data.Version (Version(Version))+import Debian.Debianize.Atoms as Atoms ()+import Debian.Debianize.Atoms as Atoms+ (Atoms, packageDescription, rulesFragments, website, serverInfo, link, backups, executable,+ install, installTo, installCabalExecTo, file, installDir, logrotateStanza, postInst,+ installInit, installCabalExec, rulesFragments, packageDescription, executable,+ serverInfo, website, backups, depends, epochMap, versionSplits)+import Debian.Debianize.ControlFile as Debian (PackageType(..))+import Debian.Debianize.Types (InstallFile(..), Server(..), Site(..), VersionSplits(..))+import Debian.Debianize.Utility (trim)+import Debian.Orphans ()+import Debian.Policy (apacheLogDirectory, apacheErrorLog, apacheAccessLog, databaseDirectory, serverAppLog, serverAccessLog)+import Debian.Relation (BinPkgName(BinPkgName), Relation(Rel))+import Distribution.Package (PackageIdentifier(..), PackageName(PackageName))+import qualified Distribution.PackageDescription as Cabal+import Distribution.Text (display)+import Prelude hiding (writeFile, init, unlines, log, map)+import System.FilePath ((</>))+import System.Process (showCommandForUser)+import Text.PrettyPrint.ANSI.Leijen (Pretty(pretty))++-- | This may not look like a goodie, but it incorporates knowledge+-- about the debian repository - what the epoch number of HaXml is,+-- the fact that the debian package name of parsec changed, etc.+defaultAtoms :: Atoms+defaultAtoms =+ setL epochMap knownEpochMappings $+ setL versionSplits knownVersionSplits $+ mempty++-- | These are the instances of debian names changing that I know+-- about. I know they really shouldn't be hard coded. Send a patch.+-- Note that this inherits the lack of type safety of the mkPkgName+-- function.+knownVersionSplits :: [VersionSplits]+knownVersionSplits =+ [ VersionSplits {+ packageName = PackageName "parsec"+ , oldestPackage = PackageName "parsec2"+ , splits = [(Version [3] [], PackageName "parsec3")] }+ , VersionSplits {+ packageName = PackageName "QuickCheck"+ , oldestPackage = PackageName "quickcheck1"+ , splits = [(Version [2] [], PackageName "quickcheck2")] }+ ]++-- | We should always call this, just as we should always apply+-- knownVersionSplits.+knownEpochMappings :: Map PackageName Int+knownEpochMappings =+ Map.fromList [(PackageName "HaXml", 1)]++-- | Create equals dependencies. For each pair (A, B), use dpkg-query+-- to find out B's version number, version B. Then write a rule into+-- P's .substvar that makes P require that that exact version of A,+-- and another that makes P conflict with any older version of A.+tightDependencyFixup :: [(BinPkgName, BinPkgName)] -> BinPkgName -> Atoms -> Atoms+tightDependencyFixup [] _ deb = deb+tightDependencyFixup pairs p deb =+ modL rulesFragments+ (Set.insert+ (Text.unlines $+ ([ "binary-fixup/" <> name <> "::"+ , "\techo -n 'haskell:Depends=' >> debian/" <> name <> ".substvars" ] +++ intersperse ("\techo -n ', ' >> debian/" <> name <> ".substvars") (List.map equals pairs) +++ [ "\techo '' >> debian/" <> name <> ".substvars"+ , "\techo -n 'haskell:Conflicts=' >> debian/" <> name <> ".substvars" ] +++ intersperse ("\techo -n ', ' >> debian/" <> name <> ".substvars") (List.map newer pairs) +++ [ "\techo '' >> debian/" <> name <> ".substvars" ]))) deb+ where+ equals (installed, dependent) = "\tdpkg-query -W -f='" <> display' dependent <> " (=$${Version})' " <> display' installed <> " >> debian/" <> name <> ".substvars"+ newer (installed, dependent) = "\tdpkg-query -W -f='" <> display' dependent <> " (>>$${Version})' " <> display' installed <> " >> debian/" <> name <> ".substvars"+ name = display' p+ display' = pack . show . pretty++-- | Add a debian binary package to the debianization containing a cabal executable file.+doExecutable :: BinPkgName -> InstallFile -> Atoms -> Atoms+doExecutable bin x deb = modL executable (Map.insertWith (error "executable") bin x) deb++-- | Add a debian binary package to the debianization containing a cabal executable file set up to be a server.+doServer :: BinPkgName -> Server -> Atoms -> Atoms+doServer bin x deb = modL serverInfo (Map.insertWith (error "serverInfo") bin x) deb++-- | Add a debian binary package to the debianization containing a cabal executable file set up to be a web site.+doWebsite :: BinPkgName -> Site -> Atoms -> Atoms+doWebsite bin x deb = modL website (Map.insertWith (error "website") bin x) deb++-- | Add a debian binary package to the debianization containing a cabal executable file set up to be a backup script.+doBackups :: BinPkgName -> String -> Atoms -> Atoms+doBackups bin s deb =+ modL backups (Map.insertWith (error "backups") bin s) $+ modL Atoms.depends (Map.insertWith union bin (singleton (Rel (BinPkgName "anacron") Nothing Nothing))) $+ deb++describe :: Atoms -> PackageType -> PackageIdentifier -> Text+describe atoms typ ident =+ debianDescription (Cabal.synopsis pkgDesc) (Cabal.description pkgDesc) (Cabal.author pkgDesc) (Cabal.maintainer pkgDesc) (Cabal.pkgUrl pkgDesc) typ ident+ where+ pkgDesc = fromMaybe (error $ "describe " ++ show ident) $ getL packageDescription atoms++debianDescription :: String -> String -> String -> String -> String -> PackageType -> PackageIdentifier -> Text+debianDescription synopsis' description' author' maintainer' url typ pkgId =+ debianDescriptionBase synopsis' description' author' maintainer' url <> "\n" <>+ case typ of+ Profiling ->+ Text.intercalate "\n"+ [" .",+ " This package provides a library for the Haskell programming language, compiled",+ " for profiling. See http:///www.haskell.org/ for more information on Haskell."]+ Development ->+ Text.intercalate "\n"+ [" .",+ " This package provides a library for the Haskell programming language.",+ " See http:///www.haskell.org/ for more information on Haskell."]+ Documentation ->+ Text.intercalate "\n"+ [" .",+ " This package provides the documentation for a library for the Haskell",+ " programming language.",+ " See http:///www.haskell.org/ for more information on Haskell." ]+ Exec ->+ Text.intercalate "\n"+ [" .",+ " An executable built from the " <> pack (display (pkgName pkgId)) <> " package."]+{- ServerPackage ->+ Text.intercalate "\n"+ [" .",+ " A server built from the " <> pack (display (pkgName pkgId)) <> " package."] -}+ Utilities ->+ Text.intercalate "\n"+ [" .",+ " Utility files associated with the " <> pack (display (pkgName pkgId)) <> " package."]+ x -> error $ "Unexpected library package name suffix: " ++ show x++-- | The Cabal package has one synopsis and one description field+-- for the entire package, while in a Debian package there is a+-- description field (of which the first line is synopsis) in+-- each binary package. So the cabal description forms the base+-- of the debian description, each of which is amended.+debianDescriptionBase :: String -> String -> String -> String -> String -> Text+debianDescriptionBase synopsis' description' author' maintainer' url =+ (pack . unwords . words $ synopsis') <>+ case description' of+ "" -> ""+ text ->+ let text' = text ++ "\n" +++ list "" ("\n Author: " ++) author' +++ list "" ("\n Upstream-Maintainer: " ++) maintainer' +++ list "" ("\n Url: " ++) url in+ "\n " <> (pack . trim . List.intercalate "\n " . List.map addDot . lines $ text')+ where+ addDot line = if all (flip elem " \t") line then "." else line+ list :: b -> ([a] -> b) -> [a] -> b+ list d f l = case l of [] -> d; _ -> f l++oldClckwrksSiteFlags :: Site -> [String]+oldClckwrksSiteFlags x =+ [ -- According to the happstack-server documentation this needs a trailing slash.+ "--base-uri", "http://" ++ domain x ++ "/"+ , "--http-port", show port]+oldClckwrksServerFlags :: Server -> [String]+oldClckwrksServerFlags x =+ [ -- According to the happstack-server documentation this needs a trailing slash.+ "--base-uri", "http://" ++ hostname x ++ ":" ++ show (port x) ++ "/"+ , "--http-port", show port]++watchAtom :: PackageName -> Text+watchAtom (PackageName pkgname) =+ pack $ "version=3\nopts=\"downloadurlmangle=s|archive/([\\w\\d_-]+)/([\\d\\.]+)/|archive/$1/$2/$1-$2.tar.gz|,\\\nfilenamemangle=s|(.*)/$|" ++ pkgname +++ "-$1.tar.gz|\" \\\n http://hackage.haskell.org/packages/archive/" ++ pkgname +++ " \\\n ([\\d\\.]*\\d)/\n"++siteAtoms :: BinPkgName -> Site -> Atoms -> Atoms+siteAtoms b site =+ modL installDir (Map.insertWith Set.union b (singleton "/etc/apache2/sites-available")) .+ modL link (Map.insertWith Set.union b (singleton ("/etc/apache2/sites-available/" ++ domain site, "/etc/apache2/sites-enabled/" ++ domain site))) .+ modL file (Map.insertWith Set.union b (singleton ("/etc/apache2/sites-available" </> domain site, apacheConfig))) .+ modL installDir (Map.insertWith Set.union b (singleton (apacheLogDirectory b))) .+ modL logrotateStanza (Map.insertWith Set.union b (singleton (Text.unlines $+ [ pack (apacheAccessLog b) <> " {"+ , " weekly"+ , " rotate 5"+ , " compress"+ , " missingok"+ , "}"]))) .+ modL logrotateStanza (Map.insertWith Set.union b (singleton (Text.unlines $+ [ pack (apacheErrorLog b) <> " {"+ , " weekly"+ , " rotate 5"+ , " compress"+ , " missingok"+ , "}" ]))) .+ serverAtoms b (server site) True+ where+ -- An apache site configuration file. This is installed via a line+ -- in debianFiles.+ apacheConfig =+ Text.unlines $+ [ "<VirtualHost *:80>"+ , " ServerAdmin " <> pack (serverAdmin site)+ , " ServerName www." <> pack (domain site)+ , " ServerAlias " <> pack (domain site)+ , ""+ , " ErrorLog " <> pack (apacheErrorLog b)+ , " CustomLog " <> pack (apacheAccessLog b) <> " combined"+ , ""+ , " ProxyRequests Off"+ , " AllowEncodedSlashes NoDecode"+ , ""+ , " <Proxy *>"+ , " AddDefaultCharset off"+ , " Order deny,allow"+ , " #Allow from .example.com"+ , " Deny from all"+ , " #Allow from all"+ , " </Proxy>"+ , ""+ , " <Proxy http://127.0.0.1:" <> port' <> "/*>"+ , " AddDefaultCharset off"+ , " Order deny,allow"+ , " #Allow from .example.com"+ , " #Deny from all"+ , " Allow from all"+ , " </Proxy>"+ , ""+ , " SetEnv proxy-sendcl 1"+ , ""+ , " ProxyPass / http://127.0.0.1:" <> port' <> "/ nocanon"+ , " ProxyPassReverse / http://127.0.0.1:" <> port' <> "/"+ , "</VirtualHost>" ]+ port' = pack (show (port (server site)))++serverAtoms :: BinPkgName -> Server -> Bool -> Atoms -> Atoms+serverAtoms b server isSite =+ modL postInst (insertWith (error "serverAtoms") b debianPostinst) .+ modL installInit (Map.insertWith (error "serverAtoms") b debianInit) .+ serverLogrotate' b .+ execAtoms b exec+ where+ exec = installFile server+ debianInit =+ Text.unlines $+ [ "#! /bin/sh -e"+ , ""+ , ". /lib/lsb/init-functions"+ , ""+ , "case \"$1\" in"+ , " start)"+ , " test -x /usr/bin/" <> pack (destName exec) <> " || exit 0"+ , " log_begin_msg \"Starting " <> pack (destName exec) <> "...\""+ , " mkdir -p " <> pack (databaseDirectory b)+ , " " <> startCommand+ , " log_end_msg $?"+ , " ;;"+ , " stop)"+ , " log_begin_msg \"Stopping " <> pack (destName exec) <> "...\""+ , " " <> stopCommand+ , " log_end_msg $?"+ , " ;;"+ , " *)"+ , " log_success_msg \"Usage: ${0} {start|stop}\""+ , " exit 1"+ , "esac"+ , ""+ , "exit 0" ]+ startCommand = pack $ showCommandForUser "start-stop-daemon" (startOptions ++ commonOptions ++ ["--"] ++ serverOptions)+ stopCommand = pack $ showCommandForUser "start-stop-daemon" (stopOptions ++ commonOptions)+ commonOptions = ["--pidfile", "/var/run/" ++ destName exec]+ startOptions = ["--start", "-b", "--make-pidfile", "-d", databaseDirectory b, "--exec", "/usr/bin" </> destName exec]+ stopOptions = ["--stop", "--oknodo"] ++ if retry server /= "" then ["--retry=" ++ retry server ] else []+ serverOptions = serverFlags server ++ commonServerOptions+ -- Without these, happstack servers chew up CPU even when idle+ commonServerOptions = ["+RTS", "-IO", "-RTS"]++ debianPostinst =+ Text.unlines $+ ([ "#!/bin/sh"+ , ""+ , "case \"$1\" in"+ , " configure)" ] +++ (if isSite+ then [ " # Apache won't start if this directory doesn't exist"+ , " mkdir -p " <> pack (apacheLogDirectory b)+ , " # Restart apache so it sees the new file in /etc/apache2/sites-enabled"+ , " /usr/sbin/a2enmod proxy"+ , " /usr/sbin/a2enmod proxy_http"+ , " service apache2 restart" ]+ else []) +++ [ " service " <> pack (show (pretty b)) <> " start"+ , " ;;"+ , "esac"+ , ""+ , "#DEBHELPER#"+ , ""+ , "exit 0" ])++-- | A configuration file for the logrotate facility, installed via a line+-- in debianFiles.+serverLogrotate' :: BinPkgName -> Atoms -> Atoms+serverLogrotate' b =+ modL logrotateStanza (insertWith Set.union b (singleton (Text.unlines $ [ pack (serverAccessLog b) <> " {"+ , " weekly"+ , " rotate 5"+ , " compress"+ , " missingok"+ , "}" ]))) .+ modL logrotateStanza (insertWith Set.union b (singleton (Text.unlines $ [ pack (serverAppLog b) <> " {"+ , " weekly"+ , " rotate 5"+ , " compress"+ , " missingok"+ , "}" ])))++backupAtoms :: BinPkgName -> String -> Atoms -> Atoms+backupAtoms b name =+ modL postInst (insertWith (error "backupAtoms") b+ (Text.unlines $+ [ "#!/bin/sh"+ , ""+ , "case \"$1\" in"+ , " configure)"+ , " " <> pack ("/etc/cron.hourly" </> name) <> " --initialize"+ , " ;;"+ , "esac" ])) .+ execAtoms b (InstallFile { execName = name+ , destName = name+ , sourceDir = Nothing+ , destDir = Just "/etc/cron.hourly" })++execAtoms :: BinPkgName -> InstallFile -> Atoms -> Atoms+execAtoms b ifile r =+ modL rulesFragments (Set.insert (pack ("build" </> show (pretty b) ++ ":: build-ghc-stamp"))) .+ fileAtoms b ifile $+ r++fileAtoms :: BinPkgName -> InstallFile -> Atoms -> Atoms+fileAtoms b installFile r =+ fileAtoms' b (sourceDir installFile) (execName installFile) (destDir installFile) (destName installFile) r++fileAtoms' :: BinPkgName -> Maybe FilePath -> String -> Maybe FilePath -> String -> Atoms -> Atoms+fileAtoms' b sourceDir execName destDir destName r =+ case (sourceDir, execName == destName) of+ (Nothing, True) -> modL installCabalExec (insertWith Set.union b (singleton (execName, d))) r+ (Just s, True) -> modL install (insertWith Set.union b (singleton (s </> execName, d))) r+ (Nothing, False) -> modL installCabalExecTo (insertWith Set.union b (singleton (execName, (d </> destName)))) r+ (Just s, False) -> modL installTo (insertWith Set.union b (singleton (s </> execName, d </> destName))) r+ where+ d = fromMaybe "usr/bin" destDir
+ src/Debian/Debianize/Input.hs view
@@ -0,0 +1,315 @@+-- | Read an existing Debianization from a directory file.+{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, OverloadedStrings, ScopedTypeVariables, TypeSynonymInstances #-}+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}+module Debian.Debianize.Input+ ( inputDebianization+ , inputChangeLog+ , inputCabalization+ , inputCopyright+ , inputMaintainer+ ) where++import Debug.Trace (trace)++import Control.Exception (SomeException, catch, bracket)+import Control.Monad (when, foldM, filterM)+import Control.Monad.Trans (MonadIO, liftIO)+import Data.Char (isSpace)+import Data.Lens.Lazy (getL, setL, modL)+import Data.Map as Map (insertWith)+import Data.Maybe (fromMaybe, fromJust)+import Data.Monoid (mempty)+import Data.Set as Set (toList, fromList, insert, union, singleton)+import Data.Text (Text, unpack, pack, lines, words, break, strip, null)+import Data.Text.IO (readFile)+import Debian.Changes (ChangeLog(..), parseChangeLog)+import Debian.Control (Control'(unControl), Paragraph'(..), stripWS, parseControlFromFile, Field, Field'(..), ControlFunctions)+import Debian.Debianize.Atoms as Atoms+ (Atoms, rulesHead, compat, sourceFormat, watch, changelog, control, copyright,+ intermediateFiles, postInst, postRm, preInst, preRm, install, installDir, warning,+ logrotateStanza, installInit, link, packageDescription, compiler, maintainer, verbosity,+ compilerVersion, cabalFlagAssignments)+import Debian.Debianize.ControlFile (SourceDebDescription(..), BinaryDebDescription(..), PackageRelations(..),+ VersionControlSpec(..), XField(..), newSourceDebDescription', newBinaryDebDescription)+import Debian.Debianize.Utility (getDirectoryContents', readFile', withCurrentDirectory)+import Debian.Orphans ()+import Debian.Policy (Section(..), parseStandardsVersion, readPriority, readSection, parsePackageArchitectures, parseMaintainer,+ parseUploaders, readSourceFormat, getDebianMaintainer, haskellMaintainer)+import Debian.Relation (Relations, BinPkgName(..), SrcPkgName(..), parseRelations)+import Distribution.License (License(..))+import Distribution.Package (Package(packageId))+import Distribution.PackageDescription as Cabal (PackageDescription(licenseFile, license, maintainer))+import Distribution.PackageDescription.Configuration (finalizePackageDescription)+import Distribution.PackageDescription.Parse (readPackageDescription)+import Distribution.Simple.Compiler (CompilerId(..), CompilerFlavor(..), Compiler(..))+import Distribution.Simple.Configure (configCompiler)+import Distribution.Simple.Program (defaultProgramConfiguration)+import Distribution.Simple.Utils (defaultPackageDesc, die, setupMessage)+import Distribution.System (Platform(..), buildOS, buildArch)+import Distribution.Verbosity (Verbosity, intToVerbosity)+import Prelude hiding (readFile, lines, words, break, null, log, sum)+import System.Cmd (system)+import System.Directory (doesFileExist)+import System.Exit (ExitCode(..))+import System.FilePath ((</>), takeExtension, dropExtension)+import System.Posix.Files (setFileCreationMask)+import System.IO.Error (catchIOError)+import Text.ParserCombinators.Parsec.Rfc2822 (NameAddr)++inputDebianization :: FilePath -> IO Atoms+inputDebianization top =+ do (ctl, _) <- inputSourceDebDescription debian `catchIOError` (\ e -> error ("Failure parsing SourceDebDescription: " ++ show e))+ -- Different from snd of above?+ atoms <- inputAtomsFromDirectory debian mempty `catch` (\ (e :: SomeException) -> error ("Failure parsing atoms: " ++ show e))+ return $ modL control (const ctl) atoms+ where+ debian = top </> "debian"++inputSourceDebDescription :: FilePath -> IO (SourceDebDescription, [Field])+inputSourceDebDescription debian =+ do paras <- parseControlFromFile (debian </> "control") >>= either (error . show) (return . unControl)+ case paras of+ [] -> error "Missing source paragraph"+ [_] -> error "Missing binary paragraph"+ (hd : tl) -> return $ parseSourceDebDescription hd tl++parseSourceDebDescription :: Paragraph' String -> [Paragraph' String] -> (SourceDebDescription, [Field])+parseSourceDebDescription (Paragraph fields) binaryParagraphs =+ foldr readField (src, []) fields'+ where+ fields' = map stripField fields+ src = (newSourceDebDescription' findSource findMaint) {binaryPackages = bins}+ findSource = findMap "Source" SrcPkgName fields'+ findMaint = findMap "Maintainer" (either error id . parseMaintainer) fields'+ -- findStandards = findMap "Standards-Version" parseStandardsVersion fields'++ (bins, _extra) = unzip $ map parseBinaryDebDescription binaryParagraphs+ readField :: Field -> (SourceDebDescription, [Field]) -> (SourceDebDescription, [Field])+ -- Mandatory+ readField (Field ("Source", _)) x = x+ readField (Field ("Maintainer", _)) x = x+ -- readField (Field ("Standards-Version", _)) x = x+ -- Recommended+ readField (Field ("Standards-Version", value)) (desc, unrecognized) = (desc {standardsVersion = Just (parseStandardsVersion value)}, unrecognized)+ readField (Field ("Priority", value)) (desc, unrecognized) = (desc {priority = Just (readPriority value)}, unrecognized)+ readField (Field ("Section", value)) (desc, unrecognized) = (desc {section = Just (MainSection value)}, unrecognized)+ -- Optional+ readField (Field ("Homepage", value)) (desc, unrecognized) = (desc {homepage = Just (strip (pack value))}, unrecognized)+ readField (Field ("Uploaders", value)) (desc, unrecognized) = (desc {uploaders = either (const []) id (parseUploaders value)}, unrecognized)+ readField (Field ("DM-Upload-Allowed", value)) (desc, unrecognized) = (desc {dmUploadAllowed = yes value}, unrecognized)+ readField (Field ("Build-Depends", value)) (desc, unrecognized) = (desc {buildDepends = rels value}, unrecognized)+ readField (Field ("Build-Conflicts", value)) (desc, unrecognized) = (desc {buildConflicts = rels value}, unrecognized)+ readField (Field ("Build-Depends-Indep", value)) (desc, unrecognized) = (desc {buildDependsIndep = rels value}, unrecognized)+ readField (Field ("Build-Conflicts-Indep", value)) (desc, unrecognized) = (desc {buildConflictsIndep = rels value}, unrecognized)+ readField (Field ("Vcs-Browser", s)) (desc, unrecognized) = (desc {vcsFields = insert (VCSBrowser (pack s)) (vcsFields desc)}, unrecognized)+ readField (Field ("Vcs-Arch", s)) (desc, unrecognized) = (desc {vcsFields = insert (VCSArch (pack s)) (vcsFields desc)}, unrecognized)+ readField (Field ("Vcs-Bzr", s)) (desc, unrecognized) = (desc {vcsFields = insert (VCSBzr (pack s)) (vcsFields desc)}, unrecognized)+ readField (Field ("Vcs-Cvs", s)) (desc, unrecognized) = (desc {vcsFields = insert (VCSCvs (pack s)) (vcsFields desc)}, unrecognized)+ readField (Field ("Vcs-Darcs", s)) (desc, unrecognized) = (desc {vcsFields = insert (VCSDarcs (pack s)) (vcsFields desc)}, unrecognized)+ readField (Field ("Vcs-Git", s)) (desc, unrecognized) = (desc {vcsFields = insert (VCSGit (pack s)) (vcsFields desc)}, unrecognized)+ readField (Field ("Vcs-Hg", s)) (desc, unrecognized) = (desc {vcsFields = insert (VCSHg (pack s)) (vcsFields desc)}, unrecognized)+ readField (Field ("Vcs-Mtn", s)) (desc, unrecognized) = (desc {vcsFields = insert (VCSMtn (pack s)) (vcsFields desc)}, unrecognized)+ readField (Field ("Vcs-Svn", s)) (desc, unrecognized) = (desc {vcsFields = insert (VCSSvn (pack s)) (vcsFields desc)}, unrecognized)+ readField field@(Field ('X' : fld, value)) (desc, unrecognized) =+ case span (`elem` "BCS") fld of+ (xs, '-' : more) -> (desc {xFields = insert (XField (fromList (map (read' . (: [])) xs)) (pack more) (pack value)) (xFields desc)}, unrecognized)+ _ -> (desc, field : unrecognized)+ readField field (desc, unrecognized) = (desc, field : unrecognized)++parseBinaryDebDescription :: Paragraph' String -> (BinaryDebDescription, [Field])+parseBinaryDebDescription (Paragraph fields) =+ foldr readField (bin, []) fields'+ where+ fields' = map stripField fields+ bin = newBinaryDebDescription findPackage findArchitecture+ findPackage = findMap "Package" BinPkgName fields'+ findArchitecture = findMap "Architecture" parsePackageArchitectures fields'+{-+(BinPkgName (fromJust (fieldValue "Package" bin)))+(read' (fromJust (fieldValue "Architecture" bin)))+, []+ foldr readField (newBinaryDebDescription (BinPkgName (fromJust (fieldValue "Package" bin))) (read' (fromJust (fieldValue "Architecture" bin))), []) (map stripField fields)+-}++ readField :: Field -> (BinaryDebDescription, [Field]) -> (BinaryDebDescription, [Field])+ readField (Field ("Package", value)) (desc, unrecognized) = (desc {package = BinPkgName value}, unrecognized)+ readField (Field ("Architecture", value)) (desc, unrecognized) = (desc {architecture = parsePackageArchitectures value}, unrecognized)+ readField (Field ("Section", value)) (desc, unrecognized) = (desc {binarySection = Just (readSection value)}, unrecognized)+ readField (Field ("Priority", value)) (desc, unrecognized) = (desc {binaryPriority = Just (readPriority value)}, unrecognized)+ readField (Field ("Essential", value)) (desc, unrecognized) = (desc {essential = yes value}, unrecognized)+ readField (Field ("Depends", value)) (desc, unrecognized) = (desc {relations = (relations desc) {depends = rels value}}, unrecognized)+ readField (Field ("Recommends", value)) (desc, unrecognized) = (desc {relations = (relations desc) {recommends = rels value}}, unrecognized)+ readField (Field ("Suggests", value)) (desc, unrecognized) = (desc {relations = (relations desc) {suggests = rels value}}, unrecognized)+ readField (Field ("Pre-Depends", value)) (desc, unrecognized) = (desc {relations = (relations desc) {preDepends = rels value}}, unrecognized)+ readField (Field ("Breaks", value)) (desc, unrecognized) = (desc {relations = (relations desc) {breaks = rels value}}, unrecognized)+ readField (Field ("Conflicts", value)) (desc, unrecognized) = (desc {relations = (relations desc) {conflicts = rels value}}, unrecognized)+ readField (Field ("Provides", value)) (desc, unrecognized) = (desc {relations = (relations desc) {provides = rels value}}, unrecognized)+ readField (Field ("Replaces", value)) (desc, unrecognized) = (desc {relations = (relations desc) {replaces = rels value}}, unrecognized)+ readField (Field ("Built-Using", value)) (desc, unrecognized) = (desc {relations = (relations desc) {builtUsing = rels value}}, unrecognized)+ readField (Field ("Description", value)) (desc, unrecognized) = (desc {description = pack value}, unrecognized)+ readField field (desc, unrecognized) = (desc, field : unrecognized)++-- | Look for a field and apply a function to its value+findMap :: String -> (String -> a) -> [Field] -> a+findMap field f fields =+ fromMaybe (error $ "Missing field: " ++ field) (foldr findMap' Nothing fields)+ where+ findMap' (Field (fld, val)) x = if fld == field then Just (f val) else x+ findMap' _ x = x++read' :: Read a => String -> a+read' s = trace ("read " ++ show s) (read s)++stripField :: ControlFunctions a => Field' a -> Field' a+stripField (Field (a, b)) = Field (a, stripWS b)+stripField x = x++rels :: String -> Relations+rels s =+ either (\ e -> error ("Relations field error: " ++ show e ++ "\n " ++ s)) id (parseRelations s)++yes :: String -> Bool+yes "yes" = True+yes "no" = False+yes x = error $ "Expecting yes or no: " ++ x++inputChangeLog :: FilePath -> IO ChangeLog+inputChangeLog debian = readFile (debian </> "changelog") >>= return . parseChangeLog . unpack -- `catch` handleDoesNotExist :: IO ChangeLog++inputAtomsFromDirectory :: FilePath -> Atoms -> IO Atoms -- .install files, .init files, etc.+inputAtomsFromDirectory debian xs =+ findFiles xs >>= doFiles (debian </> "cabalInstall")+ where+ findFiles :: Atoms -> IO Atoms+ findFiles xs' =+ getDirectoryContents' debian >>=+ return . (++ ["source/format"]) >>=+ filterM (doesFileExist . (debian </>)) >>=+ foldM (\ xs'' name -> inputAtoms debian name xs'') xs'+ doFiles :: FilePath -> Atoms -> IO Atoms+ doFiles tmp xs' =+ do sums <- getDirectoryContents' tmp `catchIOError` (\ _ -> return [])+ paths <- mapM (\ sum -> getDirectoryContents' (tmp </> sum) >>= return . map (sum </>)) sums >>= return . concat+ files <- mapM (readFile . (tmp </>)) paths+ foldM (\ xs'' (path, file) -> return $ modL intermediateFiles (Set.insert ("debian/cabalInstall" </> path, file)) xs'') xs' (zip paths files)++inputAtoms :: FilePath -> FilePath -> Atoms -> IO Atoms+inputAtoms _ path xs | elem path ["control"] = return xs+inputAtoms debian name@"source/format" xs = readFile (debian </> name) >>= \ text -> return $ (either (modL warning . Set.insert) (setL sourceFormat . Just) (readSourceFormat text)) xs+inputAtoms debian name@"watch" xs = readFile (debian </> name) >>= \ text -> return $ setL watch (Just text) xs+inputAtoms debian name@"rules" xs = readFile (debian </> name) >>= \ text -> return $ setL rulesHead (Just text) xs+inputAtoms debian name@"compat" xs = readFile (debian </> name) >>= \ text -> return $ setL compat (Just (read (unpack text))) xs+inputAtoms debian name@"copyright" xs = readFile (debian </> name) >>= \ text -> return $ setL copyright (Just (Right text)) xs+inputAtoms debian name@"changelog" xs =+ readFile (debian </> name) >>= return . parseChangeLog . unpack >>= \ log -> return $ setL changelog (Just log) xs+inputAtoms debian name xs =+ case (BinPkgName (dropExtension name), takeExtension name) of+ (p, ".install") -> readFile (debian </> name) >>= \ text -> return $ foldr (readInstall p) xs (lines text)+ (p, ".dirs") -> readFile (debian </> name) >>= \ text -> return $ foldr (readDir p) xs (lines text)+ (p, ".init") -> readFile (debian </> name) >>= \ text -> return $ modL installInit (insertWith (error "inputAtoms") p text) xs+ (p, ".logrotate") -> readFile (debian </> name) >>= \ text -> return $ modL logrotateStanza (insertWith Set.union p (singleton text)) xs+ (p, ".links") -> readFile (debian </> name) >>= \ text -> return $ foldr (readLink p) xs (lines text)+ (p, ".postinst") -> readFile (debian </> name) >>= \ text -> return $ modL postInst (insertWith (error "inputAtoms") p text) xs+ (p, ".postrm") -> readFile (debian </> name) >>= \ text -> return $ modL postRm (insertWith (error "inputAtoms") p text) xs+ (p, ".preinst") -> readFile (debian </> name) >>= \ text -> return $ modL preInst (insertWith (error "inputAtoms") p text) xs+ (p, ".prerm") -> readFile (debian </> name) >>= \ text -> return $ modL preRm (insertWith (error "inputAtoms") p text) xs+ (_, ".log") -> return xs -- Generated by debhelper+ (_, ".debhelper") -> return xs -- Generated by debhelper+ (_, ".hs") -> return xs -- Code that uses this library+ (_, ".setup") -> return xs -- Compiled Setup.hs file+ (_, ".substvars") -> return xs -- Unsupported+ (_, "") -> return xs -- File with no extension+ (_, x) | last x == '~' -> return xs -- backup file+ _ -> trace ("Ignored: " ++ debian </> name) (return xs)++readLink :: BinPkgName -> Text -> Atoms -> Atoms+readLink p line atoms =+ case words line of+ [a, b] -> modL link (insertWith Set.union p (singleton (unpack a, unpack b))) atoms+ [] -> atoms+ _ -> trace ("readLink: " ++ show line) atoms++readInstall :: BinPkgName -> Text -> Atoms -> Atoms+readInstall p line atoms =+ case break isSpace line of+ (_, b) | null b -> error $ "readInstall: syntax error in .install file for " ++ show p ++ ": " ++ show line+ (a, b) -> modL install (insertWith union p (singleton (unpack (strip a), unpack (strip b)))) atoms++readDir :: BinPkgName -> Text -> Atoms -> Atoms+readDir p line atoms = modL installDir (insertWith union p (singleton (unpack line))) atoms++inputCabalization :: FilePath -> Atoms -> IO Atoms+inputCabalization top atoms =+ withCurrentDirectory top $ do+ descPath <- defaultPackageDesc vb+ genPkgDesc <- readPackageDescription vb descPath+ (compiler', _) <- configCompiler (Just GHC) Nothing Nothing defaultProgramConfiguration vb+ let compiler'' = case getL compilerVersion atoms of+ (Just ver) -> compiler' {compilerId = CompilerId GHC ver}+ _ -> compiler'+ case finalizePackageDescription (toList (getL cabalFlagAssignments atoms)) (const True) (Platform buildArch buildOS) (compilerId compiler'') [] genPkgDesc of+ Left e -> error $ "Failed to load cabal package description: " ++ show e+ Right (pkgDesc, _) -> do+ liftIO $ bracket (setFileCreationMask 0o022) setFileCreationMask $ \ _ -> autoreconf vb pkgDesc+ return $ setL compiler (Just compiler'') $+ setL packageDescription (Just pkgDesc) $ atoms+ where+ vb = intToVerbosity' (getL verbosity atoms)++-- | Run the package's configuration script.+autoreconf :: Verbosity -> PackageDescription -> IO ()+autoreconf verbose pkgDesc = do+ ac <- doesFileExist "configure.ac"+ when ac $ do+ c <- doesFileExist "configure"+ when (not c) $ do+ setupMessage verbose "Running autoreconf" (packageId pkgDesc)+ ret <- system "autoreconf"+ case ret of+ ExitSuccess -> return ()+ ExitFailure n -> die ("autoreconf failed with status " ++ show n)++-- | Try to read the license file specified in the cabal package,+-- otherwise return a text representation of the License field.+inputCopyright :: PackageDescription -> IO Text+inputCopyright pkgDesc = readFile' (licenseFile pkgDesc) `catchIOError` handle+ where handle _e =+ do -- here <- getCurrentDirectory+ -- hPutStrLn stderr ("Error reading " ++ licenseFile pkgDesc ++ " from " ++ here ++ ": " ++ show _e)+ return . pack . showLicense . license $ pkgDesc++-- | Convert from license to RPM-friendly (now Debian-friendly?)+-- description. The strings are taken from TagsCheck.py in the+-- rpmlint distribution.+showLicense :: License -> String+showLicense (Apache _) = "Apache"+showLicense (GPL _) = "GPL"+showLicense (LGPL _) = "LGPL"+showLicense BSD3 = "BSD"+showLicense BSD4 = "BSD-like"+showLicense PublicDomain = "Public Domain"+showLicense AllRightsReserved = "Proprietary"+showLicense OtherLicense = "Non-distributable"+showLicense MIT = "MIT"+showLicense (UnknownLicense _) = "Unknown"++-- | Try to compute the debian maintainer from the maintainer field of the+-- cabal package, or from the value returned by getDebianMaintainer.+inputMaintainer :: Atoms -> IO (Maybe NameAddr)+inputMaintainer atoms =+ debianPackageMaintainer >>= maybe cabalPackageMaintainer (return . Just) >>=+ maybe getDebianMaintainer (return . Just) >>=+ maybe lastResortMaintainer (return . Just)+ where+ debianPackageMaintainer :: IO (Maybe NameAddr)+ debianPackageMaintainer = return (getL Atoms.maintainer atoms)+ cabalPackageMaintainer :: IO (Maybe NameAddr)+ cabalPackageMaintainer = return $ case fmap Cabal.maintainer (getL packageDescription atoms) of+ Nothing -> Nothing+ Just "" -> Nothing+ Just x -> either (const Nothing) Just (parseMaintainer (takeWhile (\ c -> c /= ',' && c /= '\n') x))+ lastResortMaintainer :: IO (Maybe NameAddr)+ lastResortMaintainer = return (Just haskellMaintainer)++intToVerbosity' :: Int -> Verbosity+intToVerbosity' n = fromJust (intToVerbosity (max 0 (min 3 n)))
+ src/Debian/Debianize/Interspersed.hs view
@@ -0,0 +1,61 @@+-- | A class used while converting Cabal dependencies into Debian+-- dependencies.++{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, StandaloneDeriving, TypeSynonymInstances #-}+{-# OPTIONS_GHC -Wall -Werror #-}+module Debian.Debianize.Interspersed+ ( Interspersed(..)+ ) where++import Debug.Trace++-- | A class of Bs insterspersed with Cs. It is used when converting+-- the cabal dependencies to debian, where the "around" type is the+-- binary package name and the "between" type is the version number.+-- +-- Minimum implementation is a method to return the leftmost B, and+-- another to return the following (C,B) pairs. Its unfortunate to+-- require lists in the implementation, a fold function would be+-- better (though I find implementing such folds to be a pain in the+-- you-know-what.)+-- +-- The class provides implementations of three folds, each of which+-- exposes slightly different views of the data.+class Interspersed t around between | t -> around, t -> between where+ leftmost :: t -> around+ pairs :: t -> [(between, around)]++ foldTriples :: (around -> between -> around -> r -> r) -> r -> t -> r+ foldTriples f r0 x = snd $ foldl (\ (b1, r) (c, b2) -> (b2, f b1 c b2 r)) (leftmost x, r0) (pairs x)++ -- Treat the b's as the centers and the c's as the things to their+ -- left and right. Use Maybe to make up for the missing c's at the+ -- ends.+ foldInverted :: (Maybe between -> around -> Maybe between -> r -> r) -> r -> t -> r+ foldInverted f r0 x =+ (\ (bn, an, r) -> f bn an Nothing r) $+ foldl g (Nothing, leftmost x, r0) (pairs x)+ where+ g (b1, a1, r) (b2, a2) = (Just b2, a2, f b1 a1 (Just b2) r)++ foldArounds :: (around -> around -> r -> r) -> r -> t -> r+ foldArounds f r0 x = snd $ foldl (\ (a1, r) (_, a2) -> (a2, f a1 a2 r)) (leftmost x, r0) (pairs x)++ foldBetweens :: (between -> r -> r) -> r -> t -> r+ foldBetweens f r0 x = foldl (\ r (b, _) -> (f b r)) r0 (pairs x)++-- | An example+data Splits = Splits Double [(String, Double)] deriving Show++instance Interspersed Splits Double String where+ leftmost (Splits x _) = x+ pairs (Splits _ x) = x++_splits :: Splits+_splits = Splits 1.0 [("between 1 and 2", 2.0), ("between 2 and 3", 3.0)]++_test1 :: ()+_test1 = foldTriples (\ l s r () -> trace ("l=" ++ show l ++ " s=" ++ show s ++ " r=" ++ show r) ()) () _splits++_test2 :: ()+_test2 = foldInverted (\ sl f sr () -> trace ("sl=" ++ show sl ++ " f=" ++ show f ++ " sr=" ++ show sr) ()) () _splits
+ src/Debian/Debianize/Options.hs view
@@ -0,0 +1,135 @@+module Debian.Debianize.Options+ ( compileArgs+ , options+ ) where++import Data.Char (toLower, isDigit, ord)+import Data.Lens.Lazy (setL, modL)+import Data.Map as Map (insertWith)+import Data.Set as Set (fromList, insert, union, singleton)+import Data.Version (parseVersion)+import Debian.Debianize.Atoms -- (Atoms, depends, conflicts)+import Debian.Debianize.Goodies (doExecutable)+import Debian.Debianize.Types (InstallFile(..), DebAction(..))+import Debian.Orphans ()+import Debian.Policy (SourceFormat(Quilt3), parseMaintainer)+import Debian.Relation (BinPkgName(..), SrcPkgName(..), Relation(..))+import Debian.Version (parseDebianVersion)+import Distribution.PackageDescription (FlagName(..))+import Distribution.Package (PackageName(..))+import Prelude hiding (readFile, lines, null, log, sum)+import System.Console.GetOpt (ArgDescr(..), OptDescr(..), ArgOrder(RequireOrder), getOpt')+import System.FilePath ((</>), splitFileName)+import Text.ParserCombinators.ReadP (readP_to_S)+import Text.Regex.TDFA ((=~))++compileArgs :: [String] -> Atoms -> Atoms+compileArgs args atoms =+ case getOpt' RequireOrder options args of+ (os, [], [], []) -> foldl (flip ($)) atoms os+ (_, non, unk, errs) -> error ("Errors: " ++ show errs +++ ", Unrecognized: " ++ show unk +++ ", Non-Options: " ++ show non)++-- | Options that modify other atoms.+options :: [OptDescr (Atoms -> Atoms)]+options =+ [ Option "v" ["verbose"] (ReqArg (\ s atoms -> setL verbosity (read s) atoms) "n")+ "Change build verbosity",+ Option "n" ["dry-run", "compare"] (NoArg (\ atoms -> setL dryRun True atoms))+ "Just compare the existing debianization to the one we would generate.",+ Option "h?" ["help"] (NoArg (\ atoms -> setL debAction Usage atoms))+ "Show this help text",+ Option "" ["debianize"] (NoArg (\ atoms -> setL debAction Debianize atoms))+ "Generate a new debianization, replacing any existing one. One of --debianize or --substvar is required.",+ Option "" ["substvar"] (ReqArg (\ name atoms -> setL debAction (SubstVar (read name)) atoms) "Doc, Prof, or Dev")+ (unlines ["Write out the list of dependencies required for the dev, prof or doc package depending",+ "on the argument. This value can be added to the appropriate substvars file."]),+ Option "" ["executable"] (ReqArg (\ path x -> executableOption path (\ bin e -> doExecutable bin e x)) "SOURCEPATH or SOURCEPATH:DESTDIR")+ "Create individual eponymous executable packages for these executables. Other executables and data files are gathered into a single utils package.",+ Option "" ["ghc-version"] (ReqArg (\ ver x -> setL compilerVersion (Just (last (map fst (readP_to_S parseVersion ver)))) x) "VERSION")+ "Version of GHC in build environment",+ Option "" ["disable-haddock"] (NoArg (setL noDocumentationLibrary True))+ "Don't generate API documentation. Use this if build is crashing due to a haddock error.",+ Option "" ["missing-dependency"] (ReqArg (\ name atoms -> modL missingDependencies (insert (BinPkgName name)) atoms) "DEB")+ "Mark a package missing, do not add it to any dependency lists in the debianization.",+ Option "" ["source-package-name"] (ReqArg (\ name x -> setL sourcePackageName (Just (SrcPkgName name)) x) "NAME")+ "Use this name for the debian source package. Default is haskell-<cabalname>, where the cabal package name is downcased.",+ Option "" ["disable-library-profiling"] (NoArg (setL noProfilingLibrary True))+ "Don't generate profiling libraries",+ Option "f" ["flags"] (ReqArg (\ fs atoms -> modL cabalFlagAssignments (union (fromList (flagList fs))) atoms) "FLAGS")+ "Set given flags in Cabal conditionals",+ Option "" ["maintainer"] (ReqArg (\ maint x -> setL maintainer (either (error ("Invalid maintainer string: " ++ show maint)) Just (parseMaintainer maint)) x) "Maintainer Name <email addr>")+ "Override the Maintainer name and email in $DEBEMAIL/$EMAIL/$DEBFULLNAME/$FULLNAME",+ Option "" ["build-dep"] (ReqArg (\ name atoms -> modL buildDeps (Set.insert (BinPkgName name)) atoms) "Debian binary package name")+ "Specify a package to add to the build dependency list for this source package, e.g. '--build-dep libglib2.0-dev'.",+ Option "" ["build-dep-indep"] (ReqArg (\ name atoms -> modL buildDepsIndep (Set.insert (BinPkgName name)) atoms) "Debian binary package name")+ "Specify a package to add to the architecture independent build dependency list for this source package, e.g. '--build-dep-indep perl'.",+ Option "" ["dev-dep"] (ReqArg (\ name atoms -> modL extraDevDeps (Set.insert (BinPkgName name)) atoms) "Debian binary package name")+ "Specify a package to add to the Depends: list of the -dev package, e.g. '--dev-dep libncurses5-dev'. It might be good if this implied --build-dep.",+ Option "" ["depends"] (ReqArg (\ arg atoms -> foldr (\ (p, r) atoms' -> modL depends (Map.insertWith union p (singleton r)) atoms') atoms (parseDeps arg)) "deb:deb,deb:deb,...")+ "Generalized --dev-dep - specify pairs A:B of debian binary package names, each A gets a Depends: B",+ Option "" ["conflicts"] (ReqArg (\ arg atoms -> foldr (\ (p, r) atoms' -> modL conflicts (Map.insertWith union p (singleton r)) atoms') atoms (parseDeps arg)) "deb:deb,deb:deb,...")+ "Specify pairs A:B of debian binary package names, each A gets a Conflicts: B. Note that B can have debian style version relations",+ Option "" ["map-dep"] (ReqArg (\ pair atoms -> case break (== '=') pair of+ (cab, (_ : deb)) -> modL extraLibMap (Map.insertWith Set.union cab (singleton (b deb))) atoms+ (_, "") -> error "usage: --map-dep CABALNAME=DEBIANNAME") "CABALNAME=DEBIANNAME")+ "Specify a mapping from the name appearing in the Extra-Library field of the cabal file to a debian binary package name, e.g. --dep-map cryptopp=libcrypto-dev",+ Option "" ["deb-version"] (ReqArg (\ version atoms -> setL debVersion (Just (parseDebianVersion version)) atoms) "VERSION")+ "Specify the version number for the debian package. This will pin the version and should be considered dangerous.",+ Option "" ["revision"] (ReqArg (setL revision . Just) "REVISION")+ "Add this string to the cabal version to get the debian version number. By default this is '-1~hackage1'. Debian policy says this must either be empty (--revision '') or begin with a dash.",+ Option "" ["epoch-map"] (ReqArg (\ pair atoms -> case break (== '=') pair of+ (_, (_ : ['0'])) -> atoms+ (cab, (_ : [d])) | isDigit d -> modL epochMap (Map.insertWith (flip const) (PackageName cab) (ord d - ord '0')) atoms+ _ -> error "usage: --epoch-map CABALNAME=DIGIT") "CABALNAME=DIGIT")+ "Specify a mapping from the cabal package name to a digit to use as the debian package epoch number, e.g. --epoch-map HTTP=1",+ Option "" ["exec-map"] (ReqArg (\ s atoms -> case break (== '=') s of+ (cab, (_ : deb)) -> modL execMap (Map.insertWith (flip const) cab (b deb)) atoms+ _ -> error "usage: --exec-map CABALNAME=DEBNAME") "EXECNAME=DEBIANNAME")+ "Specify a mapping from the name appearing in the Build-Tool field of the cabal file to a debian binary package name, e.g. --exec-map trhsx=haskell-hsx-utils",+ Option "" ["omit-lt-deps"] (NoArg (setL omitLTDeps True))+ "Don't generate the << dependency when we see a cabal equals dependency.",+ Option "" ["quilt"] (NoArg (setL sourceFormat (Just Quilt3)))+ "The package has an upstream tarball, write '3.0 (quilt)' into source/format.",+ Option "" ["builddir"] (ReqArg (\ s atoms -> setL buildDir (Just (s </> "build")) atoms) "PATH")+ "Subdirectory where cabal does its build, dist/build by default, dist-ghc when run by haskell-devscripts. The build subdirectory is added to match the behavior of the --builddir option in the Setup script."+ ]++anyrel :: BinPkgName -> Relation+anyrel x = Rel x Nothing Nothing++-- | Process a --executable command line argument+executableOption :: String -> (BinPkgName -> InstallFile -> a) -> a+executableOption arg f =+ case span (/= ':') arg of+ (sp, md) ->+ let (sd, name) = splitFileName sp in+ f (BinPkgName name)+ (InstallFile { execName = name+ , destName = name+ , sourceDir = case sd of "./" -> Nothing; _ -> Just sd+ , destDir = case md of (':' : dd) -> Just dd; _ -> Nothing })++parseDeps :: String -> [(BinPkgName, Relation)]+parseDeps arg =+ map pair (split arg)+ where+ split s =+ case s =~ "^[ \t,]*([^,]+)[ \t,]*" :: (String, String, String, [String]) of+ (_, _, tl, [hd]) -> hd : split tl+ (_, _, "", _) -> []+ _ -> error $ "Invalid dependency: " ++ show s+ pair s =+ case s =~ "^[ \t:]*([^ \t:]+)[ \t]*:[ \t]*(.+)[ \t]*" :: (String, String, String, [String]) of+ (_, _, _, [x, y]) -> (b x, anyrel (b y))+ _ -> error $ "Invalid dependency: " ++ show s++-- Lifted from Distribution.Simple.Setup, since it's not exported.+flagList :: String -> [(FlagName, Bool)]+flagList = map tagWithValue . words+ where tagWithValue ('-':name) = (FlagName (map toLower name), False)+ tagWithValue name = (FlagName (map toLower name), True)++b :: String -> BinPkgName+b = BinPkgName
+ src/Debian/Debianize/SubstVars.hs view
@@ -0,0 +1,138 @@+{-# LANGUAGE CPP, ScopedTypeVariables, TupleSections, TypeSynonymInstances #-}+{-# OPTIONS -Wall -fno-warn-name-shadowing #-}++-- | Support for generating Debianization from Cabal data.++module Debian.Debianize.SubstVars+ ( substvars+ ) where++import Control.Exception (SomeException, try)+import Control.Monad (foldM)+import Control.Monad.Reader (ReaderT(runReaderT))+import Control.Monad.Trans (lift)+import Data.Lens.Lazy (getL, modL)+import Data.List+import qualified Data.Map as Map+import Data.Maybe+import qualified Data.Set as Set+import Data.Text (pack)+import Debian.Control+import Debian.Debianize.Atoms (Atoms, compiler, dryRun, packageInfo)+import Debian.Debianize.Dependencies (cabalDependencies, debDeps, debNameFromType, filterMissing)+import Debian.Debianize.Input (inputCabalization)+import Debian.Debianize.Types (PackageInfo(PackageInfo, cabalName, devDeb, profDeb, docDeb), DebType)+import Debian.Debianize.Utility (buildDebVersionMap, DebMap, showDeps, dpkgFileMap, cond, debOfFile, (!), diffFile, replaceFile)+import qualified Debian.Relation as D+import Distribution.Package (Dependency(..), PackageName(PackageName))+import Distribution.Simple.Compiler (CompilerFlavor(..), compilerFlavor, Compiler(..))+import Distribution.Simple.Utils (die)+import Distribution.Text (display)+import System.Directory (doesDirectoryExist, getDirectoryContents)+import System.FilePath ((</>))+import Text.PrettyPrint.ANSI.Leijen (pretty)++-- | Expand the contents of the .substvars file for a library package.+-- Each cabal package corresponds to a directory <name>-<version>,+-- either in /usr/lib or in /usr/lib/haskell-packages/ghc/lib. In+-- that directory is a compiler subdirectory such as ghc-6.8.2. In+-- the ghc subdirectory is one or two library files of the form+-- libHS<name>-<version>.a and libHS<name>-<version>_p.a. We can+-- determine the debian package names by running dpkg -S on these+-- names, or examining the /var/lib/dpkg/info/\*.list files. From+-- these we can determine the source package name, and from that the+-- documentation package name.+substvars :: Atoms+ -> DebType -- ^ The type of deb we want to write substvars for - Dev, Prof, or Doc+ -> IO ()+substvars atoms debType =+ do atoms' <- inputCabalization "." atoms+ debVersions <- buildDebVersionMap+ atoms'' <- libPaths (fromMaybe (error "substvars") $ getL compiler atoms') debVersions atoms'+ control <- readFile "debian/control" >>= either (error . show) return . parseControl "debian/control"+ substvars' atoms'' debType control++substvars' :: Atoms -> DebType -> Control' String -> IO ()+substvars' atoms debType control =+ case (missingBuildDeps, path) of+ -- There should already be a .substvars file produced by dh_haskell_prep,+ -- keep the relations listed there. They will contain something like this:+ -- libghc-cabal-debian-prof.substvars:+ -- haskell:Depends=ghc-prof (<< 6.8.2-999), ghc-prof (>= 6.8.2), libghc-cabal-debian-dev (= 0.4)+ -- libghc-cabal-debian-dev.substvars:+ -- haskell:Depends=ghc (<< 6.8.2-999), ghc (>= 6.8.2)+ -- haskell-cabal-debian-doc.substvars:+ -- haskell:Depends=ghc-doc, haddock (>= 2.1.0), haddock (<< 2.1.0-999)+ ([], Just path') ->+ readFile path' >>= \ old ->+ let new = addDeps old in+ diffFile path' (pack new) >>= maybe (putStrLn ("cabal-debian substvars: No updates found for " ++ show path'))+ (\ diff -> if getL dryRun atoms then putStr diff else replaceFile path' new)+ ([], Nothing) -> return ()+ (missing, _) ->+ die ("These debian packages need to be added to the build dependency list so the required cabal packages are available:\n " ++ intercalate "\n " (map (show . pretty . fst) missing) +++ "\nIf this is an obsolete package you may need to withdraw the old versions from the\n" +++ "upstream repository, and uninstall and purge it from your local system.")+ where+ addDeps old =+ case partition (isPrefixOf "haskell:Depends=") (lines old) of+ ([], other) -> unlines (("haskell:Depends=" ++ showDeps (filterMissing atoms deps)) : other)+ (hdeps, more) ->+ case deps of+ [] -> unlines (hdeps ++ more)+ _ -> unlines (map (++ (", " ++ showDeps (filterMissing atoms deps))) hdeps ++ more)+ path = fmap (\ (D.BinPkgName x) -> "debian/" ++ x ++ ".substvars") name+ name = debNameFromType control debType+ deps = debDeps debType atoms control+ -- We must have build dependencies on the profiling and documentation packages+ -- of all the cabal packages.+ missingBuildDeps =+ let requiredDebs =+ concat (map (\ (Dependency name _) ->+ case Map.lookup name (getL packageInfo atoms) of+ Just info ->+ let prof = maybe (devDeb info) Just (profDeb info) in+ let doc = docDeb info in+ catMaybes [prof, doc]+ Nothing -> []) (cabalDependencies atoms)) in+ filter (not . (`elem` buildDepNames) . fst) requiredDebs+ buildDepNames :: [D.BinPkgName]+ buildDepNames = concat (map (map (\ (D.Rel s _ _) -> s)) buildDeps)+ buildDeps :: D.Relations+ buildDeps = (either (error . show) id . D.parseRelations $ bd) ++ (either (error . show) id . D.parseRelations $ bdi)+ --sourceName = maybe (error "Invalid control file") (\ (Field (_, s)) -> stripWS s) (lookupP "Source" (head (unControl control)))+ bd = maybe "" (\ (Field (_a, b)) -> stripWS b) . lookupP "Build-Depends" . head . unControl $ control+ bdi = maybe "" (\ (Field (_a, b)) -> stripWS b) . lookupP "Build-Depends-Indep" . head . unControl $ control++libPaths :: Compiler -> DebMap -> Atoms -> IO Atoms+libPaths compiler debVersions atoms+ | compilerFlavor compiler == GHC =+ do a <- getDirPaths "/usr/lib"+ b <- getDirPaths "/usr/lib/haskell-packages/ghc/lib"+ -- Build a map from names of installed debs to version numbers+ dpkgFileMap >>= runReaderT (foldM (packageInfo' compiler debVersions) atoms (a ++ b))+ | True = error $ "Can't handle compiler flavor: " ++ show (compilerFlavor compiler)+ where+ getDirPaths path = try (getDirectoryContents path) >>= return . map (\ x -> (path, x)) . either (\ (_ :: SomeException) -> []) id++packageInfo' :: Compiler -> DebMap -> Atoms -> (FilePath, String) -> ReaderT (Map.Map FilePath (Set.Set D.BinPkgName)) IO Atoms+packageInfo' compiler debVersions atoms (d, f) =+ case parseNameVersion f of+ Nothing -> return atoms+ Just (p, v) -> lift (doesDirectoryExist (d </> f </> cdir)) >>= cond (return atoms) (info (p, v))+ where+ parseNameVersion s =+ case (break (== '-') (reverse s)) of+ (_a, "") -> Nothing+ (a, b) -> Just (reverse (tail b), reverse a)+ cdir = display (compilerId compiler)+ info (p, v) =+ do dev <- debOfFile ("^" ++ d </> p ++ "-" ++ v </> cdir </> "libHS" ++ p ++ "-" ++ v ++ ".a$")+ prof <- debOfFile ("^" ++ d </> p ++ "-" ++ v </> cdir </> "libHS" ++ p ++ "-" ++ v ++ "_p.a$")+ doc <- debOfFile ("/" ++ p ++ ".haddock$")+ return $ modL packageInfo (Map.insert+ (PackageName p)+ (PackageInfo { cabalName = PackageName p+ , devDeb = maybe Nothing (\ x -> Just (x, debVersions ! x)) dev+ , profDeb = maybe Nothing (\ x -> Just (x, debVersions ! x)) prof+ , docDeb = maybe Nothing (\ x -> Just (x, debVersions ! x)) doc })) atoms
+ src/Debian/Debianize/Tests.hs view
@@ -0,0 +1,611 @@+{-# LANGUAGE OverloadedStrings, RankNTypes, ScopedTypeVariables, StandaloneDeriving #-}+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}+module Debian.Debianize.Tests+ ( tests+ ) where++import Data.Algorithm.Diff.Context (contextDiff)+import Data.Algorithm.Diff.Pretty (prettyDiff)+import Data.Function (on)+import Data.Lens.Lazy (setL, getL, modL)+import Data.List (sortBy)+import Data.Map as Map (differenceWithKey, intersectionWithKey)+import qualified Data.Map as Map+import Data.Maybe (fromMaybe)+import Data.Monoid (mconcat, (<>), mempty)+import Data.Set as Set (fromList, union, insert, singleton)+import qualified Data.Text as T+import Debian.Changes (ChangeLog(..), ChangeLogEntry(..), parseEntry)+import Debian.Debianize.Debianize (debianization)+import Debian.Debianize.Atoms as Atoms+ (Atoms, rulesHead, compat, sourceFormat, changelog, sourcePackageName, control, missingDependencies, revision,+ binaryArchitectures, copyright, debVersion, execMap, buildDeps, buildDepsIndep, utilsPackageName, description,+ depends, conflicts, install, installData)+import Debian.Debianize.ControlFile as Deb (SourceDebDescription(..), BinaryDebDescription(..), PackageRelations(..), VersionControlSpec(..))+import Debian.Debianize.Debianize (writeDebianization)+import Debian.Debianize.Dependencies (getRulesHead)+import Debian.Debianize.Files (toFileMap)+import Debian.Debianize.Finalize (finalizeDebianization)+import Debian.Debianize.Goodies (defaultAtoms, tightDependencyFixup, doExecutable, doWebsite, doServer, doBackups)+import Debian.Debianize.Input (inputChangeLog, inputDebianization, inputCabalization)+import Debian.Debianize.Types (InstallFile(..), Server(..), Site(..))+import Debian.Policy (databaseDirectory, StandardsVersion(StandardsVersion), getDebhelperCompatLevel,+ getDebianStandardsVersion, PackagePriority(Extra), PackageArchitectures(All),+ SourceFormat(Native3), Section(..), parseMaintainer)+import Debian.Relation (Relation(..), VersionReq(..), SrcPkgName(..), BinPkgName(..))+import Debian.Release (ReleaseName(ReleaseName, relName))+import Debian.Version (buildDebianVersion, parseDebianVersion)+import Distribution.License (License(BSD3))+import Prelude hiding (log)+import System.FilePath ((</>))+import Test.HUnit+import Text.ParserCombinators.Parsec.Rfc2822 (NameAddr(..))+import Text.PrettyPrint.ANSI.Leijen (Pretty, pretty, text)++-- | Create a Debianization based on a changelog entry and a license+-- value. Uses the currently installed versions of debhelper and+-- debian-policy to set the compatibility levels.+newDebianization :: ChangeLog -> Int -> StandardsVersion -> Atoms+newDebianization (ChangeLog (WhiteSpace {} : _)) _ _ = error "defaultDebianization: Invalid changelog entry"+newDebianization (log@(ChangeLog (entry : _))) level standards =+ setL changelog (Just log) $+ setL compat (Just level) $+ modL control (\ x -> x { source = Just (SrcPkgName (logPackage entry))+ , maintainer = (either error Just (parseMaintainer (logWho entry)))+ , standardsVersion = Just standards }) $+ defaultAtoms+newDebianization _ _ _ = error "Invalid changelog"++newDebianization' :: Int -> StandardsVersion -> Atoms+newDebianization' level standards =+ setL compat (Just level) $+ modL control (\ x -> x { standardsVersion = Just standards }) $+ defaultAtoms++tests :: Test+tests = TestLabel "Debianization Tests" (TestList [test1, test2, test3, test4, test5, test6, test7, test8, test9])++test1 :: Test+test1 =+ TestLabel "test1" $+ TestCase (do level <- getDebhelperCompatLevel+ standards <- getDebianStandardsVersion+ let deb = finalizeDebianization $ setL copyright (Just (Left BSD3)) $+ newDebianization (ChangeLog [testEntry]) level standards+ assertEqual "test1" [] (diffDebianizations testDeb1 deb))+ where+ testDeb1 :: Atoms+ testDeb1 =+ setL rulesHead (Just . T.unlines $+ [ "#!/usr/bin/make -f"+ , ""+ , "include /usr/share/cdbs/1/rules/debhelper.mk"+ , "include /usr/share/cdbs/1/class/hlibrary.mk" ]) $+ setL compat (Just 9) $ -- This will change as new version of debhelper are released+ setL copyright (Just (Left BSD3)) $+ modL control+ (\ y -> y { source = Just (SrcPkgName {unSrcPkgName = "haskell-cabal-debian"})+ , maintainer = Just (NameAddr (Just "David Fox") "dsf@seereason.com")+ , standardsVersion = Just (StandardsVersion 3 9 3 (Just 1)) -- This will change as new versions of debian-policy are released+ , buildDepends = [[Rel (BinPkgName "debhelper") (Just (GRE (parseDebianVersion ("7.0" :: String)))) Nothing],+ [Rel (BinPkgName "haskell-devscripts") (Just (GRE (parseDebianVersion ("0.8" :: String)))) Nothing],+ [Rel (BinPkgName "cdbs") Nothing Nothing],+ [Rel (BinPkgName "ghc") Nothing Nothing],+ [Rel (BinPkgName "ghc-prof") Nothing Nothing]]+ , buildDependsIndep = [[Rel (BinPkgName "ghc-doc") Nothing Nothing]]+ }) $+ (newDebianization log 9 (StandardsVersion 3 9 3 (Just 1)))+ log = ChangeLog [Entry { logPackage = "haskell-cabal-debian"+ , logVersion = buildDebianVersion Nothing "2.6.2" Nothing+ , logDists = [ReleaseName {relName = "unstable"}]+ , logUrgency = "low"+ , logComments = " * Fix a bug constructing the destination pathnames that was dropping\n files that were supposed to be installed into packages.\n"+ , logWho = "David Fox <dsf@seereason.com>"+ , logDate = "Thu, 20 Dec 2012 06:49:25 -0800" }]++test2 :: Test+test2 =+ TestLabel "test2" $+ TestCase (do level <- getDebhelperCompatLevel+ standards <- getDebianStandardsVersion+ let deb = finalizeDebianization $ setL copyright (Just (Left BSD3)) $ newDebianization (ChangeLog [testEntry]) level standards+ assertEqual "test2" [] (diffDebianizations expect deb))+ where+ expect =+ setL rulesHead (Just . T.unlines $+ ["#!/usr/bin/make -f",+ "",+ "include /usr/share/cdbs/1/rules/debhelper.mk",+ "include /usr/share/cdbs/1/class/hlibrary.mk"]) $+ setL compat (Just 9) $+ setL copyright (Just (Left BSD3)) $+ modL control+ (\ y -> y+ { source = Just (SrcPkgName {unSrcPkgName = "haskell-cabal-debian"}),+ maintainer = Just (NameAddr {nameAddr_name = Just "David Fox", nameAddr_addr = "dsf@seereason.com"}),+ standardsVersion = Just (StandardsVersion 3 9 3 (Just 1)),+ buildDepends = [[Rel (BinPkgName "debhelper") (Just (GRE (parseDebianVersion ("7.0" :: String)))) Nothing],+ [Rel (BinPkgName "haskell-devscripts") (Just (GRE (parseDebianVersion ("0.8" :: String)))) Nothing],+ [Rel (BinPkgName "cdbs") Nothing Nothing],+ [Rel (BinPkgName "ghc") Nothing Nothing],+ [Rel (BinPkgName "ghc-prof") Nothing Nothing]],+ buildDependsIndep = [[Rel (BinPkgName "ghc-doc") Nothing Nothing]]+ }) $+ (newDebianization log 9 (StandardsVersion 3 9 3 (Just 1)))+ log = ChangeLog [Entry {logPackage = "haskell-cabal-debian",+ logVersion = Debian.Version.parseDebianVersion ("2.6.2" :: String),+ logDists = [ReleaseName {relName = "unstable"}],+ logUrgency = "low",+ logComments = unlines [" * Fix a bug constructing the destination pathnames that was dropping",+ " files that were supposed to be installed into packages."],+ logWho = "David Fox <dsf@seereason.com>",+ logDate = "Thu, 20 Dec 2012 06:49:25 -0800"}]++test3 :: Test+test3 =+ TestLabel "test3" $+ TestCase (do deb <- inputDebianization "test-data/haskell-devscripts"+ assertEqual "test3" [] (diffDebianizations testDeb2 deb))+ where+ testDeb2 :: Atoms+ testDeb2 =+ setL sourceFormat (Just Native3) $+ setL rulesHead (Just "#!/usr/bin/make -f\n# -*- makefile -*-\n\n# Uncomment this to turn on verbose mode.\n#export DH_VERBOSE=1\n\nDEB_VERSION := $(shell dpkg-parsechangelog | egrep '^Version:' | cut -f 2 -d ' ')\n\nmanpages = $(shell cat debian/manpages)\n\n%.1: %.pod\n\tpod2man -c 'Haskell devscripts documentation' -r 'Haskell devscripts $(DEB_VERSION)' $< > $@\n\n%.1: %\n\tpod2man -c 'Haskell devscripts documentation' -r 'Haskell devscripts $(DEB_VERSION)' $< > $@\n\n.PHONY: build\nbuild: $(manpages)\n\ninstall-stamp:\n\tdh install\n\n.PHONY: install\ninstall: install-stamp\n\nbinary-indep-stamp: install-stamp\n\tdh binary-indep\n\ttouch $@\n\n.PHONY: binary-indep\nbinary-indep: binary-indep-stamp\n\n.PHONY: binary-arch\nbinary-arch: install-stamp\n\n.PHONY: binary\nbinary: binary-indep-stamp\n\n.PHONY: clean\nclean:\n\tdh clean\n\trm -f $(manpages)\n\n\n") $+ setL compat (Just 7) $+ setL copyright (Just (Right "This package was debianized by John Goerzen <jgoerzen@complete.org> on\nWed, 6 Oct 2004 09:46:14 -0500.\n\nCopyright information removed from this test data.\n\n")) $+ modL control+ (\ y -> y+ { source = Just (SrcPkgName {unSrcPkgName = "haskell-devscripts"})+ , maintainer = Just (NameAddr {nameAddr_name = Just "Debian Haskell Group", nameAddr_addr = "pkg-haskell-maintainers@lists.alioth.debian.org"})+ , uploaders = [NameAddr {nameAddr_name = Just "Marco Silva", nameAddr_addr = "marcot@debian.org"},NameAddr {nameAddr_name = Just "Joachim Breitner", nameAddr_addr = "nomeata@debian.org"}]+ , priority = Just Extra+ , section = Just (MainSection "haskell")+ , buildDepends = (buildDepends y) ++ [[Rel (BinPkgName {unBinPkgName = "debhelper"}) (Just (GRE (Debian.Version.parseDebianVersion ("7" :: String)))) Nothing]]+ , buildDependsIndep = (buildDependsIndep y) ++ [[Rel (BinPkgName {unBinPkgName = "perl"}) Nothing Nothing]]+ , standardsVersion = Just (StandardsVersion 3 9 4 Nothing)+ , vcsFields = Set.union (vcsFields y) (Set.fromList [ VCSBrowser "http://darcs.debian.org/cgi-bin/darcsweb.cgi?r=pkg-haskell/haskell-devscripts"+ , VCSDarcs "http://darcs.debian.org/pkg-haskell/haskell-devscripts"])+ , binaryPackages = [BinaryDebDescription { package = BinPkgName {unBinPkgName = "haskell-devscripts"}+ , architecture = All+ , binarySection = Nothing+ , binaryPriority = Nothing+ , essential = False+ , Deb.description =+ (T.intercalate "\n"+ ["Tools to help Debian developers build Haskell packages",+ " This package provides a collection of scripts to help build Haskell",+ " packages for Debian. Unlike haskell-utils, this package is not",+ " expected to be installed on the machines of end users.",+ " .",+ " This package is designed to support Cabalized Haskell libraries. It",+ " is designed to build a library for each supported Debian compiler or",+ " interpreter, generate appropriate postinst/prerm files for each one,",+ " generate appropriate substvars entries for each one, and install the",+ " package in the Debian temporary area as part of the build process."])+ , relations =+ PackageRelations+ { Deb.depends =+ [ [Rel (BinPkgName {unBinPkgName = "dctrl-tools"}) Nothing Nothing]+ , [Rel (BinPkgName {unBinPkgName = "debhelper"}) Nothing Nothing]+ , [Rel (BinPkgName {unBinPkgName = "dh-buildinfo"}) Nothing Nothing]+ , [Rel (BinPkgName {unBinPkgName = "ghc"}) (Just (GRE (Debian.Version.parseDebianVersion ("7.6" :: String)))) Nothing]+ , [Rel (BinPkgName {unBinPkgName = "cdbs"}) Nothing Nothing]+ , [Rel (BinPkgName {unBinPkgName = "${misc:Depends}"}) Nothing Nothing]+ , [Rel (BinPkgName {unBinPkgName = "html-xml-utils"}) Nothing Nothing]+ , [Rel (BinPkgName {unBinPkgName = "hscolour"}) (Just (GRE (Debian.Version.parseDebianVersion ("1.8" :: String)))) Nothing]+ , [Rel (BinPkgName {unBinPkgName = "ghc-haddock"}) (Just (GRE (Debian.Version.parseDebianVersion ("7.4" :: String)))) Nothing] ]+ , recommends = []+ , suggests = []+ , preDepends = []+ , breaks = []+ , Deb.conflicts = []+ , provides = []+ , replaces = []+ , builtUsing = [] }}]}) $+ (newDebianization log 7 (StandardsVersion 3 9 4 Nothing))+ log = ChangeLog [Entry { logPackage = "haskell-devscripts"+ , logVersion = Debian.Version.parseDebianVersion ("0.8.13" :: String)+ , logDists = [ReleaseName {relName = "experimental"}]+ , logUrgency = "low"+ , logComments = " [ Joachim Breitner ]\n * Improve parsing of \"Setup register\" output, patch by David Fox\n * Enable creation of hoogle files, thanks to Kiwamu Okabe for the\n suggestion. \n\n [ Kiwamu Okabe ]\n * Need --html option to fix bug that --hoogle option don't output html file.\n * Support to create /usr/lib/ghc-doc/hoogle/*.txt for hoogle package.\n\n [ Joachim Breitner ]\n * Symlink hoogle\8217s txt files to /usr/lib/ghc-doc/hoogle/\n * Bump ghc dependency to 7.6 \n * Bump standards version\n"+ , logWho = "Joachim Breitner <nomeata@debian.org>"+ , logDate = "Mon, 08 Oct 2012 21:14:50 +0200" },+ Entry { logPackage = "haskell-devscripts"+ , logVersion = Debian.Version.parseDebianVersion ("0.8.12" :: String)+ , logDists = [ReleaseName {relName = "unstable"}]+ , logUrgency = "low"+ , logComments = " * Depend on ghc >= 7.4, adjusting to its haddock --interface-version\n behaviour.\n"+ , logWho = "Joachim Breitner <nomeata@debian.org>"+ , logDate = "Sat, 04 Feb 2012 10:50:33 +0100"}]++test4 :: Test+test4 =+ TestLabel "test4" $+ TestCase (do old <- inputDebianization "test-data/clckwrks-dot-com/output"+ new <- inputCabalization "test-data/clckwrks-dot-com/input" (newDebianization' 7 (StandardsVersion 3 9 4 Nothing)) >>=+ debianization "test-data/clckwrks-dot-com/input" .+ (modL control (\ y -> y {homepage = Just "http://www.clckwrks.com/"}) .+ setL sourceFormat (Just Native3) .+ modL missingDependencies (insert (BinPkgName "libghc-clckwrks-theme-clckwrks-doc")) .+ setL revision Nothing .+ doWebsite (BinPkgName "clckwrks-dot-com-production") (theSite (BinPkgName "clckwrks-dot-com-production")) .+ doBackups (BinPkgName "clckwrks-dot-com-backups") "clckwrks-dot-com-backups" .+ fixRules .+ tight .+ setL changelog (getL changelog old))+ assertEqual "test4" [] (diffDebianizations old (copyFirstLogEntry old new)))+ where+ -- A log entry gets added when the Debianization is generated,+ -- it won't match so drop it for the comparison.+ serverNames = map BinPkgName ["clckwrks-dot-com-production"] -- , "clckwrks-dot-com-staging", "clckwrks-dot-com-development"]+ -- Insert a line just above the debhelper.mk include+ fixRules deb =+ modL rulesHead (\ mt -> (Just . f) (fromMaybe (getRulesHead deb) mt)) deb+ where+ f t = T.unlines $ concat $+ map (\ line -> if line == "include /usr/share/cdbs/1/rules/debhelper.mk"+ then ["DEB_SETUP_GHC_CONFIGURE_ARGS = -fbackups", "", line] :: [T.Text]+ else [line] :: [T.Text]) (T.lines t)+{-+ mapAtoms f deb+ where+ f :: DebAtomKey -> DebAtom -> Set (DebAtomKey, DebAtom)+ f Source (DebRulesHead t) =+ singleton (Source, DebRulesHead (T.unlines $ concat $+ map (\ line -> if line == "include /usr/share/cdbs/1/rules/debhelper.mk"+ then ["DEB_SETUP_GHC_CONFIGURE_ARGS = -fbackups", "", line] :: [T.Text]+ else [line] :: [T.Text]) (T.lines t)))+ f k a = singleton (k, a)+-}+ tight deb = foldr (tightDependencyFixup+ -- For each pair (A, B) make sure that this package requires the+ -- same exact version of package B as the version of A currently+ -- installed during the build.+ [(BinPkgName "libghc-clckwrks-theme-clckwrks-dev", BinPkgName "haskell-clckwrks-theme-clckwrks-utils"),+ (BinPkgName "libghc-clckwrks-plugin-media-dev", BinPkgName "haskell-clckwrks-plugin-media-utils"),+ (BinPkgName "libghc-clckwrks-plugin-bugs-dev", BinPkgName "haskell-clckwrks-plugin-bugs-utils"),+ (BinPkgName "libghc-clckwrks-dev", BinPkgName "haskell-clckwrks-utils")]) deb serverNames++ theSite :: BinPkgName -> Site+ theSite deb =+ Site { domain = hostname'+ , serverAdmin = "logic@seereason.com"+ , server = theServer deb }+ theServer :: BinPkgName -> Server+ theServer deb =+ Server { hostname =+ case deb of+ BinPkgName "clckwrks-dot-com-production" -> hostname'+ _ -> hostname'+ , port = portNum deb+ , headerMessage = "Generated by clckwrks-dot-com/Setup.hs"+ , retry = "60"+ , serverFlags =+ [ "--http-port", show (portNum deb)+ , "--hide-port"+ , "--hostname", hostname'+ , "--top", databaseDirectory deb+ , "--enable-analytics"+ , "--jquery-path", "/usr/share/javascript/jquery/"+ , "--jqueryui-path", "/usr/share/javascript/jquery-ui/"+ , "--jstree-path", jstreePath+ , "--json2-path",json2Path+ ]+ , installFile =+ InstallFile { execName = "clckwrks-dot-com-server"+ , destName = show (pretty deb)+ , sourceDir = Nothing+ , destDir = Nothing }+ }+ hostname' = "clckwrks.com"+ portNum :: BinPkgName -> Int+ portNum (BinPkgName deb) =+ case deb of+ "clckwrks-dot-com-production" -> 9029+ "clckwrks-dot-com-staging" -> 9038+ "clckwrks-dot-com-development" -> 9039+ _ -> error $ "Unexpected package name: " ++ deb+ jstreePath = "/usr/share/clckwrks-0.13.2/jstree"+ json2Path = "/usr/share/clckwrks-0.13.2/json2"++anyrel :: BinPkgName -> Relation+anyrel b = Rel b Nothing Nothing++test5 :: Test+test5 =+ TestLabel "test5" $+ TestCase (do old <- inputDebianization "test-data/creativeprompts/output"+ let standards = fromMaybe (error "test5") (standardsVersion (getL control old))+ level = fromMaybe (error "test5") (getL compat old)+ new <- debianization "test-data/creativeprompts/input"+ (setL sourceFormat (Just Native3) $+ modL binaryArchitectures (Map.insert (BinPkgName "creativeprompts-data") All) $+ modL binaryArchitectures (Map.insert (BinPkgName "creativeprompts-development") All) $+ modL binaryArchitectures (Map.insert (BinPkgName "creativeprompts-production") All) $+ setL utilsPackageName (Just (BinPkgName "creativeprompts-data")) $+ modL Atoms.description (Map.insertWith (error "test5") (BinPkgName "creativeprompts-data")+ (T.intercalate "\n" [ "creativeprompts.com data files"+ , " Static data files for creativeprompts.com"])) $+ modL Atoms.description (Map.insertWith (error "test5") (BinPkgName "creativeprompts-production")+ (T.intercalate "\n" [ "Configuration for running the creativeprompts.com server"+ , " Production version of the blog server, runs on port"+ , " 9021 with HTML validation turned off." ])) $+ modL Atoms.description (Map.insertWith (error "test5") (BinPkgName "creativeprompts-development")+ (T.intercalate "\n" [ "Configuration for running the creativeprompts.com server"+ , " Testing version of the blog server, runs on port"+ , " 8000 with HTML validation turned on." ])) $+ modL Atoms.description (Map.insertWith (error "test5") (BinPkgName "creativeprompts-backups")+ (T.intercalate "\n" [ "backup program for creativeprompts.com"+ , " Install this somewhere other than creativeprompts.com to run automated"+ , " backups of the database."])) $+ modL Atoms.depends (Map.insertWith union (BinPkgName "creativeprompts-server") (singleton (anyrel (BinPkgName "markdown")))) $+ modL execMap (Map.insertWith (error "Conflict in execMap") "trhsx" (BinPkgName "haskell-hsx-utils")) $+ doBackups (BinPkgName "creativeprompts-backups") "creativeprompts-backups" $+ doServer (BinPkgName "creativeprompts-development") (theServer (BinPkgName "creativeprompts-development")) $+ doWebsite (BinPkgName "creativeprompts-production") (theSite (BinPkgName "creativeprompts-production")) $+ setL changelog (getL changelog old) $+ (newDebianization' level standards))+ assertEqual "test5" [] (diffDebianizations old (copyFirstLogEntry old new)))+ where+ theSite :: BinPkgName -> Site+ theSite deb =+ Site { domain = hostname'+ , serverAdmin = "logic@seereason.com"+ , server = theServer deb }+ theServer :: BinPkgName -> Server+ theServer deb =+ Server { hostname =+ case deb of+ BinPkgName "clckwrks-dot-com-production" -> hostname'+ _ -> hostname'+ , port = portNum deb+ , headerMessage = "Generated by creativeprompts-dot-com/debian/Debianize.hs"+ , retry = "60"+ , serverFlags =+ [ "--http-port", show (portNum deb)+ , "--hide-port"+ , "--hostname", hostname'+ , "--top", databaseDirectory deb+ , "--enable-analytics"+ , "--jquery-path", "/usr/share/javascript/jquery/"+ , "--jqueryui-path", "/usr/share/javascript/jquery-ui/"+ , "--jstree-path", jstreePath+ , "--json2-path",json2Path+ ]+ , installFile =+ InstallFile { execName = "creativeprompts-server"+ , destName = show (pretty deb)+ , sourceDir = Nothing+ , destDir = Nothing }+ }+ hostname' = "creativeprompts.com"+ portNum :: BinPkgName -> Int+ portNum (BinPkgName deb) =+ case deb of+ "creativeprompts-production" -> 9022+ "creativeprompts-staging" -> 9033+ "creativeprompts-development" -> 9034+ _ -> error $ "Unexpected package name: " ++ deb+ jstreePath = "/usr/share/clckwrks-0.13.2/jstree"+ json2Path = "/usr/share/clckwrks-0.13.2/json2"+++copyFirstLogEntry :: Atoms -> Atoms -> Atoms+copyFirstLogEntry deb1 deb2 =+ modL changelog (const (Just (ChangeLog (hd1 : tl2)))) deb2+ where+ ChangeLog (hd1 : _) = fromMaybe (error "Missing debian/changelog") (getL changelog deb1)+ ChangeLog (_ : tl2) = fromMaybe (error "Missing debian/changelog") (getL changelog deb2)++copyChangelog :: Atoms -> Atoms -> Atoms+copyChangelog deb1 deb2 = modL changelog (const (getL changelog deb1)) deb2++test6 :: Test+test6 =+ TestLabel "test6" $+ TestCase ( do old <- inputDebianization "test-data/artvaluereport2/output"+ new <- debianization "test-data/artvaluereport2/input"+ (modL control (\ y -> y {homepage = Just "http://appraisalreportonline.com"}) $+ setL sourcePackageName (Just (SrcPkgName "haskell-artvaluereport2")) $+ setL utilsPackageName (Just (BinPkgName "artvaluereport2-server")) $+ modL binaryArchitectures (Map.insert (BinPkgName "artvaluereport2-development") All) $+ modL binaryArchitectures (Map.insert (BinPkgName "artvaluereport2-production") All) $+ modL binaryArchitectures (Map.insert (BinPkgName "artvaluereport2-staging") All) $+ modL buildDepsIndep (Set.insert (BinPkgName "libjs-jcrop")) $+ modL buildDepsIndep (Set.insert (BinPkgName "libjs-jquery")) $+ modL buildDepsIndep (Set.insert (BinPkgName "libjs-jquery-u")) $++ modL Atoms.depends (Map.insertWith union (BinPkgName "artvaluereport2-development") (singleton (anyrel (BinPkgName "artvaluereport2-server")))) $+ modL Atoms.depends (Map.insertWith union (BinPkgName "artvaluereport2-production") (singleton (anyrel (BinPkgName "artvaluereport2-server")))) $+ modL Atoms.depends (Map.insertWith union (BinPkgName "artvaluereport2-production") (singleton (anyrel (BinPkgName "apache2")))) $+ modL Atoms.depends (Map.insertWith union (BinPkgName "artvaluereport2-staging") (singleton (anyrel (BinPkgName "artvaluereport2-server")))) $+ -- This should go into the "real" data directory. And maybe a different icon for each server?+ modL install (Map.insertWith union (BinPkgName "artvaluereport2-server") (singleton ("theme/ArtValueReport_SunsetSpectrum.ico", "usr/share/artvaluereport2-data"))) $+ modL Atoms.description (Map.insertWith (error "test6") (BinPkgName "artvaluereport2-backups")+ (T.intercalate "\n"+ [ "backup program for the appraisalreportonline.com site"+ , " Install this somewhere other than where the server is running get"+ , " automated backups of the database." ])) $+ doBackups (BinPkgName "artvaluereport2-backups") "artvaluereport2-backups" $+ doWebsite (BinPkgName "artvaluereport2-production") (theSite (BinPkgName "artvaluereport2-production")) $+ doServer (BinPkgName "artvaluereport2-staging") (theServer (BinPkgName "artvaluereport2-staging")) $+ doServer (BinPkgName "artvaluereport2-development") (theServer (BinPkgName "artvaluereport2-development")) $+ flip (foldr (\ s deb -> modL Atoms.depends (Map.insertWith union (BinPkgName "artvaluereport2-server") (singleton (anyrel (BinPkgName s)))) deb))+ ["libjs-jquery", "libjs-jquery-ui", "libjs-jcrop", "libjpeg-progs", "netpbm",+ "texlive-latex-recommended", "texlive-latex-extra", "texlive-fonts-recommended", "texlive-fonts-extra"] $+ doExecutable (BinPkgName "artvaluereport2-server") (InstallFile "artvaluereport2-server" Nothing Nothing "artvaluereport2-server") $+ setL changelog (getL changelog old) $+ (newDebianization' 7 (StandardsVersion 3 9 1 Nothing)))+ writeDebianization "/tmp" new+ assertEqual "test6" [] (diffDebianizations old (copyFirstLogEntry old new)))+ where+ -- hints = dependencyHints (putExecMap "trhsx" (BinPkgName "haskell-hsx-utils") $ putExtraDevDep (BinPkgName "markdown") $ Flags.defaultFlags)+ -- A log entry gets added when the Debianization is generated,+ -- it won't match so drop it for the comparison.+ -- dropFirstLogEntry (deb@(Debianization {changelog = ChangeLog (_ : tl)})) = deb {changelog = ChangeLog tl}+ theSite :: BinPkgName -> Site+ theSite deb =+ Site { domain = hostname'+ , serverAdmin = "logic@seereason.com"+ , server = theServer deb }+ theServer :: BinPkgName -> Server+ theServer deb =+ Server { hostname =+ case deb of+ BinPkgName "artvaluereport2-production" -> hostname'+ _ -> hostname'+ , port = portNum deb+ , headerMessage = "Generated by artvaluereport2/Setup.hs"+ , retry = "60"+ , serverFlags =+ ([ "--http-port", show (portNum deb)+ , "--base-uri", case deb of+ BinPkgName "artvaluereport2-production" -> "http://" ++ hostname' ++ "/"+ _ -> "http://seereason.com:" ++ show (portNum deb) ++ "/"+ , "--top", databaseDirectory deb+ , "--logs", "/var/log/" ++ show (pretty deb)+ , "--log-mode", case deb of+ BinPkgName "artvaluereport2-production" -> "Production"+ _ -> "Development"+ , "--static", "/usr/share/artvaluereport2-data"+ , "--no-validate" ] +++ (case deb of+ BinPkgName "artvaluereport2-production" -> [{-"--enable-analytics"-}]+ _ -> []) {- +++ [ "--jquery-path", "/usr/share/javascript/jquery/"+ , "--jqueryui-path", "/usr/share/javascript/jquery-ui/"+ , "--jstree-path", jstreePath+ , "--json2-path",json2Path ] -})+ , installFile =+ InstallFile { execName = "artvaluereport2-server"+ , destName = show (pretty deb)+ , sourceDir = Nothing+ , destDir = Nothing }+ }+ hostname' = "my.appraisalreportonline.com"+ portNum :: BinPkgName -> Int+ portNum (BinPkgName deb) =+ case deb of+ "artvaluereport2-production" -> 9027+ "artvaluereport2-staging" -> 9031+ "artvaluereport2-development" -> 9032+ _ -> error $ "Unexpected package name: " ++ deb++test7 :: Test+test7 =+ TestLabel "test7" $+ TestCase ( do old <- inputDebianization "."+ new <- debianization "."+ (modL control (\ y -> y {homepage = Just "http://src.seereason.com/cabal-debian"}) $+ setL sourceFormat (Just Native3) $+ setL utilsPackageName (Just (BinPkgName "cabal-debian")) $+ modL Atoms.depends (Map.insertWith union (BinPkgName "cabal-debian") (singleton (anyrel (BinPkgName "apt-file")))) $+ modL Atoms.conflicts (Map.insertWith union (BinPkgName "cabal-debian")+ (singleton (Rel (BinPkgName "haskell-debian-utils") (Just (SLT (parseDebianVersion ("3.59" :: String)))) Nothing))) $+ modL Atoms.description (Map.insertWith (error "test7") (BinPkgName "cabal-debian")+ (T.intercalate "\n"+ [ "Create a debianization for a cabal package"+ , " Tool for creating debianizations of Haskell packages based on the .cabal"+ , " file. If apt-file is installed it will use it to discover what is the"+ , " debian package name of a C library."+ , " ."+ , " Author: David Fox <dsf@seereason.com>"+ , " Upstream-Maintainer: David Fox <dsf@seereason.com>" ])) $+ copyChangelog old $+ newDebianization' 7 (StandardsVersion 3 9 3 Nothing))+ assertEqual "test7" [] (diffDebianizations old (copyChangelog old new)))++test8 :: Test+test8 =+ TestLabel "test8" $+ TestCase ( do old <- inputDebianization "test-data/artvaluereport-data/output"+ log <- inputChangeLog "test-data/artvaluereport-data/input/debian"+ new <- debianization "test-data/artvaluereport-data/input"+ (modL buildDeps (Set.insert (BinPkgName "haskell-hsx-utils")) $+ modL control (\ y -> y {homepage = Just "http://artvaluereportonline.com"}) $+ setL sourceFormat (Just Native3) $+ setL changelog (Just log) $+ (newDebianization' 7 (StandardsVersion 3 9 3 Nothing)))+ assertEqual "test8" [] (diffDebianizations old (copyChangelog old new))+ )++test9 :: Test+test9 =+ TestLabel "test9" $+ TestCase ( do old <- inputDebianization "test-data/alex/output"+ new <- debianization "test-data/alex/input"+ (modL buildDeps (Set.insert (BinPkgName "alex")) $+ doExecutable (BinPkgName "alex") (InstallFile {execName = "alex", destName = "alex", sourceDir = Nothing, destDir = Nothing}) $+ setL debVersion (Just (parseDebianVersion ("3.0.2-1~hackage1" :: String))) $+ setL sourceFormat (Just Native3) $+ modL control (\ y -> y {homepage = Just "http://www.haskell.org/alex/"}) $+ (\ atoms -> foldr (\ name atoms -> modL installData (Map.insertWith union (BinPkgName "alex") (singleton (name, name))) atoms)+ atoms+ [ "AlexTemplate"+ , "AlexTemplate-debug"+ , "AlexTemplate-ghc"+ , "AlexTemplate-ghc-debug"+ , "AlexWrapper-basic"+ , "AlexWrapper-basic-bytestring"+ , "AlexWrapper-gscan"+ , "AlexWrapper-monad"+ , "AlexWrapper-monad-bytestring"+ , "AlexWrapper-monadUserState"+ , "AlexWrapper-monadUserState-bytestring"+ , "AlexWrapper-posn"+ , "AlexWrapper-posn-bytestring"+ , "AlexWrapper-strict-bytestring"]) $+ newDebianization' 7 (StandardsVersion 3 9 3 Nothing))+ assertEqual "test9" [] (diffDebianizations old (copyFirstLogEntry old new)))++data Change k a+ = Created k a+ | Deleted k a+ | Modified k a a+ | Unchanged k a+ deriving (Eq, Show)++diffMaps :: (Ord k, Eq a, Show k, Show a) => Map.Map k a -> Map.Map k a -> [Change k a]+diffMaps old new =+ Map.elems (intersectionWithKey combine1 old new) +++ map (uncurry Deleted) (Map.toList (differenceWithKey combine2 old new)) +++ map (uncurry Created) (Map.toList (differenceWithKey combine2 new old))+ where+ combine1 k a b = if a == b then Unchanged k a else Modified k a b+ combine2 _ _ _ = Nothing++diffDebianizations :: Atoms -> Atoms -> String -- [Change FilePath T.Text]+diffDebianizations old new =+ show (mconcat (map prettyChange (filter (not . isUnchanged) (diffMaps old' new'))))+ where+ old' = toFileMap (sortBinaryDebs old) -- (sortBinaryDebs (fromMaybe newSourceDebDescription . getL control $ old))+ new' = toFileMap (sortBinaryDebs new) -- (sortBinaryDebs (fromMaybe newSourceDebDescription . getL control $ new))+ isUnchanged (Unchanged _ _) = True+ isUnchanged _ = False+ prettyChange (Unchanged p _) = text ("Unchanged: " <> p <> "\n")+ prettyChange (Deleted p _) = text ("Deleted: " <> p <> "\n")+ prettyChange (Created p b) =+ text ("Created: " <> p <> "\n") <>+ prettyDiff ("old" </> p) ("new" </> p)+ -- We use split here instead of lines so we can+ -- detect whether the file has a final newline+ -- character.+ (contextDiff 2 mempty (T.split (== '\n') b))+ prettyChange (Modified p a b) =+ text ("Modified: " <> p<> "\n") <>+ prettyDiff ("old" </> p) ("new" </> p)+ -- We use split here instead of lines so we can+ -- detect whether the file has a final newline+ -- character.+ (contextDiff 2 (T.split (== '\n') a) (T.split (== '\n') b))+ sortBinaryDebs atoms = modL control (\ deb -> deb {binaryPackages = sortBy (compare `on` package) (binaryPackages deb)}) atoms++testEntry :: ChangeLogEntry+testEntry =+ either (error "Error in test changelog entry") fst+ (parseEntry (unlines [ "haskell-cabal-debian (2.6.2) unstable; urgency=low"+ , ""+ , " * Fix a bug constructing the destination pathnames that was dropping"+ , " files that were supposed to be installed into packages."+ , ""+ , " -- David Fox <dsf@seereason.com> Thu, 20 Dec 2012 06:49:25 -0800" ]))
+ src/Debian/Debianize/Types.hs view
@@ -0,0 +1,66 @@+module Debian.Debianize.Types+ ( PackageInfo(..)+ , Site(..)+ , Server(..)+ , InstallFile(..)+ , VersionSplits(..)+ , DebType(..)+ , DebAction(..)+ ) where++import Data.Version (Version)+import Debian.Orphans ()+import Debian.Relation (BinPkgName)+import Debian.Version (DebianVersion)+import Distribution.Package (PackageName)+import Prelude hiding (init, unlines, log)++data PackageInfo = PackageInfo { cabalName :: PackageName+ , devDeb :: Maybe (BinPkgName, DebianVersion)+ , profDeb :: Maybe (BinPkgName, DebianVersion)+ , docDeb :: Maybe (BinPkgName, DebianVersion) } deriving (Eq, Ord, Show)++-- | Information about the web site we are packaging.+data Site+ = Site+ { domain :: String -- ^ The domain name assigned to the server.+ -- An apache configuration will be generated to+ -- redirect requests from this domain to hostname:port+ , serverAdmin :: String -- ^ Apache ServerAdmin parameter+ , server :: Server -- ^ The hint to install the server job+ } deriving (Read, Show, Eq, Ord)++-- | Information about the server we are packaging.+data Server+ = Server+ { hostname :: String -- ^ Host on which the server will run+ , port :: Int -- ^ Port on which the server will run.+ -- Obviously, this must assign each and+ -- every server package to a different+ -- port.+ , headerMessage :: String -- ^ A comment that will be inserted to+ -- explain how the file was generated+ , retry :: String -- ^ start-stop-daemon --retry argument+ , serverFlags :: [String] -- ^ Extra flags to pass to the server via the init script+ , installFile :: InstallFile -- ^ The hint to install the server executable+ } deriving (Read, Show, Eq, Ord)++data InstallFile+ = InstallFile+ { execName :: String -- ^ The name of the executable file+ , sourceDir :: Maybe FilePath -- ^ where to find it, default is dist/build/<execName>/+ , destDir :: Maybe FilePath -- ^ where to put it, default is usr/bin/<execName>+ , destName :: String -- ^ name to give installed executable+ } deriving (Read, Show, Eq, Ord)++data VersionSplits+ = VersionSplits {+ packageName :: PackageName+ , oldestPackage :: PackageName+ , splits :: [(Version, PackageName)] -- Assumed to be in version number order+ } deriving (Eq, Ord, Show)++data DebAction = Usage | Debianize | SubstVar DebType deriving (Read, Show, Eq, Ord)++-- | A redundant data type, too lazy to expunge.+data DebType = Dev | Prof | Doc deriving (Eq, Ord, Read, Show)
+ src/Debian/Debianize/Utility.hs view
@@ -0,0 +1,231 @@+-- | Functions used by but not related to cabal-debian, these could+-- conceivably be moved into more general libraries.+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wall #-}+module Debian.Debianize.Utility+ ( DebMap+ , buildDebVersionMap+ , (!)+ , trim+ , strictReadF+ , replaceFile+ , modifyFile+ , diffFile+ , removeIfExists+ , dpkgFileMap+ , cond+ , debOfFile+ , readFile'+ , showDeps+ , showDeps'+ , withCurrentDirectory+ , getDirectoryContents'+ , setMapMaybe+ , zipMaps+ , foldEmpty+ , maybeL+ , indent+ ) where++import Control.Exception as E (catch, try, bracket, IOException)+import Control.Monad (when)+import Control.Monad.Reader (ReaderT, ask)+import Data.Char (isSpace)+import Data.List as List (isSuffixOf, intercalate, map, lines)+import Data.Lens.Lazy (Lens, modL)+import Data.Map as Map (Map, foldWithKey, empty, fromList, findWithDefault, insert, map, lookup)+import Data.Maybe (catMaybes, mapMaybe)+import Data.Set (Set, toList)+import qualified Data.Set as Set+import Data.Text as Text (Text, unpack, lines)+import Data.Text.IO (hGetContents)+import Debian.Control (parseControl, lookupP, Field'(Field), unControl, stripWS)+import Debian.Version (DebianVersion, prettyDebianVersion)+import Debian.Version.String (parseDebianVersion)+import qualified Debian.Relation as D+import Prelude hiding (map, lookup)+import System.Directory (doesFileExist, doesDirectoryExist, removeFile, renameFile, removeDirectory, getDirectoryContents, getCurrentDirectory, setCurrentDirectory)+import System.Exit(ExitCode(ExitSuccess, ExitFailure))+import System.FilePath ((</>), dropExtension)+import System.IO (IOMode (ReadMode), withFile, openFile, hSetBinaryMode)+import System.IO.Error (isDoesNotExistError)+import System.Process (readProcessWithExitCode, showCommandForUser)+import Text.PrettyPrint.ANSI.Leijen (pretty)++type DebMap = Map.Map D.BinPkgName (Maybe DebianVersion)++-- | Read and parse the status file for installed debian packages.+buildDebVersionMap :: IO DebMap+buildDebVersionMap =+ readFile "/var/lib/dpkg/status" >>=+ return . either (const []) unControl . parseControl "/var/lib/dpkg/status" >>=+ mapM (\ p -> case (lookupP "Package" p, lookupP "Version" p) of+ (Just (Field (_, name)), Just (Field (_, version))) ->+ return (Just (D.BinPkgName (stripWS name), Just (parseDebianVersion (stripWS version))))+ _ -> return Nothing) >>=+ return . Map.fromList . catMaybes++(!) :: DebMap -> D.BinPkgName -> DebianVersion+m ! k = maybe (error ("No version number for " ++ (show . pretty $ k) ++ " in " ++ show (Map.map (maybe Nothing (Just . prettyDebianVersion)) m))) id (Map.findWithDefault Nothing k m)++trim :: String -> String+trim = dropWhile isSpace++strictReadF :: (Text -> r) -> FilePath -> IO r+strictReadF f path = withFile path ReadMode (\h -> hGetContents h >>= (\x -> return $! f x))+-- strictRead = strictReadF id++-- | Write a file which we might still be reading from in+-- order to compute the text argument.+replaceFile :: FilePath -> String -> IO ()+replaceFile path text =+ do removeFile back `E.catch` (\ (e :: IOException) -> when (not (isDoesNotExistError e)) (ioError e))+ renameFile path back `E.catch` (\ (e :: IOException) -> when (not (isDoesNotExistError e)) (ioError e))+ writeFile path text+ where+ back = path ++ "~"++-- | Compute the new file contents from the old. If f returns Nothing+-- do not write.+modifyFile :: FilePath -> (String -> IO (Maybe String)) -> IO ()+modifyFile path f =+ do removeFile back `E.catch` (\ (e :: IOException) -> when (not (isDoesNotExistError e)) (ioError e))+ try (renameFile path back) >>=+ either (\ (e :: IOException) -> if not (isDoesNotExistError e)+ then ioError e+ else f "" >>= maybe (return ()) (writeFile path))+ (\ () -> readFile back >>= f >>= maybe (return ()) (writeFile path))+ where+ back = path ++ "~"++diffFile :: FilePath -> Text -> IO (Maybe String)+diffFile path text =+ readProcessWithExitCode cmd args (unpack text) >>= \ (code, out, _err) ->+ case code of+ ExitSuccess -> return Nothing+ ExitFailure 1 -> return (Just out)+ _ -> error (showCommandForUser cmd args {- ++ " < " ++ show text -} ++ " -> " ++ show code)+ where+ cmd = "diff"+ args = ["-ruw", path, "-"]++removeFileIfExists :: FilePath -> IO ()+removeFileIfExists x = doesFileExist x >>= (`when` (removeFile x))++removeDirectoryIfExists :: FilePath -> IO ()+removeDirectoryIfExists x = doesDirectoryExist x >>= (`when` (removeDirectory x))++removeIfExists :: FilePath -> IO ()+removeIfExists x = removeFileIfExists x >> removeDirectoryIfExists x++-- |Create a map from pathname to the names of the packages that contains that pathname.+-- We need to make sure we consume all the files, so +dpkgFileMap :: IO (Map.Map FilePath (Set.Set D.BinPkgName))+dpkgFileMap =+ do+ let fp = "/var/lib/dpkg/info"+ names <- getDirectoryContents fp >>= return . filter (isSuffixOf ".list")+ let paths = List.map (fp </>) names+ files <- mapM (strictReadF Text.lines) paths+ return $ Map.fromList $ zip (List.map dropExtension names) (List.map (Set.fromList . List.map (D.BinPkgName . unpack)) $ files)++-- |Given a path, return the name of the package that owns it.+debOfFile :: FilePath -> ReaderT (Map.Map FilePath (Set.Set D.BinPkgName)) IO (Maybe D.BinPkgName)+debOfFile path =+ do mp <- ask+ return $ testPath (lookup path mp)+ where+ -- testPath :: Maybe (Set.Set FilePath) -> Maybe FilePath+ testPath Nothing = Nothing+ testPath (Just s) =+ case Set.size s of+ 1 -> Just (Set.findMin s)+ _ -> Nothing++cond :: t -> t -> Bool -> t+cond ifF _ifT False = ifF+cond _ifF ifT True = ifT++readFile' :: FilePath -> IO Text+readFile' path =+ do file <- openFile path ReadMode+ hSetBinaryMode file True+ hGetContents file++-- Would like to call pretty instead of D.prettyRelations, but the+-- Pretty instance for [a] doesn't work for us.+showDeps :: [[D.Relation]] -> String+showDeps = show . D.prettyRelations++-- The extra space after prefix' is here for historical reasons(?)+showDeps' :: [a] -> [[D.Relation]] -> String+showDeps' prefix xss =+ intercalate ("\n" ++ prefix' ++ " ") . Prelude.lines . show . D.prettyRelations $ xss+ where prefix' = List.map (\ _ -> ' ') prefix++-- | From Darcs.Utils+withCurrentDirectory :: FilePath -> IO a -> IO a+withCurrentDirectory name m =+ E.bracket+ (do cwd <- getCurrentDirectory+ setCurrentDirectory name+ return cwd)+ (\oldwd -> setCurrentDirectory oldwd {- `catchall` return () -})+ (const m)++{-+catchall :: IO a -> IO a -> IO a+a `catchall` b = a `catchNonSignal` (\_ -> b)++-- catchNonSignal is a drop-in replacement for Control.Exception.catch, which allows+-- us to catch anything but a signal. Useful for situations where we want+-- don't want to inhibit ctrl-C.++catchNonSignal :: IO a -> (E.SomeException -> IO a) -> IO a+catchNonSignal comp handler = catch comp handler'+ where handler' se =+ case fromException se :: Maybe SignalException of+ Nothing -> handler se+ Just _ -> E.throw se++newtype SignalException = SignalException Signal deriving (Show, Typeable)++instance Exception SignalException where+ toException e = SomeException e+ fromException (SomeException e) = cast e+-}++getDirectoryContents' :: FilePath -> IO [FilePath]+getDirectoryContents' dir =+ getDirectoryContents dir >>= return . filter (not . dotFile)+ where+ dotFile "." = True+ dotFile ".." = True+ dotFile _ = False++setMapMaybe :: (Ord a, Ord b) => (a -> Maybe b) -> Set a -> Set b+setMapMaybe p = Set.fromList . mapMaybe p . toList++zipMaps :: Ord k => (k -> Maybe a -> Maybe b -> Maybe c) -> Map k a -> Map k b -> Map k c+zipMaps f m n =+ foldWithKey h (foldWithKey g empty m) n+ where+ g k a r = case f k (Just a) (lookup k n) of+ Just c -> Map.insert k c r -- Both m and n have entries for k+ Nothing -> r -- Only m has an entry for k+ h k b r = case lookup k m of+ Nothing -> case f k Nothing (Just b) of+ Just c -> Map.insert k c r -- Only n has an entry for k+ Nothing -> r+ Just _ -> r++foldEmpty :: r -> ([a] -> r) -> [a] -> r+foldEmpty r _ [] = r+foldEmpty _ f l = f l++-- | If the current value of getL x is Nothing, replace it with f.+maybeL :: Lens a (Maybe b) -> Maybe b -> a -> a+maybeL lens mb x = modL lens (maybe mb Just) x++indent :: [Char] -> String -> String+indent prefix text = unlines (List.map (prefix ++) (List.lines text))
+ src/Debian/Orphans.hs view
@@ -0,0 +1,172 @@+{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, StandaloneDeriving #-}+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}+module Debian.Orphans where++import Data.Function (on)+import Data.Generics (Data, Typeable)+import Data.List (isPrefixOf)+import Data.Maybe (fromMaybe)+import Data.Monoid ((<>))+import Data.Text (Text, unpack)+import Data.Version (Version(..), showVersion)+import Debian.Changes (ChangeLog(..), ChangeLogEntry(..))+import Debian.Control (Field'(..))+import Debian.Relation (Relation(..), VersionReq(..), ArchitectureReq(..),+ BinPkgName(..), SrcPkgName(..))+import Debian.Version (DebianVersion, prettyDebianVersion, parseDebianVersion)+import Distribution.Compiler (CompilerId(..), CompilerFlavor(..))+import Distribution.License (License(..))+import Distribution.PackageDescription (PackageDescription(package), Executable(..))+import Distribution.Simple.Compiler (Compiler(..))+import Distribution.Version (VersionRange(..), foldVersionRange')+import Language.Haskell.Extension (Extension(..), KnownExtension(..), Language(..))+import Text.PrettyPrint.ANSI.Leijen (Pretty(pretty), text)+import Text.ParserCombinators.Parsec.Rfc2822 (NameAddr(..))++deriving instance Typeable Compiler+deriving instance Typeable CompilerId+deriving instance Typeable CompilerFlavor+deriving instance Typeable Language+deriving instance Typeable Extension+deriving instance Typeable KnownExtension++deriving instance Data Extension+deriving instance Data KnownExtension+deriving instance Data Language+deriving instance Data Compiler+deriving instance Data CompilerId+deriving instance Data CompilerFlavor++deriving instance Ord Language+deriving instance Ord KnownExtension+deriving instance Ord Extension+deriving instance Eq Compiler+deriving instance Ord Compiler+deriving instance Ord NameAddr+deriving instance Ord License++instance Ord Executable where+ compare = compare `on` exeName++instance Ord PackageDescription where+ compare = compare `on` package++instance Pretty Text where+ pretty = text . unpack++{-+instance Show (Control' String) where+ show _ = "<control file>"++instance Show ChangeLog where+ show _ = "<log entry>"+-}++deriving instance Read ArchitectureReq+deriving instance Read BinPkgName+deriving instance Read ChangeLog+deriving instance Read ChangeLogEntry+deriving instance Read Relation+deriving instance Read SrcPkgName+deriving instance Read VersionReq++deriving instance Show ArchitectureReq+deriving instance Show ChangeLog+deriving instance Show ChangeLogEntry+deriving instance Show Relation+deriving instance Show VersionReq++instance Show DebianVersion where+ show v = "(Debian.Version.parseDebianVersion (" ++ show (show (prettyDebianVersion v)) ++ " :: String))"++instance Read DebianVersion where+ readsPrec _ s =+ case dropPrefix "Debian.Version.parseDebianVersion " s of+ Just s' -> case reads s' :: [(String, String)] of+ []-> []+ (v, s'') : _ -> [(parseDebianVersion v, s'')]+ Nothing -> []++dropPrefix :: String -> String -> Maybe String+dropPrefix p s = if isPrefixOf p s then Just (drop (length p) s) else Nothing++deriving instance Data ArchitectureReq+deriving instance Data BinPkgName+deriving instance Data ChangeLog+deriving instance Data ChangeLogEntry+-- deriving instance Data NameAddr+deriving instance Data Relation+deriving instance Data SrcPkgName+deriving instance Data VersionReq++deriving instance Typeable ArchitectureReq+deriving instance Typeable BinPkgName+deriving instance Typeable ChangeLog+deriving instance Typeable ChangeLogEntry+-- deriving instance Typeable NameAddr+deriving instance Typeable Relation+deriving instance Typeable SrcPkgName+deriving instance Typeable VersionReq++deriving instance Ord ChangeLog+deriving instance Ord ChangeLogEntry++{-+instance Pretty SrcPkgName where+ pretty (SrcPkgName x) = pretty x++instance Pretty BinPkgName where+ pretty (BinPkgName x) = pretty x+-}++deriving instance Typeable License+deriving instance Data Version+deriving instance Data License++-- Convert from license to RPM-friendly description. The strings are+-- taken from TagsCheck.py in the rpmlint distribution.+instance Pretty License where+ pretty (GPL _) = text "GPL"+ pretty (LGPL _) = text "LGPL"+ pretty (Apache _) = text "Apache"+ pretty BSD3 = text "BSD"+ pretty BSD4 = text "BSD-like"+ pretty PublicDomain = text "Public Domain"+ pretty AllRightsReserved = text "Proprietary"+ pretty OtherLicense = text "Non-distributable"+ pretty MIT = text "MIT"+ pretty (UnknownLicense _) = text "Unknown"++deriving instance Data NameAddr+deriving instance Typeable NameAddr+deriving instance Read NameAddr++-- This Pretty instance gives a string used to create a valid+-- changelog entry, it *must* have a name followed by an email address+-- in angle brackets.+instance Pretty NameAddr where+ pretty x = text (fromMaybe (nameAddr_addr x) (nameAddr_name x) ++ " <" ++ nameAddr_addr x ++ ">")+ -- pretty x = text (maybe (nameAddr_addr x) (\ n -> n ++ " <" ++ nameAddr_addr x ++ ">") (nameAddr_name x))++deriving instance Show (Field' String)++instance Pretty VersionRange where+ pretty range =+ foldVersionRange'+ (text "*")+ (\ v -> text "=" <> pretty v)+ (\ v -> text ">" <> pretty v)+ (\ v -> text "<" <> pretty v)+ (\ v -> text ">=" <> pretty v)+ (\ v -> text "<=" <> pretty v)+ (\ x _ -> text "=" <> pretty x <> text ".*") -- not exactly right+ (\ x y -> text "(" <> x <> text " || " <> y <> text ")")+ (\ x y -> text "(" <> x <> text " && " <> y <> text ")")+ (\ x -> text "(" <> x <> text ")")+ range++instance Pretty Version where+ pretty = text . showVersion++instance Pretty DebianVersion where+ pretty = text . show
+ src/Debian/Policy.hs view
@@ -0,0 +1,233 @@+-- | Code pulled out of cabal-debian that straightforwardly implements+-- parts of the Debian policy manual, or other bits of Linux standards.+{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}+module Debian.Policy+ ( -- * Paths+ databaseDirectory+ , apacheLogDirectory+ , apacheErrorLog+ , apacheAccessLog+ , serverLogDirectory+ , serverAppLog+ , serverAccessLog+ -- * Installed packages+ , debianPackageVersion+ , getDebhelperCompatLevel+ , StandardsVersion(..)+ , getDebianStandardsVersion+ , parseStandardsVersion+ -- * Package fields+ , SourceFormat(..)+ , readSourceFormat+ , PackagePriority(..)+ , readPriority+ , PackageArchitectures(..)+ , parsePackageArchitectures+ , Section(..)+ , readSection+ , Area(..)+ , parseUploaders+ , parseMaintainer+ , getDebianMaintainer+ , haskellMaintainer+ ) where++import Codec.Binary.UTF8.String (decodeString)+import Control.Arrow (second)+import Control.Monad (mplus)+import Data.Char (toLower, isSpace)+import Data.List (groupBy, intercalate)+import Data.Generics (Data, Typeable)+import Data.Text (Text, pack, unpack, strip)+import Data.Monoid ((<>))+import Debian.Relation (BinPkgName)+import Debian.Version (DebianVersion, parseDebianVersion, version)+import System.Environment (getEnvironment)+import System.FilePath ((</>))+import System.Process (readProcess)+import Text.Parsec (parse)+import Text.ParserCombinators.Parsec.Rfc2822 (NameAddr, address)+import Text.PrettyPrint.ANSI.Leijen (Pretty(pretty), text)++databaseDirectory :: BinPkgName -> String+databaseDirectory x = "/srv" </> show (pretty x)++apacheLogDirectory :: BinPkgName -> String+apacheLogDirectory x = "/var/log/apache2/" ++ show (pretty x)++apacheErrorLog :: BinPkgName -> String+apacheErrorLog x = apacheLogDirectory x </> "error.log"++apacheAccessLog :: BinPkgName -> String+apacheAccessLog x = apacheLogDirectory x </> "access.log"++serverLogDirectory :: BinPkgName -> String+serverLogDirectory x = "/var/log/" ++ show (pretty x)++serverAppLog :: BinPkgName -> String+serverAppLog x = serverLogDirectory x </> "app.log"++serverAccessLog :: BinPkgName -> String+serverAccessLog x = serverLogDirectory x </> "access.log"++debianPackageVersion :: String -> IO DebianVersion+debianPackageVersion name =+ readProcess "dpkg-query" ["--show", "--showformat=${version}", name] "" >>=+ return . parseDebianVersion++-- | Assumes debhelper is installed+getDebhelperCompatLevel :: IO Int+getDebhelperCompatLevel =+ debianPackageVersion "debhelper" >>= return . read . takeWhile (/= '.') . version++data StandardsVersion = StandardsVersion Int Int Int (Maybe Int) deriving (Eq, Ord, Show, Data, Typeable)++instance Pretty StandardsVersion where+ pretty (StandardsVersion a b c (Just d)) = text $ show a <> "." <> show b <> "." <> show c <> "." <> show d+ pretty (StandardsVersion a b c Nothing) = text $ show a <> "." <> show b <> "." <> show c++-- | Assumes debian-policy is installed+getDebianStandardsVersion :: IO StandardsVersion+getDebianStandardsVersion = debianPackageVersion "debian-policy" >>= \ v -> return (parseStandardsVersion (version v))++parseStandardsVersion :: String -> StandardsVersion+parseStandardsVersion s =+ case filter (/= ".") (groupBy (\ a b -> (a == '.') == (b == '.')) s) of+ (a : b : c : d : _) -> StandardsVersion (read a) (read b) (read c) (Just (read d))+ (a : b : c : _) -> StandardsVersion (read a) (read b) (read c) Nothing+ _ -> error $ "Invalid Standards-Version string: " ++ show s++data SourceFormat+ = Native3+ | Quilt3+ deriving (Eq, Ord, Show, Data, Typeable)++instance Pretty SourceFormat where+ pretty Quilt3 = text "3.0 (quilt)\n"+ pretty Native3 = text "3.0 (native)\n"++readSourceFormat :: Text -> Either Text SourceFormat+readSourceFormat s =+ case () of+ _ | strip s == "3.0 (native)" -> Right Native3+ _ | strip s == "3.0 (quilt)" -> Right Quilt3+ _ -> Left $ "Invalid debian/source/format: " <> pack (show (strip s))++data PackagePriority+ = Required+ | Important+ | Standard+ | Optional+ | Extra+ deriving (Eq, Ord, Read, Show, Data, Typeable)++readPriority :: String -> PackagePriority+readPriority s =+ case unpack (strip (pack s)) of+ "required" -> Required+ "important" -> Important+ "standard" -> Standard+ "optional" -> Optional+ "extra" -> Extra+ x -> error $ "Invalid priority string: " ++ show x++instance Pretty PackagePriority where+ pretty = text . map toLower . show++-- | The architectures for which a binary deb can be built.+data PackageArchitectures+ = All -- ^ The package is architecture independenct+ | Any -- ^ The package can be built for any architecture+ | Names [String] -- ^ The list of suitable architectures+ deriving (Read, Eq, Ord, Show, Data, Typeable)++instance Pretty PackageArchitectures where+ pretty All = text "all"+ pretty Any = text "any"+ pretty (Names xs) = text $ intercalate " " xs++parsePackageArchitectures :: String -> PackageArchitectures+parsePackageArchitectures "all" = All+parsePackageArchitectures "any" = Any+parsePackageArchitectures s = error $ "FIXME: parsePackageArchitectures " ++ show s++data Section+ = MainSection String -- Equivalent to AreaSection Main s?+ | AreaSection Area String+ deriving (Read, Eq, Ord, Show, Data, Typeable)++readSection :: String -> Section+readSection s =+ case break (== '/') s of+ ("contrib", '/' : b) -> AreaSection Contrib (tail b)+ ("non-free", '/' : b) -> AreaSection NonFree (tail b)+ ("main", '/' : b) -> AreaSection Main (tail b)+ (a, '/' : _) -> error $ "readSection - unknown area: " ++ show a+ (a, _) -> MainSection a++instance Pretty Section where+ pretty (MainSection sec) = text sec+ pretty (AreaSection area sec) = pretty area <> text ("/" <> sec)++-- Is this really all that is allowed here? Doesn't Ubuntu have different areas?+data Area+ = Main+ | Contrib+ | NonFree+ deriving (Read, Eq, Ord, Show, Data, Typeable)++instance Pretty Area where+ pretty Main = text "main"+ pretty Contrib = text "contrib"+ pretty NonFree = text "non-free"++{-+Create a debian maintainer field from the environment variables:++ DEBFULLNAME (preferred) or NAME+ DEBEMAIL (preferred) or EMAIL++More work could be done to match dch, but this is sufficient for+now. Here is what the man page for dch has to say:++ If the environment variable DEBFULLNAME is set, this will be used for+ the maintainer full name; if not, then NAME will be checked. If the+ environment variable DEBEMAIL is set, this will be used for the email+ address. If this variable has the form "name <email>", then the+ maintainer name will also be taken from here if neither DEBFULLNAME+ nor NAME is set. If this variable is not set, the same test is+ performed on the environment variable EMAIL. Next, if the full name+ has still not been determined, then use getpwuid(3) to determine the+ name from the pass‐word file. If this fails, use the previous+ changelog entry. For the email address, if it has not been set from+ DEBEMAIL or EMAIL, then look in /etc/mailname, then attempt to build+ it from the username and FQDN, otherwise use the email address in the+ previous changelog entry. In other words, it’s a good idea to set+ DEBEMAIL and DEBFULLNAME when using this script.++-}+getDebianMaintainer :: IO (Maybe NameAddr)+getDebianMaintainer =+ do env <- map (second decodeString) `fmap` getEnvironment+ return $ do fullname <- lookup "DEBFULLNAME" env `mplus` lookup "NAME" env+ email <- lookup "DEBEMAIL" env `mplus` lookup "EMAIL" env+ either (const Nothing) Just (parseMaintainer (fullname ++ " <" ++ email ++ ">"))++haskellMaintainer :: NameAddr+haskellMaintainer =+ either error id (parseMaintainer "Debian Haskell Group <pkg-haskell-maintainers@lists.alioth.debian.org>")++parseUploaders :: String -> Either String [NameAddr]+parseUploaders x =+ either (Left . show) Right (parse address "" ("Names: " ++ map fixWhite x ++ ";"))+ -- either (\ e -> error ("Failure parsing uploader list: " ++ show x ++ " -> " ++ show e)) id $ + where+ fixWhite c = if isSpace c then ' ' else c++parseMaintainer :: String -> Either String NameAddr+parseMaintainer x =+ case parseUploaders x of+ Left s -> Left s+ Right [y] -> Right y+ Right [] -> Left $ "Missing maintainer: " ++ show x+ Right ys -> Left $ "Too many maintainers: " ++ show ys
+ src/Distribution/Version/Invert.hs view
@@ -0,0 +1,40 @@+{-# OPTIONS_GHC -Wall #-}+module Distribution.Version.Invert+ ( invertVersionRange+ , invertVersionIntervals+ ) where++import Distribution.Version (Version(Version, versionBranch, versionTags), VersionRange, fromVersionIntervals, asVersionIntervals, mkVersionIntervals,+ LowerBound(LowerBound), UpperBound(UpperBound, NoUpperBound), Bound(InclusiveBound, ExclusiveBound))++invertVersionRange :: VersionRange -> VersionRange+invertVersionRange = fromVersionIntervals . maybe (error "invertVersionRange") id . mkVersionIntervals . invertVersionIntervals . asVersionIntervals++invertVersionIntervals :: [(LowerBound, UpperBound)] -> [(LowerBound, UpperBound)]+invertVersionIntervals xs =+ case xs of+ [] -> [(lb0, NoUpperBound)]+ ((LowerBound (Version {versionBranch = [0], versionTags = []}) InclusiveBound, ub) : more) ->+ invertVersionIntervals' ub more+ ((lb, ub) : more) ->+ (lb0, invertLowerBound lb) : invertVersionIntervals' ub more+ where+ invertVersionIntervals' :: UpperBound -> [(LowerBound, UpperBound)] -> [(LowerBound, UpperBound)]+ invertVersionIntervals' NoUpperBound [] = []+ invertVersionIntervals' ub0 [] = [(invertUpperBound ub0, NoUpperBound)]+ invertVersionIntervals' ub0 [(lb, NoUpperBound)] = [(invertUpperBound ub0, invertLowerBound lb)]+ invertVersionIntervals' ub0 ((lb, ub1) : more) = (invertUpperBound ub0, invertLowerBound lb) : invertVersionIntervals' ub1 more++ invertLowerBound :: LowerBound -> UpperBound+ invertLowerBound (LowerBound v b) = UpperBound v (invertBound b)++ invertUpperBound :: UpperBound -> LowerBound+ invertUpperBound (UpperBound v b) = LowerBound v (invertBound b)+ invertUpperBound NoUpperBound = error "NoUpperBound: unexpected"++ invertBound :: Bound -> Bound+ invertBound ExclusiveBound = InclusiveBound+ invertBound InclusiveBound = ExclusiveBound++ lb0 :: LowerBound+ lb0 = LowerBound (Version {versionBranch = [0], versionTags = []}) InclusiveBound
+ src/Tests.hs view
@@ -0,0 +1,7 @@+module Main where++import Debian.Debianize.Tests (tests)+import Test.HUnit (runTestTT)++main :: IO ()+main = runTestTT tests >>= putStrLn . show
+ src/Triplets.hs view
@@ -0,0 +1,306 @@+{-# LANGUAGE RankNTypes, ScopedTypeVariables, TemplateHaskell, DeriveDataTypeable, StandaloneDeriving #-}+{-# OPTIONS -Wwarn -fno-warn-name-shadowing -fno-warn-orphans #-}+module Triplets+ ( gzipWithM3+ , gzipWithT3+ , gzipWithA3+ , gzip3+ , gzipQ3+ , gzipBut3+ , gzipButA3+ -- , gzipBut3'+ , extQ2+ , extQ3+ , extT3+ , mkQ3+ , mkQ2+ -- , mkP3+ -- , gzip3F+ , mergeBy+ --, mergeByM+ --, GenericA+ , GB, GM, GA, PB, PM, PA+ ) where++import Prelude hiding (GT)+import Control.Applicative (Applicative(..))+import Control.Applicative.Error (Failing(..))+import Control.Monad (MonadPlus(mzero, mplus))+import Data.Generics (Data, Typeable, Typeable1, toConstr, cast, gcast, gmapAccumQ, gshow, gfoldlAccum,+ unGT, GenericT, GenericT'(GT), gmapAccumT,+ unGM, GenericM, GenericM'(GM), gmapAccumM,+ unGQ, GenericQ, GenericQ'(GQ), gmapQ)+import Data.Maybe (fromMaybe)++instance Monad Failing where+ return = Success+ m >>= f =+ case m of+ (Failure errs) -> (Failure errs)+ (Success a) -> f a+ fail errMsg = Failure [errMsg]++instance MonadPlus Failing where+ mzero = Failure []+ mplus (Failure xs) (Failure ys) = Failure (xs ++ ys)+ mplus success@(Success _) _ = success+ mplus _ success@(Success _) = success+ +deriving instance Typeable1 Failing+deriving instance Data a => Data (Failing a)+deriving instance Read a => Read (Failing a)+deriving instance Eq a => Eq (Failing a)+deriving instance Ord a => Ord (Failing a)++cast' :: (Monad m, Typeable a, Typeable b) => a -> m b+cast' = maybe (fail "cast") return . cast++--orElse' = mplus++-- As originally defined: Twin map for transformation++{-+gzipWithT2 :: GenericQ (GenericT) -> GenericQ (GenericT)+gzipWithT2 f x y = case gmapAccumT perkid funs y of+ ([], c) -> c+ _ -> error "gzipWithT2"+ where+ perkid a d = (tail a, unGT (head a) d)+ funs = gmapQ (\k -> GT (f k)) x+-}+++-- As originally defined: Twin map for transformation++{-+gzipWithM2 :: Monad m => GenericQ (GenericM m) -> GenericQ (GenericM m)+gzipWithM2 f x y = case gmapAccumM perkid funs y of+ ([], c) -> c+ _ -> error "gzipWithM"+ where+ perkid a d = (tail a, unGM (head a) d)+ funs = gmapQ (\k -> GM (f k)) x+-}+++-- As originally defined: generic zip++{-+gzip2 ::+ (forall x. Data x => x -> x -> Maybe x)+ -> (forall x. Data x => x -> x -> Maybe x)++gzip2 f = gzip2' f'+ where+ f' :: GenericQ (GenericM Maybe)+ f' x y = cast x >>= \x' -> f x' y+ gzip2' :: GenericQ (GenericM Maybe) -> GenericQ (GenericM Maybe)+ gzip2' f x y =+ f x y+ `orElse`+ if toConstr x == toConstr y+ then gzipWithM2 (gzip2' f) x y+ else Nothing+-}++-- For three args now++--type GenericT = forall a. Data a => a -> a+--type GenericQ r = forall a. Data a => a -> r+--type GenericM m = forall a. Data a => a -> m a++gzipWithT3 ::+ GenericQ (GenericQ (GenericT))+ -> GenericQ (GenericQ (GenericT))+gzipWithT3 f x y z =+ case gmapAccumT perkid funs z of+ ([], c) -> c+ _ -> error "gzipWithT3"+ where+ perkid a d = (tail a, unGT (head a) d)+ funs = case gmapAccumQ perkid' funs' y of+ ([], q) -> q+ _ -> error "gzipWithT3"+ where+ perkid' a d = (tail a, unGQ (head a) d)+ funs' = gmapQ (\k -> (GQ (\k' -> GT (f k k')))) x++gzipWithM3 :: Monad m+ => GenericQ (GenericQ (GenericM m))+ -> GenericQ (GenericQ (GenericM m))+gzipWithM3 f x y z =+ case gmapAccumM perkid funs z of+ ([], c) -> c+ _ -> error "gzipWithM3"+ where+ perkid a d = (tail a, unGM (head a) d)+ funs = case gmapAccumQ perkid' funs' y of+ ([], q) -> q+ _ -> error "gzipWithM3"+ where+ perkid' a d = (tail a, unGQ (head a) d)+ funs' = gmapQ (\k -> (GQ (\k' -> GM (f k k')))) x++gzipWithA3 :: forall f. Applicative f => GA f -> GA f+gzipWithA3 f x y z =+ case gmapAccumA perkid funs z of+ ([], c) -> c+ _ -> error "gzipWithA3"+ where+ perkid a d = (tail a, unGM (head a) d)+ funs = case gmapAccumQ perkid' funs' y of+ ([], q) -> q+ _ -> error "gzipWithA3"+ where+ perkid' a d = (tail a, unGQ (head a) d)+ funs' = gmapQ (\k -> (GQ (\k' -> GM (f k k')))) x++type GB = GenericQ (GenericQ (GenericQ Bool))+-- ^ Generic Bool Query, (Data a, Data b, Data c) => a -> b -> c -> Bool+type GM = MonadPlus m => GenericQ (GenericQ (GenericM m))+-- ^ Generic Maybe Query, (Data a, Data b, Data c) => a -> b -> c -> Maybe c+type PB = forall x. Data x => x -> x -> x -> Bool+-- ^ Polymorphic Bool Query+type PM = forall m x. (MonadPlus m, Data x) => x -> x -> x -> m x+-- ^ Polymorphic Failing Query, forall x. Data x => x -> x -> x -> Failing x+type GA f = GenericQ (GenericQ (GenericM f))+-- ^ Generic Applicative Query, forall a. Data a => a -> (forall b. Data b => b -> (forall c. Data c => c -> f c))+type PA f = forall x. Data x => x -> x -> x -> f x+-- ^ Polymorphic Applicative Query++-- | gmapA with accumulation (untested)+-- (Move to Data.Generics.Twins)+gmapAccumA :: forall a d f. (Data d, Applicative f)+ => (forall e. Data e => a -> e -> (a, f e))+ -> a -> d -> (a, f d)+gmapAccumA f a0 d0 = gfoldlAccum k z a0 d0+ where+ k :: forall d e. (Data d) =>+ a -> f (d -> e) -> d -> (a, f e)+ k a c d = let (a',d') = f a d+ c' = c <*> d'+ in (a', c')+ z :: forall t a f. (Applicative f) =>+ t -> a -> (t, f a)+ z a x = (a, pure x)++-- |The purpose of gzip3 is to map a polymorphic (generic) function+-- over the "elements" of three instances of a type. The function+-- returns a Maybe value of the same type as the elements passed. If+-- it returns a Just the subtree is not traversed, the returned value+-- is used. If it returns Nothing the subtree is traversed. This+-- traversal may succeed where the top level test failed, resulting in+-- a successful zip. For example, the merge function wouldn't merge+-- these three values:+-- (1, 1) (1, 2) (2, 1) -> (?, ?)+-- but it could merge the two unzipped triples:+-- (1, 1, 2) -> 2+-- (1, 2, 1) -> 2+-- -> (2, 2)+gzip3 :: PM -> PM+gzip3 f = gzipBut3 f gzipQ3++-- |This is the minimal condition for recursing into a value - the+-- constructors must all match.+gzipQ3 :: GM+gzipQ3 x y z = + if and [toConstr x == toConstr y, toConstr y == toConstr z]+ then return undefined+ else fail ("Conflict: x=" ++ gshow x ++ " y=" ++ gshow y ++ " z=" ++ gshow z)++-- |This function adds a test to limit the recursion of gzip3. For+-- example, with the merge function mentioned above you might want to+-- avoid merging strings character by character:+-- +-- gzip3 merge "dim" "kim" "dip" -> Just "kip" (no!)+-- +-- so you would pass a limiting function to prevent recursing into strings:+-- +-- let continue =+-- (\ x y z -> extQ3 gzipQ3 x y z) x y z+-- where+-- stringFail :: String -> String -> String -> Bool+-- stringFail _ _ _ = False+-- gzipBut3 merge continue "dim" "kim" "dip" -> Nothing+-- +-- this can also save a lot of time examining all the heads and tails+-- of every string.+gzipBut3 :: PM -> GM -> PM+gzipBut3 merge continue x y z =+ gzip3' merge' x y z+ where+ -- If the three elements aren't all the type of f's arguments,+ -- this expression will return Nothing. Also, the f function+ -- might return Nothing. In those cases we call gzipWithM3 to+ -- traverse the sub-elements.+ merge' :: GM+ merge' x y z = cast' x >>= \x' -> cast' y >>= \y' -> merge x' y' z+ gzip3' :: GM -> GM+ gzip3' merge x y z =+ merge x y z+ `mplus`+ (continue x y z >> gzipWithM3 (gzip3' merge) x y z)++-- | gzipWithA3 plus a continue function to prevent recursion into+-- particular types. (UNTESTED)+gzipButA3 :: forall f. (Applicative f) => PM -> GB -> PA f -> PA f+gzipButA3 merge continue conflict x y z =+ gzip3' x y z+ where+ gzip3' :: GA f+ gzip3' x y z =+ case merge' x y z of+ Just x' -> pure x'+ Nothing ->+ if continue x y z+ then gzipWithA3 gzip3' x y z+ else conflict' x y z+ merge' :: GM+ merge' x y z =+ case (cast x, cast y) of+ (Just x', Just y') -> merge x' y' z+ _ -> fail "type conflict"+ conflict' :: GA f+ conflict' x y z =+ case (cast x, cast y) of+ (Just x', Just y') -> conflict x' y' z+ _ -> error "type conflict"++-- | Not to be confused with ext2Q, this extends queries of two+-- arguments (rather than queries involving constructors with two type+-- parameters.)+extQ2 :: (Typeable a, Typeable b, Typeable d, Typeable e)+ => (a -> b -> r) -> (d -> e -> r) -> a -> b -> r+extQ2 d q x y = fromMaybe (d x y) $ cast x >>= \x' -> cast y >>= \y' -> Just (q x' y')++extQ3 :: (Typeable a, Typeable b, Typeable c, Typeable d, Typeable e, Typeable f)+ => (a -> b -> c -> r) -> (d -> e -> f -> r) -> a -> b -> c -> r+extQ3 d q x y z = fromMaybe (d x y z) $ cast x >>= \x' -> cast y >>= \y' -> cast z >>= \z' -> Just (q x' y' z')++mkQ3 :: (Typeable a, Typeable b, Typeable c, Typeable d, Typeable e, Typeable f)+ => r -> (a -> b -> c -> r) -> d -> e -> f -> r+mkQ3 d q x y z = extQ3 (\ _ _ _ -> d) q x y z++extT3 :: (Typeable a, Typeable b)+ => (a -> a -> a -> Maybe a) -> (b -> b -> b -> Maybe b) -> a -> a -> a -> Maybe a+extT3 d q x y z = fromMaybe (d x y z) $ cast x >>= \x' -> cast y >>= \y' -> cast z >>= \z' -> gcast (q x' y' z')++mkQ2 :: (Data a, Data b, Data c) => (a -> b -> r) -> (c -> c -> r) -> a -> b -> r+mkQ2 d q x y = fromMaybe (d x y) $ cast x >>= \x' -> cast y >>= \y' -> Just (q x' y')++-- |This function implements the f function used to do three way+-- merging. A triplet (original, new1, new2) conflicts if the two+-- new values each differ from the original, and from each other.+-- Otherwise, the new value that differs from the original is kept, or+-- either of the new values if they match. However, even if the+-- values conflict, it still might be possible to do the merge by+-- examining the components of the value. So conflict is typically+-- (\ _ _ _ -> Nothing), while eq could be geq, but it could also+-- just return false for more complex datatypes that we don't want+-- to repeatedly traverse.+mergeBy :: forall a m. MonadPlus m => (a -> a -> a -> m a) -> (a -> a -> Bool) -> a -> a -> a -> m a+mergeBy conflict eq original left right =+ if eq original left then return right+ else if eq original right then return left+ else if eq left right then return left+ else conflict original left right