Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: NixOS/cabal2nix
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 06144c353a98
Choose a base ref
...
head repository: NixOS/cabal2nix
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2e95bda8e21a
Choose a head ref
  • 1 commit
  • 1 file changed
  • 1 contributor

Commits on Mar 6, 2019

  1. cabal2nix: greatly simplify the CLI

     - No more support for hpack.
     - No more support for fetching files remotely.
     - No more options for modifying the generated output.
     - No more fancy features related to git, darcs, hg, or whatever.
    peti committed Mar 6, 2019
    Copy the full SHA
    2e95bda View commit details
Showing with 30 additions and 155 deletions.
  1. +30 −155 src/Cabal2nix.hs
185 changes: 30 additions & 155 deletions src/Cabal2nix.hs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
{-# LANGUAGE RecordWildCards #-}

module Cabal2nix
( main, cabal2nix, cabal2nix', cabal2nixWithDB, parseArgs
( main, cabal2nix, cabal2nix', parseArgs
, Options(..)
)
where
@@ -13,102 +13,44 @@ import Control.Lens
import Control.Monad
import Data.List
import Data.List.Split
import Data.Maybe ( fromMaybe, isJust, listToMaybe )
import Data.Maybe ( fromMaybe, listToMaybe )
import Data.Monoid ( (<>) )
import qualified Data.Set as Set
import Data.String
import Data.Time
import qualified Distribution.Compat.ReadP as P
import Distribution.Compiler
import Distribution.Nixpkgs.Fetch
import Distribution.Nixpkgs.Haskell
import Distribution.Nixpkgs.Haskell.FromCabal
import Distribution.Nixpkgs.Haskell.FromCabal.Flags
import qualified Distribution.Nixpkgs.Haskell.FromCabal.PostProcess as PP (pkg)
import qualified Distribution.Nixpkgs.Haskell.Hackage as DB
import Distribution.Nixpkgs.Haskell.PackageSourceSpec
import Distribution.Nixpkgs.Meta
import Distribution.PackageDescription ( mkFlagName, mkFlagAssignment, FlagAssignment )
import Distribution.Package ( packageId )
import Distribution.PackageDescription hiding ( options )
import Distribution.PackageDescription.Parsec
import Distribution.Simple.Utils ( lowercase )
import Distribution.System
import Distribution.Text
import Distribution.Verbosity
import Language.Nix
import Options.Applicative
import Paths_cabal2nix ( version )
import System.Environment ( getArgs )
import System.IO ( hFlush, hPutStrLn, stdout, stderr )
import qualified Text.PrettyPrint.ANSI.Leijen as P2
import Text.PrettyPrint.HughesPJClass ( Doc, Pretty(..), text, vcat, hcat, semi, render, prettyShow )
import System.IO ( hFlush, stdout, stderr )
import Text.PrettyPrint.HughesPJClass ( prettyShow )

data Options = Options
{ optSha256 :: Maybe String
, optMaintainer :: [String]
--, optPlatform :: [String] -- TODO: fix command line handling of platforms
, optHaddock :: Bool
, optHpack :: HpackUse
, optDoCheck :: Bool
, optJailbreak :: Bool
, optDoBenchmark :: Bool
, optRevision :: Maybe String
, optHyperlinkSource :: Bool
, optEnableLibraryProfiling :: Bool
, optEnableExecutableProfiling :: Bool
, optEnableProfiling :: Maybe Bool
, optExtraArgs :: [String]
, optHackageDb :: Maybe FilePath
, optNixShellOutput :: Bool
, optFlags :: [String]
, optCompiler :: CompilerId
, optSystem :: Platform
, optSubpath :: Maybe FilePath
, optHackageSnapshot :: Maybe UTCTime
, optNixpkgsIdentifier :: NixpkgsResolver
, optUrl :: String
, optFetchSubmodules :: Bool
, optUrl :: FilePath
}

options :: Parser Options
options = Options
<$> optional (strOption $ long "sha256" <> metavar "HASH" <> help "sha256 hash of source tarball")
<*> many (strOption $ long "maintainer" <> metavar "MAINTAINER" <> help "maintainer of this package (may be specified multiple times)")
-- <*> many (strOption $ long "platform" <> metavar "PLATFORM" <> help "supported build platforms (may be specified multiple times)")
<*> flag True False (long "no-haddock" <> help "don't run Haddock when building this package")
<*>
(
flag' ForceHpack (long "hpack" <> help "run hpack before configuring this package (only non-hackage packages)")
<|>
flag' NoHpack (long "no-hpack" <> help "disable hpack run and use only cabal disregarding package.yaml existence")
<|>
pure PackageYamlHpack
)
<*> flag True False (long "no-check" <> help "don't run regression test suites of this package")
<*> switch (long "jailbreak" <> help "disregard version restrictions on build inputs")
<*> switch (long "benchmark" <> help "enable benchmarks for this package")
<*> optional (strOption $ long "revision" <> help "revision to use when fetching from VCS")
<*> flag True False (long "no-hyperlink-source" <> help "don't generate pretty-printed source code for the documentation")
<*> switch (long "enable-library-profiling" <> help "enable library profiling in the generated build")
<*> switch (long "enable-executable-profiling" <> help "enable executable profiling in the generated build")
<*> optional (switch (long "enable-profiling" <> help "enable both library and executable profiling in the generated build"))
<*> many (strOption $ long "extra-arguments" <> help "extra parameters required for the function body")
<*> optional (strOption $ long "hackage-db" <> metavar "PATH" <> help "path to the local hackage db in tar format")
<*> switch (long "shell" <> help "generate output suitable for nix-shell")
<*> many (strOption $ short 'f' <> long "flag" <> help "Cabal flag (may be specified multiple times)")
<*> option (readP parse) (long "compiler" <> help "compiler to use when evaluating the Cabal file" <> value buildCompilerId <> showDefaultWith display)
<*> option (maybeReader parsePlatform) (long "system" <> help "host system (in either short Nix format or full LLVM style) to use when evaluating the Cabal file" <> value buildPlatform <> showDefaultWith display)
<*> optional (strOption $ long "subpath" <> metavar "PATH" <> help "Path to Cabal file's directory relative to the URI (default is root directory)")
<*> optional (option utcTimeReader (long "hackage-snapshot" <> help "hackage snapshot time, ISO format"))
<*> pure (\i -> Just (binding # (i, path # [ident # "pkgs", i])))
<*> strArgument (metavar "URI")
<*> flag True False (long "dont-fetch-submodules" <> help "do not fetch git submodules from git sources")

-- | A parser for the date. Hackage updates happen maybe once or twice a month.
-- Example: parseTime defaultTimeLocale "%FT%T%QZ" "2017-11-20T12:18:35Z" :: Maybe UTCTime
utcTimeReader :: ReadM UTCTime
utcTimeReader = eitherReader $ \arg ->
case parseTimeM True defaultTimeLocale "%FT%T%QZ" arg of
Nothing -> Left $ "Cannot parse date, ISO format used ('2017-11-20T12:18:35Z'): " ++ arg
Just utcTime -> Right utcTime
<*> strArgument (metavar "CABAL-FILE")

readP :: P.ReadP a a -> ReadM a
readP p = eitherReader $ \s -> case [ r' | (r',"") <- P.readP_to_S p s ] of
@@ -168,104 +110,37 @@ pinfo = info
<*> options
)
( fullDesc
<> header "cabal2nix converts Cabal files into build instructions for Nix."
<> progDescDoc (Just (P2.vcat
[ P2.text ""
, P2.text "Recognized URI schemes:"
, P2.text ""
, P2.text " cabal://pkgname-pkgversion download the specified package from Hackage"
, P2.text " cabal://pkgname download latest version of this package from Hackage"
, P2.text " file:///local/path load the Cabal file from the local disk"
, P2.text " /local/path abbreviated version of file URI"
, P2.text " <git/svn/bzr/hg URL> download the source from the specified repository"
, P2.text ""
, P2.fillSep (map P2.text (words ( "If the URI refers to a cabal file, information for building the package "
++ "will be retrieved from that file, but hackage will be used as a source "
++ "for the derivation. Otherwise, the supplied URI will be used to as the "
++ "source for the derivation and the information is taken from the cabal file "
++ "at the root of the downloaded source."
)))
]))
<> header "cabal2nix converts a Cabal file into build instructions for Nix."
)

main :: IO ()
main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() ->
cabal2nix =<< getArgs

hpackOverrides :: Derivation -> Derivation
hpackOverrides = over phaseOverrides (++ "preConfigure = \"hpack\";")
. set (libraryDepends . tool . contains (PP.pkg "hpack")) True

cabal2nix' :: Options -> IO (Either Doc Derivation)
cabal2nix' :: Options -> IO Derivation
cabal2nix' opts@Options{..} = do
pkg <- getPackage optHpack optFetchSubmodules optHackageDb optHackageSnapshot $
Source optUrl (fromMaybe "" optRevision) (maybe UnknownHash Guess optSha256) (fromMaybe "" optSubpath)
processPackage opts pkg

cabal2nixWithDB :: DB.HackageDB -> Options -> IO (Either Doc Derivation)
cabal2nixWithDB db opts@Options{..} = do
when (isJust optHackageDb) $ hPutStrLn stderr "WARN: HackageDB provided directly; ignoring --hackage-db"
when (isJust optHackageSnapshot) $ hPutStrLn stderr "WARN: HackageDB provided directly; ignoring --hackage-snapshot"
pkg <- getPackage' optHpack optFetchSubmodules (return db) $
Source optUrl (fromMaybe "" optRevision) (maybe UnknownHash Guess optSha256) (fromMaybe "" optSubpath)
processPackage opts pkg

processPackage :: Options -> Package -> IO (Either Doc Derivation)
processPackage Options{..} pkg = do
let
withHpackOverrides :: Derivation -> Derivation
withHpackOverrides = if pkgRanHpack pkg then hpackOverrides else id
gpd <- readGenericPackageDescription normal optUrl
return $ processPackage opts (Package undefined undefined gpd)

flags :: FlagAssignment
flags = configureCabalFlags (packageId (pkgCabal pkg)) `mappend` readFlagList optFlags

deriv :: Derivation
deriv = withHpackOverrides $ fromGenericPackageDescription (const True)
optNixpkgsIdentifier
optSystem
(unknownCompilerInfo optCompiler NoAbiTag)
flags
[]
(pkgCabal pkg)
& src .~ pkgSource pkg
& subpath .~ fromMaybe "." optSubpath
& runHaddock %~ (optHaddock &&)
& jailbreak .~ optJailbreak
& hyperlinkSource .~ optHyperlinkSource
& enableLibraryProfiling .~ (fromMaybe False optEnableProfiling || optEnableLibraryProfiling)
& enableExecutableProfiling .~ (fromMaybe False optEnableProfiling || optEnableExecutableProfiling)
& metaSection.maintainers .~ Set.fromList (map (review ident) optMaintainer)
-- & metaSection.platforms .~ Set.fromList optPlatform
& doCheck &&~ optDoCheck
& doBenchmark ||~ optDoBenchmark
& extraFunctionArgs %~ Set.union (Set.fromList ("inherit stdenv":map (fromString . ("inherit " ++)) optExtraArgs))

shell :: Doc
shell = vcat
[ text "{ nixpkgs ? import <nixpkgs> {}, compiler ? \"default\", doBenchmark ? false }:"
, text ""
, text "let"
, text ""
, text " inherit (nixpkgs) pkgs;"
, text ""
, hcat [ text " f = ", pPrint deriv, semi ]
, text ""
, text " haskellPackages = if compiler == \"default\""
, text " then pkgs.haskellPackages"
, text " else pkgs.haskell.packages.${compiler};"
, text ""
, text " variant = if doBenchmark then pkgs.haskell.lib.doBenchmark else pkgs.lib.id;"
, text ""
, text " drv = variant (haskellPackages.callPackage f {});"
, text ""
, text "in"
, text ""
, text " if pkgs.lib.inNixShell then drv.env else drv"
]
pure $ if optNixShellOutput then Left shell else Right deriv
processPackage :: Options -> Package -> Derivation
processPackage Options{..} (Package srcSpec _ gpd) = deriv
where
flags :: FlagAssignment
flags = configureCabalFlags (packageId gpd) `mappend` readFlagList optFlags

deriv :: Derivation
deriv = fromGenericPackageDescription (const True)
(\i -> Just (binding # (i, path # [ident # "pkgs", i])))
optSystem
(unknownCompilerInfo optCompiler NoAbiTag)
flags
[]
gpd
& src .~ DerivationSource "url" "mirror://hackage/" "" "" Nothing
& extraFunctionArgs . contains "inherit stdenv" .~ True

cabal2nix :: [String] -> IO ()
cabal2nix = parseArgs >=> cabal2nix' >=> putStrLn . either render prettyShow
cabal2nix = parseArgs >=> cabal2nix' >=> putStrLn . prettyShow

parseArgs :: [String] -> IO Options
parseArgs = handleParseResult . execParserPure defaultPrefs pinfo