Skip to content

Commit b7ab15f

Browse files
Alex Carterjakepetroules
authored andcommitted
Upstream: Extract Extension Point Definition and Extension's target extension point identifier from swift const values
1 parent 187d0ae commit b7ab15f

File tree

11 files changed

+626
-8
lines changed

11 files changed

+626
-8
lines changed

Sources/SWBApplePlatform/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ add_library(SWBApplePlatform
1717
CoreDataCompiler.swift
1818
CoreMLCompiler.swift
1919
DittoTool.swift
20+
ExtensionPointExtractorTaskProducer.swift
2021
ExtensionPointsCompiler.swift
22+
EXUtil.swift
2123
IIGCompiler.swift
2224
InstrumentsPackageBuilderSpec.swift
2325
IntentsCompiler.swift
@@ -63,6 +65,7 @@ SwiftBuild_Bundle(MODULE SWBApplePlatform FILES
6365
Specs/Embedded-Shared.xcspec
6466
Specs/Embedded-Simulator.xcspec
6567
Specs/EmbeddedBinaryValidationUtility.xcspec
68+
Specs/EXUtil.xcspec
6669
Specs/GenerateAppPlaygroundAssetCatalog.xcspec
6770
Specs/GenerateTextureAtlas.xcspec
6871
Specs/IBCompiler.xcspec

Sources/SWBApplePlatform/EXUtil.swift

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SWBUtil
14+
import SWBMacro
15+
import SWBCore
16+
import SWBProtocol
17+
import Foundation
18+
19+
final class ExtensionPointExtractorSpec: GenericCommandLineToolSpec, SpecIdentifierType, @unchecked Sendable {
20+
public static let identifier = "com.apple.compilers.extract-appextensionpoints"
21+
22+
static func shouldConstructTask(scope: MacroEvaluationScope, productType: ProductTypeSpec?, isApplePlatform: Bool) -> Bool {
23+
let isNormalVariant = scope.evaluate(BuiltinMacros.CURRENT_VARIANT) == "normal"
24+
let buildComponents = scope.evaluate(BuiltinMacros.BUILD_COMPONENTS)
25+
let isBuild = buildComponents.contains("build")
26+
let indexEnableBuildArena = scope.evaluate(BuiltinMacros.INDEX_ENABLE_BUILD_ARENA)
27+
let isAppProductType = productType?.conformsTo(identifier: "com.apple.product-type.application") ?? false
28+
let extensionPointExtractorEnabled = scope.evaluate(BuiltinMacros.EX_ENABLE_EXTENSION_POINT_GENERATION)
29+
30+
let result = (
31+
isBuild
32+
&& isNormalVariant
33+
&& extensionPointExtractorEnabled
34+
&& !indexEnableBuildArena
35+
&& isAppProductType
36+
&& isApplePlatform
37+
)
38+
return result
39+
}
40+
41+
override func constructTasks(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate) async {
42+
guard Self.shouldConstructTask(scope: cbc.scope, productType: cbc.producer.productType, isApplePlatform: cbc.producer.isApplePlatform) else {
43+
return
44+
}
45+
46+
let inputs = cbc.inputs.map { input in
47+
return delegate.createNode(input.absolutePath)
48+
}.filter { node in
49+
node.path.fileExtension == "swiftconstvalues"
50+
}
51+
var outputs = [any PlannedNode]()
52+
53+
let outputPath = cbc.scope.evaluate(BuiltinMacros.EXTENSIONS_FOLDER_PATH).join(Path("\(cbc.scope.evaluate(BuiltinMacros.PRODUCT_MODULE_NAME))-generated.appexpt"))
54+
outputs.append(delegate.createNode(outputPath))
55+
56+
let commandLine = await commandLineFromTemplate(cbc, delegate, optionContext: discoveredCommandLineToolSpecInfo(cbc.producer, cbc.scope, delegate)).map(\.asString)
57+
58+
delegate.createTask(type: self,
59+
ruleInfo: defaultRuleInfo(cbc, delegate),
60+
commandLine: commandLine,
61+
environment: environmentFromSpec(cbc, delegate),
62+
workingDirectory: cbc.producer.defaultWorkingDirectory,
63+
inputs: inputs,
64+
outputs: outputs,
65+
action: nil,
66+
execDescription: resolveExecutionDescription(cbc, delegate),
67+
enableSandboxing: enableSandboxing)
68+
}
69+
}
70+
71+
final class AppExtensionPlistGeneratorSpec: GenericCommandLineToolSpec, SpecIdentifierType, @unchecked Sendable {
72+
public static let identifier = "com.apple.compilers.appextension-plist-generator"
73+
74+
static func shouldConstructTask(scope: MacroEvaluationScope, productType: ProductTypeSpec?, isApplePlatform: Bool) -> Bool {
75+
let isNormalVariant = scope.evaluate(BuiltinMacros.CURRENT_VARIANT) == "normal"
76+
let buildComponents = scope.evaluate(BuiltinMacros.BUILD_COMPONENTS)
77+
let isBuild = buildComponents.contains("build")
78+
let indexEnableBuildArena = scope.evaluate(BuiltinMacros.INDEX_ENABLE_BUILD_ARENA)
79+
let isAppExtensionProductType = productType?.conformsTo(identifier: "com.apple.product-type.extensionkit-extension") ?? false
80+
let extensionPointAttributesGenerationEnabled = !scope.evaluate(BuiltinMacros.EX_DISABLE_APPEXTENSION_ATTRIBUTES_GENERATION)
81+
82+
let result = ( isBuild
83+
&& isNormalVariant
84+
&& extensionPointAttributesGenerationEnabled
85+
&& !indexEnableBuildArena
86+
&& (isAppExtensionProductType)
87+
&& isApplePlatform )
88+
89+
return result
90+
}
91+
92+
override func constructTasks(_ cbc: CommandBuildContext, _ delegate: any TaskGenerationDelegate) async {
93+
let scope = cbc.scope
94+
let productType = cbc.producer.productType
95+
let isApplePlatform = cbc.producer.isApplePlatform
96+
guard Self.shouldConstructTask(scope: scope, productType: productType, isApplePlatform: isApplePlatform) else {
97+
return
98+
}
99+
100+
let inputs = cbc.inputs.map { input in
101+
return delegate.createNode(input.absolutePath)
102+
}.filter { node in
103+
node.path.fileExtension == "swiftconstvalues"
104+
}
105+
var outputs = [any PlannedNode]()
106+
let outputPath = cbc.output
107+
outputs.append(delegate.createNode(outputPath))
108+
109+
110+
let commandLine = await commandLineFromTemplate(cbc, delegate, optionContext: discoveredCommandLineToolSpecInfo(cbc.producer, cbc.scope, delegate)).map(\.asString)
111+
112+
delegate.createTask(type: self,
113+
ruleInfo: defaultRuleInfo(cbc, delegate),
114+
commandLine: commandLine,
115+
environment: environmentFromSpec(cbc, delegate),
116+
workingDirectory: cbc.producer.defaultWorkingDirectory,
117+
inputs: inputs,
118+
outputs: outputs,
119+
action: nil,
120+
execDescription: resolveExecutionDescription(cbc, delegate),
121+
enableSandboxing: enableSandboxing
122+
)
123+
}
124+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SWBCore
14+
import SWBUtil
15+
import SWBMacro
16+
import SWBTaskConstruction
17+
18+
final class ExtensionPointExtractorTaskProducer: PhasedTaskProducer, TaskProducer {
19+
20+
override var defaultTaskOrderingOptions: TaskOrderingOptions {
21+
return .unsignedProductRequirement
22+
}
23+
24+
private func filterBuildFiles(_ buildFiles: [BuildFile]?, identifiers: [String], buildFilesProcessingContext: BuildFilesProcessingContext) -> [FileToBuild] {
25+
guard let buildFiles else {
26+
return []
27+
}
28+
29+
let fileTypes = identifiers.compactMap { identifier in
30+
context.lookupFileType(identifier: identifier)
31+
}
32+
33+
return fileTypes.flatMap { fileType in
34+
buildFiles.compactMap { buildFile in
35+
guard let resolvedBuildFileInfo = try? self.context.resolveBuildFileReference(buildFile),
36+
!buildFilesProcessingContext.isExcluded(resolvedBuildFileInfo.absolutePath, filters: buildFile.platformFilters),
37+
resolvedBuildFileInfo.fileType.conformsTo(fileType) else {
38+
return nil
39+
}
40+
41+
return FileToBuild(absolutePath: resolvedBuildFileInfo.absolutePath, fileType: fileType)
42+
}
43+
}
44+
}
45+
46+
func generateTasks() async -> [any PlannedTask] {
47+
48+
guard ExtensionPointExtractorSpec.shouldConstructTask(scope: context.settings.globalScope, productType: context.productType, isApplePlatform: context.isApplePlatform) else {
49+
return []
50+
}
51+
52+
context.addDeferredProducer {
53+
54+
let scope = self.context.settings.globalScope
55+
let buildFilesProcessingContext = BuildFilesProcessingContext(scope)
56+
57+
let perArchConstMetadataFiles = self.context.generatedSwiftConstMetadataFiles()
58+
59+
let constMetadataFiles: [Path]
60+
if let firstArch = perArchConstMetadataFiles.keys.sorted().first {
61+
constMetadataFiles = perArchConstMetadataFiles[firstArch]!
62+
} else {
63+
constMetadataFiles = []
64+
}
65+
66+
let constMetadataFilesToBuild = constMetadataFiles.map { absolutePath -> FileToBuild in
67+
let fileType = self.context.workspaceContext.core.specRegistry.getSpec("file") as! FileTypeSpec
68+
return FileToBuild(absolutePath: absolutePath, fileType: fileType)
69+
}
70+
71+
let inputs = constMetadataFilesToBuild
72+
guard inputs.isEmpty == false else {
73+
return []
74+
}
75+
76+
var deferredTasks: [any PlannedTask] = []
77+
78+
let cbc = CommandBuildContext(producer: self.context, scope: scope, inputs: inputs, resourcesDir: buildFilesProcessingContext.resourcesDir)
79+
await self.appendGeneratedTasks(&deferredTasks) { delegate in
80+
let domain = self.context.settings.platform?.name ?? ""
81+
guard let spec = self.context.specRegistry.getSpec("com.apple.compilers.extract-appextensionpoints", domain:domain) as? ExtensionPointExtractorSpec else {
82+
return
83+
}
84+
await spec.constructTasks(cbc, delegate)
85+
}
86+
87+
return deferredTasks
88+
}
89+
return []
90+
}
91+
}
92+
93+
94+
final class AppExtensionInfoPlistGeneratorTaskProducer: PhasedTaskProducer, TaskProducer {
95+
96+
override var defaultTaskOrderingOptions: TaskOrderingOptions {
97+
return .unsignedProductRequirement
98+
}
99+
100+
private func filterBuildFiles(_ buildFiles: [BuildFile]?, identifiers: [String], buildFilesProcessingContext: BuildFilesProcessingContext) -> [FileToBuild] {
101+
guard let buildFiles else {
102+
return []
103+
}
104+
105+
let fileTypes = identifiers.compactMap { identifier in
106+
context.lookupFileType(identifier: identifier)
107+
}
108+
109+
return fileTypes.flatMap { fileType in
110+
buildFiles.compactMap { buildFile in
111+
guard let resolvedBuildFileInfo = try? self.context.resolveBuildFileReference(buildFile),
112+
!buildFilesProcessingContext.isExcluded(resolvedBuildFileInfo.absolutePath, filters: buildFile.platformFilters),
113+
resolvedBuildFileInfo.fileType.conformsTo(fileType) else {
114+
return nil
115+
}
116+
117+
return FileToBuild(absolutePath: resolvedBuildFileInfo.absolutePath, fileType: fileType)
118+
}
119+
}
120+
}
121+
122+
func generateTasks() async -> [any PlannedTask] {
123+
124+
let scope = context.settings.globalScope
125+
let productType = context.productType
126+
let isApplePlatform = context.isApplePlatform
127+
guard AppExtensionPlistGeneratorSpec.shouldConstructTask(scope: scope, productType: productType, isApplePlatform: isApplePlatform) else {
128+
return []
129+
}
130+
131+
let tasks: [any PlannedTask] = []
132+
let buildFilesProcessingContext = BuildFilesProcessingContext(scope)
133+
134+
let moduelName = context.settings.globalScope.evaluate(BuiltinMacros.TARGET_NAME)
135+
let plistPath = buildFilesProcessingContext.tmpResourcesDir.join(Path("\(moduelName)-appextension-generated-info.plist"))
136+
137+
context.addDeferredProducer {
138+
139+
let perArchConstMetadataFiles = self.context.generatedSwiftConstMetadataFiles()
140+
141+
let constMetadataFiles: [Path]
142+
if let firstArch = perArchConstMetadataFiles.keys.sorted().first {
143+
constMetadataFiles = perArchConstMetadataFiles[firstArch]!
144+
} else {
145+
constMetadataFiles = []
146+
}
147+
148+
let constMetadataFilesToBuild = constMetadataFiles.map { absolutePath -> FileToBuild in
149+
let fileType = self.context.workspaceContext.core.specRegistry.getSpec("file") as! FileTypeSpec
150+
return FileToBuild(absolutePath: absolutePath, fileType: fileType)
151+
}
152+
153+
let inputs = constMetadataFilesToBuild
154+
var deferredTasks: [any PlannedTask] = []
155+
156+
let cbc = CommandBuildContext(producer: self.context, scope: scope, inputs: inputs, output: plistPath)
157+
158+
await self.appendGeneratedTasks(&deferredTasks) { delegate in
159+
let domain = self.context.settings.platform?.name ?? ""
160+
guard let spec = self.context.specRegistry.getSpec("com.apple.compilers.appextension-plist-generator",domain: domain) as? AppExtensionPlistGeneratorSpec else {
161+
return
162+
}
163+
await spec.constructTasks(cbc, delegate)
164+
}
165+
166+
return deferredTasks
167+
}
168+
self.context.addGeneratedInfoPlistContent(plistPath)
169+
return tasks
170+
}
171+
}

Sources/SWBApplePlatform/Plugin.swift

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ struct TaskProducersExtension: TaskProducerExtension {
4747
}
4848

4949
var unorderedPostSetupTaskProducers: [any TaskProducerFactory] {
50-
[
51-
StubBinaryTaskProducerFactory()
52-
]
50+
[StubBinaryTaskProducerFactory(),
51+
AppExtensionInfoPlistGeneratorTaskProducerFactory(),
52+
ExtensionPointExtractorTaskProducerFactory()]
5353
}
5454

5555
var unorderedPostBuildPhasesTaskProducers: [any TaskProducerFactory] {
@@ -63,6 +63,26 @@ struct TaskProducersExtension: TaskProducerExtension {
6363
}
6464
}
6565

66+
struct ExtensionPointExtractorTaskProducerFactory: TaskProducerFactory {
67+
var name: String {
68+
"ExtensionPointExtractorTaskProducer"
69+
}
70+
71+
func createTaskProducer(_ context: TargetTaskProducerContext, startPhaseNodes: [PlannedVirtualNode], endPhaseNode: PlannedVirtualNode) -> any TaskProducer {
72+
ExtensionPointExtractorTaskProducer(context, phaseStartNodes: startPhaseNodes, phaseEndNode: endPhaseNode)
73+
}
74+
}
75+
76+
struct AppExtensionInfoPlistGeneratorTaskProducerFactory: TaskProducerFactory {
77+
var name: String {
78+
"AppExtensionInfoPlistGeneratorTaskProducer"
79+
}
80+
81+
func createTaskProducer(_ context: TargetTaskProducerContext, startPhaseNodes: [PlannedVirtualNode], endPhaseNode: PlannedVirtualNode) -> any TaskProducer {
82+
AppExtensionInfoPlistGeneratorTaskProducer(context, phaseStartNodes: startPhaseNodes, phaseEndNode: endPhaseNode)
83+
}
84+
}
85+
6686
struct StubBinaryTaskProducerFactory: TaskProducerFactory, GlobalTaskProducerFactory {
6787
var name: String {
6888
"StubBinaryTaskProducer"
@@ -100,9 +120,11 @@ struct RealityAssetsTaskProducerFactory: TaskProducerFactory {
100120
struct ApplePlatformSpecsExtension: SpecificationsExtension {
101121
func specificationClasses() -> [any SpecIdentifierType.Type] {
102122
[
103-
ActoolCompilerSpec.self,
123+
AppExtensionPlistGeneratorSpec.self,
104124
AppIntentsMetadataCompilerSpec.self,
105125
AppIntentsSSUTrainingCompilerSpec.self,
126+
ExtensionPointExtractorSpec.self,
127+
ActoolCompilerSpec.self,
106128
CoreDataModelCompilerSpec.self,
107129
CoreMLCompilerSpec.self,
108130
CopyTiffFileSpec.self,
@@ -232,7 +254,12 @@ struct AppleSettingsBuilderExtension: SettingsBuilderExtension {
232254
]
233255
}
234256

235-
func addBuiltinDefaults(fromEnvironment environment: [String : String], parameters: BuildParameters) throws -> [String : String] { [:] }
257+
func addBuiltinDefaults(fromEnvironment environment: [String : String], parameters: BuildParameters) throws -> [String : String] {
258+
let appIntentsProtocols = "AppIntent EntityQuery AppEntity TransientEntity AppEnum AppShortcutProviding AppShortcutsProvider AnyResolverProviding AppIntentsPackage DynamicOptionsProvider _IntentValueRepresentable _AssistantIntentsProvider _GenerativeFunctionExtractable IntentValueQuery Resolver"
259+
let extensionKitProtocols = "AppExtension ExtensionPointDefining"
260+
let constValueProtocols = [appIntentsProtocols, extensionKitProtocols].joined(separator: " ")
261+
return ["SWIFT_EMIT_CONST_VALUE_PROTOCOLS" : constValueProtocols]
262+
}
236263
func addOverrides(fromEnvironment: [String : String], parameters: BuildParameters) throws -> [String : String] { [:] }
237264
func addProductTypeDefaults(productType: ProductTypeSpec) -> [String : String] { [:] }
238265
func addSDKOverridingSettings(_ sdk: SDK, _ variant: SDKVariant?, _ sparseSDKs: [SDK], specLookupContext: any SWBCore.SpecLookupContext) throws -> [String : String] { [:] }

0 commit comments

Comments
 (0)