Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Sources/SWBCore/ProjectModel/Target.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public final class CustomTask: ProjectModelItem, Sendable {
public let outputFilePaths: [MacroStringExpression]
public let enableSandboxing: Bool
public let preparesForIndexing: Bool
/// Platform filters used by `CustomTaskProducer` to gate this task per-`ConfiguredTarget`.
/// An empty set matches every platform.
public let platformFilters: Set<PlatformFilter>

init(_ model: SWBProtocol.CustomTask, _ pifLoader: PIFLoader) {
self.commandLine = model.commandLine.map { pifLoader.userNamespace.parseString($0) }
Expand All @@ -46,6 +49,7 @@ public final class CustomTask: ProjectModelItem, Sendable {
self.outputFilePaths = model.outputFilePaths.map { pifLoader.userNamespace.parseString($0) }
self.enableSandboxing = model.enableSandboxing
self.preparesForIndexing = model.preparesForIndexing
self.platformFilters = Set(model.platformFilters.map { SWBCore.PlatformFilter($0, pifLoader) })
}

init(fromDictionary pifDict: ProjectModelItemPIF, withPIFLoader pifLoader: PIFLoader) throws {
Expand Down Expand Up @@ -74,6 +78,11 @@ public final class CustomTask: ProjectModelItem, Sendable {
self.outputFilePaths = try Self.parseValueForKeyAsArrayOfStrings(PIFKey_CustomTask_outputFilePaths, pifDict: pifDict).map { pifLoader.userNamespace.parseString($0) }
self.enableSandboxing = try Self.parseValueForKeyAsBool(PIFKey_CustomTask_enableSandboxing, pifDict: pifDict)
self.preparesForIndexing = try Self.parseValueForKeyAsBool(PIFKey_CustomTask_preparesForIndexing, pifDict: pifDict)
// Optional for back-compat with PIFs emitted before the field existed; defaults to
// empty set, which matches every platform via PlatformFilter.matches([]).
self.platformFilters = try Set(Self.parseOptionalValueForKeyAsArrayOfProjectModelItems(PIFKey_platformFilters, pifDict: pifDict, pifLoader: pifLoader, construct: {
try PlatformFilter(fromDictionary: $0, withPIFLoader: pifLoader)
}) ?? [])
}
}

Expand Down
2 changes: 2 additions & 0 deletions Sources/SWBProtocol/PIFKeyConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ public let PIFKey_CustomTask_inputFilePaths = "inputFilePaths"
public let PIFKey_CustomTask_outputFilePaths = "outputFilePaths"
public let PIFKey_CustomTask_enableSandboxing = "enableSandboxing"
public let PIFKey_CustomTask_preparesForIndexing = "preparesForIndexing"
// CustomTask reuses the shared PIFKey_platformFilters key (see line 24 above)
// for the same reason BuildFile and TargetDependency do — same JSON key, same shape.

// Special value for PIFKey_BuildRule_fileTypeIdentifier
public let PIFKey_BuildRule_fileTypeIdentifier_pattern_proxy = "pattern.proxy"
Expand Down
10 changes: 9 additions & 1 deletion Sources/SWBProtocol/ProjectModel/CustomTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ public struct CustomTask: SerializableCodable, Sendable {
public let outputFilePaths: [MacroExpressionSource]
public let enableSandboxing: Bool
public let preparesForIndexing: Bool
/// Platform filters used to gate this task per-`ConfiguredTarget`.
/// An empty set matches every platform.
public let platformFilters: Set<PlatformFilter>

public init(commandLine: [MacroExpressionSource], environment: [(MacroExpressionSource, MacroExpressionSource)], workingDirectory: MacroExpressionSource, executionDescription: MacroExpressionSource, inputFilePaths: [MacroExpressionSource], outputFilePaths: [MacroExpressionSource], enableSandboxing: Bool, preparesForIndexing: Bool) {
public init(commandLine: [MacroExpressionSource], environment: [(MacroExpressionSource, MacroExpressionSource)], workingDirectory: MacroExpressionSource, executionDescription: MacroExpressionSource, inputFilePaths: [MacroExpressionSource], outputFilePaths: [MacroExpressionSource], enableSandboxing: Bool, preparesForIndexing: Bool, platformFilters: Set<PlatformFilter> = []) {
self.commandLine = commandLine
self.environment = environment
self.workingDirectory = workingDirectory
Expand All @@ -31,6 +34,7 @@ public struct CustomTask: SerializableCodable, Sendable {
self.outputFilePaths = outputFilePaths
self.enableSandboxing = enableSandboxing
self.preparesForIndexing = preparesForIndexing
self.platformFilters = platformFilters
}

enum CodingKeys: CodingKey {
Expand All @@ -43,6 +47,7 @@ public struct CustomTask: SerializableCodable, Sendable {
case outputFilePaths
case enableSandboxing
case preparesForIndexing
case platformFilters
}

public func encode(to encoder: any Encoder) throws {
Expand All @@ -56,6 +61,7 @@ public struct CustomTask: SerializableCodable, Sendable {
try container.encode(outputFilePaths, forKey: .outputFilePaths)
try container.encode(enableSandboxing, forKey: .enableSandboxing)
try container.encode(preparesForIndexing, forKey: .preparesForIndexing)
try container.encode(platformFilters, forKey: .platformFilters)
}

public init(from decoder: any Decoder) throws {
Expand All @@ -70,6 +76,8 @@ public struct CustomTask: SerializableCodable, Sendable {
self.outputFilePaths = try container.decode([MacroExpressionSource].self, forKey: .outputFilePaths)
self.enableSandboxing = try container.decode(Bool.self, forKey: .enableSandboxing)
self.preparesForIndexing = try container.decode(Bool.self, forKey: .preparesForIndexing)
// Deliberate divergence from BuildFile precedent: existing PIFs in the wild lack this key.
self.platformFilters = try container.decodeIfPresent(Set<PlatformFilter>.self, forKey: .platformFilters) ?? []
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,18 @@ final class CustomTaskProducer: PhasedTaskProducer, TaskProducer {
func generateTasks() async -> [any PlannedTask] {
var tasks: [any PlannedTask] = []

// The current ConfiguredTarget's PlatformFilter, derived from PLATFORM_NAME / SDK info
// in globalScope. Used below to gate per-platform CustomTasks. Mirrors the pattern
// used by 8+ task producers for BuildFile.platformFilters.
let currentPlatformFilter = PlatformFilter(context.settings.globalScope)

for customTask in context.configuredTarget?.target.customTasks ?? [] {
// Skip tasks whose platform filters don't match the current ConfiguredTarget.
// Empty platformFilters matches every platform (preserves today's behavior for
// callers that don't tag their custom tasks).
guard currentPlatformFilter.matches(customTask.platformFilters) else {
continue
}

let commandLine = customTask.commandLine.map { context.settings.globalScope.evaluate($0) }
var environmentAssignments = await computeScriptEnvironment(.shellScriptPhase, scope: context.settings.globalScope, settings: context.settings, workspaceContext: context.workspaceContext, allDeploymentTargetMacroNames: context.allDeploymentTargetMacroNames())
Expand Down
18 changes: 17 additions & 1 deletion Sources/SwiftBuild/ProjectModel/CustomTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ extension ProjectModel {
public var outputFilePaths: [String]
public var enableSandboxing: Bool
public var preparesForIndexing: Bool
/// Platform filters used to gate this task per-`ConfiguredTarget`.
///
/// An empty set matches every platform, preserving today's behavior for callers that
/// don't tag their custom tasks. `CustomTaskProducer` evaluates these against the
/// `ConfiguredTarget`'s scope to decide whether to emit a task for a given CT.
public var platformFilters: Set<PlatformFilter>

public init(
commandLine: [String],
Expand All @@ -51,7 +57,8 @@ extension ProjectModel {
inputFilePaths: [String],
outputFilePaths: [String],
enableSandboxing: Bool,
preparesForIndexing: Bool
preparesForIndexing: Bool,
platformFilters: Set<PlatformFilter> = []
) {
self.commandLine = commandLine
self.environment = environment
Expand All @@ -61,6 +68,7 @@ extension ProjectModel {
self.outputFilePaths = outputFilePaths
self.enableSandboxing = enableSandboxing
self.preparesForIndexing = preparesForIndexing
self.platformFilters = platformFilters
}
}
}
Expand All @@ -82,6 +90,12 @@ extension ProjectModel.CustomTask: Codable {
self.outputFilePaths = try container.decode([String].self, forKey: .outputFilePaths)
self.enableSandboxing = try container.decode(String.self, forKey: .enableSandboxing) == "true"
self.preparesForIndexing = try container.decode(String.self, forKey: .preparesForIndexing) == "true"
// Deliberate divergence from BuildFile.platformFilters: existing PIFs in the wild
// were emitted before this field existed, so missing-key must round-trip as empty.
self.platformFilters = try container.decodeIfPresent(
Set<ProjectModel.PlatformFilter>.self,
forKey: .platformFilters
) ?? []
}

public func encode(to encoder: any Encoder) throws {
Expand All @@ -94,6 +108,7 @@ extension ProjectModel.CustomTask: Codable {
try container.encode(self.outputFilePaths, forKey: .outputFilePaths)
try container.encode(self.enableSandboxing ? "true" : "false", forKey: .enableSandboxing)
try container.encode(self.preparesForIndexing ? "true" : "false", forKey: .preparesForIndexing)
try container.encode(self.platformFilters.sorted(), forKey: .platformFilters)
}

enum CodingKeys: String, CodingKey {
Expand All @@ -105,5 +120,6 @@ extension ProjectModel.CustomTask: Codable {
case outputFilePaths
case enableSandboxing
case preparesForIndexing
case platformFilters
}
}