git.iko.soy/typeable/cabal2nix
on master help sign in
Converted Hackage4Nix.hs and Cabal2Nix to use the re-factored data types.
commit8d51e97124c6d3ce8e8a1bb7013653c96b4367eb authorPeter Simons <simons@cryp.to> dateSun 14 Aug 2011, 23:13 (15 years ago) parent49165f2
4 files changed+117−202
cabal2nix.cabal+1 src/Cabal2Nix.hs+21−10 src/Cabal2Nix/Package.hs+44−127 src/Hackage4Nix.hs+51−65
cabal2nix.cabal+1
@@ -45,6 +45,7 @@ Executable cabal2nix
4545 main-is: Cabal2Nix.hs
4646 hs-source-dirs: src
4747 Build-Depends: base >= 3 && < 5, Cabal, process, HTTP, pretty, filepath, directory
48+ Extensions: PatternGuards
4849 Ghc-Options: -Wall
4950 other-modules: Cabal2Nix.Pretty
5051 Cabal2Nix.License
src/Cabal2Nix.hs+21−10
@@ -1,19 +1,21 @@
11module Main ( main ) where
22
33import System.IO ( hPutStrLn, hFlush, stdout, stderr )
4−import System.Environment ( getArgs )
4+import System.Environment -- ( getArgs )
55import Control.Exception ( bracket )
66import System.Exit ( exitFailure )
77import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
88import Distribution.PackageDescription ( package, packageDescription )
9−import Distribution.Text ( simpleParse )
9+import Distribution.Text
1010import Data.List ( isPrefixOf )
1111import Control.Monad ( when )
1212import Network.HTTP ( simpleHTTP, getRequest, getResponseBody )
1313import System.Console.GetOpt ( OptDescr(..), ArgDescr(..), ArgOrder(..), usageInfo, getOpt )
1414
15−import Cabal2Nix.Package ( showNixPkg, cabal2nix )
15+import Cabal2Nix.Package ( cabal2nix )
1616import Cabal2Nix.Hackage ( hackagePath, Ext(..), hashPackage )
17+import Distribution.NixOS.Derivation.Cabal
18+import Distribution.NixOS.Derivation.Meta
1719
1820readCabalFile :: FilePath -> IO String
1921readCabalFile path
@@ -60,11 +62,11 @@ main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() -> do
6062 let uri = args
6163 hash = [ h | SHA256 h <- opts ]
6264 noHaddock = NoHaddock `elem` opts
63− maintainers = [ m | Maintainer m <- opts ]
64− platforms' = [ p | Platform p <- opts ]
65− platforms
66− | null platforms' = if not (null maintainers) then ["self.ghc.meta.platforms"] else []
67− | otherwise = platforms'
65+ maints = [ m | Maintainer m <- opts ]
66+ plats' = [ p | Platform p <- opts ]
67+ plats
68+ | null plats' = if not (null maints) then ["self.ghc.meta.plats"] else []
69+ | otherwise = plats'
6870
6971 when (length uri /= 1) (cmdlineError "*** exactly one URI must be specified")
7072 when (length hash > 1) (cmdlineError "*** the --sha256 option may be specified only once")
@@ -78,6 +80,15 @@ main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() -> do
7880
7981 let packageId = package (packageDescription cabal)
8082
81− sha256 <- if null hash then hashPackage packageId else return (head hash)
83+ sha <- if null hash then hashPackage packageId else return (head hash)
8284
83− putStr (showNixPkg (cabal2nix cabal sha256 noHaddock platforms maintainers))
85+ let deriv = (cabal2nix cabal)
86+ { sha256 = sha
87+ , runHaddock = not noHaddock
88+ , metaSection = (metaSection deriv)
89+ { maintainers = maints
90+ , platforms = plats
91+ }
92+ }
93+
94+ putStr (show (disp deriv))
src/Cabal2Nix/Package.hs+44−127
@@ -1,145 +1,62 @@
1−module Cabal2Nix.Package
2− ( cabal2nix, showNixPkg
3− , PkgName, PkgVersion, PkgSHA256, PkgURL, PkgDescription, PkgLicense
4− , PkgIsLib, PkgIsExe, PkgDependencies, PkgBuildTools, PkgExtraLibs
5− , PkgPkgconfigDeps, PkgPlatforms, PkgMaintainers, PkgNoHaddock, Pkg(..)
6− )
7− where
1+module Cabal2Nix.Package where
82
9−import Data.List
103import Data.Maybe
114import Distribution.Compiler
12−import Distribution.Package
13−import Distribution.PackageDescription
5+import qualified Distribution.Package as Cabal
6+import qualified Distribution.PackageDescription as Cabal
147import Distribution.PackageDescription.Configuration
158import Distribution.System
169import Distribution.Version
17−import Text.PrettyPrint
18−import Data.Version ( showVersion )
19−
20−import Cabal2Nix.License
10+import Distribution.NixOS.Derivation.Cabal
11+import Distribution.NixOS.Derivation.Meta
12+import Distribution.NixOS.Derivation.License
2113import Cabal2Nix.Name
2214import Cabal2Nix.CorePackages
23−import Cabal2Nix.Pretty
24−
25−type PkgName = String
26−type PkgVersion = String
27−type PkgSHA256 = String
28−type PkgURL = String
29−type PkgDescription = String
30−type PkgLicense = String
31−type PkgIsLib = Bool
32−type PkgIsExe = Bool
33−type PkgDependencies = [Dependency] -- [CondTree ConfVar [Dependency] ()]
34−type PkgBuildTools = [Dependency]
35−type PkgExtraLibs = [String]
36−type PkgPkgconfigDeps = [String]
37−type PkgNoHaddock = Bool
38−type PkgPlatforms = [String]
39−type PkgMaintainers = [String]
40−
41−data Pkg = Pkg PkgName
42− PkgVersion
43− PkgSHA256
44− PkgURL
45− PkgDescription
46− PkgLicense
47− PkgIsLib
48− PkgIsExe
49− PkgDependencies
50− PkgBuildTools
51− PkgExtraLibs
52− PkgPkgconfigDeps
53− PkgNoHaddock
54− PkgPlatforms
55− PkgMaintainers
56− deriving (Show)
57−
58−showNixPkg :: Pkg -> String
59−showNixPkg (Pkg name ver sha256 url desc lic isLib isExe deps
60− tools libs pcs noHaddock platforms maintainers) =
61− render doc
62− where
63− doc = funargs (map text ("cabal" : pkgInputs)) $$
64− vcat [
65− text "",
66− text "cabal.mkDerivation" <+> lparen <> text "self" <>
67− colon <+> lbrace,
68− nest 2 $ vcat [
69− attr "pname" $ string name,
70− attr "version" $ string ver,
71− attr "sha256" $ string sha256,
72− boolattr "isLibrary" (not isLib || isExe) isLib,
73− boolattr "isExecutable" (not isLib || isExe) isExe,
74− listattr "buildDepends" pkgDeps,
75− listattr "buildTools" pkgBuildTools,
76− listattr "extraLibraries" pkgLibs,
77− listattr "pkgconfigDepends" pkgPCs,
78− boolattr "noHaddock" noHaddock noHaddock,
79− vcat [
80− text "meta" <+> equals <+> lbrace,
81− nest 2 $ vcat [
82− onlyIf url $ attr "homepage" $ string url,
83− onlyIf desc $ attr "description" $ string desc,
84− attr "license" $ text lic,
85− onlyIf platforms $
86− sep [
87− text "platforms" <+> equals,
88− nest 2 ((fsep $ punctuate (text " ++") $ map text platforms)) <> semi
89− ],
90− listattr "maintainers" maintainers
91− ],
92− rbrace <> semi
93− ]
94− ],
95− rbrace <> rparen,
96− text ""
97− ]
98− pkgLibs = nub $ sort $ concatMap libNixName libs
99− pkgPCs = nub $ sort $ concatMap libNixName pcs
100− pkgDeps = nub $ sort $ map toNixName $
101− filter (`notElem` (name : corePackages)) $ map unDep deps
102− pkgBuildTools = nub $ sort $ map toNixName $
103− filter (`notElem` coreBuildTools) $ map unDep tools
104− pkgInputs = nub $ pkgLibs ++ pkgPCs ++ pkgBuildTools ++ pkgDeps
105−
15+import Data.List
10616
107−cabal2nix :: GenericPackageDescription -> PkgSHA256 -> PkgNoHaddock -> PkgPlatforms -> PkgMaintainers -> Pkg
108−cabal2nix cabal sha256 noHaddock platforms maintainers =
109− Pkg pkgname pkgver sha256 url desc lic
110− isLib isExe
111− (buildDepends tpkg)
112− tools
113− libs
114− pcs
115− noHaddock
116− [ if '.' `elem` p then p else "self.stdenv.lib.platforms." ++ p | p <- platforms ]
117− [ if '.' `elem` m then m else "self.stdenv.lib.maintainers." ++ m | m <- maintainers ]
17+cabal2nix :: Cabal.GenericPackageDescription -> Derivation
18+cabal2nix cabal = MkDerivation
19+ { pname = Cabal.pkgName pkg
20+ , version = Cabal.pkgVersion pkg
21+ , sha256 = "cabal2nix left the she256 field undefined"
22+ , isLibrary = isJust (Cabal.library tpkg)
23+ , isExecutable = not (null (Cabal.executables tpkg))
24+ , buildDepends = pkgDeps
25+ , buildTools = pkgBuildTools
26+ , extraLibs = pkgLibs
27+ , pkgConfDeps = pkgPCs
28+ , runHaddock = True
29+ , metaSection = Meta
30+ { homepage = Cabal.homepage descr
31+ , description = normalizeDescription (Cabal.synopsis descr)
32+ , license = fromCabalLicense (Cabal.license descr)
33+ , platforms = []
34+ , maintainers = []
35+ }
36+ }
11837 where
119− pkg = packageDescription cabal
120− PackageName pkgname = pkgName (package pkg)
121− pkgver = showVersion (pkgVersion (package pkg))
122− lic = showLic (license pkg)
123− url = homepage pkg
124− desc = normalizeDescription (synopsis pkg)
125− -- globalDeps = buildDepends pkg
126− -- Potentially dangerous: determine a default flattening of the
127− -- package description. Better approach: export the conditional
128− -- structure and reflect it in the generated file.
38+ descr = Cabal.packageDescription cabal
39+ pkg = Cabal.package descr
40+ Cabal.PackageName name = Cabal.pkgName pkg
41+ deps = (Cabal.buildDepends tpkg)
42+ libDeps = map Cabal.libBuildInfo $ maybeToList (Cabal.library tpkg)
43+ exeDeps = map Cabal.buildInfo $ Cabal.executables tpkg
44+ tools = concatMap Cabal.buildTools (libDeps ++ exeDeps)
45+ libs = concatMap Cabal.extraLibs (libDeps ++ exeDeps)
46+ pcs = map unDep $ concatMap Cabal.pkgconfigDepends (libDeps ++ exeDeps)
12947 Right (tpkg, _) = finalizePackageDescription [] (const True)
13048 (Platform I386 Linux) -- shouldn't be hardcoded
13149 (CompilerId GHC (Version [7,0,4] [])) -- dito
13250 [] cabal
133− isLib = isJust (library tpkg)
134− isExe = not (null (executables tpkg))
135− libDeps = map libBuildInfo $ maybeToList (library tpkg)
136− exeDeps = map buildInfo $ executables tpkg
137− libs = concatMap extraLibs (libDeps ++ exeDeps)
138− pcs = map unDep $ concatMap pkgconfigDepends (libDeps ++ exeDeps)
139− tools = concatMap buildTools (libDeps ++ exeDeps)
51+ pkgLibs = nub $ sort $ concatMap libNixName libs
52+ pkgPCs = nub $ sort $ concatMap libNixName pcs
53+ pkgDeps = nub $ sort $ map toNixName $
54+ filter (`notElem` (name : corePackages)) $ map unDep deps
55+ pkgBuildTools = nub $ sort $ map toNixName $
56+ filter (`notElem` coreBuildTools) $ map unDep tools
14057
141−unDep :: Dependency -> String
142−unDep (Dependency (PackageName x) _) = x
58+unDep :: Cabal.Dependency -> String
59+unDep (Cabal.Dependency (Cabal.PackageName x) _) = x
14360
14461normalizeDescription :: String -> String
14562normalizeDescription desc
src/Hackage4Nix.hs+51−65
@@ -8,16 +8,23 @@ import System.Directory
88import System.Environment
99import System.Exit
1010import Data.List
11+-- import Distribution.Package
1112import qualified Data.Set as Set
1213import Control.Monad.State
1314import Control.Exception ( bracket )
1415import Text.Regex.Posix
1516import Data.Version
16−import Text.ParserCombinators.ReadP ( readP_to_S )
17+-- import Text.ParserCombinators.ReadP ( readP_to_S, readS_to_P )
1718import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
1819import Distribution.PackageDescription ( GenericPackageDescription() )
20+import Distribution.Text
1921import System.Console.GetOpt ( OptDescr(..), ArgDescr(..), ArgOrder(..), usageInfo, getOpt )
20−import Cabal2Nix.Package ( cabal2nix, showNixPkg, PkgName, PkgVersion, PkgSHA256, PkgPlatforms, PkgMaintainers, PkgNoHaddock )
22+import Cabal2Nix.Package
23+import Distribution.NixOS.Derivation.Cabal
24+import Distribution.NixOS.Derivation.Meta
25+
26+data Pkg = Pkg Derivation FilePath Bool
27+ deriving (Show, Eq, Ord)
2128
2229type PkgSet = Set.Set Pkg
2330
@@ -70,19 +77,10 @@ discoverNixFiles yield dirOrFile
7077 (True,_) -> return ()
7178 (False,_) -> io (readDirectory dirOrFile) >>= mapM_ (discoverNixFiles yield . (dirOrFile </>))
7279
73−type Regenerate = Bool
74−data Pkg = Pkg PkgName PkgVersion PkgSHA256 PkgNoHaddock PkgPlatforms PkgMaintainers FilePath Regenerate
75− deriving (Show, Eq, Ord)
76−
77−regsubmatch :: String -> String -> [String]
78−regsubmatch buf patt = let (_,_,_,x) = f in x
79− where f :: (String,String,String,[String])
80− f = match (makeRegexOpts compExtended execBlank patt) buf
80+regenerateDerivation :: Derivation -> String -> Bool
81+regenerateDerivation deriv buf = not (display (pname deriv) `elem` patchedPackages) &&
82+ not (buf =~ "preConfigure|configureFlags|postInstall|patchPhase")
8183
82−normalizeMaintainer :: String -> String
83−normalizeMaintainer x
84− | "self.stdenv.lib.maintainers." `isPrefixOf` x = drop 28 x
85− | otherwise = x
8684
8785parseNixFile :: FilePath -> String -> Hackage4Nix (Maybe Pkg)
8886parseNixFile path buf
@@ -92,46 +90,24 @@ parseNixFile path buf
9290 = msgDebug ("ignore known bad package " ++ path) >> return Nothing
9391 | buf =~ "src = (fetchurl|fetchgit|sourceFromHead)"
9492 = msgDebug ("ignore non-hackage package " ++ path) >> return Nothing
95− | [name] <- buf `regsubmatch` "name *= *\"([^\"]+)\""
96− , [vers] <- buf `regsubmatch` "version *= *\"([^\"]+)\""
97− , [sha] <- buf `regsubmatch` "sha256 *= *\"([^\"]+)\""
98− , plats <- buf `regsubmatch` "platforms *= *([^;]+);"
99− , maint <- buf `regsubmatch` "maintainers *= *\\[([^\"]+)]"
100− , haddock <- buf `regsubmatch` "noHaddock *= *(true|false) *;"
101− = let plats' = concatMap words (map (map (\c -> if c == '+' then ' ' else c)) plats)
102− maint' = concatMap words maint
103− noHaddock
104− | "true":[] <- haddock = True
105− | otherwise = False
106− regenerate = not (name `elem` patchedPackages) &&
107− not (buf =~ "preConfigure|configureFlags|postInstall|patchPhase")
108− in
109− return $ Just $ Pkg name vers sha noHaddock plats'
110− (map normalizeMaintainer maint')
111− path regenerate
93+ | Just deriv <- parseDerivation buf
94+ = return (Just (Pkg deriv path (regenerateDerivation deriv buf)))
11295 | otherwise = msgInfo ("failed to parse file " ++ path) >> return Nothing
11396
114−readVersion :: String -> Version
115−readVersion str =
116− case [ v | (v,[]) <- readP_to_S parseVersion str ] of
117− [ v' ] -> v'
118− _ -> error ("invalid version specifier " ++ show str)
119−
12097selectLatestVersions :: PkgSet -> PkgSet
12198selectLatestVersions = Set.fromList . nubBy f2 . sortBy f1 . Set.toList
12299 where
123− f1 (Pkg n1 v1 _ _ _ _ _ _) (Pkg n2 v2 _ _ _ _ _ _)
124− | n1 == n2 = compare v2 v1
125− | otherwise = compare n1 n2
126− f2 (Pkg n1 _ _ _ _ _ _ _) (Pkg n2 _ _ _ _ _ _ _)
127− = n1 == n2
100+ f1 (Pkg deriv1 _ _) (Pkg deriv2 _ _)
101+ | pname deriv1 == pname deriv2 = compare (version deriv2) (version deriv1)
102+ | otherwise = compare (pname deriv1) (pname deriv2)
103+ f2 (Pkg deriv1 _ _) (Pkg deriv2 _ _) = pname deriv1 == pname deriv2
128104
129−discoverUpdates :: PkgName -> PkgVersion -> Hackage4Nix [PkgVersion]
105+discoverUpdates :: String -> String -> Hackage4Nix [String]
130106discoverUpdates name vers = do
131107 hackage <- gets _hackageDb
132108 versionStrings <- io $ readDirectory (hackage </> name)
133109 let versions = map readVersion versionStrings
134− return (sort [ showVersion v | v <- versions, v > readVersion vers ])
110+ return [ showVersion v | v <- versions, v > readVersion vers ]
135111
136112updateNixPkgs :: [FilePath] -> Hackage4Nix ()
137113updateNixPkgs paths = do
@@ -140,45 +116,55 @@ updateNixPkgs paths = do
140116 flip discoverNixFiles fileOrDir $ \file -> do
141117 nix' <- io (readFile file) >>= parseNixFile file
142118 flip (maybe (return ())) nix' $ \nix -> do
143− let Pkg name vers sha noHaddock plats maints path regenerate = nix
119+ let Pkg deriv path regenerate = nix
120+ maints = maintainers (metaSection deriv)
121+ plats = platforms (metaSection deriv)
122+ when (null maints) (msgInfo ("no maintainers configured for " ++ path))
144123 modify $ \cfg -> cfg { _pkgset = Set.insert nix (_pkgset cfg) }
145124 when regenerate $ do
146125 msgDebug ("re-generate " ++ path)
147− let maints' = nub (sort (maints ++ ["andres","simons"]))
126+ let maints' = nub (sort (maints ++ ["self.stdenv.lib.maintainers.andres","self.stdenv.lib.maintainers.simons"]))
148127 plats'
149128 | null plats && not (null maints) = ["self.ghc.meta.platforms"]
150129 | otherwise = plats
151− when (null maints) (msgInfo ("no maintainers configured for " ++ path))
152− pkg <- readCabalFile name vers
153− io $ writeFile path (showNixPkg (cabal2nix pkg sha noHaddock plats' maints'))
130+ pkg <- readCabalFile (display (pname deriv)) (display (version deriv))
131+ let deriv' = (cabal2nix pkg) { sha256 = sha256 deriv, runHaddock = runHaddock deriv }
132+ deriv'' = deriv' { metaSection = (metaSection deriv')
133+ { maintainers = maints'
134+ , platforms = plats'
135+ }
136+ }
137+ io $ writeFile path (show (disp deriv''))
154138 pkgset <- gets (selectLatestVersions . _pkgset)
155139 updates' <- flip mapM (Set.elems pkgset) $ \pkg -> do
156− let Pkg name vers _ _ _ _ _ _ = pkg
157− updates <- discoverUpdates name vers
140+ let Pkg deriv _ _ = pkg
141+ updates <- discoverUpdates (display (pname deriv)) (display (version deriv))
158142 return (pkg,updates)
159143 let updates = [ u | u@(_,(_:_)) <- updates' ]
160144 when (not (null updates)) $ do
161145 msgInfo "The following updates are available:"
162146 flip mapM_ updates $ \(pkg,versions) -> do
163− let Pkg name vers _ noHaddock plats maints path regenerate = pkg
147+ let Pkg deriv path regenerate = pkg
164148 msgInfo ""
165− msgInfo $ name ++ "-" ++ vers ++ ":"
149+ msgInfo $ (display (pname deriv)) ++ "-" ++ (display (version deriv)) ++ ":"
166150 flip mapM_ versions $ \newVersion -> do
167− msgInfo $ " " ++ genCabal2NixCmdline (Pkg name newVersion undefined noHaddock plats maints path regenerate)
151+ let deriv' = deriv { version = readVersion newVersion }
152+ msgInfo $ " " ++ genCabal2NixCmdline (Pkg deriv' path regenerate)
168153 return ()
169154
170155genCabal2NixCmdline :: Pkg -> String
171−genCabal2NixCmdline (Pkg name vers _ noHaddock plats maints path _) = unwords $ ["cabal2nix"] ++ opts ++ [">"++path']
172− where
173− opts = [cabal] ++ maints' ++ plats' ++ if noHaddock then ["--no-haddock"] else []
174− cabal = "cabal://" ++ name ++ "-" ++ vers
175− maints' = [ "--maintainer=" ++ m | m <- maints ]
176− plats'
177− | ["self.ghc.meta.platforms"] == plats = []
178− | otherwise = [ "--platform=" ++ p | p <- plats ]
179− path'
180− | path =~ "/[0-9\\.]+\\.nix$" = replaceFileName path (vers <.> "nix")
181− | otherwise = path
156+genCabal2NixCmdline (Pkg deriv path _) = unwords $ ["cabal2nix"] ++ opts ++ [">"++path']
157+ where
158+ meta = metaSection deriv
159+ opts = [cabal] ++ maints' ++ plats' ++ if runHaddock deriv then [] else ["--no-haddock"]
160+ cabal = "cabal://" ++ display (pname deriv) ++ "-" ++ display (version deriv)
161+ maints' = [ "--maintainer=" ++ m | m <- maintainers meta ]
162+ plats'
163+ | ["self.ghc.meta.platforms"] == platforms meta = []
164+ | otherwise = [ "--platform=" ++ p | p <- platforms meta ]
165+ path'
166+ | path =~ "/[0-9\\.]+\\.nix$" = replaceFileName path (display (version deriv) <.> "nix")
167+ | otherwise = path
182168
183169data CliOption = PrintHelp | Verbose | HackageDB FilePath
184170 deriving (Eq)