2018-04-09 09:52:10 +02:00
|
|
|
{-# LANGUAGE AllowAmbiguousTypes #-}
|
|
|
|
{-# LANGUAGE ApplicativeDo #-}
|
|
|
|
{-# LANGUAGE ConstraintKinds #-}
|
2018-04-11 06:01:48 +02:00
|
|
|
{-# LANGUAGE DeriveFunctor #-}
|
2018-04-09 09:52:10 +02:00
|
|
|
{-# LANGUAGE FlexibleContexts #-}
|
|
|
|
{-# LANGUAGE FlexibleInstances #-}
|
2018-04-11 06:01:48 +02:00
|
|
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
2018-04-09 09:52:10 +02:00
|
|
|
{-# LANGUAGE LambdaCase #-}
|
|
|
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
|
|
|
{-# LANGUAGE MultiWayIf #-}
|
2018-04-11 06:01:48 +02:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
2018-04-09 09:52:10 +02:00
|
|
|
{-# LANGUAGE PartialTypeSignatures #-}
|
|
|
|
{-# LANGUAGE RankNTypes #-}
|
|
|
|
{-# LANGUAGE ScopedTypeVariables #-}
|
|
|
|
{-# LANGUAGE TypeApplications #-}
|
|
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
|
|
{-# LANGUAGE TypeSynonymInstances #-}
|
|
|
|
{-# LANGUAGE UndecidableInstances #-}
|
2018-04-14 01:37:11 +02:00
|
|
|
{-# LANGUAGE ViewPatterns #-}
|
2018-04-09 09:52:10 +02:00
|
|
|
|
2018-04-11 06:01:48 +02:00
|
|
|
{-# OPTIONS_GHC -Wno-missing-signatures #-}
|
2018-04-09 09:52:10 +02:00
|
|
|
{-# OPTIONS_GHC -Wno-orphans #-}
|
2018-04-11 06:01:48 +02:00
|
|
|
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
|
2018-04-09 09:52:10 +02:00
|
|
|
|
|
|
|
module Nix.Exec where
|
|
|
|
|
2018-04-14 04:29:18 +02:00
|
|
|
import Control.Applicative
|
2018-04-09 09:52:10 +02:00
|
|
|
import Control.Monad
|
2018-04-11 06:01:48 +02:00
|
|
|
import Control.Monad.Catch
|
|
|
|
import Control.Monad.Fix
|
|
|
|
import Control.Monad.IO.Class
|
|
|
|
import Control.Monad.Reader (MonadReader, asks)
|
|
|
|
import Control.Monad.Trans.Reader hiding (asks)
|
|
|
|
import qualified Data.ByteString as BS
|
2018-04-09 09:52:10 +02:00
|
|
|
import Data.Coerce
|
2018-04-11 06:01:48 +02:00
|
|
|
import Data.Fix
|
2018-04-09 09:52:10 +02:00
|
|
|
import Data.Functor.Compose
|
2018-04-14 03:09:12 +02:00
|
|
|
import Data.HashMap.Lazy (HashMap)
|
2018-04-09 09:52:10 +02:00
|
|
|
import qualified Data.HashMap.Lazy as M
|
2018-04-11 06:01:48 +02:00
|
|
|
import Data.IORef
|
|
|
|
import Data.List
|
|
|
|
import Data.List.Split
|
2018-04-09 09:52:10 +02:00
|
|
|
import Data.Maybe (mapMaybe)
|
|
|
|
import Data.Text (Text)
|
|
|
|
import qualified Data.Text as Text
|
|
|
|
import Nix.Atoms
|
2018-04-11 06:01:48 +02:00
|
|
|
import Nix.Context
|
2018-04-14 03:09:12 +02:00
|
|
|
import Nix.Convert
|
2018-04-11 06:01:48 +02:00
|
|
|
import Nix.Effects
|
2018-04-09 09:52:10 +02:00
|
|
|
import Nix.Eval
|
2018-04-11 06:01:48 +02:00
|
|
|
import qualified Nix.Eval as Eval
|
2018-04-09 09:52:10 +02:00
|
|
|
import Nix.Expr
|
|
|
|
import Nix.Normal
|
2018-04-11 06:01:48 +02:00
|
|
|
import Nix.Parser
|
2018-04-09 09:52:10 +02:00
|
|
|
import Nix.Pretty
|
2018-04-11 06:01:48 +02:00
|
|
|
import Nix.Scope
|
2018-04-09 09:52:10 +02:00
|
|
|
import Nix.Stack
|
|
|
|
import Nix.Thunk
|
|
|
|
import Nix.Utils
|
|
|
|
import Nix.Value
|
2018-04-11 06:01:48 +02:00
|
|
|
import System.Directory
|
|
|
|
import System.Environment
|
|
|
|
import System.Exit (ExitCode (ExitSuccess))
|
|
|
|
import System.FilePath
|
|
|
|
import qualified System.Info
|
|
|
|
import System.Posix.Files
|
|
|
|
import System.Process (readProcessWithExitCode)
|
2018-04-14 04:29:18 +02:00
|
|
|
import {-# SOURCE #-} Nix.Entry as Entry
|
2018-04-09 09:52:10 +02:00
|
|
|
|
2018-04-14 03:09:12 +02:00
|
|
|
nverr :: forall e m a. MonadNix e m => String -> m a
|
2018-04-09 09:52:10 +02:00
|
|
|
nverr = evalError @(NValue m)
|
|
|
|
|
2018-04-14 03:09:12 +02:00
|
|
|
instance MonadNix e m => MonadThunk (NValue m) (NThunk m) m where
|
2018-04-09 09:52:10 +02:00
|
|
|
thunk = fmap coerce . buildThunk
|
|
|
|
force = forceThunk . coerce
|
|
|
|
value = coerce . valueRef
|
|
|
|
|
2018-04-14 03:09:12 +02:00
|
|
|
instance MonadNix e m => MonadEval (NValue m) m where
|
2018-04-11 20:53:30 +02:00
|
|
|
freeVariable var =
|
|
|
|
nverr $ "Undefined variable '" ++ Text.unpack var ++ "'"
|
2018-04-09 09:52:10 +02:00
|
|
|
|
|
|
|
evalCurPos = do
|
|
|
|
Compose (Ann (SrcSpan delta _) _):_ <-
|
|
|
|
asks (mapMaybe (either (const Nothing) Just)
|
2018-04-11 06:01:48 +02:00
|
|
|
. view @_ @Frames hasLens)
|
2018-04-16 04:05:44 +02:00
|
|
|
toValue delta
|
2018-04-09 09:52:10 +02:00
|
|
|
|
|
|
|
evalConstant = pure . NVConstant
|
2018-04-16 19:56:29 +02:00
|
|
|
evalString = (pure .) . NVStr
|
2018-04-09 09:52:10 +02:00
|
|
|
evalLiteralPath = fmap NVPath . makeAbsolutePath
|
|
|
|
evalEnvPath = fmap NVPath . findEnvPath
|
|
|
|
evalUnary = execUnaryOp
|
|
|
|
evalBinary = execBinaryOp
|
2018-04-16 19:10:47 +02:00
|
|
|
evalWith = evalWithAttrSet
|
2018-04-11 03:26:29 +02:00
|
|
|
|
2018-04-16 19:10:47 +02:00
|
|
|
evalIf c t f = fromValue c >>= \b -> if b then t else f
|
2018-04-11 20:53:30 +02:00
|
|
|
|
2018-04-16 19:10:47 +02:00
|
|
|
evalAssert c body = fromValue c >>= \b ->
|
|
|
|
if b then body else nverr "assertion failed"
|
2018-04-11 06:01:48 +02:00
|
|
|
|
|
|
|
evalApp = callFunc
|
|
|
|
evalAbs = (pure .) . NVClosure
|
2018-04-09 09:52:10 +02:00
|
|
|
|
|
|
|
evalError = throwError
|
|
|
|
|
|
|
|
infixl 1 `callFunc`
|
2018-04-14 03:09:12 +02:00
|
|
|
callFunc :: MonadNix e m => NValue m -> m (NValue m) -> m (NValue m)
|
2018-04-09 09:52:10 +02:00
|
|
|
callFunc fun arg = case fun of
|
|
|
|
NVClosure _ f -> do
|
|
|
|
traceM "callFunc:NVFunction"
|
|
|
|
f arg
|
|
|
|
NVBuiltin name f -> do
|
|
|
|
traceM $ "callFunc:NVBuiltin " ++ name
|
2018-04-17 06:39:41 +02:00
|
|
|
f arg
|
2018-04-09 09:52:10 +02:00
|
|
|
s@(NVSet m _) | Just f <- M.lookup "__functor" m -> do
|
|
|
|
traceM "callFunc:__functor"
|
2018-04-17 00:02:04 +02:00
|
|
|
force f $ (`callFunc` pure s) >=> (`callFunc` arg)
|
2018-04-17 23:53:50 +02:00
|
|
|
x -> arg >>= \arg' ->
|
|
|
|
throwError $ "Attempt to call non-function '" ++ show x
|
|
|
|
++ "' with arg: " ++ show arg'
|
2018-04-09 09:52:10 +02:00
|
|
|
|
|
|
|
execUnaryOp
|
|
|
|
:: (Framed e m, MonadVar m, MonadFile m)
|
|
|
|
=> NUnaryOp -> NValue m -> m (NValue m)
|
|
|
|
execUnaryOp op arg = do
|
|
|
|
traceM "NUnary"
|
|
|
|
case arg of
|
|
|
|
NVConstant c -> case (op, c) of
|
|
|
|
(NNeg, NInt i) -> return $ NVConstant $ NInt (-i)
|
|
|
|
(NNeg, NFloat f) -> return $ NVConstant $ NFloat (-f)
|
|
|
|
(NNot, NBool b) -> return $ NVConstant $ NBool (not b)
|
|
|
|
_ -> throwError $ "unsupported argument type for unary operator "
|
|
|
|
++ show op
|
|
|
|
x -> throwError $ "argument to unary operator"
|
2018-04-12 05:46:44 +02:00
|
|
|
++ " must evaluate to an atomic type: " ++ show x
|
2018-04-09 09:52:10 +02:00
|
|
|
|
|
|
|
execBinaryOp
|
2018-04-14 03:09:12 +02:00
|
|
|
:: forall e m. (MonadNix e m, MonadEval (NValue m) m)
|
2018-04-09 09:52:10 +02:00
|
|
|
=> NBinaryOp -> NValue m -> m (NValue m) -> m (NValue m)
|
|
|
|
|
2018-04-17 23:59:24 +02:00
|
|
|
execBinaryOp NOr larg rarg = fromNix larg >>= \l ->
|
|
|
|
if l
|
|
|
|
then toNix True
|
|
|
|
else rarg >>= fromNix @Bool >>= toNix
|
|
|
|
|
|
|
|
execBinaryOp NAnd larg rarg = fromNix larg >>= \l ->
|
|
|
|
if l
|
|
|
|
then rarg >>= fromNix @Bool >>= toNix
|
|
|
|
else toNix False
|
2018-04-09 09:52:10 +02:00
|
|
|
|
|
|
|
-- jww (2018-04-08): Refactor so that eval (NBinary ..) *always* dispatches
|
|
|
|
-- based on operator first
|
|
|
|
execBinaryOp op larg rarg = do
|
|
|
|
let lval = larg
|
|
|
|
rval <- traceM "NBinary:right" >> rarg
|
|
|
|
|
|
|
|
let unsupportedTypes =
|
2018-04-12 05:46:44 +02:00
|
|
|
"Unsupported argument types for binary operator "
|
|
|
|
++ show op ++ ": " ++ show lval ++ ", " ++ show rval
|
2018-04-09 09:52:10 +02:00
|
|
|
numBinOp :: (forall a. Num a => a -> a -> a) -> NAtom -> NAtom
|
|
|
|
-> m (NValue m)
|
|
|
|
numBinOp f = numBinOp' f f
|
|
|
|
numBinOp'
|
|
|
|
:: (Integer -> Integer -> Integer)
|
|
|
|
-> (Float -> Float -> Float)
|
|
|
|
-> NAtom -> NAtom -> m (NValue m)
|
|
|
|
numBinOp' intF floatF l r = case (l, r) of
|
|
|
|
(NInt li, NInt ri) ->
|
2018-04-16 04:05:44 +02:00
|
|
|
toValue $ li `intF` ri
|
2018-04-09 09:52:10 +02:00
|
|
|
(NInt li, NFloat rf) ->
|
2018-04-16 04:05:44 +02:00
|
|
|
toValue $ fromInteger li `floatF` rf
|
2018-04-09 09:52:10 +02:00
|
|
|
(NFloat lf, NInt ri) ->
|
2018-04-16 04:05:44 +02:00
|
|
|
toValue $ lf `floatF` fromInteger ri
|
2018-04-09 09:52:10 +02:00
|
|
|
(NFloat lf, NFloat rf) ->
|
2018-04-16 04:05:44 +02:00
|
|
|
toValue $ lf `floatF` rf
|
2018-04-11 06:01:48 +02:00
|
|
|
_ -> nverr unsupportedTypes
|
|
|
|
|
|
|
|
nverr = evalError @(NValue m)
|
2018-04-09 09:52:10 +02:00
|
|
|
|
|
|
|
case (lval, rval) of
|
|
|
|
(NVConstant lc, NVConstant rc) -> case (op, lc, rc) of
|
2018-04-16 04:05:44 +02:00
|
|
|
(NEq, _, _) -> toValue =<< valueEq lval rval
|
|
|
|
(NNEq, _, _) -> toValue . not =<< valueEq lval rval
|
|
|
|
(NLt, l, r) -> toValue $ l < r
|
|
|
|
(NLte, l, r) -> toValue $ l <= r
|
|
|
|
(NGt, l, r) -> toValue $ l > r
|
|
|
|
(NGte, l, r) -> toValue $ l >= r
|
2018-04-09 09:52:10 +02:00
|
|
|
(NAnd, _, _) -> nverr "should be impossible: && is handled above"
|
|
|
|
(NOr, _, _) -> nverr "should be impossible: || is handled above"
|
|
|
|
(NPlus, l, r) -> numBinOp (+) l r
|
|
|
|
(NMinus, l, r) -> numBinOp (-) l r
|
|
|
|
(NMult, l, r) -> numBinOp (*) l r
|
|
|
|
(NDiv, l, r) -> numBinOp' div (/) l r
|
2018-04-16 04:05:44 +02:00
|
|
|
(NImpl, NBool l, NBool r) -> toValue $ not l || r
|
2018-04-09 09:52:10 +02:00
|
|
|
_ -> nverr unsupportedTypes
|
|
|
|
|
|
|
|
(NVStr ls lc, NVStr rs rc) -> case op of
|
|
|
|
NPlus -> pure $ NVStr (ls `mappend` rs) (lc `mappend` rc)
|
2018-04-16 04:05:44 +02:00
|
|
|
NEq -> toValue =<< valueEq lval rval
|
|
|
|
NNEq -> toValue . not =<< valueEq lval rval
|
|
|
|
NLt -> toValue $ ls < rs
|
|
|
|
NLte -> toValue $ ls <= rs
|
|
|
|
NGt -> toValue $ ls > rs
|
|
|
|
NGte -> toValue $ ls >= rs
|
2018-04-09 09:52:10 +02:00
|
|
|
_ -> nverr unsupportedTypes
|
|
|
|
|
|
|
|
(NVStr _ _, NVConstant NNull) -> case op of
|
2018-04-16 04:05:44 +02:00
|
|
|
NEq -> toValue =<< valueEq lval (NVStr "" mempty)
|
|
|
|
NNEq -> toValue . not =<< valueEq lval (NVStr "" mempty)
|
2018-04-09 09:52:10 +02:00
|
|
|
_ -> nverr unsupportedTypes
|
|
|
|
|
|
|
|
(NVConstant NNull, NVStr _ _) -> case op of
|
2018-04-16 04:05:44 +02:00
|
|
|
NEq -> toValue =<< valueEq (NVStr "" mempty) rval
|
|
|
|
NNEq -> toValue . not =<< valueEq (NVStr "" mempty) rval
|
2018-04-09 09:52:10 +02:00
|
|
|
_ -> nverr unsupportedTypes
|
|
|
|
|
|
|
|
(NVSet ls lp, NVSet rs rp) -> case op of
|
|
|
|
NUpdate -> pure $ NVSet (rs `M.union` ls) (rp `M.union` lp)
|
2018-04-16 04:05:44 +02:00
|
|
|
NEq -> toValue =<< valueEq lval rval
|
|
|
|
NNEq -> toValue . not =<< valueEq lval rval
|
2018-04-09 09:52:10 +02:00
|
|
|
_ -> nverr unsupportedTypes
|
|
|
|
|
|
|
|
(NVList ls, NVList rs) -> case op of
|
|
|
|
NConcat -> pure $ NVList $ ls ++ rs
|
2018-04-16 04:05:44 +02:00
|
|
|
NEq -> toValue =<< valueEq lval rval
|
|
|
|
NNEq -> toValue . not =<< valueEq lval rval
|
2018-04-09 09:52:10 +02:00
|
|
|
_ -> nverr unsupportedTypes
|
|
|
|
|
|
|
|
(NVList ls, NVConstant NNull) -> case op of
|
|
|
|
NConcat -> pure $ NVList ls
|
2018-04-16 04:05:44 +02:00
|
|
|
NEq -> toValue =<< valueEq lval (NVList [])
|
|
|
|
NNEq -> toValue . not =<< valueEq lval (NVList [])
|
2018-04-09 09:52:10 +02:00
|
|
|
_ -> nverr unsupportedTypes
|
|
|
|
|
|
|
|
(NVConstant NNull, NVList rs) -> case op of
|
|
|
|
NConcat -> pure $ NVList rs
|
2018-04-16 04:05:44 +02:00
|
|
|
NEq -> toValue =<< valueEq (NVList []) rval
|
|
|
|
NNEq -> toValue . not =<< valueEq (NVList []) rval
|
2018-04-09 09:52:10 +02:00
|
|
|
_ -> nverr unsupportedTypes
|
|
|
|
|
2018-04-13 10:20:40 +02:00
|
|
|
(NVPath p, NVStr s _) -> case op of
|
|
|
|
-- jww (2018-04-13): Do we need to make the path absolute here?
|
2018-04-16 04:05:44 +02:00
|
|
|
NEq -> toValue $ p == Text.unpack s
|
|
|
|
NNEq -> toValue $ p /= Text.unpack s
|
2018-04-13 10:20:40 +02:00
|
|
|
NPlus -> NVPath <$> makeAbsolutePath (p `mappend` Text.unpack s)
|
2018-04-09 09:52:10 +02:00
|
|
|
_ -> nverr unsupportedTypes
|
|
|
|
|
|
|
|
(NVPath ls, NVPath rs) -> case op of
|
|
|
|
NPlus -> NVPath <$> makeAbsolutePath (ls ++ rs)
|
|
|
|
_ -> nverr unsupportedTypes
|
|
|
|
|
|
|
|
_ -> nverr unsupportedTypes
|
2018-04-11 06:01:48 +02:00
|
|
|
|
|
|
|
newtype Lazy m a = Lazy
|
|
|
|
{ runLazy :: ReaderT (Context (Lazy m) (NThunk (Lazy m))) m a }
|
2018-04-14 04:29:18 +02:00
|
|
|
deriving (Functor, Applicative, Alternative, Monad, MonadPlus,
|
|
|
|
MonadFix, MonadIO,
|
2018-04-11 06:01:48 +02:00
|
|
|
MonadReader (Context (Lazy m) (NThunk (Lazy m))))
|
|
|
|
|
|
|
|
instance MonadIO m => MonadVar (Lazy m) where
|
|
|
|
type Var (Lazy m) = IORef
|
|
|
|
|
|
|
|
newVar = liftIO . newIORef
|
|
|
|
readVar = liftIO . readIORef
|
|
|
|
writeVar = (liftIO .) . writeIORef
|
|
|
|
atomicModifyVar = (liftIO .) . atomicModifyIORef
|
|
|
|
|
|
|
|
instance MonadIO m => MonadFile (Lazy m) where
|
|
|
|
readFile = liftIO . BS.readFile
|
|
|
|
|
|
|
|
instance MonadCatch m => MonadCatch (Lazy m) where
|
|
|
|
catch (Lazy (ReaderT m)) f = Lazy $ ReaderT $ \e ->
|
|
|
|
catch (m e) ((`runReaderT` e) . runLazy . f)
|
|
|
|
|
|
|
|
instance MonadThrow m => MonadThrow (Lazy m) where
|
|
|
|
throwM = Lazy . throwM
|
|
|
|
|
2018-04-14 03:09:12 +02:00
|
|
|
instance (MonadFix m, MonadCatch m, MonadThrow m, MonadIO m)
|
|
|
|
=> MonadEffects (Lazy m) where
|
2018-04-11 06:01:48 +02:00
|
|
|
addPath path = do
|
|
|
|
(exitCode, out, _) <-
|
|
|
|
liftIO $ readProcessWithExitCode "nix-store" ["--add", path] ""
|
|
|
|
case exitCode of
|
|
|
|
ExitSuccess -> do
|
|
|
|
let dropTrailingLinefeed p = take (length p - 1) p
|
|
|
|
return $ StorePath $ dropTrailingLinefeed out
|
|
|
|
_ -> throwError $ "addPath: failed: nix-store --add " ++ show path
|
|
|
|
|
|
|
|
makeAbsolutePath origPath = do
|
|
|
|
absPath <- if isAbsolute origPath then pure origPath else do
|
|
|
|
cwd <- do
|
|
|
|
mres <- lookupVar @_ @(NThunk (Lazy m)) "__cur_file"
|
|
|
|
case mres of
|
|
|
|
Nothing -> liftIO getCurrentDirectory
|
|
|
|
Just v -> force v $ \case
|
|
|
|
NVPath s -> return $ takeDirectory s
|
|
|
|
v -> throwError $ "when resolving relative path,"
|
|
|
|
++ " __cur_file is in scope,"
|
|
|
|
++ " but is not a path; it is: "
|
2018-04-11 06:11:46 +02:00
|
|
|
++ show v
|
2018-04-14 01:37:11 +02:00
|
|
|
pure $ cwd <///> origPath
|
2018-04-11 06:01:48 +02:00
|
|
|
liftIO $ removeDotDotIndirections <$> canonicalizePath absPath
|
|
|
|
|
2018-04-14 03:09:12 +02:00
|
|
|
findEnvPath = findEnvPathM
|
2018-04-13 09:52:09 +02:00
|
|
|
|
2018-04-11 06:01:48 +02:00
|
|
|
pathExists = liftIO . fileExist
|
|
|
|
|
|
|
|
-- jww (2018-03-29): Cache which files have been read in.
|
|
|
|
importPath scope origPath = do
|
|
|
|
path <- liftIO $ pathToDefaultNixFile origPath
|
|
|
|
mres <- lookupVar @(Context (Lazy m) (NThunk (Lazy m)))
|
|
|
|
"__cur_file"
|
|
|
|
path' <- case mres of
|
|
|
|
Nothing -> do
|
|
|
|
traceM "No known current directory"
|
|
|
|
return path
|
|
|
|
Just p -> force p $ normalForm >=> \case
|
|
|
|
Fix (NVPath p') -> do
|
|
|
|
traceM $ "Current file being evaluated is: "
|
|
|
|
++ show p'
|
|
|
|
return $ takeDirectory p' </> path
|
|
|
|
x -> error $ "How can the current directory be: " ++ show x
|
|
|
|
|
|
|
|
traceM $ "Importing file " ++ path'
|
|
|
|
|
|
|
|
withStringContext ("While importing file " ++ show path') $ do
|
|
|
|
eres <- Lazy $ parseNixFileLoc path'
|
|
|
|
case eres of
|
|
|
|
Failure err -> error $ "Parse failed: " ++ show err
|
|
|
|
Success expr -> do
|
|
|
|
let ref = value @_ @_ @(Lazy m) (NVPath path')
|
|
|
|
-- Use this cookie so that when we evaluate the next
|
|
|
|
-- import, we'll remember which directory its containing
|
|
|
|
-- file was in.
|
|
|
|
pushScope (M.singleton "__cur_file" ref)
|
|
|
|
(pushScope scope (framedEvalExpr Eval.eval expr))
|
|
|
|
|
|
|
|
getEnvVar = liftIO . lookupEnv
|
|
|
|
|
|
|
|
getCurrentSystemOS = return $ Text.pack System.Info.os
|
|
|
|
|
|
|
|
-- Invert the conversion done by GHC_CONVERT_CPU in GHC's aclocal.m4
|
|
|
|
getCurrentSystemArch = return $ Text.pack $ case System.Info.arch of
|
|
|
|
"i386" -> "i686"
|
|
|
|
arch -> arch
|
|
|
|
|
|
|
|
listDirectory = liftIO . System.Directory.listDirectory
|
|
|
|
getSymbolicLinkStatus = liftIO . System.Posix.Files.getSymbolicLinkStatus
|
|
|
|
|
|
|
|
derivationStrict v = do
|
|
|
|
v' <- normalForm v
|
2018-04-11 22:56:18 +02:00
|
|
|
nixInstantiateExpr $ "derivationStrict " ++ show (prettyNixValue v')
|
|
|
|
|
|
|
|
nixInstantiateExpr expr = do
|
2018-04-11 06:01:48 +02:00
|
|
|
(exitCode, out, _) <-
|
|
|
|
liftIO $ readProcessWithExitCode "nix-instantiate"
|
2018-04-11 22:56:18 +02:00
|
|
|
[ "--eval", "--expr", expr] ""
|
2018-04-11 06:01:48 +02:00
|
|
|
case exitCode of
|
|
|
|
ExitSuccess ->
|
2018-04-11 22:56:18 +02:00
|
|
|
case parseNixTextLoc (Text.pack out) of
|
|
|
|
Failure err ->
|
|
|
|
throwError $ "Error parsing output of nix-instantiate: "
|
|
|
|
++ show err
|
2018-04-14 04:29:18 +02:00
|
|
|
Success v -> framedEvalExpr Eval.eval v
|
2018-04-11 22:56:18 +02:00
|
|
|
err -> throwError $ "nix-instantiate failed: " ++ show err
|
2018-04-11 06:01:48 +02:00
|
|
|
|
|
|
|
runLazyM :: MonadIO m => Lazy m a -> m a
|
2018-04-11 20:53:30 +02:00
|
|
|
runLazyM = flip runReaderT newContext . runLazy
|
2018-04-11 06:01:48 +02:00
|
|
|
|
|
|
|
-- | 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
|
|
|
|
|
|
|
|
-- Given a path, determine the nix file to load
|
|
|
|
pathToDefaultNixFile :: FilePath -> IO FilePath
|
|
|
|
pathToDefaultNixFile p = do
|
|
|
|
isDir <- doesDirectoryExist p
|
2018-04-14 01:37:11 +02:00
|
|
|
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 ]
|
2018-04-13 19:38:57 +02:00
|
|
|
|
2018-04-13 19:43:18 +02:00
|
|
|
nixFilePath :: (MonadEffects m, MonadIO m) => FilePath -> m (Maybe FilePath)
|
|
|
|
nixFilePath path = do
|
|
|
|
path <- makeAbsolutePath path
|
|
|
|
exists <- liftIO $ doesDirectoryExist path
|
2018-04-14 01:37:11 +02:00
|
|
|
path' <- if exists
|
|
|
|
then makeAbsolutePath $ path </> "default.nix"
|
|
|
|
else return path
|
2018-04-13 19:43:18 +02:00
|
|
|
exists <- liftIO $ doesFileExist path'
|
|
|
|
return $ if exists then Just path' else Nothing
|
2018-04-14 03:09:12 +02:00
|
|
|
|
|
|
|
findEnvPathM :: forall e m. (MonadNix e m, MonadIO m)
|
|
|
|
=> FilePath -> m FilePath
|
|
|
|
findEnvPathM name = do
|
|
|
|
mres <- lookupVar @_ @(NThunk m) "__nixPath"
|
|
|
|
mpath <- case mres of
|
|
|
|
Nothing -> error "impossible"
|
2018-04-16 04:05:44 +02:00
|
|
|
Just x -> force x $ fromValue >=> \(l :: [NThunk m]) ->
|
2018-04-15 09:51:50 +02:00
|
|
|
foldM go Nothing l
|
2018-04-14 03:09:12 +02:00
|
|
|
case mpath of
|
|
|
|
Nothing ->
|
|
|
|
throwError $ "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 -> NThunk m -> m (Maybe FilePath)
|
|
|
|
go p@(Just _) _ = pure p
|
2018-04-16 04:05:44 +02:00
|
|
|
go Nothing l = force l $ fromValue >=> \(s :: HashMap Text (NThunk m)) ->
|
2018-04-14 03:09:12 +02:00
|
|
|
case M.lookup "path" s of
|
2018-04-16 04:05:44 +02:00
|
|
|
Just p -> force p $ fromValue >=> \(Path path) ->
|
2018-04-14 03:09:12 +02:00
|
|
|
case M.lookup "prefix" s of
|
|
|
|
Nothing -> tryPath path Nothing
|
2018-04-16 05:22:24 +02:00
|
|
|
Just pf -> force pf $ fromValueMay >=> \case
|
2018-04-14 03:09:12 +02:00
|
|
|
Just (pfx :: Text) | not (Text.null pfx) ->
|
|
|
|
tryPath path (Just (Text.unpack pfx))
|
|
|
|
_ -> tryPath path Nothing
|
|
|
|
Nothing ->
|
|
|
|
throwError $ "__nixPath must be a list of attr sets"
|
|
|
|
++ " with 'path' elements, but saw: " ++ show s
|
|
|
|
|
|
|
|
tryPath p (Just n) | n':ns <- splitDirectories name, n == n' =
|
|
|
|
nixFilePath $ p <///> joinPath ns
|
|
|
|
tryPath p _ = nixFilePath $ p <///> name
|