@@ -220,6 +220,9 @@ private final class ToolWrapper {
220220 _delegate. is_result_valid = {
221221 return BuildSystem . toCommandWrapper ( $0!) . isResultValid ( $1!, $2!)
222222 }
223+ _delegate. is_result_valid_with_fallback = {
224+ return BuildSystem . toCommandWrapper ( $0!) . isResultValid ( $1!, $2!, $3!, $4!)
225+ }
223226 } else {
224227 _delegate. execute_command_ex = nil
225228 _delegate. is_result_valid = nil
@@ -362,6 +365,20 @@ public protocol ProducesCustomBuildValue: AnyObject {
362365 /// - command: A handle to the executing command.
363366 /// - buildValue: The most recently computed build value.
364367 func isResultValid( _ command: Command , _ buildValue: BuildValue ) -> Bool
368+
369+ /// Called to check if the current result for this command remains valid.
370+ ///
371+ /// - command: A handle to the executing command.
372+ /// - buildValue: The most recently computed build value.
373+ /// - fallback: The default implementation: llbuild::buildsystem::ExternalCommand::isResultValid().
374+ func isResultValid( _ command: Command , _ buildValue: BuildValue , _ fallback: @escaping ( Command , BuildValue ) -> Bool ) -> Bool
375+ }
376+
377+ public extension ProducesCustomBuildValue {
378+ func isResultValid( _ command: Command , _ buildValue: BuildValue , _ fallback: @escaping ( Command , BuildValue ) -> Bool ) -> Bool {
379+ // This should default to the fallback, but instead we defer to ProducesCustomBuildValue.isResultValid(_:_:) for backward compatibility.
380+ return isResultValid ( command, buildValue)
381+ }
365382}
366383
367384// Extension to provide a default implementation of execute(_ Command, _ commandInterface) and
@@ -545,6 +562,23 @@ private final class CommandWrapper {
545562
546563 return ( command as! ProducesCustomBuildValue ) . isResultValid ( _command, buildValue)
547564 }
565+
566+ func isResultValid(
567+ _: OpaquePointer ,
568+ _ value: OpaquePointer ,
569+ _ fallbackCtx: UnsafeMutableRawPointer ? ,
570+ _ fallback: @escaping ( UnsafeMutableRawPointer ? , OpaquePointer ? , OpaquePointer ? ) -> Bool
571+ ) -> Bool {
572+ guard let buildValue = BuildValue . construct ( from: value) else {
573+ fatalError ( " Could not decode incoming build value. " )
574+ }
575+
576+ func fallbackWrapper( _ command: Command , value: BuildValue ) -> Bool {
577+ return fallback ( fallbackCtx, command. handle, BuildValue . clone ( value) )
578+ }
579+
580+ return ( command as! ProducesCustomBuildValue ) . isResultValid ( _command, buildValue, fallbackWrapper)
581+ }
548582}
549583
550584/// Encapsulates a diagnostic as reported by the build system.
0 commit comments