File: AppIntentsSSUTrainingCompiler.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 (133 lines) | stat: -rw-r--r-- 6,881 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
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

import SWBUtil
import Foundation
import SWBMacro
public import SWBCore

final public class AppIntentsSSUTrainingCompilerSpec: GenericCommandLineToolSpec, SpecIdentifierType, @unchecked Sendable {
    public static let identifier = "com.apple.compilers.appintents-ssu-training"

    private func shouldConstructSsuTrainingTask(_ cbc: CommandBuildContext) -> Bool {
        guard cbc.producer.canConstructAppIntentsSSUTask else {
            return false
        }

        // For installLoc builds, we expect two input files named AppShortcuts.strings and Info.plist.
        if cbc.scope.evaluate(BuiltinMacros.BUILD_COMPONENTS).contains("installLoc") {
            guard cbc.inputs.count == 2 else {
                if cbc.inputs.count > 2 {
                    assertionFailure("AppIntents YAML Generation task construction was passed context with more than two input files.")
                } else {
                    assertionFailure("AppIntents YAML Generation task construction was passed context without required input files.")
                }
                return false
            }
        } else {
            guard cbc.inputs.count == 1 || cbc.inputs.count == 2 else {
                assertionFailure("AppIntents YAML Generation task construction was passed context without required input files.")
                return false
            }
        }

        let stringsFileType = cbc.producer.lookupFileType(identifier: "text.plist.strings")!
        let xcstringsFileType = cbc.producer.lookupFileType(identifier: "text.json.xcstrings")!
        let plistFileType = cbc.producer.lookupFileType(identifier: "text.plist")!

        if cbc.scope.evaluate(BuiltinMacros.BUILD_COMPONENTS).contains("installLoc") {
            guard cbc.inputs[0].fileType.conformsTo(stringsFileType) || cbc.inputs[0].fileType.conformsTo(xcstringsFileType),
                  cbc.inputs[0].absolutePath.basename.hasSuffix("InfoPlist.strings") ||
                    cbc.inputs[0].absolutePath.basename.hasSuffix("InfoPlist.xcstrings") else {
                assertionFailure("AppIntents YAML Generation task construction was passed context without InfoPlist.strings file.")
                return false
            }
        } else {
            guard cbc.inputs[0].fileType.conformsTo(plistFileType),
                  cbc.inputs[0].absolutePath.basename == "Info.plist" else {
                assertionFailure("AppIntents YAML Generation task construction was passed context without Info.plist file.")
                return false
            }
        }
        if cbc.inputs.count == 2 {
            guard cbc.inputs[1].fileType.conformsTo(stringsFileType) || cbc.inputs[1].fileType.conformsTo(xcstringsFileType),
                  ["AppShortcuts.strings", "AppShortcuts.xcstrings"].contains(cbc.inputs[1].absolutePath.basename) else {
                assertionFailure("AppIntents YAML Generation task construction was passed context without AppShortcuts.strings file.")
                return false
            }
        }

        return true
    }

    override public func constructTasks(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate) async {
        guard shouldConstructSsuTrainingTask(cbc) else {
            return
        }

        guard let resourcesDir = cbc.resourcesDir else {
            assertionFailure("Resources directory does not exist")
            return
        }

        var inputs: [any PlannedNode] = cbc.inputs.map { delegate.createNode($0.absolutePath) }
        let outputs: [any PlannedNode] = [cbc.scope.evaluate(BuiltinMacros.TARGET_TEMP_DIR).join("ssu/root.ssu.yaml")].map(delegate.createNode)
        var metadataDependencyFileListFiles = [String]()

        // Validate AppShortcuts strings/xcstrings if it is used as an input
        if inputs.count == 2 {
            // Workaround until we have rdar://93626172 (Re-enable AppIntentsMetadataProcessor outputs)
            let inputNodeIdentifier: String
            if let configuredTarget = cbc.producer.configuredTarget {
                inputNodeIdentifier = "ValidateAppShortcutStringsMetadata \(configuredTarget.guid) \(cbc.inputs[1].absolutePath.str)"
            } else {
                inputNodeIdentifier = "ValidateAppShortcutStringsMetadata \(cbc.inputs[1].absolutePath.str)"
            }
            let inputOrderingNode = delegate.createVirtualNode(inputNodeIdentifier) // Create the NL training task only if AppShortcuts.strings is validated
            inputs.append(inputOrderingNode)
        }

        if !cbc.scope.evaluate(BuiltinMacros.BUILD_COMPONENTS).contains("installLoc") {
            // Workaround until we have rdar://93626172 (Re-enable AppIntentsMetadataProcessor outputs)
            let inputOrderingNode = delegate.createVirtualNode("ExtractAppIntentsMetadata \(resourcesDir.join("Metadata.appintents").str )")
            inputs.append(inputOrderingNode)
        }

        let metadataFileListPath = cbc.scope.evaluate(BuiltinMacros.LM_AUX_INTENTS_METADATA_FILES_LIST_PATH)
        if !metadataFileListPath.isEmpty {
            metadataDependencyFileListFiles.append(metadataFileListPath.str)
            inputs.append(delegate.createNode(metadataFileListPath))
        }

        func lookup(_ macro: MacroDeclaration) -> MacroExpression? {
            switch macro {
            case BuiltinMacros.LM_INTENTS_METADATA_FILES_LIST_PATH:
                return cbc.scope.table.namespace.parseLiteralStringList(metadataDependencyFileListFiles)
            default:
                return nil
            }
        }

        let commandLine = await commandLineFromTemplate(cbc, delegate, optionContext: discoveredCommandLineToolSpecInfo(cbc.producer, cbc.scope, delegate), lookup: lookup).map(\.asString)
        delegate.createTask(type: self,
                            ruleInfo: defaultRuleInfo(cbc, delegate),
                            commandLine: commandLine,
                            environment: environmentFromSpec(cbc, delegate),
                            workingDirectory: cbc.producer.defaultWorkingDirectory,
                            inputs: inputs,
                            outputs: outputs,
                            action: nil,
                            execDescription: resolveExecutionDescription(cbc, delegate),
                            enableSandboxing: enableSandboxing)
    }
}