2 {-# LANGUAGE ConstraintKinds #-}
3 {-# LANGUAGE DeriveAnyClass #-}
4 {-# LANGUAGE DeriveGeneric #-}
5 {-# LANGUAGE DerivingStrategies #-}
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 #-}
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
41 -- *** Helpers for executing commands
52 -- ** Collections of rules
59 -- ** Rule inputs/outputs
62 -- ** File/directory monitoring
63 , MonitorFilePath
(..)
64 , MonitorKindFile
(..)
67 -- *** Monadic API for generation of 'ActionId'
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
83 import Distribution
.Verbosity
87 import Control
.Monad
.Fix
90 import Control
.Monad
.Trans
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
99 import qualified Control
.Monad
.Trans
.Writer
.Strict
as Writer
101 import qualified Data
.ByteString
.Lazy
as LBS
102 import qualified Data
.List
.NonEmpty
as NE
103 import qualified Data
.Map
.Strict
as Map
107 import qualified Data
.Kind
as Hs
108 import Data
.Type
.Equality
115 import GHC
.Show (showCommaSpace
)
117 import System
.IO.Unsafe
120 import qualified Type
.Reflection
as Typeable
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
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.
159 -- | A unique identifier for a t'Rule'.
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.
175 = -- | Please use the 'staticRule' or 'dynamicRule' smart constructors
176 -- instead of this constructor, in order to avoid relying on internal
177 -- implementation details.
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.
194 => Command arg
(IO ())
196 -> NE
.NonEmpty Location
198 staticRule cmd dep res
=
200 { ruleCommands
= StaticRuleCommand
{staticRuleCommand
= cmd
}
201 , staticDependencies
= dep
205 -- | A rule with dynamic dependencies.
207 -- Prefer using this smart constructor instead of v'Rule' whenever possible.
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 ())
214 -> NE
.NonEmpty Location
216 dynamicRule dict depsCmd action dep res
=
220 { dynamicRuleInstances
= dict
221 , dynamicDeps
= DynDepsCmd
{dynDepsCmd
= depsCmd
}
222 , dynamicRuleCommand
= action
224 , staticDependencies
= dep
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
235 -- In practice, this will be something like @( dir, toFilePath modName )@,
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
256 -- | A dependency of a rule.
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
)
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
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'
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
329 instance Monoid
(Rules env
) where
330 mempty
= Rules
$ const noRules
332 -- | An empty collection of rules.
336 -- | Construct a collection of rules.
340 -- > myRules :: Rules env
341 -- > myRules = rules $ static f
343 -- > f :: env -> RulesM ()
344 -- > f env = do { ... } -- use the monadic API here
346 :: StaticPtr
(env
-> RulesM
())
347 -- ^ a static computation of rules
349 rules f
= Rules
$ \env
-> RulesT
$ do
350 Reader
.withReaderT
(\rulesEnv
-> rulesEnv
{rulesEnvUnitId
= unitId
}) $
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.
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
""
369 { rulesEnvVerbosity
= verbosity
370 , rulesEnvUnitId
= noUnitId
373 (`State
.execStateT` Map
.empty) $
374 (`Reader
.runReaderT` env0
) $
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.
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.
401 . StaticPtr
(Dict
(Binary arg
, Show arg
))
402 -> StaticPtr
(arg
-> res
)
405 mkCommand dict actionPtr arg
=
407 { actionPtr
= actionPtr
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.
424 (deps
:: Hs
.Type
-> Hs
.Type
-> Hs
.Type
)
425 (ruleCmd
:: Hs
.Type
-> Hs
.Type
-> Hs
.Type
)
427 -- | A rule with statically-known dependencies.
429 :: forall arg deps ruleCmd
431 => { staticRuleCommand
:: !(ruleCmd arg
(IO ()))
432 -- ^ The command to execute the rule.
434 -> RuleCommands deps ruleCmd
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
485 StaticRuleCommand
{staticRuleCommand
= _
:: Command args
(IO ())} ->
486 StaticRuleCommand
{staticRuleCommand
= CmdOmitted
:: NoCmd args
(IO ())}
488 { dynamicRuleCommand
= _
:: Command args
(depsRes
-> IO ())
489 , dynamicRuleInstances
= instsPtr
493 { dynamicRuleInstances
= instsPtr
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
503 { dynamicRuleInstances
= instsPtr
504 , dynamicDeps
= DynDepsCmd
{dynDepsCmd
= depsCmd
}
506 | Dict
<- deRefStaticPtr instsPtr
->
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
}
517 ( DynamicRuleCommands
518 { dynamicRuleInstances
= instsPtr
519 , dynamicRuleCommand
= cmd
:: Command arg
(depsRes
-> IO ())
520 , dynamicDeps
= _
:: DynDepsCmd depsArg depsRes
524 case mbDepsResBinary
of
528 [ "Missing ByteString argument in 'ruleExecCmd'."
529 , "Run 'runRuleDynDepsCmd' on the rule to obtain this data."
532 | Dict
<- deRefStaticPtr instsPtr
->
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
} ->
546 --------------------------------------------------------------------------------
549 -- | A wrapper used to pass evidence of a constraint as an explicit value.
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
)
561 . showString "actionArg = "
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
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
)
583 cmdKey
<- get
@StaticKey
584 instsKey
<- get
@StaticKey
585 case unsafePerformIO
$ unsafeLookupStaticPtr cmdKey
of
587 | Just instsPtr
<- unsafePerformIO
$ unsafeLookupStaticPtr instsKey
588 , Dict
<- deRefStaticPtr
@(Dict
(Binary arg
, Show arg
)) instsPtr
->
591 return $ Command
{actionPtr
= cmdPtr
, actionArg
= arg
, cmdInstances
= instsPtr
}
592 _
-> error "failed to look up static pointer key for action"
595 ( forall arg res
. Show (ruleCmd arg res
)
596 , forall depsArg depsRes
. Show depsRes
=> Show (deps depsArg depsRes
)
598 => Show (RuleCommands deps ruleCmd
)
600 showsPrec prec
(StaticRuleCommand
{staticRuleCommand
= cmd
}) =
601 showParen (prec
>= 11) $
602 showString "StaticRuleCommand {"
603 . showString "staticRuleCommand = "
608 ( DynamicRuleCommands
610 , dynamicRuleCommand
= cmd
611 , dynamicRuleInstances
= instsPtr
614 | Dict
<- deRefStaticPtr instsPtr
=
615 showParen (prec
>= 11) $
616 showString "DynamicRuleCommands {"
617 . showString "dynamicDeps = "
620 . showString "dynamicRuleCommand = "
625 ( forall arg res
. Eq
(ruleCmd arg res
)
626 , forall depsArg depsRes
. Eq depsRes
=> Eq
(deps depsArg depsRes
)
628 => Eq
(RuleCommands deps ruleCmd
)
630 StaticRuleCommand
{staticRuleCommand
= ruleCmd1
:: ruleCmd arg1
(IO ())}
631 == StaticRuleCommand
{staticRuleCommand
= ruleCmd2
:: ruleCmd arg2
(IO ())}
632 | Just Refl
<- eqT
@arg1
@arg2
=
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
=
649 && ruleCmd1
== ruleCmd2
650 && staticKey instsPtr1
== staticKey instsPtr2
654 ( forall arg res
. Binary
(ruleCmd arg res
)
655 , forall depsArg depsRes
. Binary depsRes
=> Binary
(deps depsArg depsRes
)
657 => Binary
(RuleCommands deps ruleCmd
)
660 StaticRuleCommand
{staticRuleCommand
= ruleCmd
:: ruleCmd arg
(IO ())} -> do
662 put
$ Typeable
.SomeTypeRep
(Typeable
.typeRep
@arg
)
665 { dynamicDeps
= deps
:: deps depsArg depsRes
666 , dynamicRuleCommand
= ruleCmd
:: ruleCmd arg
(depsRes
-> IO ())
667 , dynamicRuleInstances
= instsPtr
668 } | Dict
<- deRefStaticPtr instsPtr
->
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
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 ()))
687 Typeable
.withTypeable trArg
$
689 { staticRuleCommand
= ruleCmd
692 error "internal error when decoding static rule command"
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
)
709 Typeable
.withTypeable trDepsArg
$
710 Typeable
.withTypeable trDepsRes
$
711 Typeable
.withTypeable trArg
$
714 , dynamicRuleCommand
= ruleCmd
715 , dynamicRuleInstances
= instsPtr
718 error "internal error when decoding dynamic rule commands"