git.iko.soy/typeable/cabal2nix
on master help sign in
Migrate cabal2nix's Main module to a library-exposed module.

The motivation is to support calling cabal2nix from another Haskell library using the same interface provided by the executable.

commitd3f7b6e24cd43048e586012632c850ef80bb7944 authorJacob Mitchell <jacob.mitchell@iohk.io> dateTue 6 Jun 2017, 10:34 (9 years ago) parent2ed2e10
4 files changed+178−164
cabal2nix.cabal+1 cabal2nix/Main.hs+2−164 package.yaml+1 src/Cabal2nix.hs+174
cabal2nix.cabal+1
@@ -105,6 +105,7 @@ library
105105 , transformers
106106 , yaml
107107 exposed-modules:
108+ Cabal2nix
108109 Distribution.Nixpkgs.Fetch
109110 Distribution.Nixpkgs.Haskell
110111 Distribution.Nixpkgs.Haskell.BuildInfo
cabal2nix/Main.hs+2−164
@@ -1,166 +1,4 @@
1−{-# LANGUAGE RecordWildCards #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−
4−module Main ( main ) where
5−
6−import Control.Exception ( bracket )
7−import Control.Lens
8−import Data.Maybe ( fromMaybe )
9−import Data.Monoid ( (<>) )
10−import qualified Data.Set as Set
11−import Data.String
12−import qualified Distribution.Compat.ReadP as P
13−import Distribution.Compiler
14−import Distribution.Nixpkgs.Fetch
15−import Distribution.Nixpkgs.Haskell
16−import Distribution.Nixpkgs.Haskell.FromCabal
17−import Distribution.Nixpkgs.Haskell.PackageSourceSpec
18−import Distribution.Nixpkgs.Meta
19−import Distribution.PackageDescription ( FlagName(..), FlagAssignment )
20−import Distribution.Simple.Utils ( lowercase )
21−import Distribution.System
22−import Distribution.Text
23−import Language.Nix
24−import Options.Applicative
25−import Paths_cabal2nix ( version )
26−import System.IO ( hFlush, stdout, stderr )
27−import qualified Text.PrettyPrint.ANSI.Leijen as P2 hiding ( (<$>), (<>) )
28−import Text.PrettyPrint.HughesPJClass ( Doc, Pretty(..), text, vcat, hcat, semi )
29−
30−data Options = Options
31− { optSha256 :: Maybe String
32− , optMaintainer :: [String]
33−--, optPlatform :: [String] -- TODO: fix command line handling of platforms
34− , optHaddock :: Bool
35− , optDoCheck :: Bool
36− , optJailbreak :: Bool
37− , optRevision :: Maybe String
38− , optHyperlinkSource :: Bool
39− , optEnableLibraryProfiling :: Bool
40− , optEnableExecutableProfiling :: Bool
41− , optEnableProfiling :: Maybe Bool
42− , optExtraArgs :: [String]
43− , optHackageDb :: Maybe FilePath
44− , optNixShellOutput :: Bool
45− , optFlags :: [String]
46− , optCompiler :: CompilerId
47− , optSystem :: Platform
48− , optSubpath :: Maybe FilePath
49− , optUrl :: String
50− }
51− deriving (Show)
52−
53−options :: Parser Options
54−options = Options
55− <$> optional (strOption $ long "sha256" <> metavar "HASH" <> help "sha256 hash of source tarball")
56− <*> many (strOption $ long "maintainer" <> metavar "MAINTAINER" <> help "maintainer of this package (may be specified multiple times)")
57−-- <*> many (strOption $ long "platform" <> metavar "PLATFORM" <> help "supported build platforms (may be specified multiple times)")
58− <*> flag True False (long "no-haddock" <> help "don't run Haddock when building this package")
59− <*> flag True False (long "no-check" <> help "don't run regression test suites of this package")
60− <*> switch (long "jailbreak" <> help "disregard version restrictions on build inputs")
61− <*> optional (strOption $ long "revision" <> help "revision to use when fetching from VCS")
62− <*> flag True False (long "no-hyperlink-source" <> help "don't generate pretty-printed source code for the documentation")
63− <*> switch (long "enable-library-profiling" <> help "enable library profiling in the generated build")
64− <*> switch (long "enable-executable-profiling" <> help "enable executable profiling in the generated build")
65− <*> optional (switch (long "enable-profiling" <> help "enable both library and executable profiling in the generated build"))
66− <*> many (strOption $ long "extra-arguments" <> help "extra parameters required for the function body")
67− <*> optional (strOption $ long "hackage-db" <> metavar "PATH" <> help "path to the local hackage db in tar format")
68− <*> switch (long "shell" <> help "generate output suitable for nix-shell")
69− <*> many (strOption $ short 'f' <> long "flag" <> help "Cabal flag (may be specified multiple times)")
70− <*> option (readP parse) (long "compiler" <> help "compiler to use when evaluating the Cabal file" <> value buildCompilerId <> showDefaultWith display)
71− <*> option (readP parsePlatform) (long "system" <> help "target system to use when evaluating the Cabal file" <> value buildPlatform <> showDefaultWith display)
72− <*> optional (strOption $ long "subpath" <> metavar "PATH" <> help "Path to Cabal file's directory relative to the URI (default is root directory)")
73− <*> strArgument (metavar "URI")
74−
75−readP :: P.ReadP a a -> ReadM a
76−readP p = eitherReader $ \s -> case [ r' | (r',"") <- P.readP_to_S p s ] of
77− (r:_) -> Right r
78− _ -> Left ("invalid value " ++ show s)
79−
80−parsePlatform :: P.ReadP r Platform
81−parsePlatform = do arch <- P.choice [P.string "i686" >> return I386, P.string "x86_64" >> return X86_64]
82− _ <- P.char '-'
83− os <- P.choice [P.string "linux" >> return Linux, P.string "darwin" >> return OSX]
84− return (Platform arch os)
85−
86−pinfo :: ParserInfo Options
87−pinfo = info
88− ( helper
89− <*> infoOption ("cabal2nix " ++ display version) (long "version" <> help "Show version number")
90− <*> options
91− )
92− ( fullDesc
93− <> header "cabal2nix converts Cabal files into build instructions for Nix."
94− <> progDescDoc (Just (P2.vcat
95− [ P2.text ""
96− , P2.text "Recognized URI schemes:"
97− , P2.text ""
98− , P2.text " cabal://pkgname-pkgversion download the specified package from Hackage"
99− , P2.text " cabal://pkgname download latest version of this package from Hackage"
100− , P2.text " file:///local/path load the Cabal file from the local disk"
101− , P2.text " /local/path abbreviated version of file URI"
102− , P2.text " <git/svn/bzr/hg URL> download the source from the specified repository"
103− , P2.text ""
104− , P2.fillSep (map P2.text (words ( "If the URI refers to a cabal file, information for building the package "
105− ++ "will be retrieved from that file, but hackage will be used as a source "
106− ++ "for the derivation. Otherwise, the supplied URI will be used to as the "
107− ++ "souce for the derivation and the information is taken from the cabal file "
108− ++ "at the root of the downloaded source."
109− )))
110− ]))
111− )
1+import qualified Cabal2nix as C
1122
1133main :: IO ()
114−main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() -> do
115− Options {..} <- execParser pinfo
116−
117− pkg <- getPackage optHackageDb $ Source optUrl (fromMaybe "" optRevision) (maybe UnknownHash Guess optSha256) (fromMaybe "" optSubpath)
118−
119− let
120− deriv :: Derivation
121− deriv = fromGenericPackageDescription (const True)
122− (\i -> Just (binding # (i, path # [i])))
123− optSystem
124− (unknownCompilerInfo optCompiler NoAbiTag)
125− (readFlagList optFlags)
126− []
127− (pkgCabal pkg)
128− & src .~ pkgSource pkg
129− & subpath .~ (fromMaybe "." optSubpath)
130− & runHaddock .~ optHaddock
131− & jailbreak .~ optJailbreak
132− & hyperlinkSource .~ optHyperlinkSource
133− & enableLibraryProfiling .~ (fromMaybe False optEnableProfiling || optEnableLibraryProfiling)
134− & enableExecutableProfiling .~ (fromMaybe False optEnableProfiling || optEnableExecutableProfiling)
135− & metaSection.maintainers .~ Set.fromList (map (review ident) optMaintainer)
136−-- & metaSection.platforms .~ Set.fromList optPlatform
137− & doCheck &&~ optDoCheck
138− & extraFunctionArgs %~ Set.union (Set.fromList ("inherit stdenv":map (fromString . ("inherit " ++)) optExtraArgs))
139−
140− shell :: Doc
141− shell = vcat
142− [ text "{ nixpkgs ? import <nixpkgs> {}, compiler ? \"default\" }:"
143− , text ""
144− , text "let"
145− , text ""
146− , text " inherit (nixpkgs) pkgs;"
147− , text ""
148− , hcat [ text " f = ", pPrint deriv, semi ]
149− , text ""
150− , text " haskellPackages = if compiler == \"default\""
151− , text " then pkgs.haskellPackages"
152− , text " else pkgs.haskell.packages.${compiler};"
153− , text ""
154− , text " drv = haskellPackages.callPackage f {};"
155− , text ""
156− , text "in"
157− , text ""
158− , text " if pkgs.lib.inNixShell then drv.env else drv"
159− ]
160−
161− print (if optNixShellOutput then shell else pPrint deriv)
162−
163−readFlagList :: [String] -> FlagAssignment
164−readFlagList = map tagWithValue
165− where tagWithValue ('-':fname) = (FlagName (lowercase fname), False)
166− tagWithValue fname = (FlagName (lowercase fname), True)
4+main = C.main
package.yaml+1
@@ -109,6 +109,7 @@ library:
109109 - StandaloneDeriving
110110 - TemplateHaskell
111111 exposed-modules:
112+ - Cabal2nix
112113 - Distribution.Nixpkgs.Fetch
113114 - Distribution.Nixpkgs.Haskell
114115 - Distribution.Nixpkgs.Haskell.BuildInfo
src/Cabal2nix.hsnew file+174
@@ -0,0 +1,174 @@
1+{-# LANGUAGE RecordWildCards #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+
4+module Cabal2nix
5+ ( main, cabal2nix
6+ )
7+ where
8+
9+import Control.Exception ( bracket )
10+import Control.Lens
11+import Data.Maybe ( fromMaybe )
12+import Data.Monoid ( (<>) )
13+import qualified Data.Set as Set
14+import Data.String
15+import qualified Distribution.Compat.ReadP as P
16+import Distribution.Compiler
17+import Distribution.Nixpkgs.Fetch
18+import Distribution.Nixpkgs.Haskell
19+import Distribution.Nixpkgs.Haskell.FromCabal
20+import Distribution.Nixpkgs.Haskell.PackageSourceSpec
21+import Distribution.Nixpkgs.Meta
22+import Distribution.PackageDescription ( FlagName(..), FlagAssignment )
23+import Distribution.Simple.Utils ( lowercase )
24+import Distribution.System
25+import Distribution.Text
26+import Language.Nix
27+import Options.Applicative
28+import Paths_cabal2nix ( version )
29+import System.Environment ( getArgs )
30+import System.IO ( hFlush, stdout, stderr )
31+import qualified Text.PrettyPrint.ANSI.Leijen as P2 hiding ( (<$>), (<>) )
32+import Text.PrettyPrint.HughesPJClass ( Doc, Pretty(..), text, vcat, hcat, semi )
33+
34+data Options = Options
35+ { optSha256 :: Maybe String
36+ , optMaintainer :: [String]
37+--, optPlatform :: [String] -- TODO: fix command line handling of platforms
38+ , optHaddock :: Bool
39+ , optDoCheck :: Bool
40+ , optJailbreak :: Bool
41+ , optRevision :: Maybe String
42+ , optHyperlinkSource :: Bool
43+ , optEnableLibraryProfiling :: Bool
44+ , optEnableExecutableProfiling :: Bool
45+ , optEnableProfiling :: Maybe Bool
46+ , optExtraArgs :: [String]
47+ , optHackageDb :: Maybe FilePath
48+ , optNixShellOutput :: Bool
49+ , optFlags :: [String]
50+ , optCompiler :: CompilerId
51+ , optSystem :: Platform
52+ , optSubpath :: Maybe FilePath
53+ , optUrl :: String
54+ }
55+ deriving (Show)
56+
57+options :: Parser Options
58+options = Options
59+ <$> optional (strOption $ long "sha256" <> metavar "HASH" <> help "sha256 hash of source tarball")
60+ <*> many (strOption $ long "maintainer" <> metavar "MAINTAINER" <> help "maintainer of this package (may be specified multiple times)")
61+-- <*> many (strOption $ long "platform" <> metavar "PLATFORM" <> help "supported build platforms (may be specified multiple times)")
62+ <*> flag True False (long "no-haddock" <> help "don't run Haddock when building this package")
63+ <*> flag True False (long "no-check" <> help "don't run regression test suites of this package")
64+ <*> switch (long "jailbreak" <> help "disregard version restrictions on build inputs")
65+ <*> optional (strOption $ long "revision" <> help "revision to use when fetching from VCS")
66+ <*> flag True False (long "no-hyperlink-source" <> help "don't generate pretty-printed source code for the documentation")
67+ <*> switch (long "enable-library-profiling" <> help "enable library profiling in the generated build")
68+ <*> switch (long "enable-executable-profiling" <> help "enable executable profiling in the generated build")
69+ <*> optional (switch (long "enable-profiling" <> help "enable both library and executable profiling in the generated build"))
70+ <*> many (strOption $ long "extra-arguments" <> help "extra parameters required for the function body")
71+ <*> optional (strOption $ long "hackage-db" <> metavar "PATH" <> help "path to the local hackage db in tar format")
72+ <*> switch (long "shell" <> help "generate output suitable for nix-shell")
73+ <*> many (strOption $ short 'f' <> long "flag" <> help "Cabal flag (may be specified multiple times)")
74+ <*> option (readP parse) (long "compiler" <> help "compiler to use when evaluating the Cabal file" <> value buildCompilerId <> showDefaultWith display)
75+ <*> option (readP parsePlatform) (long "system" <> help "target system to use when evaluating the Cabal file" <> value buildPlatform <> showDefaultWith display)
76+ <*> optional (strOption $ long "subpath" <> metavar "PATH" <> help "Path to Cabal file's directory relative to the URI (default is root directory)")
77+ <*> strArgument (metavar "URI")
78+
79+readP :: P.ReadP a a -> ReadM a
80+readP p = eitherReader $ \s -> case [ r' | (r',"") <- P.readP_to_S p s ] of
81+ (r:_) -> Right r
82+ _ -> Left ("invalid value " ++ show s)
83+
84+parsePlatform :: P.ReadP r Platform
85+parsePlatform = do arch <- P.choice [P.string "i686" >> return I386, P.string "x86_64" >> return X86_64]
86+ _ <- P.char '-'
87+ os <- P.choice [P.string "linux" >> return Linux, P.string "darwin" >> return OSX]
88+ return (Platform arch os)
89+
90+pinfo :: ParserInfo Options
91+pinfo = info
92+ ( helper
93+ <*> infoOption ("cabal2nix " ++ display version) (long "version" <> help "Show version number")
94+ <*> options
95+ )
96+ ( fullDesc
97+ <> header "cabal2nix converts Cabal files into build instructions for Nix."
98+ <> progDescDoc (Just (P2.vcat
99+ [ P2.text ""
100+ , P2.text "Recognized URI schemes:"
101+ , P2.text ""
102+ , P2.text " cabal://pkgname-pkgversion download the specified package from Hackage"
103+ , P2.text " cabal://pkgname download latest version of this package from Hackage"
104+ , P2.text " file:///local/path load the Cabal file from the local disk"
105+ , P2.text " /local/path abbreviated version of file URI"
106+ , P2.text " <git/svn/bzr/hg URL> download the source from the specified repository"
107+ , P2.text ""
108+ , P2.fillSep (map P2.text (words ( "If the URI refers to a cabal file, information for building the package "
109+ ++ "will be retrieved from that file, but hackage will be used as a source "
110+ ++ "for the derivation. Otherwise, the supplied URI will be used to as the "
111+ ++ "souce for the derivation and the information is taken from the cabal file "
112+ ++ "at the root of the downloaded source."
113+ )))
114+ ]))
115+ )
116+
117+main :: IO ()
118+main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() ->
119+ cabal2nix =<< getArgs
120+
121+cabal2nix :: [String] -> IO ()
122+cabal2nix args = do
123+ Options {..} <- handleParseResult $ execParserPure defaultPrefs pinfo args
124+
125+ pkg <- getPackage optHackageDb $ Source optUrl (fromMaybe "" optRevision) (maybe UnknownHash Guess optSha256) (fromMaybe "" optSubpath)
126+
127+ let
128+ deriv :: Derivation
129+ deriv = fromGenericPackageDescription (const True)
130+ (\i -> Just (binding # (i, path # [i])))
131+ optSystem
132+ (unknownCompilerInfo optCompiler NoAbiTag)
133+ (readFlagList optFlags)
134+ []
135+ (pkgCabal pkg)
136+ & src .~ pkgSource pkg
137+ & subpath .~ (fromMaybe "." optSubpath)
138+ & runHaddock .~ optHaddock
139+ & jailbreak .~ optJailbreak
140+ & hyperlinkSource .~ optHyperlinkSource
141+ & enableLibraryProfiling .~ (fromMaybe False optEnableProfiling || optEnableLibraryProfiling)
142+ & enableExecutableProfiling .~ (fromMaybe False optEnableProfiling || optEnableExecutableProfiling)
143+ & metaSection.maintainers .~ Set.fromList (map (review ident) optMaintainer)
144+-- & metaSection.platforms .~ Set.fromList optPlatform
145+ & doCheck &&~ optDoCheck
146+ & extraFunctionArgs %~ Set.union (Set.fromList ("inherit stdenv":map (fromString . ("inherit " ++)) optExtraArgs))
147+
148+ shell :: Doc
149+ shell = vcat
150+ [ text "{ nixpkgs ? import <nixpkgs> {}, compiler ? \"default\" }:"
151+ , text ""
152+ , text "let"
153+ , text ""
154+ , text " inherit (nixpkgs) pkgs;"
155+ , text ""
156+ , hcat [ text " f = ", pPrint deriv, semi ]
157+ , text ""
158+ , text " haskellPackages = if compiler == \"default\""
159+ , text " then pkgs.haskellPackages"
160+ , text " else pkgs.haskell.packages.${compiler};"
161+ , text ""
162+ , text " drv = haskellPackages.callPackage f {};"
163+ , text ""
164+ , text "in"
165+ , text ""
166+ , text " if pkgs.lib.inNixShell then drv.env else drv"
167+ ]
168+
169+ print (if optNixShellOutput then shell else pPrint deriv)
170+
171+readFlagList :: [String] -> FlagAssignment
172+readFlagList = map tagWithValue
173+ where tagWithValue ('-':fname) = (FlagName (lowercase fname), False)
174+ tagWithValue fname = (FlagName (lowercase fname), True)