File: TaskGeneration.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (1357 lines) | stat: -rw-r--r-- 72,317 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

public import Foundation
public import SWBUtil
public import SWBProtocol
public import SWBMacro

/// Base class for spec cache types.
///
/// This protocol is only used for making it explicit which types serve as spec data caches.
public protocol SpecDataCache: Sendable {
    init()
}

/// Descriptor for the user-module status of an individual target.
//
// FIXME: It is rather unfortunate that we need to expose this at this level. Only Swift uses this, currently.
public struct ModuleInfo {
    /// Whether the user-module is only created via the implicit definition from Swift.
    public let forSwiftOnly: Bool

    /// Whether the target exports the Swift generated ObjC API.
    public let exportsSwiftObjCAPI: Bool

    /// The name of the umbrella header to be used in module.modulemap file generation, if it should be generated.
    public let umbrellaHeader: String

    public struct ModuleMapPathInfo {
        /// The path of the .modulemap file in the sources.
        ///
        /// If the source file is defined but empty, then the producer will emit an error.  This will be the same as .moduleMapTmpPath if the module map is synthesized.
        public let sourcePath: Path

        /// The path of the .modulemap file in the staging location for the build.
        ///
        /// If the source file is defined but empty, then the producer will emit an error.
        public let tmpPath: Path

        /// The path of the .modulemap file in the built product location.
        ///
        /// If the source file is defined but empty, then the producer will emit an error.
        public let builtPath: Path
    }

    /// Various paths of the module.modulemap file.
    public let moduleMapPaths: ModuleMapPathInfo

    /// Various paths of the module.private.modulemap file.  Will be nil if there is no private modulemap.
    public let privateModuleMapPaths: ModuleMapPathInfo?

    /// The name of Clang module(s) known to be defined by this target, if any. The list may not be complete in the presence of build scripts, hand authored modulemaps, etc.
    public let knownClangModuleNames: [String]

    public init(forSwiftOnly: Bool, exportsSwiftObjCAPI: Bool, umbrellaHeader: String, moduleMapSourcePath: Path, privateModuleMapSourcePath: Path?, moduleMapTmpPath: Path, privateModuleMapTmpPath: Path?, moduleMapBuiltPath: Path, privateModuleMapBuiltPath: Path?, knownClangModuleNames: [String]) {
        self.forSwiftOnly = forSwiftOnly
        self.exportsSwiftObjCAPI = exportsSwiftObjCAPI
        self.umbrellaHeader = umbrellaHeader
        self.moduleMapPaths = ModuleMapPathInfo(sourcePath: moduleMapSourcePath, tmpPath: moduleMapTmpPath, builtPath: moduleMapBuiltPath)
        if let src = privateModuleMapSourcePath, let tmp = privateModuleMapTmpPath, let built = privateModuleMapBuiltPath {
            self.privateModuleMapPaths = ModuleMapPathInfo(sourcePath: src, tmpPath: tmp, builtPath: built)
        } else {
            self.privateModuleMapPaths = nil
        }
        self.knownClangModuleNames = knownClangModuleNames
    }
}

public protocol PlatformBuildContext {
    var sdk: SDK? { get }
    var sdkVariant: SDKVariant? { get }
    var platform: Platform? { get }
}

extension PlatformBuildContext {
    public var targetBuildVersionPlatform: BuildVersion.Platform? {
        return sdk?.targetBuildVersionPlatform(sdkVariant: sdkVariant)
    }

    public func targetBuildVersionPlatforms(in scope: MacroEvaluationScope) -> Set<BuildVersion.Platform>? {
        if let platform = targetBuildVersionPlatform {
            if (platform == .macOS || platform == .macCatalyst) && scope.evaluate(BuiltinMacros.IS_ZIPPERED) {
                return [.macOS, .macCatalyst]
            }
            return [platform]
        }
        return nil
    }
}

/// Protocol describing the interface producers use to communicate information to the command build context.
public protocol CommandProducer: PlatformBuildContext, SpecLookupContext, ReferenceLookupContext, PlatformInfoLookup {
    /// The configured target the command is being produced for, if any.
    var configuredTarget: ConfiguredTarget? { get }

    /// The product type being built. (Only `StandardTarget`s have product types.)
    var productType: ProductTypeSpec? { get }

    /// The SDK in effect, if any.
    var sdk: SDK? { get }

    /// The SDK variant in effect, if any.
    var sdkVariant: SDKVariant? { get }

    /// The platform in effect.
    var platform: Platform? { get }

    /// The preferred arch for the target.
    ///
    /// This is currently used for indexing.
    var preferredArch: String? { get }

    /// The list of toolchains (in priority order) in effect.
    var toolchains: [Toolchain] { get }

    /// The sparse SDKs in effect.
    var sparseSDKs: [SDK] { get }

    /// The executable search paths to use.
    var executableSearchPaths: StackedSearchPath { get }

    // FIXME: Move these specs out of here, we should just pre-bind these in the core so no one ever needs to look things like that up.

    /// The Clang compiler spec to use.
    var clangSpec: ClangCompilerSpec { get }

    /// The Clang assembler spec to use.
    var clangAssemblerSpec: ClangCompilerSpec { get }

    /// The Clang preprocessor spec to use.
    var clangPreprocessorSpec: ClangCompilerSpec { get }

    /// The Clang static analyzer tool spec to use.
    var clangStaticAnalyzerSpec: ClangCompilerSpec { get }

    /// The Clang modules verifier tool spec to use.
    var clangModuleVerifierSpec: ClangCompilerSpec { get }

    /// The diff spec to use.
    var diffSpec: CommandLineToolSpec { get }

    /// The strip spec to use.
    var stripSpec: StripToolSpec { get }

    /// The swift compiler tool spec to use.
    var swiftCompilerSpec: SwiftCompilerSpec { get }

    /// The linker spec to use.
    var ldLinkerSpec: LdLinkerSpec { get }

    /// The libtool spec to use.
    var libtoolLinkerSpec: LibtoolLinkerSpec { get }

    /// The lipo spec to use.
    var lipoSpec: LipoToolSpec { get }

    /// The code sign tool spec to use.
    var codesignSpec: CodesignToolSpec { get }

    /// The copy tool spec to use.
    var copySpec: CopyToolSpec { get }

    /// The copy-png spec to use.
    var copyPngSpec: CommandLineToolSpec { get }

    /// The copy-tiff spec to use.
    var copyTiffSpec: CommandLineToolSpec { get }

    /// The unifdef spec to use.
    var unifdefSpec: UnifdefToolSpec { get }

    /// The auxiliary file spec to use.
    var writeFileSpec: WriteFileSpec { get }

    /// The mkdir spec to use.
    var mkdirSpec: MkdirToolSpec { get }

    /// The create-build-directory spec to use.
    var createBuildDirectorySpec: CreateBuildDirectorySpec { get }

    var processSDKImportsSpec: ProcessSDKImportsSpec { get }

    /// The default working directory to use for a task, if it doesn't have a stronger preference.
    var defaultWorkingDirectory: Path { get }

    /// Resolve a file reference GUID.
    func lookupReference(for guid: String) -> Reference?

    /// The registry used for spec data caches.
    ///
    /// This is not generally intended to be used directly, clients of task generation should use `getSpecDataCache`.
    var specDataCaches: Registry<Spec, any SpecDataCache> { get }

    /// The module info for the target, if available.
    var moduleInfo: ModuleInfo? { get }

    /// Whether or not the build is using a VFS.
    var needsVFS: Bool { get }

    /// Whether or not to generate additional commands for producing assembly code.
    var generateAssemblyCommands: Bool { get }

    /// Whether or not to generate additional commands for producing preprocessed files.
    var generatePreprocessCommands: Bool { get }

    /// The file path resolver in effect.
    ///
    /// This is exposed primarily so that tool specifications can resolve paths of related references they discover, for example other references in a variant group beyond the primary one being processed.
    var filePathResolver: FilePathResolver { get }

    /// The specification registry.
    var specRegistry: SpecRegistry { get }

    /// Code signing & provisioning settings
    var signingSettings: Settings.SigningSettings? { get }

    /// The product build version of Xcode.
    var xcodeProductBuildVersion: ProductBuildVersion? { get }

    /// Information about the runtime system
    var systemInfo: SystemInfo? { get }

    /// Compute the expanded search path list (i.e. with recursive entries expanded) for the given macro.
    func expandedSearchPaths(for items: [String], scope: MacroEvaluationScope) -> [String]

    /// If On-Demand Resources is enabled, provides an asset pack for the given tag set.
    func onDemandResourcesAssetPack(for tags: ODRTagSet) -> ODRAssetPackInfo?

    /// Macro evaluation scopes of all dependencies of this target, direct and transitive.
    /// Used to query dependency information such as its Swiftmodule output path.
    func targetSwiftDependencyScopes(for target: ConfiguredTarget, arch: String, variant: String) -> [MacroEvaluationScope]

    /// Swift macro implementation descriptors to be applied to this target.
    var swiftMacroImplementationDescriptors: Set<SwiftMacroImplementationDescriptor>? { get }

    func supportsEagerLinking(scope: MacroEvaluationScope) -> Bool

    /// Returns information on the headers referenced by an individual project, identified by one of the targets in that project.
    /// - Parameter target: A target in the project to return header information for.
    /// - Returns: Information on the headers referenced by the project that the given target is a part of.
    func projectHeaderInfo(for target: Target) async -> ProjectHeaderInfo?

    /// Whether or not an SDK stat cache should be used for the build of this target.
    func shouldUseSDKStatCache() async -> Bool

    func discoveredCommandLineToolSpecInfo(_ delegate: any CoreClientTargetDiagnosticProducingDelegate, _ toolName: String, _ path: Path, _ process: @Sendable (_ contents: Data) async throws -> any DiscoveredCommandLineToolSpecInfo) async throws -> any DiscoveredCommandLineToolSpecInfo

    func discoveredCommandLineToolSpecInfo(_ delegate: any CoreClientTargetDiagnosticProducingDelegate, _ toolName: String?, _ commandLine: [String], _ process: @Sendable (_ processResult: Processes.ExecutionResult) async throws -> any DiscoveredCommandLineToolSpecInfo) async throws -> any DiscoveredCommandLineToolSpecInfo

    var canConstructAppIntentsMetadataTask: Bool { get }

    var canConstructAppIntentsSSUTask: Bool { get }

    var targetRequiredToBuildForIndexing: Bool { get }

    var targetShouldBuildModuleForInstallAPI: Bool { get }

    var supportsCompilationCaching: Bool { get }

    func lookupLibclang(path: Path) -> (libclang: Libclang?, version: Version?)

    var userPreferences: UserPreferences { get }

    var hostOperatingSystem: OperatingSystem { get }
}

extension CommandProducer {
    /// Whether the current context is targeting an Apple platform, based on the target triple's vendor being "apple".
    public var isApplePlatform: Bool {
        sdkVariant?.llvmTargetTripleVendor == "apple"
    }

    package func discoveredCommandLineToolSpecInfo<T: DiscoveredCommandLineToolSpecInfo>(_ delegate: any CoreClientTargetDiagnosticProducingDelegate, _ toolName: String, _ path: Path, _ process: @Sendable (_ contents: Data) async throws -> any DiscoveredCommandLineToolSpecInfo) async throws -> T {
        let info = try await discoveredCommandLineToolSpecInfo(delegate, toolName, path, process)
        guard let info = info as? T else {
            throw StubError.error("Expected value of type \(type(of: T.self)) but found \(type(of: info))")
        }
        return info
    }

    package func discoveredCommandLineToolSpecInfo<T: DiscoveredCommandLineToolSpecInfo>(_ delegate: any CoreClientTargetDiagnosticProducingDelegate, _ toolName: String?, _ commandLine: [String], _ process: @Sendable (_ processResult: Processes.ExecutionResult) async throws -> any DiscoveredCommandLineToolSpecInfo) async throws -> T {
        let info = try await discoveredCommandLineToolSpecInfo(delegate, toolName, commandLine, process)
        guard let info = info as? T else {
            throw StubError.error("Expected value of type \(type(of: T.self)) but found \(type(of: info))")
        }
        return info
    }
}

extension CommandProducer {
    /// Get or create the per-tool cache for the given spec.
    ///
    /// This allows individual tool implementations to store a cache for use across multiple invocations. The cache is specific to the given spec, unique for any individual `configuredTarget`, but shared for all command production using the same producer. It persists for the lifetime of the producer.
    ///
    /// NOTE: Only one cache type can be stored per spec.
    func getSpecDataCache<T: SpecDataCache>(_ spec: Spec, cacheType: T.Type) -> T {
        let cache = specDataCaches.getOrInsert(spec) {
            return cacheType.init() as T
        }
        return cache as! T
    }

    func effectiveBuildOptions(_ spec: PropertyDomainSpec) -> [BuildOption] {
        specRegistry.effectiveBuildOptions(spec)
    }

    func effectiveFlattenedOrderedBuildOptions(_ spec: PropertyDomainSpec, filter: BuildOptionsFilter = .all) -> [BuildOption] {
        specRegistry.effectiveFlattenedOrderedBuildOptions(spec, filter: filter)
    }

    /// Compute the expanded search path list (i.e. with recursive entries expanded) for the given macro.
    func expandedSearchPaths(for macro: PathListMacroDeclaration, scope: MacroEvaluationScope) -> [String] {
        return expandedSearchPaths(for: scope.evaluate(macro), scope: scope)
    }
}

/// Which build options to include when evaluating the command line of a spec. This primarily exists because the Clang spec wants to specially evaluate a bunch of options as "constant" (see `getStandardFlags`), which we can't guarantee to be the case for extended options.
public enum BuildOptionsFilter {
    /// Include only build options declared on the spec and its supertypes.
    case specOnly

    /// Include only extended build options declared via any `BuildSettingsExtension` specs.
    case extendedOnly

    /// Include build options declared by the spec itself and its supertypes, as well as via any `BuildSettingsExtension` specs.
    case all
}

extension SpecRegistry {
    func effectiveBuildOptions(_ spec: PropertyDomainSpec) -> [BuildOption] {
        var options: [BuildOption] = []
        options.append(contentsOf: spec.buildOptions)
        for extensionSpec in findSpecs(BuildSettingsExtensionSpec.self) where spec.conformsTo(identifier: extensionSpec.extendsConformsTo) {
            options.append(contentsOf: extensionSpec.buildOptions)
        }
        return options
    }

    func effectiveFlattenedBuildOptions(_ spec: PropertyDomainSpec) -> [String: BuildOption] {
        var options = spec.flattenedBuildOptions
        for extensionSpec in findSpecs(BuildSettingsExtensionSpec.self) where spec.conformsTo(identifier: extensionSpec.extendsConformsTo) {
            options.merge(extensionSpec.flattenedBuildOptions, uniquingKeysWith: { _, new in
                // Should duplicates be an error?
                return new
            })
        }
        return options
    }

    func effectiveFlattenedOrderedBuildOptions(_ spec: PropertyDomainSpec, filter: BuildOptionsFilter) -> [BuildOption] {
        var options: [BuildOption] = []
        if filter == .all || filter == .specOnly {
            options.append(contentsOf: spec.flattenedOrderedBuildOptions)
        }
        if filter == .all || filter == .extendedOnly {
            for extensionSpec in findSpecs(BuildSettingsExtensionSpec.self) where spec.conformsTo(identifier: extensionSpec.extendsConformsTo) {
                options.append(contentsOf: extensionSpec.flattenedOrderedBuildOptions)
            }
        }
        return options
    }
}

/// Describes the context in which an individual invocation of a command line spec is being built.
public struct CommandBuildContext {
    /// The settings context the command is being evaluated in.
    public let producer: any CommandProducer

    /// The scope to use for evaluating build settings.
    public let scope: MacroEvaluationScope

    /// The list of input items.
    public let inputs: [FileToBuild]

    /// The sole input item.  This method asserts that there is exactly one.
    public var input: FileToBuild {
        precondition(inputs.count == 1, "Expected only a single input for this command invocation; found \(inputs.count): \(inputs)")
        return inputs[0]
    }

    /// The list of output paths.  Currently, this can only be empty, or contain a single item.
    public let outputs: [Path]

    // FIXME: This is only used by some specs, it would be nice to have some enforcement that it is passed to the ones that require it.
    //
    /// The path of the sole output, if specified.  This property asserts that an output exists and that there is only one.
    public var output: Path {
        precondition(outputs.count == 1, "Expected an output for this command invocation; found nil")
        return outputs[0]
    }

    // FIXME: This mechanism is only used in _very_ limited circumstances currently to provide a way to enforce additional ordering between tasks (which is required by our current mutable output handling), when no other ordering is in place (i.e. no build phase is in between the producer and consumer).
    //
    /// Nodes to treat as additional inputs to tasks created with this context, for ordering purposes.
    public let commandOrderingInputs: [any PlannedNode]

    // FIXME: This mechanism is only used in _very_ limited circumstances currently to provide a way to enforce additional ordering between tasks (which is required by our current mutable output handling), when no other ordering is in place (i.e. no build phase is in between the producer and consumer).
    //
    /// Nodes to treat as additional outputs of tasks created with this context, for ordering purposes.
    public let commandOrderingOutputs: [any PlannedNode]

    /// The context to use for the task being produced.
    public let buildPhaseInfo: (any BuildPhaseInfoForToolSpec)?

    /// The path of the resources directory, if specified.
    public let resourcesDir: Path?

    /// The path of the temporary resources directory, if specified.
    public let tmpResourcesDir: Path?

    /// The path of the unlocalized resources directory, if specified.
    public let unlocalizedResourcesDir: Path?

    /// True if this is the preferred arch among all the archs we're building for same inputs.
    public let isPreferredArch: Bool

    /// Whether this command is needed for index preparation.
    public var preparesForIndexing: Bool

    /// The spec of the current arch in a per-arch task
    public let currentArchSpec: ArchitectureSpec?

    public init(
        producer: any CommandProducer, scope: MacroEvaluationScope,
        inputs: [FileToBuild], isPreferredArch: Bool = true,
        currentArchSpec: ArchitectureSpec? = nil,
        output: Path? = nil,
        commandOrderingInputs: [any PlannedNode] = [],
        commandOrderingOutputs: [any PlannedNode] = [],
        buildPhaseInfo: (any BuildPhaseInfoForToolSpec)? = nil,
        resourcesDir: Path? = nil, tmpResourcesDir: Path? = nil,
        unlocalizedResourcesDir: Path? = nil,
        preparesForIndexing: Bool = false
    ) {
        self.init(
            producer: producer, scope: scope, inputs: inputs,
            isPreferredArch: isPreferredArch,
            currentArchSpec: currentArchSpec,
            outputs: output.map { [$0] } ?? [],
            commandOrderingInputs: commandOrderingInputs,
            commandOrderingOutputs: commandOrderingOutputs,
            buildPhaseInfo: buildPhaseInfo, resourcesDir: resourcesDir,
            tmpResourcesDir: tmpResourcesDir,
            unlocalizedResourcesDir: unlocalizedResourcesDir,
            preparesForIndexing: preparesForIndexing
        )
    }

    public init(
        producer: any CommandProducer, scope: MacroEvaluationScope,
        inputs: [FileToBuild], isPreferredArch: Bool = true,
        currentArchSpec: ArchitectureSpec? = nil,
        outputs: [Path],
        commandOrderingInputs: [any PlannedNode] = [],
        commandOrderingOutputs: [any PlannedNode] = [],
        buildPhaseInfo: (any BuildPhaseInfoForToolSpec)? = nil,
        resourcesDir: Path? = nil, tmpResourcesDir: Path? = nil,
        unlocalizedResourcesDir: Path? = nil,
        preparesForIndexing: Bool = false
    ) {
        // We normalize paths here so their normalized forms are consistently available to CommandLineToolSpec instances.  The old build system normalized paths in almost all cases.
        self.producer = producer
        self.scope = scope
        self.isPreferredArch = isPreferredArch
        self.preparesForIndexing = preparesForIndexing
        self.currentArchSpec = currentArchSpec
        self.inputs = inputs
        self.outputs = outputs.map({ $0.normalize() })
        self.commandOrderingInputs = commandOrderingInputs
        self.commandOrderingOutputs = commandOrderingOutputs
        self.buildPhaseInfo = buildPhaseInfo
        self.resourcesDir = resourcesDir?.normalize()
        self.tmpResourcesDir = tmpResourcesDir?.normalize()
        self.unlocalizedResourcesDir = unlocalizedResourcesDir?.normalize()
    }
}

extension CommandBuildContext {
    /// Indicates whether the "normal" variant is being processed, or if the variant being processed is the only variant.
    /// In the case where there is no "normal" variant, we choose the first one among the list.
    ///
    /// FIXME: This is a hack designed to easily allow constructing variant-neutral tasks (tasks for which there should be only one instance of per-product, regardless of the number of variants) until we have first-class support for this (rdar://45330111).
    var isNeutralVariant: Bool {
        let variants = scope.evaluate(BuiltinMacros.BUILD_VARIANTS)
        let current = scope.evaluate(BuiltinMacros.CURRENT_VARIANT)
        return variants.count == 1 || current == "normal" || (!variants.contains("normal") && variants.first == current)
    }

    /// Indicates whether the product being built in the current command context is a framework (dynamically or statically linked).
    public var isFramework: Bool {
        return scope.isFramework
    }

    /// Make an input path absolute, resolving relative to the default
    /// working directory.
    func makeAbsolute(_ path: Path) -> Path {
        producer.defaultWorkingDirectory.join(path)
    }

    /// Returns a new `CommandBuildContext` with the specified outputs appended to its outputs array.
    public func appendingOutputs(_ outputs: [Path]) -> Self {
        Self(producer: producer, scope: scope, inputs: inputs, isPreferredArch: isPreferredArch, currentArchSpec: currentArchSpec, outputs: self.outputs + outputs, commandOrderingInputs: commandOrderingInputs, commandOrderingOutputs: commandOrderingOutputs, buildPhaseInfo: buildPhaseInfo, resourcesDir: resourcesDir, tmpResourcesDir: tmpResourcesDir, unlocalizedResourcesDir: unlocalizedResourcesDir)
    }
}

extension MacroEvaluationScope {
    var isFramework: Bool {
        let type = evaluate(BuiltinMacros.PRODUCT_TYPE)
        return type == "com.apple.product-type.framework" || type == "com.apple.product-type.framework.static"
    }
}

/// Options passed when creating a task that determine how it should be ordered within its own target and with respect to other targets. These are primarily used when creating tasks when eager compilation is enabled (which it is by default).
///
/// For use of these options, see `TaskProducerContext.additionalInputsForTask()` and `TaskProducerContext.additionalMustPrecedeTasksForTask()`.
///
/// This is an `OptionSet` because a few tasks need multiple options. For instance, compiling sources is both a compilation task and a requirement for compilation tasks of downstream targets.
public struct TaskOrderingOptions: OptionSet, CustomDebugStringConvertible, Sendable {
    public let rawValue: Int
    init(_ rawValue: Int) {
        self.rawValue = rawValue
    }

    public init(rawValue: Int) {
        self.init(rawValue)
    }

    // TASK TYPE DESCRIPTIONS: These options describe a kind of task, which may cause them to have a defined relationship to other kinds of tasks.

    /// Tasks that perform compilation, and can start before upstream targets have finished building.  They can start running once all tasks in upstream targets marked `compilationRequirement` have run.
    public static let compilation = TaskOrderingOptions(1 << 0)

    /// Tasks for compiling an indexable source file.
    public static let compilationForIndexableSourceFile = TaskOrderingOptions(1 << 2)

    /// Tasks that perform linking, and can start once all tasks in upstream targets marked `linkingRequirement` have run.
    public static let linking = TaskOrderingOptions(1 << 7)

    // UPSTREAM REQUIREMENTS: These are options which prevent these tasks from running until certain upstream tasks have finished running.

    /// Tasks that can be run immediately, without waiting for any tasks in their target or any other targets to run. If such a task is constructed as part of a build phase, it may still be ordered relative to other phases.
    public static let immediate = TaskOrderingOptions(1 << 3)

    /// Tasks that are blocked by headers of the same target completed copying
    public static let blockedByTargetHeaders = TaskOrderingOptions(1 << 4)

    // DOWNSTREAM REQUIREMENTS: These are options which prevent certain kinds of downstream tasks from running until these tasks have finished running.  By convention, these tasks end with the word 'requirement', to distinguish them from upstream requirements.

    /// Tasks that need to run before downstream targets can start compiling their sources. This includes installing module maps, copying headers, and - because of Swift - compiling sources. This option is also used when creating tasks which execute shell scripts, as we want to be conservative with such scripts and not assume they won't impact downstream compilation.
    public static let compilationRequirement = TaskOrderingOptions(1 << 5)

    /// Tasks that need to run before the unsigned product is considered to be finished.
    public static let unsignedProductRequirement = TaskOrderingOptions(1 << 6)

    /// Tasks that need to run before downstream targets can begin linking. This includes tasks which emit text-based dylibs  to unblock downstream linking if supported by the target, or the linking task itself.
    public static let linkingRequirement = TaskOrderingOptions(1 << 8)

    /// Tasks that scan a target (via libclang or Swift driver) need to be blocked on each other, adding this ordering option will guarantee this
    public static let scanning = TaskOrderingOptions(1 << 9)

    /// Tasks which are constructed as part of a build phase, but are ordered independently of all build phases.
    public static let ignorePhaseOrdering = TaskOrderingOptions(1 << 10)

    public var debugDescription: String {
        return "<TaskOrderingOptions [" + [
            "compilation": .compilation,
            "compilationForIndexableSourceFile": .compilationForIndexableSourceFile,
            "linking": .linking,

            "immediate": TaskOrderingOptions.immediate,
            "blockedByTargetHeaders": .blockedByTargetHeaders,

            "compilationRequirement": .compilationRequirement,
            "unsignedProductRequirement": .unsignedProductRequirement,
            "linkingRequirement": .linkingRequirement,
            "scanning": .scanning,
            "ignorePhaseOrdering": .ignorePhaseOrdering,
            ].compactMap { (description, rawValue) -> String? in
                return self.contains(rawValue) ? description : nil
        }.joined(separator: ", ") + "]>"
    }
}

/// A utility for constructing generated tasks.
public struct PlannedTaskBuilder {
    /// The target the task runs on behalf of, if known.
    public var forTarget: ConfiguredTarget? = nil

    /// The type of task being generated.
    public let type: any TaskTypeDescription

    /// Information emitted by the execution of this task, which will allow additional dependency information be discovered.
    public var dependencyData: DependencyDataStyle? = nil

    /// The task type specific payload, if present.
    public var payload: (any TaskPayload)? = nil

    /// The rule signature for the task.
    public var ruleInfo: [String]

    /// Additional arbitrary data used to contribute to the task's change-tracking signature.
    public var additionalSignatureData: String

    /// The command line for the task.
    public var commandLine: [CommandLineArgument]

    /// Additional output that should be displayed for the task. Each element will be emitted on a separate line.
    ///
    /// This will be emitted after the  rule info and after the `cd` and `export` directives in the transcript, but before the command line.
    public var additionalOutput: [String]

    /// The environment to use to evaluate the task.
    public var environment: EnvironmentBindings

    /// The working directory for the task.
    //
    // FIXME: Make this optional
    public var workingDirectory: Path = Path("/")

    /// The list of task inputs.
    public var inputs: [any PlannedNode]

    /// The list of task outputs.
    public var outputs: [any PlannedNode]

    /// The list of tasks this task must run before.
    public var mustPrecede: [any PlannedTask]

    /// The action to use to execute the task.
    public var action: (any PlannedTaskAction)? = nil

    /// The description to use when executing the task.
    public var execDescription: String? = nil

    public var priority: TaskPriority

    /// List of target dependencies related to this task. This is used by target gate tasks.
    public var targetDependencies = [ResolvedTargetDependency]()

    public var additionalTaskOrderingOptions: TaskOrderingOptions

    /// Whether or not the task is required to run when preparing to index.
    public var preparesForIndexing: Bool = false

    /// Whether the llbuild control file descriptor is disabled for this task
    public var llbuildControlDisabled: Bool = false

    /// Whether or not this is a gate task.
    public fileprivate(set) var isGate = false

    public var usesExecutionInputs = false

    /// Allows a task to always be executed.
    public var alwaysExecuteTask: Bool

    /// Whether the task should show its environment in logs.
    public var showEnvironment: Bool = true

    public fileprivate(set) var showInLog: Bool

    public fileprivate(set) var showCommandLineInLog: Bool

    public var enableSandboxing: Bool

    public var repairViaOwnershipAnalysis: Bool

    public init(type: any TaskTypeDescription, ruleInfo: [String], additionalSignatureData: String = "", commandLine: [CommandLineArgument], additionalOutput: [String] = [], environment: EnvironmentBindings = EnvironmentBindings(), inputs: [any PlannedNode] = [], outputs: [any PlannedNode] = [], mustPrecede: [any PlannedTask] = [], deps: DependencyDataStyle? = nil, additionalTaskOrderingOptions: TaskOrderingOptions = [], usesExecutionInputs: Bool = false, alwaysExecuteTask: Bool = false, showInLog: Bool = true, showCommandLineInLog: Bool = true, priority: TaskPriority = .unspecified, enableSandboxing: Bool = false, repairViaOwnershipAnalysis: Bool = false) {
        self.type = type
        self.ruleInfo = ruleInfo
        self.additionalSignatureData = additionalSignatureData
        self.commandLine = commandLine
        self.additionalOutput = additionalOutput
        self.environment = environment
        self.inputs = inputs
        self.outputs = outputs
        self.mustPrecede = mustPrecede
        self.dependencyData = deps
        self.additionalTaskOrderingOptions = additionalTaskOrderingOptions
        self.usesExecutionInputs = usesExecutionInputs
        self.alwaysExecuteTask = alwaysExecuteTask
        self.showInLog = showInLog
        self.showCommandLineInLog = showCommandLineInLog
        self.priority = priority
        self.enableSandboxing = enableSandboxing
        self.repairViaOwnershipAnalysis = repairViaOwnershipAnalysis
    }

    public mutating func makeGate() {
        isGate = true
        showInLog = false
    }
}

/// Interface by which core classes can request information from the client.
public protocol CoreClientDelegate {
    func executeExternalTool(commandLine: [String], workingDirectory: Path?, environment: [String: String]) async throws -> ExternalToolResult
}

public protocol CoreClientTargetDiagnosticProducingDelegate: AnyObject, TargetDiagnosticProducingDelegate, ActivityReporter {
    /// Delegate which can be used to query the client for needed information for task generation.
    var coreClientDelegate: any CoreClientDelegate { get }
}

/// Interface by which command line specs can describe tasks to build.
public protocol TaskGenerationDelegate: AnyObject, TargetDiagnosticProducingDelegate, CoreClientTargetDiagnosticProducingDelegate, ActivityReporter {
    // FIXME: It would be nice to support diagnostics specific to an individual command build.

    /// Create a virtual node.
    func createVirtualNode(_ name: String) -> PlannedVirtualNode

    /// Create a node for the given path.
    func createNode(_ path: Path) -> PlannedPathNode

    /// Create a node representing a directory on the file system.
    ///
    /// - absolutePath: The input path, which must be absolute.
    /// - excluding: The fnmatch-style patterns to ignore.
    func createDirectoryTreeNode(_ path: Path, excluding: [String]) -> PlannedDirectoryTreeNode

    /// Create a node representing a top-level directory used by the build.
    ///
    /// This is used for ensuring these directories are only created once across the entire build.
    /// If a node for `path` already exists, the existing instance will be returned.
    ///
    /// - parameter absolutePath: The input path, which must be absolute.
    func createBuildDirectoryNode(absolutePath path: Path) -> PlannedPathNode

    /// Declare an output path for possible reprocessing.
    func declareOutput(_ file: FileToBuild)

    /// Declare a generated source file.
    ///
    /// This file will be added to the generated files headermap.
    //
    // FIXME: Could we handle this automatically based simply on the declared outputs? We know from the file type which are source files.
    func declareGeneratedSourceFile(_ path: Path)

    /// Declare a generated info plist addition.
    func declareGeneratedInfoPlistContent(_ path: Path)

    func declareGeneratedPrivacyPlistContent(_ path: Path)

    /// Declare a custom TBD file.
    func declareGeneratedTBDFile(_ path: Path, forVariant variant: String)

    /// Declare a generated Swift Objective-C interface file.
    func declareGeneratedSwiftObjectiveCHeaderFile(_ path: Path, architecture: String)

    /// Declare a generated Swift Supplementary compile-time value Metadata file.
    func declareGeneratedSwiftConstMetadataFile(_ path: Path, architecture: String)

    var buildDirectories: Set<Path> { get }

    /// The set of additional inputs for codesigning. These are tracked explicitly on the codesign task and are captured during the `.planning` phase.
    var additionalCodeSignInputs: OrderedSet<Path> { get }

    /// Create a new task with a reference to the task type and scope, and with fully expanded rule info, command line, and optional input/output dependencies.
    func createTask(_ builder: inout PlannedTaskBuilder)

    /// Create a gate task for use in controlling task ordering.
    ///
    /// Gate tasks don't actually execute any work, but instead are used to realize an ordering constraint between the inputs and the output.
    func createGateTask(inputs: [any PlannedNode], output: any PlannedNode, name: String?, mustPrecede: [any PlannedTask], taskConfiguration: (inout PlannedTaskBuilder) -> Void)

    /// Delegate which can create different kinds of `TaskAction` objects.
    var taskActionCreationDelegate: any TaskActionCreationDelegate { get }

    /// Delegate which can be used to query the client for needed information for task generation.
    var clientDelegate: any CoreClientDelegate { get }

    /// Looks up `key` in a concurrency-safe mapping associated with the product plan; if a node is associated with that key, it is simply returned; otherwise, the block is invoked to create a node, and then that node is returned after associating it with the key.  This is intended for shareable intermediate artifacts, such as header precompilation tasks, that are created the first time they are needed and that can then be referenced by any other case that needs the same intermediate node.
    /// FIXME: This is arguably a bit esoteric, but creating API that explicitly deals with precompiled headers seemed wrong here.  Rather, this is intended to be a way to provide generalized sharing of nodes that are created in quite specialized ways.
    /// FIXME: The name of this method is also a bit awkward.  We could probably do better.
    func createOrReuseSharedNodeWithIdentifier(_ ident: String, creator: () -> (any PlannedNode, any Sendable)) -> (any PlannedNode, any Sendable)

    /// Adds the accessed path to the list of paths which invalidate the build description.
    func access(path: Path)

    /// Reads the contents of the file at `path` and returns its contents as a byte string, and adds the path to the list of paths which invalidate the build description.
    func readFileContents(_ path: Path) throws -> ByteString

    /// Returns true if a file exists at `path`, and adds the path to the list of paths which invalidate the build description.
    func fileExists(at path: Path) -> Bool

    /// Record an arbitrary attachment as part of the build description, which can be accessed at the returned path.
    func recordAttachment(contents: ByteString) -> Path

    /// User preferences
    var userPreferences: UserPreferences { get }
}

extension TaskGenerationDelegate {
    /// Create a node representing a directory on the file system.
    ///
    /// - absolutePath: The input path, which must be absolute.
    public func createDirectoryTreeNode(_ path: Path) -> PlannedDirectoryTreeNode {
        return createDirectoryTreeNode(path, excluding: [])
    }

    public var coreClientDelegate: any CoreClientDelegate {
        clientDelegate
    }
}

extension CoreClientTargetDiagnosticProducingDelegate {
    public func executeExternalTool(commandLine: [String], workingDirectory: Path? = nil, environment: [String: String] = [:], executionDescription: String?) async throws -> Processes.ExecutionResult {
        try await withActivity(ruleInfo: "ExecuteExternalTool " + commandLine.joined(separator: " "), executionDescription: executionDescription ?? CommandLineToolSpec.fallbackExecutionDescription, signature: ByteString(encodingAsUTF8: "\(commandLine) \(String(describing: workingDirectory)) \(environment)"), target: nil, parentActivity: ActivityID.buildDescriptionActivity) { activity in
            try await coreClientDelegate.executeExternalTool(commandLine: commandLine, workingDirectory: workingDirectory, environment: environment)
        }
    }
}

// Limit the number of concurrently-executing external processes on the host to the number of cores.
private let externalToolExecutionQueue = AsyncOperationQueue(concurrentTasks: ProcessInfo.processInfo.activeProcessorCount)

extension CoreClientDelegate {
    package func executeExternalTool(commandLine: [String], workingDirectory: Path? = nil, environment: [String: String] = [:]) async throws -> Processes.ExecutionResult {
        switch try await executeExternalTool(commandLine: commandLine, workingDirectory: workingDirectory, environment: environment) {
        case .deferred:
            guard let url = commandLine.first.map(URL.init(fileURLWithPath:)) else {
                throw StubError.error("Cannot execute empty command line.")
            }

            return try await externalToolExecutionQueue.withOperation {
                try await Process.getOutput(url: url, arguments: Array(commandLine.dropFirst()), currentDirectoryURL: workingDirectory.map { URL(fileURLWithPath: $0.str) }, environment: Environment.current.addingContents(of: .init(environment)))
            }
        case let .result(status, stdout, stderr):
            return Processes.ExecutionResult(exitStatus: status, stdout: stdout, stderr: stderr)
        }
    }
}

public extension TaskGenerationDelegate {
    /// Create a new task with a reference to the task type and scope, and with fully expanded rule info, command line, and optional input/output dependencies.
    func createTask(type: any TaskTypeDescription, dependencyData: DependencyDataStyle?, payload: (any TaskPayload)?, ruleInfo: [String], additionalSignatureData: String, commandLine: [CommandLineArgument], additionalOutput: [String], environment: EnvironmentBindings, workingDirectory: Path, inputs: [any PlannedNode], outputs: [any PlannedNode], mustPrecede: [any PlannedTask], action: (any PlannedTaskAction)?, execDescription: String?, preparesForIndexing: Bool, enableSandboxing: Bool, llbuildControlDisabled: Bool, additionalTaskOrderingOptions: TaskOrderingOptions, usesExecutionInputs: Bool = false, isGate: Bool = false, alwaysExecuteTask: Bool = false, showInLog: Bool = true, showCommandLineInLog: Bool = true, showEnvironment: Bool = false, priority: TaskPriority = .unspecified, repairViaOwnershipAnalysis: Bool = false) {
        var builder = PlannedTaskBuilder(type: type, ruleInfo: ruleInfo, additionalSignatureData: additionalSignatureData, commandLine: commandLine, additionalOutput: additionalOutput, environment: environment, enableSandboxing: enableSandboxing)
        builder.dependencyData = dependencyData
        builder.payload = payload
        builder.workingDirectory = workingDirectory
        builder.inputs = inputs
        builder.outputs = outputs
        builder.mustPrecede = mustPrecede
        builder.action = action
        builder.execDescription = execDescription
        builder.preparesForIndexing = preparesForIndexing
        builder.llbuildControlDisabled = llbuildControlDisabled
        builder.additionalTaskOrderingOptions = additionalTaskOrderingOptions
        builder.usesExecutionInputs = usesExecutionInputs
        builder.isGate = isGate
        builder.alwaysExecuteTask = alwaysExecuteTask
        builder.showInLog = showInLog
        builder.showCommandLineInLog = showCommandLineInLog
        builder.showEnvironment = showEnvironment
        builder.priority = priority
        builder.repairViaOwnershipAnalysis = repairViaOwnershipAnalysis
        createTask(&builder)
    }

    func createTask(type: any TaskTypeDescription, dependencyData: DependencyDataStyle?, payload: (any TaskPayload)?, ruleInfo: [String], additionalSignatureData: String, commandLine: [ByteString], additionalOutput: [String], environment: EnvironmentBindings, workingDirectory: Path, inputs: [any PlannedNode], outputs: [any PlannedNode], mustPrecede: [any PlannedTask], action: (any PlannedTaskAction)?, execDescription: String?, preparesForIndexing: Bool, enableSandboxing: Bool, llbuildControlDisabled: Bool, additionalTaskOrderingOptions: TaskOrderingOptions, usesExecutionInputs: Bool = false, isGate: Bool = false, alwaysExecuteTask: Bool = false, showInLog: Bool = true, showCommandLineInLog: Bool = true, showEnvironment: Bool = false, priority: TaskPriority = .unspecified, repairViaOwnershipAnalysis: Bool = false) {
        createTask(type: type, dependencyData: dependencyData, payload: payload, ruleInfo: ruleInfo, additionalSignatureData: additionalSignatureData, commandLine: commandLine.map { .literal($0) }, additionalOutput: additionalOutput, environment: environment, workingDirectory: workingDirectory, inputs: inputs, outputs: outputs, mustPrecede: mustPrecede, action: action, execDescription: execDescription, preparesForIndexing: preparesForIndexing, enableSandboxing: enableSandboxing, llbuildControlDisabled: llbuildControlDisabled, additionalTaskOrderingOptions: additionalTaskOrderingOptions, usesExecutionInputs: usesExecutionInputs, isGate: isGate, alwaysExecuteTask: alwaysExecuteTask, showInLog: showInLog, showCommandLineInLog: showCommandLineInLog, showEnvironment: showEnvironment, priority: priority, repairViaOwnershipAnalysis: repairViaOwnershipAnalysis)
    }

    /// Create a new task taking inputs and outputs as `Path` arrays.  They will be implicitly marshalled into `PlannedNode` arrays.
    func createTask(type: any TaskTypeDescription, dependencyData: DependencyDataStyle? = nil, payload: (any TaskPayload)? = nil, ruleInfo: [String], additionalSignatureData: String = "", commandLine: [ByteString], additionalOutput: [String] = [], environment: EnvironmentBindings, workingDirectory: Path, inputs: [Path], outputs: [Path], mustPrecede: [any PlannedTask] = [], action: (any PlannedTaskAction)? = nil, execDescription: String? = nil, preparesForIndexing: Bool = false, enableSandboxing: Bool, llbuildControlDisabled: Bool = false, additionalTaskOrderingOptions: TaskOrderingOptions = [], usesExecutionInputs: Bool = false, isGate: Bool = false, alwaysExecuteTask: Bool = false, showInLog: Bool = true, showCommandLineInLog: Bool = true, showEnvironment: Bool = false, priority: TaskPriority = .unspecified, repairViaOwnershipAnalysis: Bool = false) {
        return createTask(type: type, dependencyData: dependencyData, payload: payload, ruleInfo: ruleInfo, additionalSignatureData: additionalSignatureData, commandLine: commandLine, additionalOutput: additionalOutput, environment: environment, workingDirectory: workingDirectory, inputs: inputs.map(createNode), outputs: outputs.map(createNode), mustPrecede: mustPrecede, action: action, execDescription: execDescription, preparesForIndexing: preparesForIndexing, enableSandboxing: enableSandboxing, llbuildControlDisabled: llbuildControlDisabled, additionalTaskOrderingOptions: additionalTaskOrderingOptions, usesExecutionInputs: usesExecutionInputs, isGate: isGate, alwaysExecuteTask: alwaysExecuteTask, showInLog: showInLog, showCommandLineInLog: showCommandLineInLog, showEnvironment: showEnvironment, priority: priority, repairViaOwnershipAnalysis: repairViaOwnershipAnalysis)
    }

    /// Create a new task taking a command line as a `String` array, and inputs and outputs as `Path` arrays.  The command line will be implicitly marshalled into a `ByteString` array, and the inputs and outputs into `PlannedNode` arrays.
    func createTask(type: any TaskTypeDescription, dependencyData: DependencyDataStyle? = nil, payload: (any TaskPayload)? = nil, ruleInfo: [String], additionalSignatureData: String = "", commandLine: [String], additionalOutput: [String] = [], environment: EnvironmentBindings, workingDirectory: Path, inputs: [Path], outputs: [Path], mustPrecede: [any PlannedTask] = [], action: (any PlannedTaskAction)? = nil, execDescription: String? = nil, preparesForIndexing: Bool = false, enableSandboxing: Bool, llbuildControlDisabled: Bool = false, additionalTaskOrderingOptions: TaskOrderingOptions = [], usesExecutionInputs: Bool = false, isGate: Bool = false, alwaysExecuteTask: Bool = false, showInLog: Bool = true, showCommandLineInLog: Bool = true, priority: TaskPriority = .unspecified, repairViaOwnershipAnalysis: Bool = false) {
        return createTask(type: type, dependencyData: dependencyData, payload: payload, ruleInfo: ruleInfo, additionalSignatureData: additionalSignatureData, commandLine: commandLine.map{ ByteString(encodingAsUTF8: $0) }, additionalOutput: additionalOutput, environment: environment, workingDirectory: workingDirectory, inputs: inputs.map(createNode), outputs: outputs.map(createNode), mustPrecede: mustPrecede, action: action, execDescription: execDescription, preparesForIndexing: preparesForIndexing, enableSandboxing: enableSandboxing, llbuildControlDisabled: llbuildControlDisabled, additionalTaskOrderingOptions: additionalTaskOrderingOptions, usesExecutionInputs: usesExecutionInputs, isGate: isGate, alwaysExecuteTask: alwaysExecuteTask, showInLog: showInLog, showCommandLineInLog: showCommandLineInLog, priority: priority, repairViaOwnershipAnalysis: repairViaOwnershipAnalysis)
    }

    /// Create a new task taking a command line as a `String` array.  It will be implicitly marshalled into a `ByteString` array.
    func createTask(type: any TaskTypeDescription, dependencyData: DependencyDataStyle? = nil, payload: (any TaskPayload)? = nil, ruleInfo: [String], additionalSignatureData: String = "", commandLine: [String], additionalOutput: [String] = [], environment: EnvironmentBindings, workingDirectory: Path, inputs: [any PlannedNode], outputs: [any PlannedNode], mustPrecede: [any PlannedTask] = [], action: (any PlannedTaskAction)? = nil, execDescription: String? = nil, preparesForIndexing: Bool = false, enableSandboxing: Bool, llbuildControlDisabled: Bool = false, additionalTaskOrderingOptions: TaskOrderingOptions = [], usesExecutionInputs: Bool = false, isGate: Bool = false, alwaysExecuteTask: Bool = false, showInLog: Bool = true, showCommandLineInLog: Bool = true, showEnvironment: Bool = false, priority: TaskPriority = .unspecified, repairViaOwnershipAnalysis: Bool = false) {
        return createTask(type: type, dependencyData: dependencyData, payload: payload, ruleInfo: ruleInfo, additionalSignatureData: additionalSignatureData, commandLine: commandLine.map{ ByteString(encodingAsUTF8: $0) }, additionalOutput: additionalOutput, environment: environment, workingDirectory: workingDirectory, inputs: inputs, outputs: outputs, mustPrecede: mustPrecede, action: action, execDescription: execDescription, preparesForIndexing: preparesForIndexing, enableSandboxing: enableSandboxing, llbuildControlDisabled: llbuildControlDisabled, additionalTaskOrderingOptions: additionalTaskOrderingOptions, usesExecutionInputs: usesExecutionInputs, isGate: isGate, alwaysExecuteTask: alwaysExecuteTask, showInLog: showInLog, showCommandLineInLog: showCommandLineInLog, showEnvironment: showEnvironment, priority: priority, repairViaOwnershipAnalysis: repairViaOwnershipAnalysis)
    }

    func createGateTask(inputs: [any PlannedNode], output: any PlannedNode, name: String? = nil, mustPrecede: [any PlannedTask] = [], payload: (any TaskPayload)? = nil, additionalSignatureData: String = "") {
        createGateTask(inputs: inputs, output: output, name: name, mustPrecede: mustPrecede) { builder in
            builder.payload = payload
            builder.additionalSignatureData = additionalSignatureData
        }
    }
}

// Helper protocol to partially simulate an "abstract class"
public protocol ConditionallyStartable {
    /// Whether the task should run in the context of the specified build command.
    func shouldStart(_ task: any ExecutableTask, buildCommand: BuildCommand) -> Bool
}

extension ConditionallyStartable {
    public func shouldStart(_ task: any ExecutableTask, buildCommand: BuildCommand) -> Bool {
        if let target = task.forTarget, !target.target.approvedByUser {
            return false
        }
        if buildCommand.isPrepareForIndexing {
            return task.preparesForIndexing
        }

        return true
    }
}

/// Provides information and functionality for all occurrences of a particular type of task.  Every task has a reference to its task type description.
public protocol TaskTypeDescription: AnyObject, ConditionallyStartable, Sendable {
    /// Describes the concrete TaskPayload type for deserialization
    var payloadType: (any TaskPayload.Type)? { get }

    /// The aliases used by a command line tool. For e.g. lex reports itself as flex, and yacc as bison.
    var toolBasenameAliases: [String] { get }

    /// Get the serialized diagnostics used by a task, if any.
    func serializedDiagnosticsPaths(_ task: any ExecutableTask, _ fs: any FSProxy) -> [Path]

    /// Returns an indexing-information structure for each of the indexable sources of the task.
    func generateIndexingInfo(for task: any ExecutableTask, input: TaskGenerateIndexingInfoInput) -> [TaskGenerateIndexingInfoOutput]

    /// Returns a preview-information structure for the source file specified by the preview-information input argument.
    func generatePreviewInfo(for task: any ExecutableTask, input: TaskGeneratePreviewInfoInput, fs: any FSProxy) -> [TaskGeneratePreviewInfoOutput]

    /// Returns a documentation-information structure for the task.
    func generateDocumentationInfo(for task: any ExecutableTask, input: TaskGenerateDocumentationInfoInput) -> [TaskGenerateDocumentationInfoOutput]

    /// Returns a localization-information structure for the task.
    ///
    /// The returned outputs from this method are currently not expected to include .stringsdata paths, as those will be computed independently of the task type.
    func generateLocalizationInfo(for task: any ExecutableTask, input: TaskGenerateLocalizationInfoInput) -> [TaskGenerateLocalizationInfoOutput]

    /// Create a parser for the task output, if used.
    func customOutputParserType(for task: any ExecutableTask) -> (any TaskOutputParser.Type)?

    /// Any interesting path related to the task, for e.g., the file being compiled.
    func interestingPath(for task: any ExecutableTask) -> Path?

    /// The command line that should be used for the change-tracking signature, i.e.
    /// the command line with the output-agnostic arguments removed.
    /// A nil return indicates that the command line should be used as is.
    func commandLineForSignature(for task: any ExecutableTask) -> [ByteString]?

    /// Whether instances of this task are unsafe to interrupt.
    var isUnsafeToInterrupt: Bool { get }
}

public enum DependencyDataFormat: String, Sendable {
    case dependencyInfo
    case makefile
}

/// A style for how dependency information is communicated from a task.
public enum DependencyDataStyle: Equatable, Encodable, Sendable {
    /// A Darwin linker style dependency info file.
    case dependencyInfo(Path)

    /// A '.d'-style Makefile.
    case makefile(Path)

    /// A list of multiple '.d'-style Makefiles.
    case makefiles([Path])

    /// A '.d'-style makefile where all but the first output are ignored.
    case makefileIgnoringSubsequentOutputs(Path)
}

extension DependencyDataStyle: Serializable {
    private enum DependencyDataStyleCode: UInt, Serializable {
        case dependencyInfo = 0
        case makefile = 1
        case makefiles = 2
        case makefileIgnoringSubsequentOutputs = 3
    }

    public func serialize<T: Serializer>(to serializer: T) {
        serializer.serializeAggregate(2) {
            switch self {
            case .dependencyInfo(let path):
                serializer.serialize(DependencyDataStyleCode.dependencyInfo)
                serializer.serialize(path)
            case .makefile(let path):
                serializer.serialize(DependencyDataStyleCode.makefile)
                serializer.serialize(path)
            case .makefiles(let paths):
                serializer.serialize(DependencyDataStyleCode.makefiles)
                serializer.serialize(paths)
            case .makefileIgnoringSubsequentOutputs(let path):
                serializer.serialize(DependencyDataStyleCode.makefileIgnoringSubsequentOutputs)
                serializer.serialize(path)
            }
        }
    }

    public init(from deserializer: any Deserializer) throws {
        try deserializer.beginAggregate(2)
        switch try deserializer.deserialize() as DependencyDataStyleCode {
        case .dependencyInfo:
            self = .dependencyInfo(try deserializer.deserialize())
        case .makefile:
            self = .makefile(try deserializer.deserialize())
        case .makefiles:
            self = .makefiles(try deserializer.deserialize())
        case .makefileIgnoringSubsequentOutputs:
            self = .makefileIgnoringSubsequentOutputs(try deserializer.deserialize())
        }
    }
}

/// Provides a place for the TaskTypeDescription to store extra data for the task.
public protocol TaskPayload: Serializable, Sendable {
}

/// Defines the indexing information to be sent back to the client for a particular source file.
///
/// This protocol includes `PropertyListItemConvertible` to describe how the info is packaged up to send back to the client.  Someday we hope to transition this to sending the info in a strongly typed format.
public protocol SourceFileIndexingInfo: PropertyListItemConvertible {
}

/// The `SourceFileIndexingInfo` info returned when only output paths are requested.
public struct OutputPathIndexingInfo: SourceFileIndexingInfo {
    public let outputFile: Path

    public init(outputFile: Path) {
        self.outputFile = outputFile
    }

    /// The indexing info is packaged and sent to the client in a property list format.
    public var propertyListItem: PropertyListItem {
        return .plDict([
            "outputFilePath": .plString(outputFile.str),
            ] as [String: PropertyListItem])
    }
}

/// Input data for generating indexing info for a task.
public struct TaskGenerateIndexingInfoInput {
    public enum SourceFileRequest {
        case all
        case only(Path)

        public func contains(_ path: Path) -> Bool {
            switch self {
            case .all: return true
            case .only(let requestedPath): return path == requestedPath
            }
        }

        /// Returns the file path that the index info was requested for, or `nil` if the request was for all files.
        var singleFile: Path? {
            switch self {
            case .all: return nil
            case .only(let requestedPath): return requestedPath
            }
        }
    }

    /// The source file request to return indexing info for.
    public let requestedSourceFiles: SourceFileRequest

    /// Whether to return only the output path associated with the source file(s).
    public let outputPathOnly: Bool

    /// Whether the index request had enabled the dedicated index build arena.
    let enableIndexBuildArena: Bool

    /// Provide input for index info request.
    /// - Parameters:
    ///   - requestedSourceFile: a specific file to get info for or `nil` for all files of a target
    ///   - outputPathOnly: Whether to return only the output path associated with the source file(s).
    public init(requestedSourceFile: Path?, outputPathOnly: Bool, enableIndexBuildArena: Bool = false) {
        if let file = requestedSourceFile {
            self.requestedSourceFiles = .only(file)
        } else {
            self.requestedSourceFiles = .all
        }
        self.outputPathOnly = outputPathOnly
        self.enableIndexBuildArena = enableIndexBuildArena
    }

    public var withEnableIndexBuildArena: TaskGenerateIndexingInfoInput {
        return .init(requestedSourceFile: self.requestedSourceFiles.singleFile, outputPathOnly: self.outputPathOnly, enableIndexBuildArena: true)
    }

    public static var fullInfo: TaskGenerateIndexingInfoInput {
        return .init(requestedSourceFile: nil, outputPathOnly: false, enableIndexBuildArena: false)
    }

    public static var outputPathInfo: TaskGenerateIndexingInfoInput {
        return .init(requestedSourceFile: nil, outputPathOnly: true, enableIndexBuildArena: false)
    }
}

public struct TaskGenerateIndexingInfoOutput {
    public let path: Path
    public let indexingInfo: any SourceFileIndexingInfo

    public init(path: Path, indexingInfo: any SourceFileIndexingInfo) {
        self.path = path
        self.indexingInfo = indexingInfo
    }
}

/// Input data for generating preview info for a task.
public enum TaskGeneratePreviewInfoInput: Equatable {
    /// - parameter sourceFile: The source file that is being edited during the preview.
    /// - parameter thunkVariantSuffix: Suffix for disambiguating different thunk variants.
    case thunkInfo(sourceFile: Path, thunkVariantSuffix: String)

    case targetDependencyInfo
}

/// Output data for generating preview info for a task.
public struct TaskGeneratePreviewInfoOutput: Sendable {
    public struct DependencyInfo: Sendable {
        public let objectFileInputMap: [String: Set<String>]
    }

    /// Types of tasks that support preview info.
    public enum TaskType: Sendable {
        case Ld
        case Swift
    }

    /// The architecture being built for.
    public let architecture: String
    /// The build variant being built.
    public let buildVariant: String
    /// The commandline to run to update the thunk.
    public let commandLine: [String]
    /// Working directory of the task
    public let workingDirectory: Path
    /// Input path of the task.
    public let input: Path
    /// Output path of the task.
    public let output: Path
    /// Type of task that is being run.
    public let type: TaskType
}

/// Input data for generating documentation info for a task.
///
/// For a description of how this feature works, see the `SWBBuildServiceSession.generateDocumentationInfo` documentation.
public struct TaskGenerateDocumentationInfoInput {
    // No extra input is needed. Everything is passed in the payload.
    public init() {}
}

/// Output data for generating documentation info for a task.
///
/// For a description of how this feature works, see the `SWBBuildServiceSession.generateDocumentationInfo` documentation.
public struct TaskGenerateDocumentationInfoOutput {
    /// The bundle identifier of the documentation.
    public let bundleIdentifier: String
    /// The output path where the built documentation will be written.
    public let outputPath: Path
    /// The identifier of the target associated with the documentation we're building for.
    public let targetIdentifier: String?
}

/// Input data for generating localization info for a task.
public struct TaskGenerateLocalizationInfoInput {
    /// A set of Target GUIDs (not ConfiguredTarget GUIDs) that localization info is specifically being requested for.
    ///
    /// If `nil`, data for all targets is returned.
    public let targetIdentifiers: Set<String>?

    public init(targetIdentifiers: Set<String>?) {
        self.targetIdentifiers = targetIdentifiers
    }
}

/// Describes attributes of a portion of a build, for example platform and architecture, that are relevant to distinguishing localized strings extracted during a build.
public struct LocalizationBuildPortion: Hashable, Sendable {
    /// The effective name of the platform we were building for.
    ///
    /// Catalyst should be treated as a separate platform.
    public let effectivePlatformName: String

    /// The name of the build variant, e.g. "normal"
    public let variant: String

    /// The name of the architecture we were building for.
    public let architecture: String

    public init(effectivePlatformName: String, variant: String, architecture: String) {
        self.effectivePlatformName = effectivePlatformName
        self.variant = variant
        self.architecture = architecture
    }

    /// Returns a platform name to use for localization info purposes.
    public static func effectivePlatformName(scope: MacroEvaluationScope, sdkVariant: SDKVariant?) -> String {
        if let sdkVariant, sdkVariant.isMacCatalyst {
            // Treat Catalyst as a separate platform.
            return MacCatalystInfo.localizationEffectivePlatformName
        }
        return scope.evaluate(BuiltinMacros.PLATFORM_NAME)
    }
}

/// Output data for generating localization info for a task.
///
/// A single instance of this object may not necessarily encapsulate all info for a given Task.
/// A Task's `generateLocalizationInfo` method returns an array of outputs. Combine them together to get a complete picture.
public struct TaskGenerateLocalizationInfoOutput {
    /// Paths to source .xcstrings files used as inputs to the task.
    ///
    /// This collection specifically contains compilable files, AKA files in a Resources phase (not a Copy Files phase).
    public let compilableXCStringsPaths: [Path]

    /// Paths to .stringsdata files produced by this task, grouped by build attributes such as platform and architecture.
    public let producedStringsdataPaths: [LocalizationBuildPortion: [Path]]

    /// The name of the primary platform we were building for.
    ///
    /// Mac Catalyst is treated as its own platform.
    public var effectivePlatformName: String?

    /// Paths to generated source code files holding string symbols, keyed by xcstrings file path.
    public var generatedSymbolFilesByXCStringsPath = [Path: [Path]]()

    /// Create output to describe some portion of localization info for a Task.
    ///
    /// - Parameters:
    ///   - compilableXCStringsPaths: Paths to input source .xcstrings files.
    ///   - producedStringsdataPaths: Paths to output .stringsdata files.
    public init(compilableXCStringsPaths: [Path] = [],
                producedStringsdataPaths: [LocalizationBuildPortion: [Path]] = [:]) {
        self.compilableXCStringsPaths = compilableXCStringsPaths
        self.producedStringsdataPaths = producedStringsdataPaths
    }
}

/// A progress reporter for subtasks.
///
/// This can be used to report the progress when parsing a task's output using `TaskOutputParser`.
public protocol SubtaskProgressReporter: AnyObject {
    /// Reports the number of subtasks that have started scanning.
    func subtasksScanning(count: Int, forTargetName targetName: String?)

    /// Reports the number of subtasks that have started execution.
    func subtasksStarted(count: Int, forTargetName targetName: String?)

    /// Reports the number of subtasks that did not need to run.
    func subtasksSkipped(count: Int, forTargetName targetName: String?)

    /// Reports the number of subtasks that finished execution.
    func subtasksFinished(count: Int, forTargetName targetName: String?)
}

/// A parser for the tasks output, which can be used to produce richer information as a result of a build operation.
public protocol TaskOutputParser: AnyObject {
    var workspaceContext: WorkspaceContext { get }
    var buildRequestContext: BuildRequestContext { get }
    var delegate: any TaskOutputParserDelegate { get }

    /// Create the parser.
    init(for task: any ExecutableTask, workspaceContext: WorkspaceContext, buildRequestContext: BuildRequestContext, delegate: any TaskOutputParserDelegate, progressReporter: (any SubtaskProgressReporter)?)

    /// Pass bytes to the parser implementation.
    func write(bytes: ByteString)

    /// Close the output parser.
    ///
    /// This indicates that all data has been received, and the parser should perform any necessary finalization.
    func close(result: TaskResult?)
}

extension TaskOutputParserDelegate {
    func readSerializedDiagnostics(at path: Path, workingDirectory: Path, workspaceContext: WorkspaceContext) -> [Diagnostic] {
        do {
            // Using the default toolchain's libclang regardless of context should be sufficient, since we assume serialized diagnostics to be a stable format.
            let toolchain = workspaceContext.core.toolchainRegistry.defaultToolchain
            guard let libclangPath = toolchain?.librarySearchPaths.findLibrary(operatingSystem: workspaceContext.core.hostOperatingSystem, basename: "clang") ?? toolchain?.fallbackLibrarySearchPaths.findLibrary(operatingSystem: workspaceContext.core.hostOperatingSystem, basename: "clang") else {
                throw StubError.error("unable to find libclang")
            }
            guard let libclang = workspaceContext.core.lookupLibclang(path: libclangPath).libclang else {
                throw StubError.error("unable to open libclang: '\(libclangPath.str)'")
            }
            let serializedDiagnostics = try libclang.loadDiagnostics(filePath: path.str).map { Diagnostic($0, workingDirectory: workingDirectory, appendToOutputStream: false) }
            return serializedDiagnostics
        } catch {
            diagnosticsEngine.emit(Diagnostic(behavior: .warning, location: .path(path), data: DiagnosticData("Could not read serialized diagnostics file: \(error)")))
            return []
        }
    }

    @discardableResult func processSerializedDiagnostics(at path: Path, workingDirectory: Path, workspaceContext: WorkspaceContext) -> [Diagnostic] {
        let serializedDiagnostics = readSerializedDiagnostics(at: path, workingDirectory: workingDirectory, workspaceContext: workspaceContext)
        serializedDiagnostics.forEach(diagnosticsEngine.emit)
        return serializedDiagnostics
    }

    func processOptimizationRemarks(at objectFilePath: Path, workingDirectory: Path, workspaceContext: WorkspaceContext) {
        do {
            // Depending on how they are built, libRemarks may not be available. SWBCore weak-links with it, so if it's not there, bail out.
            guard Diagnostic.libRemarksAvailable else { return }
            guard workspaceContext.core.delegate.enableOptimizationRemarksParsing else { return }

            let remarks = try Diagnostic.remarks(forObjectPath: objectFilePath, fs: workspaceContext.fs, workingDirectory: workingDirectory)
            remarks.forEach(diagnosticsEngine.emit)
        } catch {
            diagnosticsEngine.emit(Diagnostic(behavior: .warning, location: .unknown, data: DiagnosticData("Could not read remarks from object file \(objectFilePath): \(error)")))
        }
    }
}

public struct BuildSystemOperationIdentifier: Hashable, Equatable, Sendable {
    public let uuid: UUID

    public init(_ uuid: UUID) {
        self.uuid = uuid
    }
}

/// Delegate protocol for task output parser actions.
//
// FIXME: This protocol is similar to `TaskOutputDelegate`, but for layering purposes they are currently distinct. We should see if this can be cleaned up.
public protocol TaskOutputParserDelegate: AnyObject {
    /// The unique identifier for the associated build operation
    var buildOperationIdentifier: BuildSystemOperationIdentifier { get }

    /// The diagnostics engine to use.
    var diagnosticsEngine: DiagnosticsEngine { get }

    /// Report a skipped subtask.
    ///
    /// - Parameters:
    ///   - signature: A stable signature for the task (the same signature that would have been provided had the task run).
    func skippedSubtask(signature: ByteString)

    /// Create a subtask with the given identifier.
    ///
    /// The client *MUST* explicitly stop all started subtasks (via calling `taskCompleted`)  before the task is complete.
    ///
    /// - Parameters:
    ///   - signature: A stable signature for the task (the same signature that would have been provided had the task been skipped).
    /// - Returns: A new delegate to use for reporting status on the subtask.
    func startSubtask(buildOperationIdentifier: BuildSystemOperationIdentifier, taskName: String, id: ByteString, signature: ByteString, ruleInfo: String, executionDescription: String, commandLine: [ByteString], additionalOutput: [String], interestingPath: Path?, workingDirectory: Path?, serializedDiagnosticsPaths: [Path]) -> any TaskOutputParserDelegate

    /// Emit output log data.
    func emitOutput(_ data: ByteString)

    func close()

    /// Called to indicate the task is complete and supply the exit status for a task.
    ///
    /// Once this is invoked, no subsequent messages will be invoked on the delegate.
    func taskCompleted(exitStatus: Processes.ExitStatus)
}