git.iko.soy/typeable/cabal2nix
on v1.6 help sign in
typeable/cabal2nix/cabal2nix.hs
145 lines 5.8 kB history raw
1-- cabal2nix.hs
2--
3-- Copyright (c) 20011 Peter Simons <simons@cryp.to>
4-- See LICENSE file for licensing details.
6module Main ( main ) where
8import System.IO
9import System.Environment
10import Control.Exception
11import System.Exit
12import Distribution.PackageDescription.Parse
13import Distribution.PackageDescription
14import Distribution.Package
15import Distribution.License
16import Data.Version
17import Data.List
18import Control.Monad
19import Data.Char
20import Network.HTTP
21import System.Process
23type PkgName = String
24type PkgVersion = [Int]
25type PkgSHA256 = String
26type PkgURL = String
27type PkgDescription = String
28type PkgLicense = License
29type PkgDependencies = [CondTree ConfVar [Dependency] ()]
30type PkgExtraLibs = [String]
32data Pkg = Pkg PkgName PkgVersion PkgSHA256 PkgURL PkgDescription PkgLicense PkgDependencies PkgExtraLibs
33 deriving (Show)
35toNixName :: String -> String
36toNixName [] = error "toNixName: empty string is not a valid argument"
37toNixName "Cabal" = "cabal"
38toNixName name = f name
39 where
40 f [] = []
41 f ('-':c:cs) | c `notElem` "-" = toUpper c : f cs
42 f ('-':_) = error ("unexpected package name " ++ show name)
43 f (c:cs) = c : f cs
45toNix :: Pkg -> String
46toNix (Pkg name ver sha256 url desc lic deps libs) =
47 "{" ++ exprArgs ++"}:\n\n"
48 ++ "cabal.mkDerivation (self : {\n"
49 ++ " pname = " ++ show name ++ ";\n"
50 ++ " version = \"" ++ showVer ++ "\";\n"
51 ++ " sha256 = " ++ show sha256 ++ ";\n"
52 ++ " propagatedBuildInputs = [" ++ depList ++ "];\n"
53 ++ " meta = {\n"
54 ++ " homepage = \"" ++ url ++ "\";\n"
55 ++ " description = " ++ show desc ++ ";\n"
56 ++ " license = " ++ showLic lic ++ ";\n"
57 ++ " };\n"
58 ++ "})\n"
59 where
60 exprArgs = concat (intersperse "," ("cabal":pkgDeps))
61 showVer = concat (intersperse "." (map show ver))
62 depList = concat (intersperse " " pkgDeps)
63 pkgDeps :: [String]
64 pkgDeps = filter (/="cabal") $ nub $ sort $ map toNixName $
65 libs ++ [ n | dep <- deps, Dependency (PackageName n) _ <- condTreeConstraints dep
66 , n `notElem` ["base","containers"]
67 ]
68 showLic (GPL Nothing) = show "GPL"
69 showLic (GPL (Just (Version [2] []))) = "self.stdenv.lib.licenses.gpl2"
70 showLic (GPL (Just (Version [3] []))) = "self.stdenv.lib.licenses.gpl3"
71 showLic (LGPL Nothing) = show "LGPL"
72 showLic (LGPL (Just (Version [2,1] []))) = "self.stdenv.lib.licenses.lgpl21"
73 showLic (LGPL (Just (Version [3] []))) = "self.stdenv.lib.licenses.lgpl3"
74 showLic BSD3 = "self.stdenv.lib.licenses.bsd3"
75 showLic BSD4 = "self.stdenv.lib.licenses.bsd4"
76 showLic MIT = "self.stdenv.lib.licenses.mit"
77 showLic PublicDomain = "self.stdenv.lib.licenses.publicDomain"
78 showLic AllRightsReserved = "unknown"
79 showLic OtherLicense = "unknown"
80 showLic l = error $ "unknown license: " ++ show l
82cabal2nix :: GenericPackageDescription -> PkgSHA256 -> Pkg
83cabal2nix cabal sha256 = Pkg pkgname pkgver sha256 url desc lic (map simplify libDeps ++ map simplify exeDeps) (libs++libs')
84 where
85 pkg = packageDescription cabal
86 PackageName pkgname = pkgName (package pkg)
87 pkgver = versionBranch (pkgVersion (package pkg))
88 lic = license pkg
89 url = homepage pkg
90 desc = synopsis pkg
91 -- globalDeps = buildDepends pkg
92 libDeps = maybe [] (\x -> [x]) (condLibrary cabal)
93 exeDeps = [ tree | (_,tree) <- condExecutables cabal ]
94 libs = concat [ extraLibs (libBuildInfo (condTreeData x)) | x <- libDeps ]
95 libs' = concat [ extraLibs (buildInfo (condTreeData x)) | x <- exeDeps ]
97simplify :: CondTree ConfVar [Dependency] a -> CondTree ConfVar [Dependency] ()
98simplify (CondNode _ deps nodes) = CondNode () deps (map simp nodes)
99 where
100 simp (cond,tree,mtree) = (cond, simplify tree, maybe Nothing (Just . simplify) mtree)
102readCabalFile :: FilePath -> IO String
103readCabalFile path
104 | "http://" `isPrefixOf` path = Network.HTTP.simpleHTTP (getRequest path) >>= getResponseBody
105 | "file://" `isPrefixOf` path = readCabalFile (drop 7 path)
106 | otherwise = readFile path
108hashPackage :: GenericPackageDescription -> IO String
109hashPackage pkg = readProcess "bash" ["-c", "exec nix-prefetch-url 2>/dev/tty " ++ url] ""
110 where
111 url = "http://hackage.haskell.org/packages/archive/" ++ name ++ "/" ++ version ++ "/" ++ name ++ "-" ++ version ++ ".tar.gz"
112 PackageIdentifier (PackageName name) version' = package (packageDescription pkg)
113 version = showVersion version'
115main :: IO ()
116main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() -> do
117 args <- getArgs
119 let usage = "Usage: cabal2nix url-to-cabal-file [sha256-hash]"
121 when (length args < 1 || length args > 2) $ do
122 mapM_ (hPutStrLn stderr) [ "*** invalid command line syntax"
123 , usage
124 ]
125 exitFailure
127 when ("--help" `elem` args || "-h" `elem` args) $ do
128 putStrLn usage
129 exitFailure
131 cabal' <- fmap parsePackageDescription (readCabalFile (head args))
132 cabal <- case cabal' of
133 ParseOk _ a -> return a
134 ParseFailed err -> do
135 hPutStrLn stderr ("*** cannot parse cabal file: " ++ show err)
136 exitFailure
138 sha256 <- case args of
139 _:hash:[] -> return hash
140 _ -> hashPackage cabal
142 let pkg = cabal2nix cabal sha256
143 putStr (toNix pkg)