Skip to content

Commit e7543d1

Browse files
committed
Read hardcoded deployment targets list from features.json instead
Eliminate the maintenance burden of having to manually maintain this list. Also, it wasn't correct for Android and QNX anyways, since those don't actually have deployment target environment variables recognized by clang. rdar://91377944
1 parent 1d66c4f commit e7543d1

File tree

17 files changed

+50
-96
lines changed

17 files changed

+50
-96
lines changed

Sources/SWBAndroidPlatform/Plugin.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,6 @@ struct AndroidEnvironmentExtension: EnvironmentExtension {
6565
}
6666

6767
struct AndroidPlatformExtension: PlatformInfoExtension {
68-
func knownDeploymentTargetMacroNames() -> Set<String> {
69-
["ANDROID_DEPLOYMENT_TARGET"]
70-
}
71-
7268
func additionalPlatforms(context: any PlatformInfoExtensionAdditionalPlatformsContext) throws -> [(path: Path, data: [String: PropertyListItem])] {
7369
[
7470
(.root, [

Sources/SWBApplePlatform/Plugin.swift

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -207,17 +207,6 @@ struct XCStringsInputFileGroupingStrategyExtension: InputFileGroupingStrategyExt
207207
}
208208

209209
struct ApplePlatformInfoExtension: PlatformInfoExtension {
210-
func knownDeploymentTargetMacroNames() -> Set<String> {
211-
[
212-
"MACOSX_DEPLOYMENT_TARGET",
213-
"IPHONEOS_DEPLOYMENT_TARGET",
214-
"TVOS_DEPLOYMENT_TARGET",
215-
"WATCHOS_DEPLOYMENT_TARGET",
216-
"DRIVERKIT_DEPLOYMENT_TARGET",
217-
"XROS_DEPLOYMENT_TARGET",
218-
]
219-
}
220-
221210
func preferredArchValue(for platformName: String) -> String? {
222211
// FIXME: rdar://65011964 (Remove PLATFORM_PREFERRED_ARCH)
223212
// Don't add values for any new platforms

Sources/SWBBuildService/Messages.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1440,7 +1440,8 @@ private struct AllExportedMacrosAndValuesMsg: MessageHandler {
14401440
let settings = try getSettings(for: session, workspaceContext: workspaceContext, requestContext: message.context, purpose: .build)
14411441

14421442
// Get the list of setting names and evaluated values. We use the same algorithm as is used to export settings to shell script build phases.
1443-
let exportedMacrosAndValues = computeScriptEnvironment(.shellScriptPhase, scope: settings.globalScope, settings: settings, workspaceContext: workspaceContext)
1443+
// We explicitly pass an empty set for `allDeploymentTargetMacroNames` because in this context we are exporting the list of known macros and not applying the special case to only exported a single deployment target like we do in shell scripts.
1444+
let exportedMacrosAndValues = computeScriptEnvironment(.shellScriptPhase, scope: settings.globalScope, settings: settings, workspaceContext: workspaceContext, allDeploymentTargetMacroNames: [])
14441445

14451446
return AllExportedMacrosAndValuesResponse(result: exportedMacrosAndValues)
14461447
}

Sources/SWBBuildSystem/CleanOperation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ package final class CleanOperation: BuildSystemOperation, TargetDependencyResolv
207207
delegate.targetPreparationStarted(self, configuredTarget: configuredTarget)
208208
delegate.targetStarted(self, configuredTarget: configuredTarget)
209209

210-
let (executable, arguments, workingDirectory, environment) = constructCommandLine(for: configuredTarget.target as! SWBCore.ExternalTarget, action: "clean", settings: settings, workspaceContext: workspaceContext, scope: settings.globalScope)
210+
let (executable, arguments, workingDirectory, environment) = constructCommandLine(for: configuredTarget.target as! SWBCore.ExternalTarget, action: "clean", settings: settings, workspaceContext: workspaceContext, scope: settings.globalScope, allDeploymentTargetMacroNames: [])
211211
let commandLine = [executable] + arguments
212212

213213
let specLookupContext = SpecLookupCtxt(specRegistry: workspaceContext.core.specRegistry, platform: settings.platform)

Sources/SWBCore/Extensions/PlatformInfoExtension.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ public struct PlatformInfoExtensionPoint: ExtensionPoint, Sendable {
2222
}
2323

2424
public protocol PlatformInfoExtension: Sendable {
25-
func knownDeploymentTargetMacroNames() -> Set<String>
26-
2725
func preferredArchValue(for: String) -> String?
2826

2927
func additionalTestLibraryPaths(scope: MacroEvaluationScope, platform: Platform?, fs: any FSProxy) -> [Path]
@@ -40,10 +38,6 @@ public protocol PlatformInfoExtension: Sendable {
4038
}
4139

4240
extension PlatformInfoExtension {
43-
public func knownDeploymentTargetMacroNames() -> Set<String> {
44-
[]
45-
}
46-
4741
public func preferredArchValue(for: String) -> String? {
4842
nil
4943
}

Sources/SWBCore/PlatformRegistry.swift

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -309,9 +309,6 @@ public final class PlatformRegistry {
309309
/// The map of platforms by name.
310310
var platformsByName = Dictionary<String, Platform>()
311311

312-
/// The set of all known deployment target macro names, even if the platforms that use those settings are not installed.
313-
private(set) var allDeploymentTargetMacroNames = Set<String>()
314-
315312
/// The default deployment targets for all installed platforms.
316313
var defaultDeploymentTargets: [String: Version] {
317314
Dictionary(uniqueKeysWithValues: Dictionary(grouping: platforms, by: { $0.defaultSDKVariant?.deploymentTargetSettingName })
@@ -353,46 +350,6 @@ public final class PlatformRegistry {
353350
}
354351
}
355352

356-
private func loadDeploymentTargetMacroNames() {
357-
// We must have loaded the extended platform info before doing this,
358-
// since deploymentTargetMacro is set on the Platform objects through there.
359-
precondition(hasLoadedExtendedInfo)
360-
361-
// Set up allDeploymentTargetMacroNames in stages to detect issues.
362-
// First we add all deployment targets from installed platforms, and emit a warning if multiple platforms declare that they use the same deployment target.
363-
var platformsByDeploymentTarget = [String: Set<String>]()
364-
for platform in platforms {
365-
if let macroName = platform.deploymentTargetMacro?.name, !macroName.isEmpty {
366-
allDeploymentTargetMacroNames.insert(macroName)
367-
368-
// Don't count simulator platforms separately, as a simulator platform always shares a deployment target with its corresponding device platform.
369-
platformsByDeploymentTarget[macroName, default: Set<String>()].insert(platform.correspondingDevicePlatform?.name ?? platform.name)
370-
}
371-
}
372-
373-
// Now add in all deployment targets we know about. This is because clang also knows about these deployment targets intrinsically when they are passed as environment variables, so we sometimes need to work with them even if the platform which defines them is not installed.
374-
@preconcurrency @PluginExtensionSystemActor func platformInfoExtensions() -> [any PlatformInfoExtensionPoint.ExtensionProtocol] {
375-
delegate.pluginManager.extensions(of: PlatformInfoExtensionPoint.self)
376-
}
377-
378-
for platformExtension in platformInfoExtensions() {
379-
for knownDeploymentTargetMacroName in platformExtension.knownDeploymentTargetMacroNames() {
380-
allDeploymentTargetMacroNames.insert(knownDeploymentTargetMacroName)
381-
platformsByDeploymentTarget[knownDeploymentTargetMacroName] = nil
382-
}
383-
}
384-
385-
// For any macros left in the dictionary, emit a warning that it's a deployment target macro we didn't know about so we can add them to the list above in the future.
386-
for macroName in platformsByDeploymentTarget.keys.sorted() {
387-
guard let platformNames = platformsByDeploymentTarget[macroName], !platformNames.isEmpty else {
388-
// This is a weird scenario - should we emit a warning here?
389-
continue
390-
}
391-
let platformList: String = (platformNames.count > 1 ? "s: " : ": ") + platformNames.sorted().joined(separator: ", ")
392-
delegate.warning("found previously-unknown deployment target macro '\(macroName)' declared by platform\(platformList)")
393-
}
394-
}
395-
396353
/// Register all platforms in the given directory.
397354
private func registerPlatformsInDirectory(_ path: Path, _ fs: any FSProxy) async {
398355
for item in (try? localFS.listdir(path))?.sorted(by: <) ?? [] {
@@ -690,8 +647,6 @@ public final class PlatformRegistry {
690647
unregisterPlatform(platform)
691648
}
692649
}
693-
694-
loadDeploymentTargetMacroNames()
695650
}
696651
var hasLoadedExtendedInfo = false
697652

Sources/SWBCore/ShellScript.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public enum ScriptType: Sendable {
2626
/// - settings: The settings to use for computing the script's environment and search paths.
2727
/// - workspaceContext: The workspace context to use for computing the script's environment.
2828
/// - scope: The scope in which to expand build settings.
29-
public func constructCommandLine(for target: ExternalTarget, action: String, settings: Settings, workspaceContext: WorkspaceContext, scope: MacroEvaluationScope) -> (executable: String, arguments: [String], workingDirectory: Path, environment: [String: String]) {
29+
public func constructCommandLine(for target: ExternalTarget, action: String, settings: Settings, workspaceContext: WorkspaceContext, scope: MacroEvaluationScope, allDeploymentTargetMacroNames: Set<String>) -> (executable: String, arguments: [String], workingDirectory: Path, environment: [String: String]) {
3030
let scope = settings.globalScope
3131
func lookup(_ macro: MacroDeclaration) -> MacroExpression? {
3232
switch macro {
@@ -44,7 +44,7 @@ public func constructCommandLine(for target: ExternalTarget, action: String, set
4444
let workingDirectory = (settings.project?.sourceRoot ?? workspaceContext.workspace.path.dirname).join(Path(scope.evaluate(target.workingDirectory, lookup: lookup)))
4545
var environment: [String: String] = [:]
4646
if target.passBuildSettingsInEnvironment {
47-
environment = computeScriptEnvironment(.externalTarget, scope: scope, settings: settings, workspaceContext: workspaceContext)
47+
environment = computeScriptEnvironment(.externalTarget, scope: scope, settings: settings, workspaceContext: workspaceContext, allDeploymentTargetMacroNames: allDeploymentTargetMacroNames)
4848
}
4949

5050
// Always set ACTION in the environment.
@@ -77,7 +77,7 @@ public func constructCommandLine(for target: ExternalTarget, action: String, set
7777
/// - scope: The scope in which to expand build settings.
7878
/// - settings: The settings to use for computing the environment.
7979
/// - workspaceContext: The workspace context to use for computing the environment
80-
public func computeScriptEnvironment(_ type: ScriptType, scope: MacroEvaluationScope, settings: Settings, workspaceContext: WorkspaceContext) -> [String: String] {
80+
public func computeScriptEnvironment(_ type: ScriptType, scope: MacroEvaluationScope, settings: Settings, workspaceContext: WorkspaceContext, allDeploymentTargetMacroNames: Set<String>) -> [String: String] {
8181
var result = [String: String]()
8282

8383
// FIXME: Note that we merged this function for shell scripts with the very similar code that was used to build the environment for external build commands. Currently in order to retain perfect compatibility we do various things conditionally based on the mode, but really this code should just be a single function that is used for both contexts at some point.
@@ -202,7 +202,7 @@ public func computeScriptEnvironment(_ type: ScriptType, scope: MacroEvaluationS
202202

203203
// Remove deployment targets for platforms other than the one we're building for. <rdar://problem/20008508>
204204
let ourDeploymentTargetName = scope.evaluate(BuiltinMacros.DEPLOYMENT_TARGET_SETTING_NAME)
205-
for deploymentTargetNameToRemove in workspaceContext.core.platformRegistry.allDeploymentTargetMacroNames {
205+
for deploymentTargetNameToRemove in allDeploymentTargetMacroNames {
206206
if ourDeploymentTargetName.isEmpty || ourDeploymentTargetName != deploymentTargetNameToRemove {
207207
result.removeValue(forKey: deploymentTargetNameToRemove)
208208
}

Sources/SWBCore/SpecImplementations/CommandLineToolSpec.swift

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public final class DiscoveredCommandLineToolSpecInfoCache: Sendable {
142142
}
143143
}
144144

145-
/// Returns the set of 'feature' names detailed in a 'features.json' file that a tool may install alongside its executable.
145+
/// Represents the set of 'feature' names detailed in a 'features.json' file that a tool may install alongside its executable.
146146
/// The format is like this:
147147
///
148148
/// {
@@ -154,18 +154,17 @@ public final class DiscoveredCommandLineToolSpecInfoCache: Sendable {
154154
/// ]
155155
/// }
156156
///
157-
func rawFeaturesFromToolFeaturesJSON(path: Path, fs: any FSProxy) throws -> Set<String> {
158-
struct FeatureSet: Codable {
159-
struct Feature: Codable {
160-
let name: String
161-
}
162-
let features: [Feature]
157+
private struct FeatureSet: Codable {
158+
struct Feature: Codable {
159+
let name: String
160+
let value: PropertyListItem?
163161
}
164-
return try Set(JSONDecoder().decode(FeatureSet.self, from: path, fs: fs).features.map(\.name))
162+
let features: [Feature]
165163
}
166164

167165
/// Set of features from a 'features.json' file that may be installed alongside its executable, see `rawFeaturesFromToolFeaturesJSON`.
168166
public struct ToolFeatures<T>: Sendable where T: Sendable, T: Hashable, T: CaseIterable, T: RawRepresentable, T.RawValue == String {
167+
private let featureSet: FeatureSet
169168
public let flags: Set<T>
170169

171170
public static var none: ToolFeatures {
@@ -174,11 +173,13 @@ public struct ToolFeatures<T>: Sendable where T: Sendable, T: Hashable, T: CaseI
174173

175174
public init(_ flags: Set<T>) {
176175
self.flags = flags
176+
self.featureSet = .init(features: flags.map { .init(name: $0.rawValue, value: nil) })
177177
}
178178

179179
public init(path: Path, fs: any FSProxy) throws {
180180
var flags: Set<T> = []
181-
let raw = try rawFeaturesFromToolFeaturesJSON(path: path, fs: fs)
181+
featureSet = try JSONDecoder().decode(FeatureSet.self, from: path, fs: fs)
182+
let raw = Set(featureSet.features.map(\.name))
182183
for flag in T.allCases {
183184
if raw.contains(flag.rawValue) {
184185
flags.insert(flag)
@@ -197,6 +198,10 @@ public struct ToolFeatures<T>: Sendable where T: Sendable, T: Hashable, T: CaseI
197198
}
198199
return has(flag)
199200
}
201+
202+
public func value(_ flag: T) -> PropertyListItem? {
203+
featureSet.features.first(where: { $0.name == flag.rawValue })?.value
204+
}
200205
}
201206

202207
public struct ProjectVersionInfo {

Sources/SWBCore/ToolInfo/ClangToolInfo.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public struct DiscoveredClangToolSpecInfo: DiscoveredCommandLineToolSpecInfo {
3737
case resourceDirUsesMajorVersionOnly = "resource-dir-uses-major-version-only"
3838
case wSystemHeadersInModule = "Wsystem-headers-in-module"
3939
case extractAPISupportsCPlusPlus = "extract-api-supports-cpp"
40+
case deploymentTargetEnvironmentVariables = "deployment-target-environment-variables"
4041
}
4142
public var toolFeatures: ToolFeatures<FeatureFlag>
4243
public func hasFeature(_ feature: String) -> Bool {
@@ -56,6 +57,10 @@ public struct DiscoveredClangToolSpecInfo: DiscoveredCommandLineToolSpecInfo {
5657
let version = hasFeature(FeatureFlag.resourceDirUsesMajorVersionOnly.rawValue) ? llvmVersion?[0].description : llvmVersion?.description
5758
return version == nil ? nil : toolPath.dirname.dirname.join("lib").join("clang").join(version)
5859
}
60+
61+
public func deploymentTargetEnvironmentVariableNames() -> Set<String> {
62+
Set(toolFeatures.value(.deploymentTargetEnvironmentVariables)?.stringArrayValue ?? [])
63+
}
5964
}
6065

6166
public struct ClangCachingBlockListInfo : ProjectFailuresBlockList, Codable, Sendable {

Sources/SWBQNXPlatform/Plugin.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,6 @@ struct QNXEnvironmentExtension: EnvironmentExtension {
5353
}
5454

5555
struct QNXPlatformExtension: PlatformInfoExtension {
56-
func knownDeploymentTargetMacroNames() -> Set<String> {
57-
["QNX_DEPLOYMENT_TARGET"]
58-
}
59-
6056
func additionalPlatforms(context: any PlatformInfoExtensionAdditionalPlatformsContext) throws -> [(path: Path, data: [String: PropertyListItem])] {
6157
[
6258
(.root, [

Sources/SWBTaskConstruction/TaskProducers/BuildPhaseTaskProducers/BuildRuleTaskProducer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ final class BuildRuleTaskProducer: StandardTaskProducer, TaskProducer, ShellBase
118118
let commandLine = [action.interpreterPath, "-c", action.scriptSource]
119119

120120
// Compute the environment to use for the shell script.
121-
var environment = computeScriptEnvironment(.shellScriptPhase, scope: cbc.scope, settings: context.settings, workspaceContext: context.workspaceContext)
121+
var environment = await computeScriptEnvironment(.shellScriptPhase, scope: cbc.scope, settings: context.settings, workspaceContext: context.workspaceContext, allDeploymentTargetMacroNames: context.allDeploymentTargetMacroNames())
122122

123123
// If we are in a headers build phase, expose visibility and output dir
124124
// information to the script and set the HEADER_OUTPUT_DIR macro value

Sources/SWBTaskConstruction/TaskProducers/BuildPhaseTaskProducers/ShellScriptTaskProducer.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ final class ShellScriptTaskProducer: PhasedTaskProducer, TaskProducer, ShellBase
157157
}
158158

159159
// Compute the environment to use for the shell script.
160-
var environment = computeScriptEnvironment(.shellScriptPhase, scope: scope, settings: context.settings, workspaceContext: context.workspaceContext)
160+
var environment = await computeScriptEnvironment(.shellScriptPhase, scope: scope, settings: context.settings, workspaceContext: context.workspaceContext, allDeploymentTargetMacroNames: context.allDeploymentTargetMacroNames())
161161

162162
// Create the set of resolved paths and export the proper variables to the script environment.
163163
var inputs = exportPaths(&environment, shellScriptBuildPhase.inputFilePaths, prefix: "SCRIPT_INPUT_FILE", considerAllPathsDirectories: true)
@@ -308,10 +308,10 @@ final class ShellScriptTaskProducer: PhasedTaskProducer, TaskProducer, ShellBase
308308
/// Construct the tasks for an individual shell-script build rule.
309309
///
310310
/// NOTE: External targets are basically shell scripts. It lives here because the behavior shares some significant logical pieces with the behavior of shell script build phases.
311-
static func constructTasksForExternalTarget(_ target: SWBCore.ExternalTarget, _ context: TaskProducerContext, cbc: CommandBuildContext, delegate: any TaskGenerationDelegate) {
311+
static func constructTasksForExternalTarget(_ target: SWBCore.ExternalTarget, _ context: TaskProducerContext, cbc: CommandBuildContext, delegate: any TaskGenerationDelegate) async {
312312
let action = cbc.scope.evaluate(BuiltinMacros.ACTION)
313313

314-
let (executable, arguments, workingDirectory, environment) = constructCommandLine(for: target, action: action, settings: context.settings, workspaceContext: context.workspaceContext, scope: cbc.scope)
314+
let (executable, arguments, workingDirectory, environment) = await constructCommandLine(for: target, action: action, settings: context.settings, workspaceContext: context.workspaceContext, scope: cbc.scope, allDeploymentTargetMacroNames: context.allDeploymentTargetMacroNames())
315315

316316
// FIXME: Need the model name.
317317

Sources/SWBTaskConstruction/TaskProducers/OtherTaskProducers/CustomTaskProducer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ final class CustomTaskProducer: PhasedTaskProducer, TaskProducer {
2323
for customTask in context.configuredTarget?.target.customTasks ?? [] {
2424

2525
let commandLine = customTask.commandLine.map { context.settings.globalScope.evaluate($0) }
26-
var environmentAssignments = computeScriptEnvironment(.shellScriptPhase, scope: context.settings.globalScope, settings: context.settings, workspaceContext: context.workspaceContext)
26+
var environmentAssignments = await computeScriptEnvironment(.shellScriptPhase, scope: context.settings.globalScope, settings: context.settings, workspaceContext: context.workspaceContext, allDeploymentTargetMacroNames: context.allDeploymentTargetMacroNames())
2727
if context.workspaceContext.core.hostOperatingSystem != .macOS {
2828
environmentAssignments = environmentAssignments.filter { $0.key.lowercased() != "path" }
2929
}

Sources/SWBTaskConstruction/TaskProducers/OtherTaskProducers/ExternalTargetTaskProducer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ final class ExternalTargetTaskProducer: StandardTaskProducer, TaskProducer {
2323
let target = context.configuredTarget!.target as! ExternalTarget
2424
var tasks = [any PlannedTask]()
2525
await appendGeneratedTasks(&tasks) { delegate in
26-
ShellScriptTaskProducer.constructTasksForExternalTarget(target, context, cbc: CommandBuildContext(producer: context, scope: context.settings.globalScope, inputs: []), delegate: delegate)
26+
await ShellScriptTaskProducer.constructTasksForExternalTarget(target, context, cbc: CommandBuildContext(producer: context, scope: context.settings.globalScope, inputs: []), delegate: delegate)
2727
}
2828
return tasks
2929
}

0 commit comments

Comments
 (0)