Split off file monitoring types into Cabal library
[cabal.git] / Cabal / src / Distribution / Simple / SetupHooks / Rule.hs
blob7e750245085f7dad333b38f180dd0bbd73d4ccff
1 {-# LANGUAGE CPP #-}
2 {-# LANGUAGE ConstraintKinds #-}
3 {-# LANGUAGE DeriveAnyClass #-}
4 {-# LANGUAGE DeriveGeneric #-}
5 {-# LANGUAGE DerivingStrategies #-}
6 {-# LANGUAGE GADTs #-}
7 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
8 {-# LANGUAGE InstanceSigs #-}
9 {-# LANGUAGE KindSignatures #-}
10 {-# LANGUAGE LambdaCase #-}
11 {-# LANGUAGE MultiWayIf #-}
12 {-# LANGUAGE NamedFieldPuns #-}
13 {-# LANGUAGE QuantifiedConstraints #-}
14 {-# LANGUAGE RankNTypes #-}
15 {-# LANGUAGE ScopedTypeVariables #-}
16 {-# LANGUAGE StandaloneDeriving #-}
17 {-# LANGUAGE TypeApplications #-}
18 {-# LANGUAGE UndecidableInstances #-}
20 -- |
21 -- Module: Distribution.Simple.SetupHooks.Rule
23 -- Internal module that defines fine-grained rules for setup hooks.
24 -- Users should import 'Distribution.Simple.SetupHooks' instead.
25 module Distribution.Simple.SetupHooks.Rule
26 ( -- * Rules
28 -- ** Rule
29 Rule (..)
30 , RuleId (..)
31 , staticRule
32 , dynamicRule
34 -- ** Commands
35 , RuleCommands (..)
36 , Command (..)
37 , runCommand
38 , mkCommand
39 , Dict (..)
41 -- *** Helpers for executing commands
42 , RuleCmds
43 , RuleDynDepsCmd
44 , RuleExecCmd
45 , DynDepsCmd (..)
46 , DepsRes (..)
47 , ruleDepsCmd
48 , runRuleDynDepsCmd
49 , ruleExecCmd
50 , runRuleExecCmd
52 -- ** Collections of rules
53 , Rules (..)
54 , Dependency (..)
55 , RuleOutput (..)
56 , rules
57 , noRules
59 -- ** Rule inputs/outputs
60 , Location
62 -- ** File/directory monitoring
63 , MonitorFilePath (..)
64 , MonitorKindFile (..)
65 , MonitorKindDir (..)
67 -- *** Monadic API for generation of 'ActionId'
68 , RulesM
69 , RulesT (..)
70 , RulesEnv (..)
71 , computeRules
73 where
75 import qualified Distribution.Compat.Binary as Binary
76 import Distribution.Compat.Prelude
78 import Distribution.Simple.FileMonitor.Types
79 import Distribution.Types.UnitId
80 import Distribution.Utils.ShortText
81 ( ShortText
83 import Distribution.Verbosity
84 ( Verbosity
87 import Control.Monad.Fix
88 ( MonadFix
90 import Control.Monad.Trans
91 ( MonadIO
92 , MonadTrans (..)
94 import qualified Control.Monad.Trans.Reader as Reader
95 import qualified Control.Monad.Trans.State as State
96 #if MIN_VERSION_transformers(0,5,6)
97 import qualified Control.Monad.Trans.Writer.CPS as Writer
98 #else
99 import qualified Control.Monad.Trans.Writer.Strict as Writer
100 #endif
101 import qualified Data.ByteString.Lazy as LBS
102 import qualified Data.List.NonEmpty as NE
103 import qualified Data.Map.Strict as Map
104 ( empty
107 import qualified Data.Kind as Hs
108 import Data.Type.Equality
109 ( (:~:) (Refl)
110 , (:~~:) (HRefl)
112 import Data.Typeable
113 ( eqT
115 import GHC.Show (showCommaSpace)
116 import GHC.StaticPtr
117 import System.IO.Unsafe
118 ( unsafePerformIO
120 import qualified Type.Reflection as Typeable
121 ( SomeTypeRep (..)
122 , TypeRep
123 , eqTypeRep
124 , typeRep
125 , typeRepKind
126 , withTypeable
129 --------------------------------------------------------------------------------
131 {- Note [Fine-grained hooks]
132 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
133 To best understand how the framework of fine-grained build rules
134 fits into Cabal and the greater Haskell ecosystem, it is helpful to think
135 that we want build tools (such as cabal-install or HLS) to be able to call
136 individual build rules on-demand, so that e.g. when a user modifies a .xyz file
137 the associated preprocessor is re-run.
139 To do this, we need to perform two different kinds of invocations:
141 Query: query the package for the rules that it provides, with their
142 dependency information. This allows one to determine when each
143 rule should be rerun.
145 (For example, if one rule preprocesses *.xyz into *.hs, we need to
146 re-run the rule whenever *.xyz is modified.)
148 Run: run the relevant action, once one has determined that the rule
149 has gone stale.
151 To do this, any Cabal package with Hooks build-type provides a SetupHooks
152 module which supports these queries; for example it can be compiled into
153 a separate executable which can be invoked in the manner described above.
156 ---------
157 -- Rules
159 -- | A unique identifier for a t'Rule'.
160 data RuleId = RuleId
161 { ruleUnitId :: !UnitId
162 , ruleName :: !ShortText
164 deriving stock (Show, Eq, Ord, Generic)
165 deriving anyclass (Binary, Structured)
167 -- | A rule consists of:
169 -- - an action to run to execute the rule,
170 -- - a description of the rule inputs and outputs.
172 -- Use 'staticRule' or 'dynamicRule' to construct a rule, overriding specific
173 -- fields, rather than directly using the 'Rule' constructor.
174 data Rule
175 = -- | Please use the 'staticRule' or 'dynamicRule' smart constructors
176 -- instead of this constructor, in order to avoid relying on internal
177 -- implementation details.
178 Rule
179 { ruleCommands :: !RuleCmds
180 -- ^ To run this rule, which t'Command's should we execute?
181 , staticDependencies :: ![Dependency]
182 -- ^ Static dependencies of this rule.
183 , results :: !(NE.NonEmpty Location)
184 -- ^ Results of this rule.
186 deriving stock (Show, Eq, Generic)
187 deriving anyclass (Binary)
189 -- | A rule with static dependencies.
191 -- Prefer using this smart constructor instead of v'Rule' whenever possible.
192 staticRule
193 :: Typeable arg
194 => Command arg (IO ())
195 -> [Dependency]
196 -> NE.NonEmpty Location
197 -> Rule
198 staticRule cmd dep res =
199 Rule
200 { ruleCommands = StaticRuleCommand{staticRuleCommand = cmd}
201 , staticDependencies = dep
202 , results = res
205 -- | A rule with dynamic dependencies.
207 -- Prefer using this smart constructor instead of v'Rule' whenever possible.
208 dynamicRule
209 :: (Typeable depsArg, Typeable depsRes, Typeable arg)
210 => StaticPtr (Dict (Binary depsRes, Show depsRes, Eq depsRes))
211 -> Command depsArg (IO ([Dependency], depsRes))
212 -> Command arg (depsRes -> IO ())
213 -> [Dependency]
214 -> NE.NonEmpty Location
215 -> Rule
216 dynamicRule dict depsCmd action dep res =
217 Rule
218 { ruleCommands =
219 DynamicRuleCommands
220 { dynamicRuleInstances = dict
221 , dynamicDeps = DynDepsCmd{dynDepsCmd = depsCmd}
222 , dynamicRuleCommand = action
224 , staticDependencies = dep
225 , results = res
228 -----------------------
229 -- Rule inputs/outputs
231 -- | A (fully resolved) location of a dependency or result of a rule,
232 -- consisting of a base directory and of a file path relative to that base
233 -- directory path.
235 -- In practice, this will be something like @( dir, toFilePath modName )@,
236 -- where:
238 -- - for a file dependency, @dir@ is one of the Cabal search directories,
239 -- - for an output, @dir@ is a directory such as @autogenComponentModulesDir@
240 -- or @componentBuildDir@.
241 type Location = (FilePath, FilePath)
243 -- The reason for splitting it up this way is that some pre-processors don't
244 -- simply generate one output @.hs@ file from one input file, but have
245 -- dependencies on other generated files (notably @c2hs@, where building one
246 -- @.hs@ file may require reading other @.chi@ files, and then compiling the
247 -- @.hs@ file may require reading a generated @.h@ file).
248 -- In these cases, the generated files need to embed relative path names to each
249 -- other (eg the generated @.hs@ file mentions the @.h@ file in the FFI imports).
250 -- This path must be relative to the base directory where the generated files
251 -- are located; it cannot be relative to the top level of the build tree because
252 -- the compilers do not look for @.h@ files relative to there, ie we do not use
253 -- @-I .@, instead we use @-I dist/build@ (or whatever dist dir has been set
254 -- by the user).
256 -- | A dependency of a rule.
257 data Dependency
258 = -- | A dependency on an output of another rule.
259 RuleDependency !RuleOutput
260 | -- | A direct dependency on a file at a particular location on disk.
262 -- This should not be used for files that are generated by other rules;
263 -- use 'RuleDependency' instead.
264 FileDependency !Location
265 deriving stock (Show, Eq, Ord, Generic)
266 deriving anyclass (Binary, Structured)
268 -- | A reference to an output of another rule.
269 data RuleOutput = RuleOutput
270 { outputOfRule :: !RuleId
271 -- ^ which rule's outputs are we referring to?
272 , outputIndex :: !Word
273 -- ^ which particular output of that rule?
275 deriving stock (Show, Eq, Ord, Generic)
276 deriving anyclass (Binary, Structured)
278 ---------
279 -- Rules
281 -- | Monad for constructing rules.
282 type RulesM a = RulesT IO a
284 -- | The environment within the monadic API.
285 data RulesEnv = RulesEnv
286 { rulesEnvVerbosity :: !Verbosity
287 , rulesEnvUnitId :: !UnitId
290 -- | Monad transformer for defining rules. Usually wraps the 'IO' monad,
291 -- allowing @IO@ actions to be performed using @liftIO@.
292 newtype RulesT m a = RulesT
293 { runRulesT
294 :: Reader.ReaderT
295 RulesEnv
296 ( State.StateT
297 (Map RuleId Rule)
298 (Writer.WriterT [MonitorFilePath] m)
302 deriving newtype (Functor, Applicative, Monad, MonadIO, MonadFix)
304 instance MonadTrans RulesT where
305 lift = RulesT . lift . lift . lift
307 -- | A collection of t'Rule's.
309 -- Use the 'rules' smart constructor instead of directly using the v'Rules'
310 -- constructor.
312 -- - Rules are registered using 'registerRule',
313 -- - Monitored files or directories are declared using 'addRuleMonitors';
314 -- a change in these will trigger the recomputation of all rules.
316 -- The @env@ type parameter represents an extra argument, which usually
317 -- consists of information known to Cabal such as 'LocalBuildInfo' and
318 -- 'ComponentLocalBuildInfo'.
319 newtype Rules env = Rules {runRules :: env -> RulesM ()}
321 -- | __Warning__: this 'Semigroup' instance is not commutative.
322 instance Semigroup (Rules env) where
323 (Rules rs1) <> (Rules rs2) =
324 Rules $ \inputs -> do
325 y1 <- rs1 inputs
326 y2 <- rs2 inputs
327 return $ y1 <> y2
329 instance Monoid (Rules env) where
330 mempty = Rules $ const noRules
332 -- | An empty collection of rules.
333 noRules :: RulesM ()
334 noRules = return ()
336 -- | Construct a collection of rules.
338 -- Usage:
340 -- > myRules :: Rules env
341 -- > myRules = rules $ static f
342 -- > where
343 -- > f :: env -> RulesM ()
344 -- > f env = do { ... } -- use the monadic API here
345 rules
346 :: StaticPtr (env -> RulesM ())
347 -- ^ a static computation of rules
348 -> Rules env
349 rules f = Rules $ \env -> RulesT $ do
350 Reader.withReaderT (\rulesEnv -> rulesEnv{rulesEnvUnitId = unitId}) $
351 runRulesT $
352 deRefStaticPtr f env
353 where
354 unitId = mkUnitId $ spInfoUnitId $ staticPtrInfo f
356 -- | Internal function: run the monadic 'Rules' computations in order
357 -- to obtain all the 'Rule's with their 'RuleId's.
358 computeRules
359 :: Verbosity
360 -> env
361 -> Rules env
362 -> IO (Map RuleId Rule, [MonitorFilePath])
363 computeRules verbosity inputs (Rules rs) = do
364 -- Bogus UnitId to start with. This will be the first thing
365 -- to be set when users use the 'rules' smart constructor.
366 let noUnitId = mkUnitId ""
367 env0 =
368 RulesEnv
369 { rulesEnvVerbosity = verbosity
370 , rulesEnvUnitId = noUnitId
372 Writer.runWriterT $
373 (`State.execStateT` Map.empty) $
374 (`Reader.runReaderT` env0) $
375 runRulesT $
376 rs inputs
378 ------------
379 -- Commands
381 -- | A command consists of a statically-known action together with a
382 -- (possibly dynamic) argument to that action.
384 -- For example, the action can consist of running an executable
385 -- (such as @happy@ or @c2hs@), while the argument consists of the variable
386 -- component of the command, e.g. the specific file to run @happy@ on.
387 data Command arg res = Command
388 { actionPtr :: !(StaticPtr (arg -> res))
389 -- ^ The (statically-known) action to execute.
390 , actionArg :: !arg
391 -- ^ The (possibly dynamic) argument to pass to the action.
392 , cmdInstances :: !(StaticPtr (Dict (Binary arg, Show arg)))
393 -- ^ Static evidence that the argument can be serialised and deserialised.
396 -- | Construct a command.
398 -- Prefer using this smart constructor instead of v'Command' whenever possible.
399 mkCommand
400 :: forall arg res
401 . StaticPtr (Dict (Binary arg, Show arg))
402 -> StaticPtr (arg -> res)
403 -> arg
404 -> Command arg res
405 mkCommand dict actionPtr arg =
406 Command
407 { actionPtr = actionPtr
408 , actionArg = arg
409 , cmdInstances = dict
412 -- | Run a 'Command'.
413 runCommand :: Command args res -> res
414 runCommand (Command{actionPtr = ptr, actionArg = arg}) =
415 deRefStaticPtr ptr arg
417 -- | Commands to execute a rule:
419 -- - for a rule with static dependencies, a single command,
420 -- - for a rule with dynamic dependencies, a command for computing dynamic
421 -- dependencies, and a command for executing the rule.
422 data
423 RuleCommands
424 (deps :: Hs.Type -> Hs.Type -> Hs.Type)
425 (ruleCmd :: Hs.Type -> Hs.Type -> Hs.Type)
426 where
427 -- | A rule with statically-known dependencies.
428 StaticRuleCommand
429 :: forall arg deps ruleCmd
430 . Typeable arg
431 => { staticRuleCommand :: !(ruleCmd arg (IO ()))
432 -- ^ The command to execute the rule.
434 -> RuleCommands deps ruleCmd
435 DynamicRuleCommands
436 :: forall depsArg depsRes arg deps ruleCmd
437 . (Typeable depsArg, Typeable depsRes, Typeable arg)
438 => { dynamicRuleInstances :: !(StaticPtr (Dict (Binary depsRes, Show depsRes, Eq depsRes)))
439 -- ^ A rule with dynamic dependencies, which consists of two parts:
441 -- - a dynamic dependency computation, that returns additional edges to
442 -- be added to the build graph together with an additional piece of data,
443 -- - the command to execute the rule itself, which receives the additional
444 -- piece of data returned by the dependency computation.
445 , -- \^ Static evidence used for serialisation, in order to pass the result
446 -- of the dependency computation to the main rule action.
447 dynamicDeps :: !(deps depsArg depsRes)
448 -- ^ A dynamic dependency computation. The resulting dependencies
449 -- will be injected into the build graph, and the result of the computation
450 -- will be passed on to the command that executes the rule.
451 , dynamicRuleCommand :: !(ruleCmd arg (depsRes -> IO ()))
452 -- ^ The command to execute the rule. It will receive the result
453 -- of the dynamic dependency computation.
455 -> RuleCommands deps ruleCmd
457 -- | A placeholder for a command that has been omitted, e.g. when we don't
458 -- care about serialising/deserialising one particular command in a datatype.
459 data NoCmd arg res = CmdOmitted
460 deriving stock (Generic, Eq, Ord, Show)
461 deriving anyclass (Binary)
463 -- | A dynamic dependency command.
464 newtype DynDepsCmd depsArg depsRes = DynDepsCmd {dynDepsCmd :: Command depsArg (IO ([Dependency], depsRes))}
465 deriving newtype (Show, Eq, Binary)
467 -- | The result of a dynamic dependency computation.
468 newtype DepsRes depsArg depsRes = DepsRes {depsRes :: depsRes}
469 deriving newtype (Show, Eq, Binary)
471 -- | Both the rule command and the (optional) dynamic dependency command.
472 type RuleCmds = RuleCommands DynDepsCmd Command
474 -- | Only the (optional) dynamic dependency command.
475 type RuleDynDepsCmd = RuleCommands DynDepsCmd NoCmd
477 -- | The rule command together with the result of the (optional) dynamic
478 -- dependency computation.
479 type RuleExecCmd = RuleCommands DepsRes Command
481 -- | Project out the (optional) dependency computation command, so that
482 -- it can be serialised without serialising anything else.
483 ruleDepsCmd :: RuleCmds -> RuleDynDepsCmd
484 ruleDepsCmd = \case
485 StaticRuleCommand{staticRuleCommand = _ :: Command args (IO ())} ->
486 StaticRuleCommand{staticRuleCommand = CmdOmitted :: NoCmd args (IO ())}
487 DynamicRuleCommands
488 { dynamicRuleCommand = _ :: Command args (depsRes -> IO ())
489 , dynamicRuleInstances = instsPtr
490 , dynamicDeps = deps
491 } ->
492 DynamicRuleCommands
493 { dynamicRuleInstances = instsPtr
494 , dynamicDeps = deps
495 , dynamicRuleCommand = CmdOmitted :: NoCmd args (depsRes -> IO ())
498 -- | Obtain the (optional) 'IO' action that computes dynamic dependencies.
499 runRuleDynDepsCmd :: RuleDynDepsCmd -> Maybe (IO ([Dependency], LBS.ByteString))
500 runRuleDynDepsCmd = \case
501 StaticRuleCommand{} -> Nothing
502 DynamicRuleCommands
503 { dynamicRuleInstances = instsPtr
504 , dynamicDeps = DynDepsCmd{dynDepsCmd = depsCmd}
506 | Dict <- deRefStaticPtr instsPtr ->
507 Just $ do
508 (deps, depsRes) <- runCommand depsCmd
509 return $ (deps, Binary.encode depsRes)
511 -- | Project out the command for running the rule, passing in the result of
512 -- the dependency computation if there was one.
513 ruleExecCmd :: RuleCmds -> Maybe LBS.ByteString -> RuleExecCmd
514 ruleExecCmd (StaticRuleCommand{staticRuleCommand = cmd}) _ =
515 StaticRuleCommand{staticRuleCommand = cmd}
516 ruleExecCmd
517 ( DynamicRuleCommands
518 { dynamicRuleInstances = instsPtr
519 , dynamicRuleCommand = cmd :: Command arg (depsRes -> IO ())
520 , dynamicDeps = _ :: DynDepsCmd depsArg depsRes
523 mbDepsResBinary =
524 case mbDepsResBinary of
525 Nothing ->
526 error $
527 unlines
528 [ "Missing ByteString argument in 'ruleExecCmd'."
529 , "Run 'runRuleDynDepsCmd' on the rule to obtain this data."
531 Just depsResBinary
532 | Dict <- deRefStaticPtr instsPtr ->
533 DynamicRuleCommands
534 { dynamicRuleInstances = instsPtr
535 , dynamicRuleCommand = cmd
536 , dynamicDeps = DepsRes (Binary.decode depsResBinary) :: DepsRes depsArg depsRes
539 -- | Obtain the 'IO' action that executes a rule.
540 runRuleExecCmd :: RuleExecCmd -> IO ()
541 runRuleExecCmd = \case
542 StaticRuleCommand{staticRuleCommand = cmd} -> runCommand cmd
543 DynamicRuleCommands{dynamicDeps = DepsRes res, dynamicRuleCommand = cmd} ->
544 runCommand cmd res
546 --------------------------------------------------------------------------------
547 -- Instances
549 -- | A wrapper used to pass evidence of a constraint as an explicit value.
550 data Dict c where
551 Dict :: c => Dict c
553 instance Show (Command arg res) where
554 showsPrec prec (Command{actionPtr = cmdPtr, actionArg = arg, cmdInstances = insts})
555 | Dict <- deRefStaticPtr insts =
556 showParen (prec >= 11) $
557 showString "Command {"
558 . showString "actionPtrKey = "
559 . shows (staticKey cmdPtr)
560 . showCommaSpace
561 . showString "actionArg = "
562 . shows arg
563 . showString "}"
565 instance Eq (Command arg res) where
566 Command{actionPtr = cmdPtr1, actionArg = arg1, cmdInstances = insts1}
567 == Command{actionPtr = cmdPtr2, actionArg = arg2, cmdInstances = insts2}
568 | staticKey cmdPtr1 == staticKey cmdPtr2
569 , staticKey insts1 == staticKey insts2
570 , Dict <- deRefStaticPtr insts1 =
571 Binary.encode arg1 == Binary.encode arg2
572 | otherwise =
573 False
575 instance Binary (Command arg res) where
576 put (Command{actionPtr = cmdPtr, actionArg = arg, cmdInstances = insts})
577 | Dict <- deRefStaticPtr insts =
579 put (staticKey cmdPtr)
580 put (staticKey insts)
581 put arg
582 get = do
583 cmdKey <- get @StaticKey
584 instsKey <- get @StaticKey
585 case unsafePerformIO $ unsafeLookupStaticPtr cmdKey of
586 Just cmdPtr
587 | Just instsPtr <- unsafePerformIO $ unsafeLookupStaticPtr instsKey
588 , Dict <- deRefStaticPtr @(Dict (Binary arg, Show arg)) instsPtr ->
590 arg <- get
591 return $ Command{actionPtr = cmdPtr, actionArg = arg, cmdInstances = instsPtr}
592 _ -> error "failed to look up static pointer key for action"
594 instance
595 ( forall arg res. Show (ruleCmd arg res)
596 , forall depsArg depsRes. Show depsRes => Show (deps depsArg depsRes)
598 => Show (RuleCommands deps ruleCmd)
599 where
600 showsPrec prec (StaticRuleCommand{staticRuleCommand = cmd}) =
601 showParen (prec >= 11) $
602 showString "StaticRuleCommand {"
603 . showString "staticRuleCommand = "
604 . shows cmd
605 . showString "}"
606 showsPrec
607 prec
608 ( DynamicRuleCommands
609 { dynamicDeps = deps
610 , dynamicRuleCommand = cmd
611 , dynamicRuleInstances = instsPtr
614 | Dict <- deRefStaticPtr instsPtr =
615 showParen (prec >= 11) $
616 showString "DynamicRuleCommands {"
617 . showString "dynamicDeps = "
618 . shows deps
619 . showCommaSpace
620 . showString "dynamicRuleCommand = "
621 . shows cmd
622 . showString "}"
624 instance
625 ( forall arg res. Eq (ruleCmd arg res)
626 , forall depsArg depsRes. Eq depsRes => Eq (deps depsArg depsRes)
628 => Eq (RuleCommands deps ruleCmd)
629 where
630 StaticRuleCommand{staticRuleCommand = ruleCmd1 :: ruleCmd arg1 (IO ())}
631 == StaticRuleCommand{staticRuleCommand = ruleCmd2 :: ruleCmd arg2 (IO ())}
632 | Just Refl <- eqT @arg1 @arg2 =
633 ruleCmd1 == ruleCmd2
634 DynamicRuleCommands
635 { dynamicDeps = depsCmd1 :: deps depsArg1 depsRes1
636 , dynamicRuleCommand = ruleCmd1 :: ruleCmd arg1 (depsRes1 -> IO ())
637 , dynamicRuleInstances = instsPtr1
639 == DynamicRuleCommands
640 { dynamicDeps = depsCmd2 :: deps depsArg2 depsRes2
641 , dynamicRuleCommand = ruleCmd2 :: ruleCmd arg2 (depsRes2 -> IO ())
642 , dynamicRuleInstances = instsPtr2
644 | Just Refl <- eqT @depsArg1 @depsArg2
645 , Just Refl <- eqT @depsRes1 @depsRes2
646 , Just Refl <- eqT @arg1 @arg2
647 , Dict <- deRefStaticPtr instsPtr1 =
648 depsCmd1 == depsCmd2
649 && ruleCmd1 == ruleCmd2
650 && staticKey instsPtr1 == staticKey instsPtr2
651 _ == _ = False
653 instance
654 ( forall arg res. Binary (ruleCmd arg res)
655 , forall depsArg depsRes. Binary depsRes => Binary (deps depsArg depsRes)
657 => Binary (RuleCommands deps ruleCmd)
658 where
659 put = \case
660 StaticRuleCommand{staticRuleCommand = ruleCmd :: ruleCmd arg (IO ())} -> do
661 put @Word 0
662 put $ Typeable.SomeTypeRep (Typeable.typeRep @arg)
663 put ruleCmd
664 DynamicRuleCommands
665 { dynamicDeps = deps :: deps depsArg depsRes
666 , dynamicRuleCommand = ruleCmd :: ruleCmd arg (depsRes -> IO ())
667 , dynamicRuleInstances = instsPtr
668 } | Dict <- deRefStaticPtr instsPtr ->
670 put @Word 1
671 put $ Typeable.SomeTypeRep (Typeable.typeRep @depsArg)
672 put $ Typeable.SomeTypeRep (Typeable.typeRep @depsRes)
673 put $ Typeable.SomeTypeRep (Typeable.typeRep @arg)
674 put $ staticKey instsPtr
675 put ruleCmd
676 put deps
677 get = do
678 tag <- get @Word
679 case tag of
680 0 -> do
681 Typeable.SomeTypeRep (trArg :: Typeable.TypeRep arg) <- get
683 | Just HRefl <- Typeable.eqTypeRep (Typeable.typeRepKind trArg) (Typeable.typeRep @Hs.Type) ->
685 ruleCmd <- get @(ruleCmd arg (IO ()))
686 return $
687 Typeable.withTypeable trArg $
688 StaticRuleCommand
689 { staticRuleCommand = ruleCmd
691 | otherwise ->
692 error "internal error when decoding static rule command"
693 _ -> do
694 Typeable.SomeTypeRep (trDepsArg :: Typeable.TypeRep depsArg) <- get
695 Typeable.SomeTypeRep (trDepsRes :: Typeable.TypeRep depsRes) <- get
696 Typeable.SomeTypeRep (trArg :: Typeable.TypeRep arg) <- get
697 instsKey <- get @StaticKey
699 | Just HRefl <- Typeable.eqTypeRep (Typeable.typeRepKind trDepsArg) (Typeable.typeRep @Hs.Type)
700 , Just HRefl <- Typeable.eqTypeRep (Typeable.typeRepKind trDepsRes) (Typeable.typeRep @Hs.Type)
701 , Just HRefl <- Typeable.eqTypeRep (Typeable.typeRepKind trArg) (Typeable.typeRep @Hs.Type)
702 , Just instsPtr <- unsafePerformIO $ unsafeLookupStaticPtr instsKey
703 , Dict :: Dict (Binary depsRes, Show depsRes, Eq depsRes) <-
704 deRefStaticPtr instsPtr ->
706 ruleCmd <- get @(ruleCmd arg (depsRes -> IO ()))
707 deps <- get @(deps depsArg depsRes)
708 return $
709 Typeable.withTypeable trDepsArg $
710 Typeable.withTypeable trDepsRes $
711 Typeable.withTypeable trArg $
712 DynamicRuleCommands
713 { dynamicDeps = deps
714 , dynamicRuleCommand = ruleCmd
715 , dynamicRuleInstances = instsPtr
717 | otherwise ->
718 error "internal error when decoding dynamic rule commands"