From ba0dc19d2dc4d1d3f7ad9757a3365edec06677a5 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Tue, 8 May 2018 16:40:56 -0700 Subject: [PATCH] Work from mightybyte: Minimize deps required, for building with GHCJS --- default.nix | 59 ++++++++------- hnix.cabal | 92 +++++++++++++++------- main/Main.hs | 1 + package.yaml | 101 +++++++++++++++++-------- src/Nix/Atoms.hs | 9 ++- src/Nix/Builtins.hs | 58 +++++++++++--- src/Nix/Cache.hs | 15 +++- src/Nix/Exec.hs | 4 + src/Nix/Expr/Types.hs | 129 +++++++++++++++++++++++++++---- src/Nix/Expr/Types/Annotated.hs | 39 +++++++++- src/Nix/Options.hs | 125 ------------------------------ src/Nix/Options/Parser.hs | 130 ++++++++++++++++++++++++++++++++ src/Nix/Render/Frame.hs | 7 ++ tests/NixLanguageTests.hs | 1 + 14 files changed, 524 insertions(+), 246 deletions(-) create mode 100644 src/Nix/Options/Parser.hs diff --git a/default.nix b/default.nix index dc283fe..abdc904 100644 --- a/default.nix +++ b/default.nix @@ -5,43 +5,49 @@ , doStrict ? false , rev ? "255a833e841628c0b834575664eae373e28cdc27" , sha256 ? "022xm1pf4fpjjy69g7qz6rpqnwpjcy1l0vj49m8xmgn553cs42ch" -# , nixpkgs ? import ((import {}).fetchFromGitHub { -# owner = "NixOS"; repo = "nixpkgs"; inherit rev sha256; }) { , nixpkgs ? import (builtins.fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz"; inherit sha256; }) { config.allowUnfree = true; config.allowBroken = false; } +# , nixpkgs ? import ((import {}).fetchFromGitHub { +# owner = "NixOS"; repo = "nixpkgs"; inherit rev sha256; }) { }: let inherit (nixpkgs) pkgs; haskellPackages = pkgs.haskell.packages.${compiler}.override { - overrides = with pkgs.haskell.lib; self: super: rec { - serialise = dontCheck super.serialise; + overrides = with pkgs.haskell.lib; self: super: + if compiler == "ghcjs" then {} else + { + cryptohash-md5 = doJailbreak super.cryptohash-md5; + cryptohash-sha1 = doJailbreak super.cryptohash-sha1; + cryptohash-sha256 = doJailbreak super.cryptohash-sha256; + cryptohash-sha512 = doJailbreak super.cryptohash-sha512; + serialise = dontCheck super.serialise; - compact = - if compiler == "ghc842" - then doJailbreak super.compact - else super.compact; + compact = + if compiler == "ghc842" + then doJailbreak super.compact + else super.compact; - ghc-datasize = - if doProfiling - then null - else pkgs.haskell.lib.overrideCabal super.ghc-datasize (attrs: { - enableLibraryProfiling = false; - enableExecutableProfiling = false; - }); + ghc-datasize = + if doProfiling + then null + else pkgs.haskell.lib.overrideCabal super.ghc-datasize (attrs: { + enableLibraryProfiling = false; + enableExecutableProfiling = false; + }); - ghc-heap-view = - if doProfiling - then null - else pkgs.haskell.lib.overrideCabal super.ghc-heap-view (attrs: { - enableLibraryProfiling = false; - enableExecutableProfiling = false; - }); - }; + ghc-heap-view = + if doProfiling + then null + else pkgs.haskell.lib.overrideCabal super.ghc-heap-view (attrs: { + enableLibraryProfiling = false; + enableExecutableProfiling = false; + }); + }; }; in haskellPackages.developPackage { @@ -52,11 +58,8 @@ in haskellPackages.developPackage { modifier = drv: pkgs.haskell.lib.overrideCabal drv (attrs: { testHaskellDepends = attrs.testHaskellDepends ++ - [ - pkgs.nix - haskellPackages.hpack - # haskellPackages.cabal-install - ]; + [ pkgs.nix + haskellPackages.hpack ]; enableLibraryProfiling = doProfiling; enableExecutableProfiling = doProfiling; diff --git a/hnix.cabal b/hnix.cabal index da54748..f5a9e7b 100644 --- a/hnix.cabal +++ b/hnix.cabal @@ -2,7 +2,7 @@ -- -- see: https://github.com/sol/hpack -- --- hash: 5dd7aaae46b28fedd3791ab641dc5093bf7e2bca549578aea96ec23ed791ed22 +-- hash: 98a178891c03317cef1e5f7a984b3d7156bb38b0ca183bea91bab9acdaff4fb3 name: hnix version: 0.5.0 @@ -85,20 +85,16 @@ library , ansi-wl-pprint , array >=0.4 && <0.6 , base >=4.9 && <5 - , base16-bytestring , binary , bytestring - , compact , containers - , cryptohash , data-fix - , deepseq + , deepseq >=1.4.2 && <1.5 , deriving-compat >=0.3 && <0.5 , directory , exceptions , filepath - , hashable - , haskeline + , hashing , http-client , http-client-tls , http-types @@ -106,17 +102,15 @@ library , lens-family-core , lens-family-th , logict - , megaparsec + , megaparsec >=6.0 && <6.6 , monadlist , mtl , optparse-applicative - , pretty-show , process , regex-tdfa , regex-tdfa-text , scientific , semigroups >=0.18 && <0.19 - , serialise , split , syb , template-haskell @@ -128,11 +122,24 @@ library , unordered-containers >=0.2.9 && <0.3 , vector , xml - if flag(tracing) - cpp-options: -DENABLE_TRACING=1 - if impl(ghc < 8.4.0) && !flag(profiling) + if !impl(ghcjs) build-depends: - ghc-datasize + base16-bytestring + , cryptohash-md5 + , cryptohash-sha1 + , cryptohash-sha256 + , cryptohash-sha512 + , serialise + if impl(ghcjs) + build-depends: + hashable >=1.2.4 && <1.3 + else + exposed-modules: + Nix.Options.Parser + build-depends: + hashable >=1.2.5 && <1.3 + , haskeline + , pretty-show default-language: Haskell2010 executable hnix @@ -148,26 +155,35 @@ executable hnix , ansi-wl-pprint , base >=4.9 && <5 , bytestring - , compact , containers , data-fix - , deepseq + , deepseq >=1.4.2 && <1.5 , exceptions , filepath + , hashing , haskeline , hnix , mtl , optparse-applicative , pretty-show , repline - , serialise , template-haskell , text , time , transformers , unordered-containers >=0.2.9 && <0.3 - if flag(tracing) - cpp-options: -DENABLE_TRACING=1 + if !impl(ghcjs) + build-depends: + base16-bytestring + , cryptohash-md5 + , cryptohash-sha1 + , cryptohash-sha256 + , cryptohash-sha512 + , serialise + if impl(ghcjs) + buildable: False + else + buildable: True default-language: Haskell2010 test-suite hnix-tests @@ -191,14 +207,14 @@ test-suite hnix-tests , ansi-wl-pprint , base >=4.9 && <5 , bytestring - , compact , containers , data-fix - , deepseq + , deepseq >=1.4.2 && <1.5 , directory , exceptions , filepath , generic-random + , hashing , hnix , interpolate , megaparsec @@ -207,7 +223,6 @@ test-suite hnix-tests , pretty-show , process , quickcheck-instances - , serialise , split , tasty , tasty-hunit @@ -219,8 +234,18 @@ test-suite hnix-tests , transformers , unix , unordered-containers >=0.2.9 && <0.3 - if flag(tracing) - cpp-options: -DENABLE_TRACING=1 + if !impl(ghcjs) + build-depends: + base16-bytestring + , cryptohash-md5 + , cryptohash-sha1 + , cryptohash-sha256 + , cryptohash-sha512 + , serialise + if impl(ghcjs) + buildable: False + else + buildable: True default-language: Haskell2010 benchmark hnix-benchmarks @@ -236,22 +261,31 @@ benchmark hnix-benchmarks ansi-wl-pprint , base >=4.9 && <5 , bytestring - , compact , containers , criterion , data-fix - , deepseq + , deepseq >=1.4.2 && <1.5 , exceptions , filepath + , hashing , hnix , mtl , optparse-applicative - , serialise , template-haskell , text , time , transformers , unordered-containers >=0.2.9 && <0.3 - if flag(tracing) - cpp-options: -DENABLE_TRACING=1 + if !impl(ghcjs) + build-depends: + base16-bytestring + , cryptohash-md5 + , cryptohash-sha1 + , cryptohash-sha256 + , cryptohash-sha512 + , serialise + if impl(ghcjs) + buildable: False + else + buildable: True default-language: Haskell2010 diff --git a/main/Main.hs b/main/Main.hs index e7211a4..3502e87 100644 --- a/main/Main.hs +++ b/main/Main.hs @@ -28,6 +28,7 @@ import Nix import Nix.Convert import qualified Nix.Eval as Eval -- import Nix.Lint +import Nix.Options.Parser import qualified Nix.Type.Env as Env import qualified Nix.Type.Infer as HM import Nix.Utils diff --git a/package.yaml b/package.yaml index 381050b..1cfdf40 100644 --- a/package.yaml +++ b/package.yaml @@ -13,28 +13,6 @@ description: extra-source-files: - README.md -dependencies: - - base >= 4.9 && < 5 - - ansi-wl-pprint - - bytestring - - compact - - containers - - data-fix - - deepseq - - exceptions - - filepath - - mtl - - optparse-applicative - - serialise - - template-haskell - - text - - time - - transformers - - unordered-containers >= 0.2.9 && < 0.3 - -ghc-options: - - -Wall - flags: tracing: description: Enable full debug tracing @@ -51,6 +29,9 @@ flags: manual: True default: False +ghc-options: + - -Wall + when: - condition: flag(optimize) ghc-options: @@ -62,34 +43,62 @@ when: - condition: flag(tracing) cpp-options: -DENABLE_TRACING=1 +dependencies: + - base >= 4.9 && < 5 + - ansi-wl-pprint + - bytestring + - containers + - data-fix + - deepseq >= 1.4.2 && < 1.5 + - exceptions + - filepath + - hashing + - mtl + - optparse-applicative + - template-haskell + - text + - time + - transformers + - unordered-containers >= 0.2.9 && < 0.3 + +when: + - condition: "os(linux) && impl(ghc < 8.2)" + dependencies: + - compact + +when: + - condition: "!impl(ghcjs)" + dependencies: + - base16-bytestring + - cryptohash-md5 + - cryptohash-sha1 + - cryptohash-sha256 + - cryptohash-sha512 + - serialise + library: source-dirs: src dependencies: - aeson - ansi-wl-pprint - - array >= 0.4 && < 0.6 - - base16-bytestring + - array >= 0.4 && < 0.6 - binary - - cryptohash - - deriving-compat >= 0.3 && < 0.5 + - deriving-compat >= 0.3 && < 0.5 - directory - - hashable - http-types - http-client - http-client-tls - - haskeline - lens-family - lens-family-core - lens-family-th - logict - - megaparsec + - megaparsec >= 6.0 && < 6.6 - monadlist - - pretty-show - process - regex-tdfa - regex-tdfa-text - scientific - - semigroups >= 0.18 && < 0.19 + - semigroups >= 0.18 && < 0.19 - split - syb - these @@ -100,6 +109,18 @@ library: - condition: impl(ghc < 8.4.0) && !flag(profiling) dependencies: - ghc-datasize + when: + - condition: "impl(ghcjs)" + then: + dependencies: + - hashable >= 1.2.4 && < 1.3 + else: + exposed-modules: + - Nix.Options.Parser + dependencies: + - hashable >= 1.2.5 && < 1.3 + - haskeline + - pretty-show executables: hnix: @@ -111,6 +132,12 @@ executables: - pretty-show - repline - haskeline + when: + - condition: "impl(ghcjs)" + then: + buildable: false + else: + buildable: true tests: hnix-tests: @@ -135,6 +162,12 @@ tests: - megaparsec - tasty-quickcheck - pretty-show + when: + - condition: "impl(ghcjs)" + then: + buildable: false + else: + buildable: true benchmarks: hnix-benchmarks: @@ -143,3 +176,9 @@ benchmarks: dependencies: - hnix - criterion + when: + - condition: "impl(ghcjs)" + then: + buildable: false + else: + buildable: true diff --git a/src/Nix/Atoms.hs b/src/Nix/Atoms.hs index 801952d..ef190d1 100644 --- a/src/Nix/Atoms.hs +++ b/src/Nix/Atoms.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} @@ -5,7 +6,9 @@ module Nix.Atoms where +#if MIN_VERSION_serialise(0, 2, 0) import Codec.Serialise +#endif import Control.DeepSeq import Data.Data import Data.Hashable @@ -26,7 +29,11 @@ data NAtom -- | Null values. There's only one of this variant. | NNull deriving (Eq, Ord, Generic, Typeable, Data, Show, Read, NFData, - Serialise, Hashable) + Hashable) + +#if MIN_VERSION_serialise(0, 2, 0) +instance Serialise NAtom +#endif -- | Translate an atom into its nix representation. atomText :: NAtom -> Text diff --git a/src/Nix/Builtins.hs b/src/Nix/Builtins.hs index a6a483a..e601df6 100644 --- a/src/Nix/Builtins.hs +++ b/src/Nix/Builtins.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} @@ -7,6 +8,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PackageImports #-} {-# LANGUAGE PartialTypeSignatures #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} @@ -24,17 +26,29 @@ import Control.Monad import Control.Monad.Catch import Control.Monad.ListM (sortByM) import Control.Monad.Reader (asks) -import qualified Crypto.Hash.MD5 as MD5 -import qualified Crypto.Hash.SHA1 as SHA1 -import qualified Crypto.Hash.SHA256 as SHA256 -import qualified Crypto.Hash.SHA512 as SHA512 + +-- Using package imports here because there is a bug in cabal2nix that forces +-- us to put the hashing package in the unconditional dependency list. +-- See https://github.com/NixOS/cabal2nix/issues/348 for more info +#if MIN_VERSION_hashing(0, 1, 0) +import Crypto.Hash +import qualified "hashing" Crypto.Hash.MD5 as MD5 +import qualified "hashing" Crypto.Hash.SHA1 as SHA1 +import qualified "hashing" Crypto.Hash.SHA256 as SHA256 +import qualified "hashing" Crypto.Hash.SHA512 as SHA512 +#else +import Data.ByteString.Base16 as Base16 +import qualified "cryptohash-md5" Crypto.Hash.MD5 as MD5 +import qualified "cryptohash-sha1" Crypto.Hash.SHA1 as SHA1 +import qualified "cryptohash-sha256" Crypto.Hash.SHA256 as SHA256 +import qualified "cryptohash-sha512" Crypto.Hash.SHA512 as SHA512 +#endif import qualified Data.Aeson as A import qualified Data.Aeson.Encoding as A import Data.Align (alignWith) import Data.Array import Data.ByteString (ByteString) import qualified Data.ByteString as B -import Data.ByteString.Base16 as Base16 import qualified Data.ByteString.Lazy as LBS import Data.Char (isDigit) import Data.Coerce @@ -127,12 +141,15 @@ builtinsList = sequence [ , add0 Normal "currentSystem" currentSystem , add0 Normal "currentTime" currentTime_ , add2 Normal "deepSeq" deepSeq +#if MIN_VERSION_base(4, 10, 0) + -- TODO Remove this CPP after the Lift instance for NExpr works with GHC 8.0 , add0 TopLevel "derivation" $(do let f = "data/nix/corepkgs/derivation.nix" addDependentFile f Success expr <- runIO $ parseNixFile f [| cata Eval.eval expr |] ) +#endif , add TopLevel "derivationStrict" derivationStrict_ , add TopLevel "dirOf" dirOf , add2 Normal "div" div_ @@ -763,14 +780,33 @@ listToAttrs = fromValue @[NThunk m] >=> \l -> hashString :: MonadNix e m => Text -> Text -> Prim m Text hashString algo s = Prim $ do - hash <- case algo of - "md5" -> pure MD5.hash - "sha1" -> pure SHA1.hash - "sha256" -> pure SHA256.hash - "sha512" -> pure SHA512.hash + case algo of + "md5" -> pure $ +#if MIN_VERSION_hashing(0, 1, 0) + Text.pack $ show (hash (encodeUtf8 s) :: MD5.MD5) +#else + decodeUtf8 $ Base16.encode $ MD5.hash $ encodeUtf8 s +#endif + "sha1" -> pure $ +#if MIN_VERSION_hashing(0, 1, 0) + Text.pack $ show (hash (encodeUtf8 s) :: SHA1.SHA1) +#else + decodeUtf8 $ Base16.encode $ SHA1.hash $ encodeUtf8 s +#endif + "sha256" -> pure $ +#if MIN_VERSION_hashing(0, 1, 0) + Text.pack $ show (hash (encodeUtf8 s) :: SHA256.SHA256) +#else + decodeUtf8 $ Base16.encode $ SHA256.hash $ encodeUtf8 s +#endif + "sha512" -> pure $ +#if MIN_VERSION_hashing(0, 1, 0) + Text.pack $ show (hash (encodeUtf8 s) :: SHA512.SHA512) +#else + decodeUtf8 $ Base16.encode $ SHA512.hash $ encodeUtf8 s +#endif _ -> throwError $ ErrorCall $ "builtins.hashString: " ++ "expected \"md5\", \"sha1\", \"sha256\", or \"sha512\", got " ++ show algo - pure $ decodeUtf8 $ Base16.encode $ hash $ encodeUtf8 s placeHolder :: MonadNix e m => m (NValue m) -> m (NValue m) placeHolder = fromValue @Text >=> \_ -> do diff --git a/src/Nix/Cache.hs b/src/Nix/Cache.hs index 67e8d53..03a9348 100644 --- a/src/Nix/Cache.hs +++ b/src/Nix/Cache.hs @@ -5,29 +5,34 @@ module Nix.Cache where import qualified Data.ByteString.Lazy as BS import Nix.Expr.Types.Annotated -#ifdef __linux__ +#if defined (__linux__) && MIN_VERSION_base(4, 10, 0) #define USE_COMPACT 1 #endif #ifdef USE_COMPACT import qualified Data.Compact as C import qualified Data.Compact.Serialize as C -#else +#endif +#if MIN_VERSION_serialise(0, 2, 0) import qualified Codec.Serialise as S #endif readCache :: FilePath -> IO NExprLoc readCache path = do -#ifdef USE_COMPACT +#if USE_COMPACT eres <- C.unsafeReadCompact path case eres of Left err -> error $ "Error reading cache file: " ++ err Right expr -> return $ C.getCompact expr #else +#if MIN_VERSION_serialise(0, 2, 0) eres <- S.deserialiseOrFail <$> BS.readFile path case eres of Left err -> error $ "Error reading cache file: " ++ show err Right expr -> return expr +#else + error "readCache not implemented for this platform" +#endif #endif writeCache :: FilePath -> NExprLoc -> IO () @@ -35,5 +40,9 @@ writeCache path expr = #ifdef USE_COMPACT C.writeCompact path =<< C.compact expr #else +#if MIN_VERSION_serialise(0, 2, 0) BS.writeFile path (S.serialise expr) +#else + error "writeCache not implemented for this platform" +#endif #endif diff --git a/src/Nix/Exec.hs b/src/Nix/Exec.hs index bff099e..72f4d5f 100644 --- a/src/Nix/Exec.hs +++ b/src/Nix/Exec.hs @@ -66,7 +66,9 @@ import Nix.Scope import Nix.Thunk import Nix.Utils import Nix.Value +#if MIN_VERSION_haskeline(0, 4, 7) import System.Console.Haskeline.MonadException hiding (catch) +#endif import System.Directory import System.Environment import System.Exit (ExitCode (ExitSuccess)) @@ -464,10 +466,12 @@ instance MonadCatch m => MonadCatch (Lazy m) where instance MonadThrow m => MonadThrow (Lazy m) where throwM = Lazy . throwM +#if MIN_VERSION_haskeline(0, 4, 7) instance MonadException m => MonadException (Lazy m) where controlIO f = Lazy $ controlIO $ \(RunIO run) -> let run' = RunIO (fmap Lazy . run . runLazy) in runLazy <$> f run' +#endif instance (MonadFix m, MonadCatch m, MonadIO m, Alternative m, MonadPlus m, Typeable m) diff --git a/src/Nix/Expr/Types.hs b/src/Nix/Expr/Types.hs index 60f042a..e9ccf74 100644 --- a/src/Nix/Expr/Types.hs +++ b/src/Nix/Expr/Types.hs @@ -1,5 +1,6 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveFoldable #-} @@ -26,8 +27,10 @@ -- | The nix expression type and supporting types. module Nix.Expr.Types where +#if MIN_VERSION_serialise(0, 2, 0) import Codec.Serialise (Serialise) import qualified Codec.Serialise as Ser +#endif import Control.Applicative import Control.DeepSeq import Control.Monad @@ -40,7 +43,9 @@ import Data.Eq.Deriving import Data.Fix import Data.Functor.Classes import Data.Hashable +#if MIN_VERSION_hashable(1, 2, 5) import Data.Hashable.Lifted +#endif import Data.List (inits, tails) import Data.List.NonEmpty (NonEmpty(..)) import qualified Data.List.NonEmpty as NE @@ -60,12 +65,31 @@ import Nix.Utils import Text.Megaparsec.Pos import Text.Read.Deriving import Text.Show.Deriving +#if MIN_VERSION_base(4, 10, 0) import Type.Reflection (eqTypeRep) import qualified Type.Reflection as Reflection +#endif type VarName = Text -instance Hashable1 NonEmpty -- an unfortunate orphan +-- unfortunate orphans +#if MIN_VERSION_hashable(1, 2, 5) +instance Hashable1 NonEmpty +#endif + +#if !MIN_VERSION_base(4, 10, 0) +instance Eq1 NonEmpty where + liftEq eq (a NE.:| as) (b NE.:| bs) = eq a b && liftEq eq as bs +instance Show1 NonEmpty where + liftShowsPrec shwP shwL p (a NE.:| as) = showParen (p > 5) $ + shwP 6 a . showString " :| " . shwL as +#endif + +#if !MIN_VERSION_binary(0, 8, 4) +instance Binary a => Binary (NE.NonEmpty a) where + get = fmap NE.fromList Bin.get + put = Bin.put . NE.toList +#endif -- | The main nix expression type. This is polymorphic so that it can be made -- a functor, which allows us to traverse expressions and map functions over @@ -113,24 +137,40 @@ data NExprF r | NAssert !r !r -- ^ Assert that the first returns true before evaluating the second. deriving (Ord, Eq, Generic, Generic1, Typeable, Data, Functor, - Foldable, Traversable, Show, NFData, NFData1, Serialise, - Hashable, Hashable1) + Foldable, Traversable, Show, NFData, + Hashable) + +#if MIN_VERSION_hashable(1, 2, 5) +instance Hashable1 NExprF +#endif + +#if MIN_VERSION_deepseq(1, 4, 3) +instance NFData1 NExprF +#endif + +#if MIN_VERSION_serialise(0, 2, 0) +instance Serialise r => Serialise (NExprF r) +#endif -- | We make an `IsString` for expressions, where the string is interpreted -- as an identifier. This is the most common use-case... instance IsString NExpr where fromString = Fix . NSym . fromString +#if MIN_VERSION_base(4, 10, 0) instance Lift (Fix NExprF) where lift = dataToExpQ $ \b -> case Reflection.typeOf b `eqTypeRep` Reflection.typeRep @Text of Just HRefl -> Just [| pack $(liftString $ unpack b) |] Nothing -> Nothing +#endif -- | The monomorphic expression type is a fixed point of the polymorphic one. type NExpr = Fix NExprF +#if MIN_VERSION_serialise(0, 2, 0) instance Serialise NExpr +#endif -- | A single line of the bindings section of a let expression or of a set. data Binding r @@ -140,8 +180,20 @@ data Binding r -- ^ Using a name already in scope, such as @inherit x;@ which is shorthand -- for @x = x;@ or @inherit (x) y;@ which means @y = x.y;@. deriving (Generic, Generic1, Typeable, Data, Ord, Eq, Functor, - Foldable, Traversable, Show, NFData, NFData1, Serialise, - Hashable, Hashable1) + Foldable, Traversable, Show, NFData, + Hashable) + +#if MIN_VERSION_hashable(1, 2, 5) +instance Hashable1 Binding +#endif + +#if MIN_VERSION_deepseq(1, 4, 3) +instance NFData1 Binding +#endif + +#if MIN_VERSION_serialise(0, 2, 0) +instance Serialise r => Serialise (Binding r) +#endif -- | @Params@ represents all the ways the formal parameters to a -- function can be represented. @@ -153,8 +205,19 @@ data Params r -- bind to the set in the function body. The bool indicates whether it is -- variadic or not. deriving (Ord, Eq, Generic, Generic1, Typeable, Data, Functor, Show, - Foldable, Traversable, NFData, NFData1, Serialise, - Hashable, Hashable1) + Foldable, Traversable, NFData, Hashable) + +#if MIN_VERSION_hashable(1, 2, 5) +instance Hashable1 Params +#endif + +#if MIN_VERSION_deepseq(1, 4, 3) +instance NFData1 Params +#endif + +#if MIN_VERSION_serialise(0, 2, 0) +instance Serialise r => Serialise (Params r) +#endif -- This uses an association list because nix XML serialization preserves the -- order of the param set. @@ -167,8 +230,10 @@ instance IsString (Params r) where -- antiquoted (surrounded by ${...}) or plain (not antiquoted). data Antiquoted (v :: *) (r :: *) = Plain !v | EscapedNewline | Antiquoted !r deriving (Ord, Eq, Generic, Generic1, Typeable, Data, Functor, Foldable, - Traversable, Show, Read, NFData, NFData1, Serialise, - Hashable, Hashable1) + Traversable, Show, Read, NFData, Hashable) + +#if MIN_VERSION_hashable(1, 2, 5) +instance Hashable v => Hashable1 (Antiquoted v) instance Hashable2 Antiquoted where liftHashWithSalt2 ha _ salt (Plain a) = @@ -177,6 +242,15 @@ instance Hashable2 Antiquoted where salt `hashWithSalt` (1 :: Int) liftHashWithSalt2 _ hb salt (Antiquoted b) = hb (salt `hashWithSalt` (2 :: Int)) b +#endif + +#if MIN_VERSION_deepseq(1, 4, 3) +instance NFData v => NFData1 (Antiquoted v) +#endif + +#if MIN_VERSION_serialise(0, 2, 0) +instance (Serialise v, Serialise r) => Serialise (Antiquoted v r) +#endif -- | An 'NString' is a list of things that are either a plain string -- or an antiquoted expression. After the antiquotes have been evaluated, @@ -190,8 +264,19 @@ data NString r -- their indentation will be stripped, but the amount stripped is -- remembered. deriving (Eq, Ord, Generic, Generic1, Typeable, Data, Functor, Foldable, - Traversable, Show, Read, NFData, NFData1, Serialise, - Hashable, Hashable1) + Traversable, Show, Read, NFData, Hashable) + +#if MIN_VERSION_hashable(1, 2, 5) +instance Hashable1 NString +#endif + +#if MIN_VERSION_deepseq(1, 4, 3) +instance NFData1 NString +#endif + +#if MIN_VERSION_serialise(0, 2, 0) +instance Serialise r => Serialise (NString r) +#endif -- | For the the 'IsString' instance, we use a plain doublequoted string. instance IsString (NString r) where @@ -221,7 +306,10 @@ data NKeyName r = DynamicKey !(Antiquoted (NString r) r) | StaticKey !VarName !(Maybe SourcePos) deriving (Eq, Ord, Generic, Typeable, Data, Show, Read, NFData, - Serialise, Hashable) + Hashable) + +#if MIN_VERSION_serialise(0, 2, 0) +instance Serialise r => Serialise (NKeyName r) instance Serialise Pos where encode x = Ser.encode (unPos x) @@ -230,6 +318,7 @@ instance Serialise Pos where instance Serialise SourcePos where encode (SourcePos f l c) = Ser.encode f <> Ser.encode l <> Ser.encode c decode = SourcePos <$> Ser.decode <*> Ser.decode <*> Ser.decode +#endif instance Hashable Pos where hashWithSalt salt x = hashWithSalt salt (unPos x) @@ -243,11 +332,13 @@ instance Generic1 NKeyName where from1 = id to1 = id +#if MIN_VERSION_deepseq(1, 4, 3) instance NFData1 NKeyName where liftRnf _ (StaticKey !_ !_) = () liftRnf _ (DynamicKey (Plain !_)) = () liftRnf _ (DynamicKey EscapedNewline) = () liftRnf k (DynamicKey (Antiquoted r)) = k r +#endif -- | Most key names are just static text, so this instance is convenient. instance IsString (NKeyName r) where @@ -258,11 +349,13 @@ instance Eq1 NKeyName where liftEq _ (StaticKey a _) (StaticKey b _) = a == b liftEq _ _ _ = False +#if MIN_VERSION_hashable(1, 2, 5) instance Hashable1 NKeyName where liftHashWithSalt h salt (DynamicKey a) = liftHashWithSalt2 (liftHashWithSalt h) h (salt `hashWithSalt` (0 :: Int)) a liftHashWithSalt _ salt (StaticKey n p) = salt `hashWithSalt` (1 :: Int) `hashWithSalt` n `hashWithSalt` p +#endif -- Deriving this instance automatically is not possible because @r@ -- occurs not only as last argument in @Antiquoted (NString r) r@ @@ -297,7 +390,11 @@ type NAttrPath r = NonEmpty (NKeyName r) -- | There are two unary operations: logical not and integer negation. data NUnaryOp = NNeg | NNot deriving (Eq, Ord, Generic, Typeable, Data, Show, Read, NFData, - Serialise, Hashable) + Hashable) + +#if MIN_VERSION_serialise(0, 2, 0) +instance Serialise NUnaryOp +#endif -- | Binary operators expressible in the nix language. data NBinaryOp @@ -318,7 +415,11 @@ data NBinaryOp | NConcat -- ^ List concatenation (++) | NApp -- ^ Apply a function to an argument. deriving (Eq, Ord, Generic, Typeable, Data, Show, Read, NFData, - Serialise, Hashable) + Hashable) + +#if MIN_VERSION_serialise(0, 2, 0) +instance Serialise NBinaryOp +#endif -- | Get the name out of the parameter (there might be none). paramName :: Params r -> Maybe VarName diff --git a/src/Nix/Expr/Types/Annotated.hs b/src/Nix/Expr/Types/Annotated.hs index 0dcd033..c971c0f 100644 --- a/src/Nix/Expr/Types/Annotated.hs +++ b/src/Nix/Expr/Types/Annotated.hs @@ -1,4 +1,5 @@ -{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveFunctor #-} @@ -20,7 +21,9 @@ module Nix.Expr.Types.Annotated , SourcePos(..), unPos, mkPos )where +#if MIN_VERSION_serialise(0, 2, 0) import Codec.Serialise +#endif import Control.DeepSeq import Data.Aeson (ToJSON(..), FromJSON(..)) import Data.Aeson.TH @@ -31,7 +34,9 @@ import Data.Fix import Data.Function (on) import Data.Functor.Compose import Data.Hashable +#if MIN_VERSION_hashable(1, 2, 5) import Data.Hashable.Lifted +#endif import Data.Ord.Deriving import Data.Semigroup import Data.Text (Text, pack) @@ -48,9 +53,13 @@ data SrcSpan = SrcSpan { spanBegin :: SourcePos , spanEnd :: SourcePos } - deriving (Ord, Eq, Generic, Typeable, Data, Show, NFData, Serialise, + deriving (Ord, Eq, Generic, Typeable, Data, Show, NFData, Hashable) +#if MIN_VERSION_serialise(0, 2, 0) +instance Serialise SrcSpan +#endif + -- | A type constructor applied to a type along with an annotation -- -- Intended to be used with 'Fix': @@ -60,8 +69,19 @@ data Ann ann a = Ann , annotated :: a } deriving (Ord, Eq, Data, Generic, Generic1, Typeable, Functor, Foldable, - Traversable, Read, Show, NFData, NFData1, Serialise, - Hashable, Hashable1) + Traversable, Read, Show, NFData, Hashable) + +#if MIN_VERSION_hashable(1, 2, 5) +instance Hashable ann => Hashable1 (Ann ann) +#endif + +#if MIN_VERSION_serialise(0, 2, 0) +instance (Serialise ann, Serialise a) => Serialise (Ann ann a) +#endif + +#if MIN_VERSION_deepseq(1, 4, 3) +instance NFData ann => NFData1 (Ann ann) +#endif $(deriveEq1 ''Ann) $(deriveEq2 ''Ann) @@ -88,9 +108,18 @@ type NExprLocF = AnnF SrcSpan NExprF -- | A nix expression with source location at each subexpression. type NExprLoc = Fix NExprLocF +#if MIN_VERSION_hashable(1, 2, 5) && MIN_VERSION_deepseq(1, 4, 3) +-- Needs deepseq-1.4.3 because Compose requires NFData1 instance NFData NExprLoc +#endif + +#if MIN_VERSION_serialise(0, 2, 0) instance Serialise NExprLoc +#endif + +#if MIN_VERSION_hashable(1, 2, 5) instance Hashable NExprLoc +#endif instance Binary SrcSpan instance (Binary ann, Binary a) => Binary (Ann ann a) @@ -100,9 +129,11 @@ instance Binary NExprLoc instance ToJSON SrcSpan instance FromJSON SrcSpan +#if MIN_VERSION_serialise(0, 2, 0) instance Serialise r => Serialise (Compose (Ann SrcSpan) NExprF r) where encode (Compose (Ann ann a)) = encode ann <> encode a decode = (Compose .) . Ann <$> decode <*> decode +#endif pattern AnnE :: forall ann (g :: * -> *). ann -> g (Fix (Compose (Ann ann) g)) -> Fix (Compose (Ann ann) g) diff --git a/src/Nix/Options.hs b/src/Nix/Options.hs index 1ce1bba..5c72b2e 100644 --- a/src/Nix/Options.hs +++ b/src/Nix/Options.hs @@ -1,13 +1,7 @@ module Nix.Options where -import Control.Arrow (second) -import Data.Char (isDigit) -import Data.Maybe (fromMaybe) import Data.Text (Text) -import qualified Data.Text as Text import Data.Time -import Options.Applicative hiding (ParserResult(..)) -import Text.PrettyPrint.ANSI.Leijen hiding ((<$>)) data Options = Options { verbose :: Verbosity @@ -83,122 +77,3 @@ data Verbosity | DebugInfo | Vomit deriving (Eq, Ord, Enum, Bounded, Show) - -decodeVerbosity :: Int -> Verbosity -decodeVerbosity 0 = ErrorsOnly -decodeVerbosity 1 = Informational -decodeVerbosity 2 = Talkative -decodeVerbosity 3 = Chatty -decodeVerbosity 4 = DebugInfo -decodeVerbosity _ = Vomit - -argPair :: Mod OptionFields (Text, Text) -> Parser (Text, Text) -argPair = option $ str >>= \s -> - case Text.findIndex (== '=') s of - Nothing -> errorWithoutStackTrace - "Format of --arg/--argstr in hnix is: name=expr" - Just i -> return $ second Text.tail $ Text.splitAt i s - -nixOptions :: UTCTime -> Parser Options -nixOptions current = Options - <$> (fromMaybe ErrorsOnly <$> - optional - (option (do a <- str - if all isDigit a - then pure $ decodeVerbosity (read a) - else fail "Argument to -v/--verbose must be a number") - ( short 'v' - <> long "verbose" - <> help "Verbose output"))) - <*> switch - ( long "trace" - <> help "Enable tracing code (even more can be seen if built with --flags=tracing)") - <*> switch - ( long "thunks" - <> help "Enable reporting of thunk tracing as well as regular evaluation") - <*> switch - ( long "values" - <> help "Enable reporting of value provenance in error messages") - <*> optional (strOption - ( long "reduce" - <> help "When done evaluating, output the evaluated part of the expression to FILE")) - <*> switch - ( long "reduce-sets" - <> help "Reduce set members that aren't used; breaks if hasAttr is used") - <*> switch - ( long "reduce-lists" - <> help "Reduce list members that aren't used; breaks if elemAt is used") - <*> switch - ( long "parse" - <> help "Whether to parse the file (also the default right now)") - <*> switch - ( long "parse-only" - <> help "Whether to parse only, no pretty printing or checking") - <*> switch - ( long "find" - <> help "If selected, find paths within attr trees") - <*> optional (strOption - ( long "find-file" - <> help "Look up the given files in Nix's search path")) - <*> switch - ( long "strict" - <> help "When used with --eval, recursively evaluate list elements and attributes") - <*> switch - ( long "force" - <> help "Whether to force the results of evaluation to normal form") - <*> switch - ( long "eval" - <> help "Whether to evaluate, or just pretty-print") - <*> switch - ( long "json" - <> help "Print the resulting value as an JSON representation") - <*> switch - ( long "xml" - <> help "Print the resulting value as an XML representation") - <*> optional (strOption - ( short 'A' - <> long "attr" - <> help "Select an attribute from the top-level Nix expression being evaluated")) - <*> many (strOption - ( short 'I' - <> long "include" - <> help "Add a path to the Nix expression search path")) - <*> switch - ( long "check" - <> help "Whether to check for syntax errors after parsing") - <*> optional (strOption - ( long "read" - <> help "Read in an expression tree from a binary cache")) - <*> switch - ( long "cache" - <> help "Write out the parsed expression tree to a binary cache") - <*> switch - ( long "repl" - <> help "After performing any indicated actions, enter the REPL") - <*> switch - ( long "ignore-errors" - <> help "Continue parsing files, even if there are errors") - <*> optional (strOption - ( short 'E' - <> long "expr" - <> help "Expression to parse or evaluate")) - <*> many (argPair - ( long "arg" - <> help "Argument to pass to an evaluated lambda")) - <*> many (argPair - ( long "argstr" - <> help "Argument string to pass to an evaluated lambda")) - <*> optional (strOption - ( short 'f' - <> long "file" - <> help "Parse all of the files given in FILE; - means stdin")) - <*> option (parseTimeOrError True defaultTimeLocale "%Y/%m/%d %H:%M:%S" <$> str) - ( long "now" - <> value current - <> help "Set current time for testing purposes") - <*> many (strArgument (metavar "FILE" <> help "Path of file to parse")) - -nixOptionsInfo :: UTCTime -> ParserInfo Options -nixOptionsInfo current = - info (helper <*> nixOptions current) - (fullDesc <> progDesc "" <> header "hnix") diff --git a/src/Nix/Options/Parser.hs b/src/Nix/Options/Parser.hs new file mode 100644 index 0000000..bb6c2b1 --- /dev/null +++ b/src/Nix/Options/Parser.hs @@ -0,0 +1,130 @@ +module Nix.Options.Parser where + +import Control.Arrow (second) +import Data.Char (isDigit) +import Data.Maybe (fromMaybe) +import Data.Text (Text) +import qualified Data.Text as Text +import Data.Time +import Nix.Options +import Options.Applicative hiding (ParserResult(..)) +import Text.PrettyPrint.ANSI.Leijen hiding ((<$>)) + +decodeVerbosity :: Int -> Verbosity +decodeVerbosity 0 = ErrorsOnly +decodeVerbosity 1 = Informational +decodeVerbosity 2 = Talkative +decodeVerbosity 3 = Chatty +decodeVerbosity 4 = DebugInfo +decodeVerbosity _ = Vomit + +argPair :: Mod OptionFields (Text, Text) -> Parser (Text, Text) +argPair = option $ str >>= \s -> + case Text.findIndex (== '=') s of + Nothing -> errorWithoutStackTrace + "Format of --arg/--argstr in hnix is: name=expr" + Just i -> return $ second Text.tail $ Text.splitAt i s + +nixOptions :: UTCTime -> Parser Options +nixOptions current = Options + <$> (fromMaybe ErrorsOnly <$> + optional + (option (do a <- str + if all isDigit a + then pure $ decodeVerbosity (read a) + else fail "Argument to -v/--verbose must be a number") + ( short 'v' + <> long "verbose" + <> help "Verbose output"))) + <*> switch + ( long "trace" + <> help "Enable tracing code (even more can be seen if built with --flags=tracing)") + <*> switch + ( long "thunks" + <> help "Enable reporting of thunk tracing as well as regular evaluation") + <*> switch + ( long "values" + <> help "Enable reporting of value provenance in error messages") + <*> optional (strOption + ( long "reduce" + <> help "When done evaluating, output the evaluated part of the expression to FILE")) + <*> switch + ( long "reduce-sets" + <> help "Reduce set members that aren't used; breaks if hasAttr is used") + <*> switch + ( long "reduce-lists" + <> help "Reduce list members that aren't used; breaks if elemAt is used") + <*> switch + ( long "parse" + <> help "Whether to parse the file (also the default right now)") + <*> switch + ( long "parse-only" + <> help "Whether to parse only, no pretty printing or checking") + <*> switch + ( long "find" + <> help "If selected, find paths within attr trees") + <*> optional (strOption + ( long "find-file" + <> help "Look up the given files in Nix's search path")) + <*> switch + ( long "strict" + <> help "When used with --eval, recursively evaluate list elements and attributes") + <*> switch + ( long "force" + <> help "Whether to force the results of evaluation to normal form") + <*> switch + ( long "eval" + <> help "Whether to evaluate, or just pretty-print") + <*> switch + ( long "json" + <> help "Print the resulting value as an JSON representation") + <*> switch + ( long "xml" + <> help "Print the resulting value as an XML representation") + <*> optional (strOption + ( short 'A' + <> long "attr" + <> help "Select an attribute from the top-level Nix expression being evaluated")) + <*> many (strOption + ( short 'I' + <> long "include" + <> help "Add a path to the Nix expression search path")) + <*> switch + ( long "check" + <> help "Whether to check for syntax errors after parsing") + <*> optional (strOption + ( long "read" + <> help "Read in an expression tree from a binary cache")) + <*> switch + ( long "cache" + <> help "Write out the parsed expression tree to a binary cache") + <*> switch + ( long "repl" + <> help "After performing any indicated actions, enter the REPL") + <*> switch + ( long "ignore-errors" + <> help "Continue parsing files, even if there are errors") + <*> optional (strOption + ( short 'E' + <> long "expr" + <> help "Expression to parse or evaluate")) + <*> many (argPair + ( long "arg" + <> help "Argument to pass to an evaluated lambda")) + <*> many (argPair + ( long "argstr" + <> help "Argument string to pass to an evaluated lambda")) + <*> optional (strOption + ( short 'f' + <> long "file" + <> help "Parse all of the files given in FILE; - means stdin")) + <*> option (parseTimeOrError True defaultTimeLocale "%Y/%m/%d %H:%M:%S" <$> str) + ( long "now" + <> value current + <> help "Set current time for testing purposes") + <*> many (strArgument (metavar "FILE" <> help "Path of file to parse")) + +nixOptionsInfo :: UTCTime -> ParserInfo Options +nixOptionsInfo current = + info (helper <*> nixOptions current) + (fullDesc <> progDesc "" <> header "hnix") diff --git a/src/Nix/Render/Frame.hs b/src/Nix/Render/Frame.hs index 4f8fa2b..1a9c5bd 100644 --- a/src/Nix/Render/Frame.hs +++ b/src/Nix/Render/Frame.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} @@ -29,7 +30,9 @@ import Nix.Value import Text.Megaparsec.Pos import qualified Text.PrettyPrint.ANSI.Leijen as P import Text.PrettyPrint.ANSI.Leijen hiding ((<$>)) +#if MIN_VERSION_pretty_show(1, 6, 16) import qualified Text.Show.Pretty as PS +#endif renderFrames :: forall v e m. (MonadReader e m, Has e Options, @@ -111,7 +114,11 @@ renderExpr _level longLabel shortLabel e@(Fix (Compose (Ann _ x))) = do opts :: Options <- asks (view hasLens) let rendered | verbose opts >= DebugInfo = +#if MIN_VERSION_pretty_show(1, 6, 16) text (PS.ppShow (stripAnnotation e)) +#else + text (show (stripAnnotation e)) +#endif | verbose opts >= Chatty = prettyNix (stripAnnotation e) | otherwise = diff --git a/tests/NixLanguageTests.hs b/tests/NixLanguageTests.hs index 5728d92..8fa7827 100644 --- a/tests/NixLanguageTests.hs +++ b/tests/NixLanguageTests.hs @@ -19,6 +19,7 @@ import Data.Time import GHC.Exts import Nix.Lint import Nix.Options +import Nix.Options.Parser import Nix.Parser import Nix.Pretty import Nix.Utils