hnix/src/Nix/Exec.hs

970 lines
31 KiB
Haskell
Raw Normal View History

2018-04-09 09:52:10 +02:00
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ApplicativeDo #-}
2018-04-29 02:12:32 +02:00
{-# LANGUAGE CPP #-}
2018-04-09 09:52:10 +02:00
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveFunctor #-}
2018-04-09 09:52:10 +02:00
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2018-04-09 09:52:10 +02:00
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
2018-04-09 09:52:10 +02:00
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
2018-04-09 09:52:10 +02:00
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ViewPatterns #-}
2018-04-09 09:52:10 +02:00
{-# OPTIONS_GHC -Wno-missing-signatures #-}
2018-04-09 09:52:10 +02:00
{-# OPTIONS_GHC -Wno-orphans #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2018-04-09 09:52:10 +02:00
module Nix.Exec where
import Prelude hiding ( putStr
, putStrLn
, print
)
2018-11-16 21:28:55 +01:00
import Control.Applicative
2018-04-09 09:52:10 +02:00
import Control.Monad
import Control.Monad.Catch hiding ( catchJust )
import Control.Monad.Fix
import Control.Monad.Reader
import Control.Monad.Ref
import Control.Monad.State.Strict
import Control.Monad.Trans.Reader ( ReaderT(..) )
import Control.Monad.Trans.State.Strict
( StateT(..) )
import Data.Fix
import Data.HashMap.Lazy ( HashMap )
import qualified Data.HashMap.Lazy as M
import Data.List
import qualified Data.List.NonEmpty as NE
import Data.List.Split
import Data.Maybe ( maybeToList )
import Data.Text ( Text )
import qualified Data.Text as Text
2018-11-17 05:51:18 +01:00
import Data.Text.Prettyprint.Doc
import Data.Typeable
2018-04-09 09:52:10 +02:00
import Nix.Atoms
2019-03-15 06:00:23 +01:00
import Nix.Cited
import Nix.Context
import Nix.Convert
import Nix.Effects
import Nix.Eval as Eval
2018-04-09 09:52:10 +02:00
import Nix.Expr
import Nix.Frames
2018-04-09 09:52:10 +02:00
import Nix.Normal
import Nix.Options
import Nix.Parser
2018-04-09 09:52:10 +02:00
import Nix.Pretty
import Nix.Render
import Nix.Scope
2019-03-16 01:59:38 +01:00
import Nix.String
2018-04-09 09:52:10 +02:00
import Nix.Thunk
import Nix.Utils
import Nix.Value
2018-05-13 23:17:55 +02:00
#ifdef MIN_VERSION_haskeline
import System.Console.Haskeline.MonadException hiding(catch)
#endif
import System.FilePath
2018-05-13 23:17:55 +02:00
#ifdef MIN_VERSION_pretty_show
import qualified Text.Show.Pretty as PS
2018-05-13 23:17:55 +02:00
#endif
#ifdef MIN_VERSION_ghc_datasize
#if MIN_VERSION_ghc_datasize(0,2,0) && __GLASGOW_HASKELL__ >= 804
2018-04-29 02:12:32 +02:00
import GHC.DataSize
#endif
#endif
2018-04-29 02:12:32 +02:00
type Cited t f m = (HasCitations1 t f m, MonadDataContext f m)
nvConstantP :: Cited t f m => Provenance t f m -> NAtom -> NValue t f m
nvConstantP p x = addProvenance p (nvConstant x)
nvStrP :: Cited t f m => Provenance t f m -> NixString -> NValue t f m
nvStrP p ns = addProvenance p (nvStr ns)
nvPathP :: Cited t f m => Provenance t f m -> FilePath -> NValue t f m
nvPathP p x = addProvenance p (nvPath x)
nvListP :: Cited t f m => Provenance t f m -> [t] -> NValue t f m
nvListP p l = addProvenance p (nvList l)
nvSetP
:: Cited t f m
=> Provenance t f m
-> AttrSet t
-> AttrSet SourcePos
-> NValue t f m
nvSetP p s x = addProvenance p (nvSet s x)
nvClosureP
:: Cited t f m
=> Provenance t f m
-> Params ()
-> (m (NValue t f m) -> m t)
-> NValue t f m
nvClosureP p x f = addProvenance p (nvClosure x f)
nvBuiltinP
:: Cited t f m
=> Provenance t f m
-> String
-> (m (NValue t f m) -> m t)
-> NValue t f m
nvBuiltinP p name f = addProvenance p (nvBuiltin name f)
type MonadCitedThunks t f m
= ( MonadThunk t m (NValue t f m)
, MonadDataErrorContext t f m
, HasCitations1 t f m
)
type MonadNix e t f m
= ( Has e SrcSpan
, Has e Options
, Scoped t m
, Framed e m
, MonadFix m
, MonadCatch m
, MonadThrow m
, Alternative m
, MonadEffects t f m
, MonadCitedThunks t f m
)
2018-04-09 09:52:10 +02:00
2019-03-15 06:00:23 +01:00
data ExecFrame t f m = Assertion SrcSpan (NValue t f m)
deriving (Show, Typeable)
2019-03-15 06:00:23 +01:00
instance MonadDataErrorContext t f m => Exception (ExecFrame t f m)
nverr
:: forall e t f s m a
. (MonadNix e t f m, FromValue NixString m t, Exception s)
=> s
-> m a
2019-03-15 06:00:23 +01:00
nverr = evalError @(NValue t f m)
2018-04-09 09:52:10 +02:00
currentPos :: forall e m . (MonadReader e m, Has e SrcSpan) => m SrcSpan
currentPos = asks (view hasLens)
2018-04-25 08:09:43 +02:00
wrapExprLoc :: SrcSpan -> NExprLocF r -> NExprLoc
wrapExprLoc span x = Fix (Fix (NSym_ span "<?>") <$ x)
instance ( MonadNix e t f m
, FromValue NixString m t
) => MonadEval (NValue t f m) m where
freeVariable var =
nverr @e @t @f
$ ErrorCall
$ "Undefined variable '"
++ Text.unpack var
++ "'"
synHole name = do
span <- currentPos
scope <- currentScopes
evalError @(NValue t f m) $ SynHole $ SynHoleInfo
{ _synHoleInfo_expr = Fix $ NSynHole_ span name
, _synHoleInfo_scope = scope
}
attrMissing ks Nothing =
evalError @(NValue t f m)
$ ErrorCall
$ "Inheriting unknown attribute: "
++ intercalate "." (map Text.unpack (NE.toList ks))
attrMissing ks (Just s) = do
s' <- prettyNValue s
evalError @(NValue t f m)
$ ErrorCall
$ "Could not look up attribute "
++ intercalate "." (map Text.unpack (NE.toList ks))
++ " in "
++ show s'
evalCurPos = do
scope <- currentScopes
span@(SrcSpan delta _) <- currentPos
addProvenance @_ @f (Provenance scope (NSym_ span "__curPos"))
<$> toValue delta
evaledSym name val = do
scope <- currentScopes
span <- currentPos
pure $ addProvenance @_ @f (Provenance scope (NSym_ span name)) val
evalConstant c = do
scope <- currentScopes
span <- currentPos
pure $ nvConstantP (Provenance scope (NConstant_ span c)) c
evalString = assembleString >=> \case
Just ns -> do
scope <- currentScopes
span <- currentPos
pure $ nvStrP
(Provenance
scope
(NStr_ span (DoubleQuoted [Plain (hackyStringIgnoreContext ns)]))
)
ns
Nothing -> nverr $ ErrorCall "Failed to assemble string"
evalLiteralPath p = do
scope <- currentScopes
span <- currentPos
nvPathP (Provenance scope (NLiteralPath_ span p))
<$> makeAbsolutePath @t @f @m p
evalEnvPath p = do
scope <- currentScopes
span <- currentPos
nvPathP (Provenance scope (NEnvPath_ span p)) <$> findEnvPath @t @f @m p
evalUnary op arg = do
scope <- currentScopes
span <- currentPos
execUnaryOp scope span op arg
evalBinary op larg rarg = do
scope <- currentScopes
span <- currentPos
execBinaryOp scope span op larg rarg
evalWith c b = do
scope <- currentScopes
span <- currentPos
(\b -> addProvenance (Provenance scope (NWith_ span Nothing (Just b))) b)
<$> evalWithAttrSet c b
evalIf c t f = do
scope <- currentScopes
span <- currentPos
fromValue c >>= \b -> if b
then
(\t -> addProvenance
(Provenance scope (NIf_ span (Just c) (Just t) Nothing))
t
)
<$> t
else
(\f -> addProvenance
(Provenance scope (NIf_ span (Just c) Nothing (Just f)))
f
)
<$> f
evalAssert c body = fromValue c >>= \b -> do
span <- currentPos
if b
then do
scope <- currentScopes
(\b ->
addProvenance (Provenance scope (NAssert_ span (Just c) (Just b))) b
)
<$> body
else nverr $ Assertion span c
evalApp f x = do
scope <- currentScopes
span <- currentPos
addProvenance (Provenance scope (NBinary_ span NApp (Just f) Nothing))
<$> callFunc f x
evalAbs p k = do
scope <- currentScopes
span <- currentPos
pure $ nvClosureP
(Provenance scope (NAbs_ span (Nothing <$ p) Nothing))
(void p)
(\arg -> wrapValue . snd <$> k arg (\_ b -> ((), ) <$> b))
evalError = throwError
2018-04-09 09:52:10 +02:00
infixl 1 `callFunc`
callFunc
:: forall e t f m
. MonadNix e t f m
=> NValue t f m
-> m (NValue t f m)
-> m (NValue t f m)
callFunc fun arg = do
frames :: Frames <- asks (view hasLens)
when (length frames > 2000) $ throwError $ ErrorCall
"Function call stack exhausted"
case fun of
NVClosure params f -> do
traceM $ "callFunc:NVFunction taking " ++ show params
force ?? pure =<< f arg
NVBuiltin name f -> do
span <- currentPos
force ?? pure =<< withFrame Info (Calling @m @t name span) (f arg)
s@(NVSet m _) | Just f <- M.lookup "__functor" m -> do
traceM "callFunc:__functor"
force f $ (`callFunc` pure s) >=> (`callFunc` arg)
x -> throwError $ ErrorCall $ "Attempt to call non-function: " ++ show x
execUnaryOp
:: (Framed e m, Cited t f m, Show t)
=> Scopes m t
-> SrcSpan
-> NUnaryOp
-> NValue t f m
-> m (NValue t f m)
execUnaryOp scope span op arg = do
traceM "NUnary"
case arg of
NVConstant c -> case (op, c) of
(NNeg, NInt i ) -> unaryOp $ NInt (-i)
(NNeg, NFloat f) -> unaryOp $ NFloat (-f)
(NNot, NBool b ) -> unaryOp $ NBool (not b)
_ ->
throwError
$ ErrorCall
$ "unsupported argument type for unary operator "
++ show op
x ->
throwError
$ ErrorCall
$ "argument to unary operator"
++ " must evaluate to an atomic type: "
++ show x
where
unaryOp = pure . nvConstantP (Provenance scope (NUnary_ span op (Just arg)))
2018-04-09 09:52:10 +02:00
execBinaryOp
:: forall e t f m
. (MonadNix e t f m, FromValue NixString m t, MonadEval (NValue t f m) m)
=> Scopes m t
-> SrcSpan
-> NBinaryOp
-> NValue t f m
-> m (NValue t f m)
-> m (NValue t f m)
execBinaryOp scope span NOr larg rarg = fromNix larg >>= \l -> if l
then orOp Nothing True
else rarg >>= \rval -> fromNix @Bool rval >>= orOp (Just rval)
where
orOp r b = pure $ nvConstantP
(Provenance scope (NBinary_ span NOr (Just larg) r))
(NBool b)
execBinaryOp scope span NAnd larg rarg = fromNix larg >>= \l -> if l
then rarg >>= \rval -> fromNix @Bool rval >>= andOp (Just rval)
else andOp Nothing False
where
andOp r b = pure $ nvConstantP
(Provenance scope (NBinary_ span NAnd (Just larg) r))
(NBool b)
2018-04-09 09:52:10 +02:00
execBinaryOp scope span op lval rarg = do
rval <- rarg
let bin :: (Provenance t f m -> a) -> a
bin f = f (Provenance scope (NBinary_ span op (Just lval) (Just rval)))
toBool = pure . bin nvConstantP . NBool
case (lval, rval) of
(NVConstant lc, NVConstant rc) -> case (op, lc, rc) of
(NEq , _, _) -> toBool =<< valueEqM lval rval
(NNEq, _, _) -> toBool . not =<< valueEqM lval rval
(NLt , l, r) -> toBool $ l < r
(NLte, l, r) -> toBool $ l <= r
(NGt , l, r) -> toBool $ l > r
(NGte, l, r) -> toBool $ l >= r
(NAnd, _, _) ->
nverr $ ErrorCall "should be impossible: && is handled above"
(NOr, _, _) ->
nverr $ ErrorCall "should be impossible: || is handled above"
(NPlus , l , r ) -> numBinOp bin (+) l r
(NMinus, l , r ) -> numBinOp bin (-) l r
(NMult , l , r ) -> numBinOp bin (*) l r
(NDiv , l , r ) -> numBinOp' bin div (/) l r
(NImpl , NBool l, NBool r) -> toBool $ not l || r
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVStr ls, NVStr rs) -> case op of
NPlus -> pure $ bin nvStrP (ls `principledStringMappend` rs)
NEq -> toBool =<< valueEqM lval rval
NNEq -> toBool . not =<< valueEqM lval rval
NLt -> toBool $ ls < rs
NLte -> toBool $ ls <= rs
NGt -> toBool $ ls > rs
NGte -> toBool $ ls >= rs
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVStr _, NVConstant NNull) -> case op of
NEq -> toBool False
NNEq -> toBool True
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVConstant NNull, NVStr _) -> case op of
NEq -> toBool False
NNEq -> toBool True
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVSet ls lp, NVSet rs rp) -> case op of
NUpdate -> pure $ bin nvSetP (rs `M.union` ls) (rp `M.union` lp)
NEq -> toBool =<< valueEqM lval rval
NNEq -> toBool . not =<< valueEqM lval rval
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVSet ls lp, NVConstant NNull) -> case op of
NUpdate -> pure $ bin nvSetP ls lp
NEq -> toBool =<< valueEqM lval (nvSet M.empty M.empty)
NNEq -> toBool . not =<< valueEqM lval (nvSet M.empty M.empty)
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVConstant NNull, NVSet rs rp) -> case op of
NUpdate -> pure $ bin nvSetP rs rp
NEq -> toBool =<< valueEqM (nvSet M.empty M.empty) rval
NNEq -> toBool . not =<< valueEqM (nvSet M.empty M.empty) rval
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(ls@NVSet{}, NVStr rs) -> case op of
NPlus ->
(\ls2 -> bin nvStrP (ls2 `principledStringMappend` rs))
<$> coerceToString DontCopyToStore CoerceStringy ls
NEq -> toBool =<< valueEqM lval rval
NNEq -> toBool . not =<< valueEqM lval rval
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVStr ls, rs@NVSet{}) -> case op of
NPlus ->
(\rs2 -> bin nvStrP (ls `principledStringMappend` rs2))
<$> coerceToString DontCopyToStore CoerceStringy rs
NEq -> toBool =<< valueEqM lval rval
NNEq -> toBool . not =<< valueEqM lval rval
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVList ls, NVList rs) -> case op of
NConcat -> pure $ bin nvListP $ ls ++ rs
NEq -> toBool =<< valueEqM lval rval
NNEq -> toBool . not =<< valueEqM lval rval
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVList ls, NVConstant NNull) -> case op of
NConcat -> pure $ bin nvListP ls
NEq -> toBool =<< valueEqM lval (nvList [])
NNEq -> toBool . not =<< valueEqM lval (nvList [])
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVConstant NNull, NVList rs) -> case op of
NConcat -> pure $ bin nvListP rs
NEq -> toBool =<< valueEqM (nvList []) rval
NNEq -> toBool . not =<< valueEqM (nvList []) rval
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVPath p, NVStr ns) -> case op of
NEq -> toBool False -- From eqValues in nix/src/libexpr/eval.cc
NNEq -> toBool True
NPlus -> bin nvPathP <$> makeAbsolutePath @t @f
(p `mappend` Text.unpack (hackyStringIgnoreContext ns))
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
(NVPath ls, NVPath rs) -> case op of
NPlus -> bin nvPathP <$> makeAbsolutePath @t @f (ls ++ rs)
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
_ -> case op of
NEq -> toBool False
NNEq -> toBool True
_ -> nverr $ ErrorCall $ unsupportedTypes lval rval
where
unsupportedTypes :: Show a => a -> a -> String
unsupportedTypes lval rval =
"Unsupported argument types for binary operator "
++ show op
++ ": "
++ show lval
++ ", "
++ show rval
numBinOp
:: (forall r . (Provenance t f m -> r) -> r)
-> (forall a . Num a => a -> a -> a)
-> NAtom
-> NAtom
-> m (NValue t f m)
numBinOp bin f = numBinOp' bin f f
numBinOp'
:: (forall r . (Provenance t f m -> r) -> r)
-> (Integer -> Integer -> Integer)
-> (Float -> Float -> Float)
-> NAtom
-> NAtom
-> m (NValue t f m)
numBinOp' bin intF floatF l r = case (l, r) of
(NInt li, NInt ri ) -> toInt $ li `intF` ri
(NInt li, NFloat rf) -> toFloat $ fromInteger li `floatF` rf
(NFloat lf, NInt ri ) -> toFloat $ lf `floatF` fromInteger ri
(NFloat lf, NFloat rf) -> toFloat $ lf `floatF` rf
_ -> nverr $ ErrorCall $ unsupportedTypes l r
where
toInt = pure . bin nvConstantP . NInt
toFloat = pure . bin nvConstantP . NFloat
-- | Data type to avoid boolean blindness on what used to be called coerceMore
data CoercionLevel
= CoerceStringy
-- ^ Coerce only stringlike types: strings, paths, and appropriate sets
| CoerceAny
-- ^ Coerce everything but functions
deriving (Eq,Ord,Enum,Bounded)
-- | Data type to avoid boolean blindness on what used to be called copyToStore
data CopyToStoreMode
= CopyToStore
-- ^ Add paths to the store as they are encountered
| DontCopyToStore
-- ^ Add paths to the store as they are encountered
deriving (Eq,Ord,Enum,Bounded)
coerceToString
:: MonadNix e t f m
=> CopyToStoreMode
-> CoercionLevel
-> NValue t f m
-> m NixString
coerceToString ctsm clevel = go
where
go = \case
NVConstant (NBool b)
|
-- TODO Return a singleton for "" and "1"
b && clevel == CoerceAny -> pure
$ principledMakeNixStringWithoutContext "1"
| clevel == CoerceAny -> pure $ principledMakeNixStringWithoutContext ""
NVConstant (NInt n) | clevel == CoerceAny ->
pure $ principledMakeNixStringWithoutContext $ Text.pack $ show n
NVConstant (NFloat n) | clevel == CoerceAny ->
pure $ principledMakeNixStringWithoutContext $ Text.pack $ show n
NVConstant NNull | clevel == CoerceAny ->
pure $ principledMakeNixStringWithoutContext ""
NVStr ns -> pure ns
NVPath p
| ctsm == CopyToStore -> storePathToNixString <$> addPath p
| otherwise -> pure $ principledMakeNixStringWithoutContext $ Text.pack p
NVList l | clevel == CoerceAny ->
nixStringUnwords <$> traverse (`force` go) l
v@(NVSet s _) | Just p <- M.lookup "__toString" s ->
force p $ (`callFunc` pure v) >=> go
NVSet s _ | Just p <- M.lookup "outPath" s -> force p go
v -> throwError $ ErrorCall $ "Expected a string, but saw: " ++ show v
nixStringUnwords =
principledIntercalateNixString (principledMakeNixStringWithoutContext " ")
storePathToNixString :: StorePath -> NixString
storePathToNixString sp = principledMakeNixStringWithSingletonContext
t
(StringContext t DirectPath)
where t = Text.pack $ unStorePath sp
2018-11-17 22:08:02 +01:00
2019-03-15 06:00:23 +01:00
fromStringNoContext :: MonadNix e t f m => NixString -> m Text
fromStringNoContext ns = case principledGetStringNoContext ns of
Just str -> return str
Nothing -> throwError $ ErrorCall "expected string with no context"
2018-11-18 00:39:45 +01:00
2019-03-15 06:00:23 +01:00
newtype Lazy t (f :: * -> *) m a = Lazy
{ runLazy :: ReaderT (Context (Lazy t f m) t)
(StateT (HashMap FilePath NExprLoc) m) a }
2019-03-16 06:54:29 +01:00
deriving
( Functor
, Applicative
, Alternative
, Monad
, MonadPlus
, MonadFix
, MonadIO
, MonadReader (Context (Lazy t f m) t)
)
2019-03-14 23:10:41 +01:00
2019-03-15 06:00:23 +01:00
instance MonadTrans (Lazy t f) where
lift = Lazy . lift . lift
2019-03-15 06:00:23 +01:00
instance MonadRef m => MonadRef (Lazy t f m) where
type Ref (Lazy t f m) = Ref m
newRef = lift . newRef
readRef = lift . readRef
writeRef r = lift . writeRef r
2019-03-15 06:00:23 +01:00
instance MonadAtomicRef m => MonadAtomicRef (Lazy t f m) where
atomicModifyRef r = lift . atomicModifyRef r
2019-03-15 06:00:23 +01:00
instance (MonadFile m, Monad m) => MonadFile (Lazy t f m)
2019-03-15 06:00:23 +01:00
instance MonadCatch m => MonadCatch (Lazy t f m) where
catch (Lazy (ReaderT m)) f =
Lazy $ ReaderT $ \e -> catch (m e) ((`runReaderT` e) . runLazy . f)
2019-03-15 06:00:23 +01:00
instance MonadThrow m => MonadThrow (Lazy t f m) where
throwM = Lazy . throwM
2018-05-13 23:17:55 +02:00
#ifdef MIN_VERSION_haskeline
2019-03-15 06:00:23 +01:00
instance MonadException m => MonadException (Lazy t f m) where
controlIO f = Lazy $ controlIO $ \(RunIO run) ->
let run' = RunIO (fmap Lazy . run . runLazy)
2018-04-29 02:13:05 +02:00
in runLazy <$> f run'
#endif
2019-03-15 06:00:23 +01:00
instance MonadStore m => MonadStore (Lazy t f m) where
addPath' = lift . addPath'
toFile_' n = lift . toFile_' n
2018-05-11 02:32:47 +02:00
2019-03-15 06:00:23 +01:00
instance MonadPutStr m => MonadPutStr (Lazy t f m)
instance MonadHttp m => MonadHttp (Lazy t f m)
instance MonadEnv m => MonadEnv (Lazy t f m)
instance MonadInstantiate m => MonadInstantiate (Lazy t f m)
instance MonadExec m => MonadExec (Lazy t f m)
instance MonadIntrospect m => MonadIntrospect (Lazy t f m)
2018-11-17 00:25:21 +01:00
instance MonadThunkId m => MonadThunkId (Lazy t f m) where
type ThunkId (Lazy t f m) = ThunkId m
instance ( MonadFix m
, MonadCatch m
, MonadFile m
, MonadStore m
, MonadPutStr m
, MonadHttp m
, MonadEnv m
, MonadInstantiate m
, MonadExec m
, MonadIntrospect m
, Alternative m
, MonadPlus m
, MonadCitedThunks t f (Lazy t f m)
, FromNix Bool (Lazy t f m) t
, FromValue NixString (Lazy t f m) t
, FromValue Path (Lazy t f m) t
, ToNix NixString (Lazy t f m) t
, ToNix [t] (Lazy t f m) t
)
=> MonadEffects t f (Lazy t f m) where
makeAbsolutePath origPath = do
origPathExpanded <- expandHomePath origPath
absPath <- if isAbsolute origPathExpanded
then pure origPathExpanded
else do
cwd <- do
mres <- lookupVar "__cur_file"
case mres of
Nothing -> getCurrentDirectory
Just v -> force v $ \case
NVPath s -> return $ takeDirectory s
v ->
throwError
$ ErrorCall
$ "when resolving relative path,"
++ " __cur_file is in scope,"
++ " but is not a path; it is: "
++ show v
pure $ cwd <///> origPathExpanded
removeDotDotIndirections <$> canonicalizePath absPath
-- Given a path, determine the nix file to load
pathToDefaultNix = pathToDefaultNixFile
findEnvPath = findEnvPathM
findPath = findPathM
importPath path = do
traceM $ "Importing file " ++ path
withFrame Info (ErrorCall $ "While importing file " ++ show path) $ do
imports <- Lazy $ ReaderT $ const get
evalExprLoc =<< case M.lookup path imports of
Just expr -> pure expr
Nothing -> do
eres <- parseNixFileLoc path
case eres of
Failure err ->
throwError
$ ErrorCall
. show
$ fillSep
$ ["Parse during import failed:", err]
Success expr -> do
Lazy $ ReaderT $ const $ modify (M.insert path expr)
pure expr
derivationStrict = fromValue @(AttrSet t) >=> \s -> do
nn <- maybe (pure False) fromNix (M.lookup "__ignoreNulls" s)
s' <- M.fromList <$> mapMaybeM (handleEntry nn) (M.toList s)
v' <- normalForm =<< toValue @(AttrSet t) @_ @(NValue t f (Lazy t f m)) s'
nixInstantiateExpr $ "derivationStrict " ++ show (prettyNValueNF v')
where
mapMaybeM :: (a -> Lazy t f m (Maybe b)) -> [a] -> Lazy t f m [b]
mapMaybeM op = foldr f (return [])
where f x xs = op x >>= (<$> xs) . (++) . maybeToList
handleEntry :: Bool -> (Text, t) -> Lazy t f m (Maybe (Text, t))
handleEntry ignoreNulls (k, v) = fmap (k, ) <$> case k of
-- The `args' attribute is special: it supplies the command-line
-- arguments to the builder.
-- TODO This use of coerceToString is probably not right and may
-- not have the right arguments.
"args" -> force v $ fmap Just . coerceNixList
"__ignoreNulls" -> pure Nothing
_ -> force v $ \case
NVConstant NNull | ignoreNulls -> pure Nothing
v' -> Just <$> coerceNix v'
where
coerceNix = toNix <=< coerceToString CopyToStore CoerceAny
coerceNixList =
toNix <=< traverse (\x -> force x coerceNix) <=< fromValue @[t]
traceEffect = putStrLn
2018-04-29 02:12:32 +02:00
getRecursiveSize :: (MonadIntrospect m, Applicative f) => a -> m (NValue t f m)
getRecursiveSize = fmap (nvConstant . NInt . fromIntegral) . recursiveSize
2018-04-29 00:09:53 +02:00
2019-03-15 06:00:23 +01:00
runLazyM :: Options -> MonadIO m => Lazy t f m a -> m a
runLazyM opts =
(`evalStateT` M.empty) . (`runReaderT` newContext opts) . runLazy
-- | Incorrectly normalize paths by rewriting patterns like @a/b/..@ to @a@.
-- This is incorrect on POSIX systems, because if @b@ is a symlink, its
-- parent may be a different directory from @a@. See the discussion at
-- https://hackage.haskell.org/package/directory-1.3.1.5/docs/System-Directory.html#v:canonicalizePath
removeDotDotIndirections :: FilePath -> FilePath
removeDotDotIndirections = intercalate "/" . go [] . splitOn "/"
where
go s [] = reverse s
go (_ : s) (".." : rest) = go s rest
go s (this : rest) = go (this : s) rest
expandHomePath :: MonadFile m => FilePath -> m FilePath
2018-04-21 08:17:57 +02:00
expandHomePath ('~' : xs) = flip (++) xs <$> getHomeDirectory
expandHomePath p = return p
2018-04-21 08:17:57 +02:00
-- Given a path, determine the nix file to load
pathToDefaultNixFile :: MonadFile m => FilePath -> m FilePath
pathToDefaultNixFile p = do
isDir <- doesDirectoryExist p
pure $ if isDir then p </> "default.nix" else p
infixr 9 <///>
(<///>) :: FilePath -> FilePath -> FilePath
x <///> y | isAbsolute y || "." `isPrefixOf` y = x </> y
| otherwise = joinByLargestOverlap x y
where
joinByLargestOverlap (splitDirectories -> xs) (splitDirectories -> ys) =
joinPath $ head
[ xs ++ drop (length tx) ys | tx <- tails xs, tx `elem` inits ys ]
findPathBy
:: forall e t f m
. (MonadNix e t f m, FromValue NixString m t, FromValue Path m t)
=> (FilePath -> m (Maybe FilePath))
-> [t]
-> FilePath
-> m FilePath
findPathBy finder l name = do
mpath <- foldM go Nothing l
case mpath of
Nothing ->
throwError
$ ErrorCall
$ "file '"
++ name
++ "' was not found in the Nix search path"
++ " (add it using $NIX_PATH or -I)"
Just path -> return path
where
go :: Maybe FilePath -> t -> m (Maybe FilePath)
go p@(Just _) _ = pure p
go Nothing l = force l $ fromValue >=> \(s :: HashMap Text t) -> do
p <- resolvePath s
force p $ fromValue >=> \(Path path) -> case M.lookup "prefix" s of
Nothing -> tryPath path Nothing
Just pf -> force pf $ fromValueMay >=> \case
Just (nsPfx :: NixString) ->
let pfx = hackyStringIgnoreContext nsPfx
in if not (Text.null pfx)
then tryPath path (Just (Text.unpack pfx))
else tryPath path Nothing
_ -> tryPath path Nothing
tryPath p (Just n) | n' : ns <- splitDirectories name, n == n' =
finder $ p <///> joinPath ns
tryPath p _ = finder $ p <///> name
resolvePath s = case M.lookup "path" s of
Just t -> return t
Nothing -> case M.lookup "uri" s of
Just ut -> thunk $ fetchTarball (force ut pure)
Nothing ->
throwError
$ ErrorCall
$ "__nixPath must be a list of attr sets"
++ " with 'path' elements, but saw: "
++ show s
findPathM
:: forall e t f m
. (MonadNix e t f m, FromValue NixString m t, FromValue Path m t)
=> [t]
-> FilePath
-> m FilePath
findPathM l name = findPathBy path l name
where
path :: MonadEffects t f m => FilePath -> m (Maybe FilePath)
path path = do
path <- makeAbsolutePath @t @f path
exists <- doesPathExist path
return $ if exists then Just path else Nothing
findEnvPathM
:: forall e t f m
. (MonadNix e t f m, FromValue NixString m t, FromValue Path m t)
=> FilePath
-> m FilePath
findEnvPathM name = do
mres <- lookupVar "__nixPath"
case mres of
Nothing -> error "impossible"
Just x ->
force x $ fromValue >=> \(l :: [t]) -> findPathBy nixFilePath l name
where
nixFilePath :: MonadEffects t f m => FilePath -> m (Maybe FilePath)
nixFilePath path = do
path <- makeAbsolutePath @t @f path
exists <- doesDirectoryExist path
path' <- if exists
then makeAbsolutePath @t @f $ path </> "default.nix"
else return path
exists <- doesFileExist path'
return $ if exists then Just path' else Nothing
addTracing
:: (MonadNix e t f m, Has e Options, MonadReader Int n, Alternative n)
=> Alg NExprLocF (m a)
-> Alg NExprLocF (n (m a))
addTracing k v = do
depth <- ask
guard (depth < 2000)
local succ $ do
v'@(Compose (Ann span x)) <- sequence v
return $ do
opts :: Options <- asks (view hasLens)
let rendered = if verbose opts >= Chatty
2018-05-13 23:17:55 +02:00
#ifdef MIN_VERSION_pretty_show
then pretty $ PS.ppShow (void x)
2018-05-13 23:17:55 +02:00
#else
then pretty $ show (void x)
2018-05-13 23:17:55 +02:00
#endif
else prettyNix (Fix (Fix (NSym "?") <$ x))
msg x = pretty ("eval: " ++ replicate depth ' ') <> x
loc <- renderLocation span (msg rendered <> " ...\n")
putStr $ show loc
res <- k v'
print $ msg rendered <> " ...done"
return res
evalExprLoc
:: forall e t f m
. (MonadNix e t f m, FromValue NixString m t, Has e Options)
=> NExprLoc
-> m (NValue t f m)
evalExprLoc expr = do
opts :: Options <- asks (view hasLens)
if tracing opts
then join . (`runReaderT` (0 :: Int)) $ adi
(addTracing phi)
(raise (addStackFrames @t . addSourcePositions))
expr
else adi phi (addStackFrames @t . addSourcePositions) expr
where
phi = Eval.eval . annotated . getCompose
raise k f x = ReaderT $ \e -> k (\t -> runReaderT (f t) e) x
fetchTarball
:: forall e t f m
. (MonadNix e t f m, FromValue NixString m t)
=> m (NValue t f m)
-> m (NValue t f m)
fetchTarball v = v >>= \case
NVSet s _ -> case M.lookup "url" s of
Nothing ->
throwError $ ErrorCall "builtins.fetchTarball: Missing url attribute"
Just url -> force url $ go (M.lookup "sha256" s)
v@NVStr{} -> go Nothing v
v ->
throwError
$ ErrorCall
$ "builtins.fetchTarball: Expected URI or set, got "
++ show v
where
go :: Maybe t -> NValue t f m -> m (NValue t f m)
go msha = \case
NVStr ns -> fetch (hackyStringIgnoreContext ns) msha
v ->
throwError
$ ErrorCall
$ "builtins.fetchTarball: Expected URI or string, got "
++ show v
{- jww (2018-04-11): This should be written using pipes in another module
2019-03-15 06:00:23 +01:00
fetch :: Text -> Maybe (NThunk m) -> m (NValue t f m)
fetch uri msha = case takeExtension (Text.unpack uri) of
".tgz" -> undefined
".gz" -> undefined
".bz2" -> undefined
".xz" -> undefined
".tar" -> undefined
ext -> throwError $ ErrorCall $ "builtins.fetchTarball: Unsupported extension '"
++ ext ++ "'"
-}
fetch :: Text -> Maybe t -> m (NValue t f m)
fetch uri Nothing =
nixInstantiateExpr $ "builtins.fetchTarball \"" ++ Text.unpack uri ++ "\""
fetch url (Just m) = fromValue m >>= \nsSha ->
let sha = hackyStringIgnoreContext nsSha
in nixInstantiateExpr
$ "builtins.fetchTarball { "
++ "url = \""
++ Text.unpack url
++ "\"; "
++ "sha256 = \""
++ Text.unpack sha
++ "\"; }"
2018-11-17 00:10:50 +01:00
2019-03-09 23:52:20 +01:00
exec
:: (MonadNix e t f m, MonadInstantiate m, FromValue NixString m t)
2019-03-09 23:52:20 +01:00
=> [String]
2019-03-15 06:00:23 +01:00
-> m (NValue t f m)
2018-11-17 00:10:50 +01:00
exec args = either throwError evalExprLoc =<< exec' args
2019-03-09 23:52:20 +01:00
nixInstantiateExpr
:: (MonadNix e t f m, MonadInstantiate m, FromValue NixString m t)
2019-03-09 23:52:20 +01:00
=> String
2019-03-15 06:00:23 +01:00
-> m (NValue t f m)
2018-11-17 00:10:50 +01:00
nixInstantiateExpr s = either throwError evalExprLoc =<< instantiateExpr s
2018-11-17 22:21:03 +01:00
2019-03-15 06:00:23 +01:00
instance Monad m => Scoped t (Lazy t f m) where
2018-11-17 22:21:03 +01:00
currentScopes = currentScopesReader
clearScopes = clearScopesReader @(Lazy t f m) @t
pushScopes = pushScopesReader
lookupVar = lookupVarReader