@@ -0,0 +1,135 @@
1+{-# LANGUAGE PatternGuards, CPP #-}
2+{- |
3+ Module : Distribution.NixOS.Derivation.Cabal
4+ License : BSD3
5+
6+ Maintainer : nix-dev@cs.uu.nl
7+ Stability : provisional
8+ Portability : PatternGuards
9+
10+ A represtation of Nix expressions based on Cabal builder defined in
11+ @pkgs\/development\/libraries\/haskell\/cabal\/cabal.nix@.
12+-}
13+
14+module Distribution.NixOS.Derivation.Cabal
15+ ( Derivation(..)
16+ , parseDerivation
17+ , module Distribution.NixOS.Derivation.Meta
18+ , module Data.Version
19+ )
20+ where
21+
22+import Distribution.NixOS.Derivation.Meta
23+import Distribution.NixOS.PrettyPrinting
24+import Distribution.Text
25+import Distribution.Package
26+#ifdef __HADDOCK__
27+import Distribution.PackageDescription ( PackageDescription )
28+#endif
29+import Distribution.PackageDescription ( FlagAssignment, FlagName(..) )
30+import Data.Version
31+import Data.List
32+import Data.Char
33+import Text.Regex.Posix hiding ( empty )
34+
35+-- | A represtation of Nix expressions for building Haskell packages.
36+-- The data type correspond closely to the definition of
37+-- 'PackageDescription' from Cabal.
38+--
39+-- Note that the "Text" instance definition provides pretty-printing,
40+-- but no parsing as of now!
41+
42+data Derivation = MkDerivation
43+ { pname :: String
44+ , version :: Version
45+ , sha256 :: String
46+ , isLibrary :: Bool
47+ , isExecutable :: Bool
48+ , buildDepends :: [String]
49+ , buildTools :: [String]
50+ , extraLibs :: [String]
51+ , pkgConfDeps :: [String]
52+ , configureFlags :: [String]
53+ , cabalFlags :: FlagAssignment
54+ , runHaddock :: Bool
55+ , metaSection :: Meta
56+ }
57+ deriving (Show, Eq, Ord)
58+
59+instance Text Derivation where
60+ disp = renderDerivation
61+ parse = error "parsing Distribution.NixOS.Derivation.Cabal.Derivation is not supported yet"
62+
63+instance Package Derivation where
64+ packageId deriv = PackageIdentifier (PackageName (pname deriv)) (version deriv)
65+
66+renderDerivation :: Derivation -> Doc
67+renderDerivation deriv = funargs (map text ("cabal" : inputs)) $$ vcat
68+ [ text ""
69+ , text "cabal.mkDerivation" <+> lparen <> text "self" <> colon <+> lbrace
70+ , nest 2 $ vcat
71+ [ attr "pname" $ string (pname deriv)
72+ , attr "version" $ doubleQuotes (disp (version deriv))
73+ , attr "sha256" $ string (sha256 deriv)
74+ , boolattr "isLibrary" (not (isLibrary deriv) || (isExecutable deriv)) (isLibrary deriv)
75+ , boolattr "isExecutable" (not (isLibrary deriv) || (isExecutable deriv)) (isExecutable deriv)
76+ , listattr "buildDepends" (buildDepends deriv)
77+ , listattr "buildTools" (buildTools deriv)
78+ , listattr "extraLibraries" (extraLibs deriv)
79+ , listattr "pkgconfigDepends" (pkgConfDeps deriv)
80+ , onlyIf renderedFlags $ attr "configureFlags" $ doubleQuotes (sep renderedFlags)
81+ , boolattr "noHaddock" (not (runHaddock deriv)) (not (runHaddock deriv))
82+ , disp (metaSection deriv)
83+ ]
84+ , rbrace <> rparen
85+ , text ""
86+ ]
87+ where
88+ inputs = nub $ sortBy (\x y -> compare (map toLower x) (map toLower y)) $
89+ filter (/="cabal") $ filter (/="Cabal") $
90+ buildDepends deriv ++ buildTools deriv ++ extraLibs deriv ++ pkgConfDeps deriv
91+ renderedFlags = [ text "-f" <> (if enable then empty else char '-') <> text f | (FlagName f, enable) <- cabalFlags deriv ]
92+ ++ map text (configureFlags deriv)
93+
94+-- | A very incomplete parser that extracts 'pname', 'version',
95+-- 'sha256', 'platforms', 'maintainers', and 'runHaddock' from the given
96+-- Nix expression.
97+
98+parseDerivation :: String -> Maybe Derivation
99+parseDerivation buf
100+ | buf =~ "cabal.mkDerivation"
101+ , [name] <- buf `regsubmatch` "pname *= *\"([^\"]+)\""
102+ , [vers'] <- buf `regsubmatch` "version *= *\"([^\"]+)\""
103+ , Just vers <- simpleParse vers'
104+ , [sha] <- buf `regsubmatch` "sha256 *= *\"([^\"]+)\""
105+ , plats <- buf `regsubmatch` "platforms *= *([^;]+);"
106+ , maint <- buf `regsubmatch` "maintainers *= *\\[([^\"]+)]"
107+ , noHaddock <- buf `regsubmatch` "noHaddock *= *(true|false) *;"
108+ = Just $ MkDerivation
109+ { pname = name
110+ , version = vers
111+ , sha256 = sha
112+ , isLibrary = False
113+ , isExecutable = False
114+ , buildDepends = []
115+ , buildTools = []
116+ , extraLibs = []
117+ , pkgConfDeps = []
118+ , configureFlags = []
119+ , cabalFlags = []
120+ , runHaddock = case noHaddock of "true":[] -> False
121+ _ -> True
122+ , metaSection = Meta
123+ { homepage = ""
124+ , description = ""
125+ , license = Unknown Nothing
126+ , maintainers = concatMap words maint
127+ , platforms = concatMap words (map (map (\c -> if c == '+' then ' ' else c)) plats)
128+ }
129+ }
130+ | otherwise = Nothing
131+
132+regsubmatch :: String -> String -> [String]
133+regsubmatch buf patt = let (_,_,_,x) = f in x
134+ where f :: (String,String,String,[String])
135+ f = match (makeRegexOpts compExtended execBlank patt) buf