hnix/main/Main.hs

315 lines
12 KiB
Haskell
Raw Normal View History

{-# language MultiWayIf #-}
{-# language TypeFamilies #-}
{-# language RecordWildCards #-}
module Main ( main ) where
import Nix.Prelude
import Relude as Prelude ( force )
import Control.Comonad ( extract )
import qualified Control.Exception as Exception
import GHC.Err ( errorWithoutStackTrace )
import Control.Monad.Free
import Control.Monad.Ref ( MonadRef(readRef) )
import Control.Monad.Catch
import System.IO ( hPutStrLn )
import qualified Data.HashMap.Lazy as M
import qualified Data.Map as Map
2018-05-03 07:32:00 +03:00
import Data.Time
import qualified Data.Text.IO as Text
import Text.Show.Pretty ( ppShow )
import Nix hiding ( force )
import Nix.Convert
2018-12-09 21:57:58 +03:00
import Nix.Json
import Nix.Options.Parser
2019-03-23 01:16:01 +03:00
import Nix.Standard
import Nix.Thunk.Basic
import Nix.Type.Env ( Env(..) )
import Nix.Type.Type ( Scheme )
import qualified Nix.Type.Infer as HM
import Nix.Value.Monad
import Options.Applicative hiding ( ParserResult(..) )
2021-03-30 17:41:17 +03:00
import Prettyprinter hiding ( list )
import Prettyprinter.Render.Text ( renderIO )
import qualified Repl
2021-07-15 21:45:19 +03:00
import Nix.Eval
main :: IO ()
2021-03-30 17:41:17 +03:00
main =
do
2021-11-07 01:21:26 +03:00
currentTime <- getCurrentTime
opts <- execParser $ nixOptionsInfo currentTime
2021-03-06 20:34:16 +03:00
main' opts
2021-03-06 20:34:16 +03:00
main' :: Options -> IO ()
2021-07-15 21:45:19 +03:00
main' opts@Options{..} = runWithBasicEffectsIO opts execContentsFilesOrRepl
2021-03-06 20:34:16 +03:00
where
2021-07-15 21:45:19 +03:00
-- 2021-07-15: NOTE: This logic should be weaved stronger through CLI options logic (OptParse-Applicative code)
-- As this logic is not stated in the CLI documentation, for example. So user has no knowledge of these.
execContentsFilesOrRepl :: StdIO
execContentsFilesOrRepl =
2021-07-15 21:45:19 +03:00
fromMaybe
loadFromCliFilePathList
2021-11-05 18:40:10 +03:00
$ loadBinaryCacheFile <|>
2021-07-15 21:45:19 +03:00
loadLiteralExpression <|>
loadExpressionFromFile
where
-- | The base case: read expressions from the last CLI directive (@[FILE]@) listed on the command line.
loadFromCliFilePathList :: StdIO
loadFromCliFilePathList =
case getFilePaths of
2021-07-15 21:45:19 +03:00
[] -> runRepl
["-"] -> readExpressionFromStdin
_paths -> processSeveralFiles (coerce _paths)
2021-07-15 21:45:19 +03:00
where
-- | Fall back to running the REPL
runRepl = withEmptyNixContext Repl.main
2021-07-15 21:45:19 +03:00
readExpressionFromStdin =
2021-11-05 18:40:10 +03:00
processExpr =<< liftIO Text.getContents
processSeveralFiles :: [Path] -> StdIO
2021-10-13 14:12:59 +03:00
processSeveralFiles = traverse_ processFile
2021-07-15 21:45:19 +03:00
where
processFile path = handleResult (pure path) =<< parseNixFileLoc path
-- | The `--read` option: load expression from a serialized file.
loadBinaryCacheFile :: Maybe StdIO
2021-07-15 21:45:19 +03:00
loadBinaryCacheFile =
2021-08-14 02:10:05 +03:00
(\ (binaryCacheFile :: Path) ->
2021-07-15 21:45:19 +03:00
do
let file = replaceExtension binaryCacheFile "nixc"
2021-11-05 18:40:10 +03:00
processCLIOptions (pure file) =<< liftIO (readCache binaryCacheFile)
) <$> getReadFrom
2021-07-15 21:45:19 +03:00
-- | The `--expr` option: read expression from the argument string
loadLiteralExpression :: Maybe StdIO
loadLiteralExpression = processExpr <$> getExpression
2021-07-15 21:45:19 +03:00
-- | The `--file` argument: read expressions from the files listed in the argument file
loadExpressionFromFile :: Maybe StdIO
2021-07-15 21:45:19 +03:00
loadExpressionFromFile =
-- We can start use Text as in the base case, requires changing Path -> Text
2021-07-15 21:45:19 +03:00
-- But that is a gradual process:
-- https://github.com/haskell-nix/hnix/issues/912
(processSeveralFiles . (coerce . toString <$>) . lines <=< liftIO) .
2021-07-15 21:45:19 +03:00
(\case
"-" -> Text.getContents
2021-08-14 02:24:00 +03:00
_fp -> readFile _fp
) <$> getFromFile
processExpr :: Text -> StdIO
2021-11-05 18:40:10 +03:00
processExpr = handleResult mempty . parseNixTextLoc
2021-03-06 20:34:16 +03:00
2021-07-15 21:45:19 +03:00
withEmptyNixContext = withNixContext mempty
-- 2021-07-15: NOTE: @handleResult@ & @process@ - have atrocious size & compexity, they need to be decomposed & refactored.
handleResult mpath =
either
(\ err ->
bool
errorWithoutStackTrace
(liftIO . hPutStrLn stderr)
isIgnoreErrors
$ "Parse failed: " <> show err
)
2021-03-11 23:17:26 +03:00
(\ expr ->
do
when isCheck $
2021-03-11 23:17:26 +03:00
do
2021-07-15 21:45:19 +03:00
expr' <- liftIO $ reduceExpr mpath expr
2021-03-11 23:17:26 +03:00
either
(\ err -> errorWithoutStackTrace $ "Type error: " <> ppShow err)
2021-11-05 18:40:10 +03:00
(liftIO . putStrLn . (<>) "Type of expression: " .
ppShow . maybeToMonoid . Map.lookup @VarName @[Scheme] "it" . coerce
2021-03-11 23:17:26 +03:00
)
2021-11-05 18:40:10 +03:00
$ HM.inferTop mempty $ curry one "it" $ stripAnnotation expr'
2021-03-11 23:17:26 +03:00
-- liftIO $ putStrLn $ runST $
-- runLintM opts . renderSymbolic =<< lint opts expr
2021-07-15 21:45:19 +03:00
catch (processCLIOptions mpath expr) $
2021-03-11 23:17:26 +03:00
\case
NixException frames ->
2021-03-30 17:41:17 +03:00
errorWithoutStackTrace . show =<<
renderFrames
@StdVal
@StdThun
2021-03-30 17:41:17 +03:00
frames
when isRepl $
2021-07-15 21:45:19 +03:00
withEmptyNixContext $
2021-03-11 23:17:26 +03:00
bool
Repl.main
2021-11-05 18:40:10 +03:00
((Repl.main' . pure) =<< nixEvalExprLoc (coerce mpath) expr)
isEvaluate
2021-03-11 23:17:26 +03:00
)
2021-07-15 21:45:19 +03:00
-- 2021-07-15: NOTE: Logic of CLI Option processing is scattered over several functions, needs to be consolicated.
processCLIOptions :: Maybe Path -> NExprLoc -> StdIO
2021-07-15 21:45:19 +03:00
processCLIOptions mpath expr
| isEvaluate =
2021-03-06 21:02:11 +03:00
if
| isTrace -> evaluateExprWith nixTracingEvalExprLoc expr
2022-01-11 17:24:27 +03:00
| Just path <- getReduce -> evaluateExprWith (reduction path . coerce) expr
| null getArg || null getArgstr -> evaluateExprWith nixEvalExprLoc expr
| otherwise -> processResult printer <=< nixEvalExprLoc (coerce mpath) $ expr
| isXml = fail "Rendering expression trees to XML is not yet implemented"
| isJson = fail "Rendering expression trees to JSON is not implemented"
2022-01-11 17:24:27 +03:00
| getVerbosity >= DebugInfo = liftIO . putStr . ppShow . stripAnnotation $ expr
| isCache , Just path <- mpath = liftIO . writeCache (replaceExtension path "nixc") $ expr
| isParseOnly = void . liftIO . Exception.evaluate . force $ expr
| otherwise =
liftIO .
2021-03-06 21:02:11 +03:00
renderIO
stdout
. layoutPretty (LayoutOptions $ AvailablePerLine 80 0.4)
. prettyNix
. stripAnnotation
$ expr
where
2021-11-06 00:24:32 +03:00
evaluateExprWith evaluator = evaluateExpression (coerce mpath) evaluator printer
printer
:: StdVal
-> StdIO
printer
| isFinder = findAttrs <=< fromValue @(AttrSet StdVal)
| otherwise = printer'
where
2022-01-11 18:20:59 +03:00
-- 2021-05-27: NOTE: With naive fix of the #941
-- This is overall a naive printer implementation, as options should interact/respect one another.
-- A nice question: "Should respect one another to what degree?": Go full combinator way, for which
-- old Nix CLI is nototrious for (and that would mean to reimplement the old Nix CLI),
-- OR: https://github.com/haskell-nix/hnix/issues/172 and have some sane standart/default behaviour for (most) keys.
printer'
2022-01-11 18:20:59 +03:00
| isXml = out (ignoreContext . toXML) normalForm
| isJson = out (ignoreContext . mempty . toJSONNixString) normalForm
| isStrict = out (show . prettyNValue) normalForm
| isValues = out (show . prettyNValueProv) removeEffects
| otherwise = out (show . prettyNValue) removeEffects
where
2022-01-11 18:20:59 +03:00
out
:: (b -> Text)
-> (a -> StandardIO b)
-> a
-> StdIO
2022-01-11 18:20:59 +03:00
out transform val = liftIO . Text.putStrLn . transform <=< val
findAttrs
:: AttrSet StdVal
-> StdIO
findAttrs = go mempty
where
go :: Text -> AttrSet StdVal -> StdIO
2021-03-30 17:41:17 +03:00
go prefix s =
2021-11-05 18:43:00 +03:00
traverse_
(\ (k, mv) ->
do
let
path = prefix <> k
(report, descend) = filterEntry path k
when report $
do
liftIO $ Text.putStrLn path
when descend $
maybe
stub
(\case
NVSet _ s' -> go (path <> ".") s'
_ -> stub
)
mv
)
=<< traverse
2021-03-30 17:41:17 +03:00
(\ (k, nv) ->
2021-05-31 16:40:23 +03:00
(k, ) <$>
2021-04-14 23:23:48 +03:00
free
2021-03-30 17:41:17 +03:00
(\ (StdThunk (extract -> Thunk _ _ ref)) ->
do
let
path = prefix <> k
2021-03-30 17:41:17 +03:00
(_, descend) = filterEntry path k
val <- readRef @StandardIO ref
2021-05-31 16:40:23 +03:00
bool
(pure Nothing)
(forceEntry path nv)
(descend &&
2021-11-05 18:43:00 +03:00
deferred
2022-01-11 17:24:27 +03:00
(const False)
(const True)
val
2021-05-31 16:40:23 +03:00
)
2021-03-30 17:41:17 +03:00
)
2021-05-31 16:40:23 +03:00
(pure . pure . Free)
2021-03-30 17:41:17 +03:00
nv
)
(sortWith fst $ M.toList $ M.mapKeys coerce s)
where
filterEntry path k = case (path, k) of
2021-03-30 17:41:17 +03:00
("stdenv", "stdenv" ) -> (True , True )
(_ , "stdenv" ) -> (False, False)
2021-03-30 17:41:17 +03:00
(_ , "out" ) -> (True , False)
(_ , "src" ) -> (True , False)
(_ , "mirrorsFile" ) -> (True , False)
(_ , "buildPhase" ) -> (True , False)
(_ , "builder" ) -> (False, False)
(_ , "drvPath" ) -> (False, False)
(_ , "outPath" ) -> (False, False)
(_ , "__impureHostDeps") -> (False, False)
(_ , "__sandboxProfile") -> (False, False)
2021-03-30 17:41:17 +03:00
("pkgs" , "pkgs" ) -> (True , True )
(_ , "pkgs" ) -> (False, False)
(_ , "drvAttrs" ) -> (False, False)
2021-03-30 17:41:17 +03:00
_ -> (True , True )
forceEntry
:: MonadValue a StandardIO
=> Text
-> a
-> StandardIO (Maybe a)
forceEntry k v =
2021-03-30 17:41:17 +03:00
catch
2021-04-15 16:05:42 +03:00
(pure <$> demand v)
2021-11-05 18:43:00 +03:00
fun
where
fun :: NixException -> StandardIO (Maybe a)
fun (coerce -> frames) =
do
liftIO
. Text.putStrLn
. (("Exception forcing " <> k <> ": ") <>)
. show =<<
renderFrames
@StdVal
@StdThun
frames
pure Nothing
2021-07-15 21:45:19 +03:00
reduction path mpathToContext annExpr =
2021-03-30 17:41:17 +03:00
do
eres <-
2021-07-15 21:45:19 +03:00
withNixContext
mpathToContext
2021-11-05 18:43:00 +03:00
$ reducingEvalExpr
evalContent
mpathToContext
annExpr
2021-03-30 17:41:17 +03:00
handleReduced path eres
handleReduced
:: (MonadThrow m, MonadIO m)
=> Path
-> (NExprLoc, Either SomeException (NValue t f m))
-> m (NValue t f m)
handleReduced (coerce -> path) (expr', eres) =
2021-03-30 17:41:17 +03:00
do
liftIO $
do
putStrLn $ "Wrote sifted expression tree to " <> path
2021-05-25 14:27:50 +03:00
writeFile path $ show $ prettyNix $ stripAnnotation expr'
either throwM pure eres