hercules-ci-agent-0.10.9: hercules-ci-agent-worker/Hercules/Agent/Worker/HerculesStore.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
#ifdef __GHCIDE__
# define NIX_IS_AT_LEAST(mm,m,p) 1
#endif
module Hercules.Agent.Worker.HerculesStore
( withHerculesStore,
HerculesStore,
nixStore,
printDiagnostics,
setBuilderCallback,
)
where
import Control.Exception
( catch,
)
import Data.Coerce (coerce)
import Foreign.C.String (withCString)
import Foreign.StablePtr (castStablePtrToPtr, newStablePtr)
import Hercules.Agent.Worker.HerculesStore.Context
( ExceptionPtr,
HerculesStore,
context,
)
import Hercules.CNix.Encapsulation (moveToForeignPtrWrapper)
import Hercules.CNix.Expr (Store (Store))
import Hercules.CNix.Expr.Context (EvalState, NixStorePathWithOutputs)
import Hercules.CNix.Std.Vector (CStdVector, StdVector)
import Hercules.CNix.Store.Context (Ref)
import Language.C.Inline.Cpp qualified as C
import Language.C.Inline.Cpp.Exception qualified as C
import Protolude
import Prelude ()
C.context context
C.include "<cstring>"
C.include "hercules-aliases.h"
C.using "namespace nix"
withHerculesStore ::
Store ->
(Ptr (Ref HerculesStore) -> IO a) ->
IO a
withHerculesStore (Store wrappedStore) =
bracket
( liftIO
[C.block| refHerculesStore* {
refStore &s = *$(refStore *wrappedStore);
refHerculesStore hs(new HerculesStore(s));
return new refHerculesStore(hs);
} |]
)
(\x -> liftIO [C.exp| void { delete $(refHerculesStore* x) } |])
nixStore :: Ptr (Ref HerculesStore) -> Store
nixStore = coerce
printDiagnostics :: Ptr (Ref HerculesStore) -> IO ()
printDiagnostics s =
[C.throwBlock| void{
(*$(refHerculesStore* s))->printDiagnostics();
}|]
{- ORMOLU_DISABLE -}
-- TODO catch pure exceptions from displayException
-- | Set the callback that is invoked when the evaluator requests a build.
--
-- The 'EvalState' is referenced by the exception the callback throws into
-- the evaluator: on Nix >= 2.34 the exception must be recoverable, so that
-- the interrupted attribute can be re-evaluated after its realisation
-- completes. It must remain valid for as long as the callback can be
-- invoked, i.e. for the duration of the evaluation.
setBuilderCallback :: Ptr (Ref HerculesStore) -> Ptr EvalState -> (StdVector NixStorePathWithOutputs -> IO ()) -> IO ()
setBuilderCallback s evalState callback = do
p <-
mkBuilderCallback $ \sp exceptionToThrowPtr ->
Control.Exception.catch (callback =<< moveToForeignPtrWrapper sp) $ \e ->
withCString (displayException (e :: SomeException)) $ \renderedException -> do
stablePtr <- castStablePtrToPtr <$> newStablePtr e
[C.block| void {
#if NIX_IS_AT_LEAST(2, 34, 0)
nix::EvalState *evalState = $(EvalState *evalState);
assert(evalState);
(*$(exception_ptr *exceptionToThrowPtr)) = std::make_exception_ptr(HerculesBuilderException(*evalState, std::string($(const char* renderedException)), $(void* stablePtr)));
#else
(void) $(EvalState *evalState);
(*$(exception_ptr *exceptionToThrowPtr)) = std::make_exception_ptr(HaskellException(std::string($(const char* renderedException)), $(void* stablePtr)));
#endif
}|]
[C.throwBlock| void {
(*$(refHerculesStore* s))->setBuilderCallback($(void (*p)(std::vector<nix::StorePathWithOutputs>*, exception_ptr *)));
}|]
{- ORMOLU_ENABLE -}
type BuilderCallback = Ptr (CStdVector NixStorePathWithOutputs) -> Ptr ExceptionPtr -> IO ()
-- Work around a problem in ghcide with foreign imports.
#ifndef __GHCIDE__
foreign import ccall "wrapper"
mkBuilderCallback :: BuilderCallback -> IO (FunPtr BuilderCallback)
#else
mkBuilderCallback :: BuilderCallback -> IO (FunPtr BuilderCallback)
mkBuilderCallback = panic "This is a stub to work around a ghcide issue. Please compile without -D__GHCIDE__"
#endif