11{-# LANGUAGE OverloadedStrings #-}
22{-# LANGUAGE NamedFieldPuns #-}
33
4- module Main
5- where
4+ module Main where
65
76import Cabal2Nix
87import Cabal2Nix.Util ( quoted )
9- import Crypto.Hash.SHA256 ( hash
10- , hashlazy
11- )
8+ import Control.Applicative ( liftA2 )
9+ import Control.Monad.Trans.State.Strict
10+ import Crypto.Hash.SHA256 ( hashlazy )
1211import qualified Data.ByteString.Base16 as Base16
1312import qualified Data.ByteString.Char8 as BS
1413import qualified Data.ByteString.Lazy as BL
15- import Data.Foldable ( toList )
14+ import Data.Foldable ( toList
15+ , for_
16+ )
17+ import Data.List ( intersperse )
18+ import Data.Map ( Map )
1619import qualified Data.Map as Map
20+ import Data.Semigroup as Sem
21+ import Data.Sequence ( Seq )
1722import qualified Data.Sequence as Seq
18- import Data.String ( IsString (fromString ) )
23+ import Data.String ( IsString (fromString )
24+ )
25+ import Data.Text ( Text )
1926import Data.Text.Encoding ( decodeUtf8 )
2027import Distribution.Hackage.DB ( hackageTarball )
2128import qualified Distribution.Hackage.DB.Parsed
2229 as P
23- import Distribution.Hackage.DB.Parsed ( parseMetaData
24- , parseVersionData
25- )
26- import Distribution.Hackage.DB.Unparsed
30+ import qualified Distribution.Hackage.DB.Unparsed
31+ as U
2732import Distribution.Pretty ( prettyShow
2833 , Pretty
2934 )
35+ import Distribution.Types.PackageName ( PackageName )
36+ import Distribution.Types.Version ( Version )
3037import Nix.Expr
3138import Nix.Pretty ( prettyNix )
32- import System.Directory ( createDirectoryIfMissing )
39+ import System.Directory ( createDirectoryIfMissing
40+ )
3341import System.Environment ( getArgs )
3442import System.FilePath ( (</>)
3543 , (<.>)
@@ -38,50 +46,78 @@ import System.FilePath ( (</>)
3846main :: IO ()
3947main = do
4048 [out] <- getArgs
41- db <- readTarball Nothing =<< hackageTarball
49+ db <- U. readTarball Nothing =<< hackageTarball
50+
51+ let (defaultNix, cabalFiles) =
52+ runState (fmap seqToSet $ foldMapWithKeyA package2nix db) mempty
4253
43- let defaultNix = seqToSet $ Map. foldMapWithKey package2nix db
4454 createDirectoryIfMissing False out
4555 writeFile (out </> " default.nix" ) $ show $ prettyNix defaultNix
56+ createDirectoryIfMissing False (out </> " hackage" )
57+
58+ for_ cabalFiles $ \ (cabalFile, pname, path) -> do
59+ gpd <- cabal2nix Nothing $ InMemory Nothing pname $ BL. toStrict cabalFile
60+ writeFile (out </> path) $ show $ prettyNix gpd
61+
62+ type GPDWriter = State (Seq (BL. ByteString , String , FilePath ))
63+
64+ newtype ApplicativeMonoid f a = ApplicativeMonoid { unApplicativeMonoid :: f a }
65+ instance (Applicative f , Semigroup a ) => Sem. Semigroup (ApplicativeMonoid f a ) where
66+ ApplicativeMonoid a <> ApplicativeMonoid b = ApplicativeMonoid $ liftA2 (Sem. <>) a b
67+ instance (Applicative f , Monoid a ) => Monoid (ApplicativeMonoid f a ) where
68+ mempty = ApplicativeMonoid $ pure mempty
69+ mappend = (Sem. <>)
70+
71+ foldMapWithKeyA
72+ :: (Applicative f , Monoid b ) => (k -> a -> f b ) -> Map k a -> f b
73+ foldMapWithKeyA f =
74+ unApplicativeMonoid . Map. foldMapWithKey (\ k -> ApplicativeMonoid . f k)
75+
76+ seqToSet :: Seq (Binding NExpr ) -> NExpr
77+ seqToSet = mkNonRecSet . toList
78+
79+ fromPretty :: (Pretty a , IsString b ) => a -> b
80+ fromPretty = fromString . prettyShow
81+
82+ package2nix :: PackageName -> U. PackageData -> GPDWriter (Seq (Binding NExpr ))
83+ package2nix pname (U. PackageData { U. versions }) = do
84+ versionBindings <- foldMapWithKeyA (version2nix pname) versions
85+ return $ Seq. singleton $ quoted (fromPretty pname) $= seqToSet versionBindings
86+
87+ version2nix
88+ :: PackageName -> Version -> U. VersionData -> GPDWriter (Seq (Binding NExpr ))
89+ version2nix pname vnum (U. VersionData { U. cabalFileRevisions, U. metaFile }) =
90+ do
91+ revisionBindings <- sequenceA
92+ $ zipWith (revBinding pname vnum) cabalFileRevisions [0 .. ]
93+ return $ Seq. singleton $ quoted (fromPretty vnum) $= mkRecSet
94+ [ " sha256" $= mkStr
95+ (fromString $ P. parseMetaData pname vnum metaFile Map. ! " sha256" )
96+ , " revisions" $= mkNonRecSet
97+ ( fmap (uncurry ($=) ) revisionBindings
98+ ++ [" default" $= (mkSym " revisions" @. fst (last revisionBindings))]
99+ )
100+ ]
46101
47- _ <- forWithKey db $ \ pname (PackageData { versions }) ->
48- forWithKey versions $ \ vnum vdata@ (VersionData { cabalFileRevisions }) ->
49- let parsedVData = parseVersionData pname vnum vdata
50- writeFiles gpd cabalFile revNum = do
51- let dir = out </> packagePath pname </> fromPretty vnum
52- revPath = dir </> revName revNum
53- createDirectoryIfMissing True dir
54- BL. writeFile (revPath <.> " cabal" ) cabalFile
55- writeFile (revPath <.> " nix" ) $ show $ prettyNix $ gpd2nix Nothing Nothing gpd
56- in sequence $ zipWith3 writeFiles
57- (toList (P. cabalFileRevisions parsedVData))
58- cabalFileRevisions
59- [(0 :: Int ) .. ]
60- return ()
61- where
62- forWithKey :: Applicative f => Map. Map k v -> (k -> v -> f x ) -> f (Map. Map k x )
63- forWithKey = flip Map. traverseWithKey
64- seqToSet = mkNonRecSet . toList
65- fromPretty :: (Pretty a , IsString b ) => a -> b
66- fromPretty = fromString . prettyShow
67- package2nix pname (PackageData { versions }) =
68- Seq. singleton $ quoted (fromPretty pname) $= seqToSet
69- (Map. foldMapWithKey (version2nix pname) versions)
70- version2nix pname vnum (VersionData { cabalFileRevisions, metaFile }) =
71- Seq. singleton $ quoted (fromPretty vnum) $= mkRecSet
72- ( (" revision" $= mkSym (revName $ length cabalFileRevisions - 1 ))
73- : (" sha256" $= mkStr (fromString $ parseMetaData pname vnum metaFile Map. ! " sha256" ))
74- : zipWith (revBinding (packagePath pname) vnum) cabalFileRevisions [(0 :: Int ) .. ]
75- )
76- revName revNum = " r" <> fromString (show revNum)
77- revBinding ppath vnum cabalFile revNum =
78- let name :: (IsString a , Semigroup a ) => a
79- name = revName revNum
80- revPath = " ." </> ppath </> fromPretty vnum </> name
81- in name $= mkNonRecSet
82- [ " outPath" $= mkRelPath (revPath <.> " nix" )
83- , " cabalFile" $= mkRelPath (revPath <.> " cabal" )
84- , " cabalSha256" $= mkStr (decodeUtf8 $ Base16. encode $ hashlazy cabalFile)
85- ]
86- packagePath pname =
87- BS. unpack (BS. take 30 $ Base16. encode $ hash $ fromPretty pname) ++ " -" ++ fromPretty pname
102+ revBinding
103+ :: PackageName
104+ -> Version
105+ -> BL. ByteString
106+ -> Integer
107+ -> GPDWriter (Text , NExpr )
108+ revBinding pname vnum cabalFile revNum = do
109+ let qualifiedName = mconcat $ intersperse
110+ " -"
111+ [prettyPname, fromPretty vnum, revName, BS. unpack cabalHash]
112+ revName :: (Semigroup a , IsString a ) => a
113+ revName = " r" <> fromString (show revNum)
114+ revPath = " ." </> " hackage" </> qualifiedName <.> " nix"
115+ prettyPname = fromPretty pname
116+ cabalHash = Base16. encode $ hashlazy cabalFile
117+ modify' $ mappend $ Seq. singleton
118+ (cabalFile, prettyPname ++ " .cabal" , revPath)
119+ return $ (,) revName $ mkNonRecSet
120+ [ " outPath" $= mkRelPath revPath
121+ , " revNum" $= mkInt revNum
122+ , " sha256" $= mkStr (decodeUtf8 cabalHash)
123+ ]
0 commit comments