Skip to content

Commit 867beed

Browse files
authored
Merge pull request #1446 from swiftlang/automerge/merge-main-2026-06-05_22-21
2 parents af813e1 + a865736 commit 867beed

23 files changed

Lines changed: 239 additions & 128 deletions

Sources/SWBCore/Core.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -628,14 +628,18 @@ public final class Core: Sendable {
628628
}
629629
}
630630

631-
package static func effectivePlatformName(platformName: String, archComponent: String) -> String {
632-
// `archComponent` is currently unused, but will be used to disambiguate build directories for platforms that
633-
// don't support universal binaries once this API is adopted.
631+
package func effectivePlatformName(platformName: String, archComponent: String) -> String {
634632
if platformName == "macosx" {
633+
// For historical compatibility reasons, macOS does not have an effective platform name.
635634
return ""
636-
} else {
635+
} else if let platform = platformRegistry.lookup(name: platformName), platform.isApplePlatform {
636+
// When targeting an Apple platform we may be building a Universal binary, so disambiguate based on only the platform.
637637
return "-\(platformName)"
638-
}
638+
} else {
639+
// Otherwise disambiguate based on the arch and platform to ensure products built for different architectures one after
640+
// another don't overwrite each other.
641+
return "-\(platformName)-\(archComponent)"
642+
}
639643
}
640644

641645
public func buildTargetInfo(triple: String) throws -> (sdkName: String, platformName: String, buildProductsDirectorySuffix: String, sdkVariant: String?, deploymentTargetSettingName: String?, deploymentTarget: String?) {
@@ -648,7 +652,7 @@ public final class Core: Sendable {
648652
throw StubError.error("unable to find a single platform name for triple '\(triple)'. results: \(platformNames)")
649653
}
650654

651-
let buildProductsDirectorySuffix = Self.effectivePlatformName(platformName: platformName, archComponent: llvmTriple.arch)
655+
let buildProductsDirectorySuffix = effectivePlatformName(platformName: platformName, archComponent: llvmTriple.arch)
652656

653657
let sdkVariants = Set(platformExtensions.compactMap({ $0.sdkVariant(triple: llvmTriple) }))
654658
if sdkVariants.count > 1 {

Sources/SWBCore/PlatformRegistry.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ public final class Platform: Sendable {
122122
return correspondingDevicePlatformName != nil
123123
}
124124

125+
public var isApplePlatform: Bool {
126+
defaultSDKVariant?.llvmTargetTripleVendor == "apple"
127+
}
128+
125129
/// The type of platform for the purposes of signing.
126130
@_spi(Testing) public var signingContext: any PlatformSigningContext {
127131
return signingContextCache.getValue(self)

Sources/SWBCore/Settings/BuiltinMacros.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ public final class BuiltinMacros {
150150
public static let SWIFT_PLATFORM_TARGET_PREFIX = BuiltinMacros.declareStringMacro("SWIFT_PLATFORM_TARGET_PREFIX")
151151
public static let TVOS_DEPLOYMENT_TARGET = BuiltinMacros.declareStringMacro("TVOS_DEPLOYMENT_TARGET")
152152
public static let VALID_ARCHS = BuiltinMacros.declareStringListMacro("VALID_ARCHS")
153+
public static let ONLY_ARCH = BuiltinMacros.declareStringMacro("ONLY_ARCH")
153154
public static let WATCHOS_DEPLOYMENT_TARGET = BuiltinMacros.declareStringMacro("WATCHOS_DEPLOYMENT_TARGET")
154155

155156
// MARK: Swift module-only properties.
@@ -2510,6 +2511,7 @@ public final class BuiltinMacros {
25102511
VALIDATE_DEPENDENCIES,
25112512
VALIDATE_DEVELOPMENT_ASSET_PATHS,
25122513
VALID_ARCHS,
2514+
ONLY_ARCH,
25132515
VECTOR_SUFFIX,
25142516
VERBOSE_PBXCP,
25152517
VERSIONING_STUB,

Sources/SWBCore/Settings/Settings.swift

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift open source project
44
//
5-
// Copyright (c) 2025 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2025-2026 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See http://swift.org/LICENSE.txt for license information
@@ -2528,7 +2528,7 @@ private class SettingsBuilder: ProjectMatchLookup {
25282528
platformTable.push(BuiltinMacros.PLATFORM_DISPLAY_NAME, literal: platform.displayName)
25292529
platformTable.push(BuiltinMacros.PLATFORM_DIR, literal: platform.path.str)
25302530

2531-
platformTable.push(BuiltinMacros.EFFECTIVE_PLATFORM_NAME, BuiltinMacros.namespace.parseString(Core.effectivePlatformName(platformName: platform.name, archComponent: "$(CURRENT_ARCH)")))
2531+
platformTable.push(BuiltinMacros.EFFECTIVE_PLATFORM_NAME, BuiltinMacros.namespace.parseString(core.effectivePlatformName(platformName: platform.name, archComponent: "$(ONLY_ARCH:default=unknown_arch)")))
25322532

25332533
if platform.name.hasSuffix("simulator") {
25342534
platformTable.push(BuiltinMacros.EFFECTIVE_PLATFORM_SUFFIX, literal: "simulator")
@@ -4136,6 +4136,37 @@ private class SettingsBuilder: ProjectMatchLookup {
41364136
// This is the method which computes the list of architectures to build for, and so is a funnel point for a bunch of load-bearing logic.
41374137
push(getCommonTargetTaskOverrides(specLookupContext, deploymentTarget, sdk), .exported)
41384138

4139+
// FIXME: There is a more random, but questionable stuff here. To be added in a test case driven fashion.
4140+
4141+
// Resolve some of the key path settings to be absolute.
4142+
//
4143+
// This is important, because they can be used to derive paths via xcspecs, and we want those paths to be absolute.
4144+
//
4145+
// FIXME: This is a bad way to enforce this, it doesn't have any type safety. What would be much better would be to introduce new macro evaluation features that let us write the specs to clearly demarcate paths.
4146+
//
4147+
// FIXME: <rdar://problem/41339901> In Xcode, this also does tilde expansion. We should support that (and test exactly where we want it to work).
4148+
// Now that these are paths not really required.
4149+
// These paths must only be normalized after we have bound the architecture settings via getCommonTargetTaskOverrides above, as they may be interpolated into the paths.
4150+
do {
4151+
var table = MacroValueAssignmentTable(namespace: userNamespace)
4152+
let scope = createScope(sdkToUse: sdk)
4153+
let macrosToNormalize = [
4154+
BuiltinMacros.SRCROOT, BuiltinMacros.SYMROOT, BuiltinMacros.OBJROOT, BuiltinMacros.DSTROOT,
4155+
BuiltinMacros.LOCROOT, BuiltinMacros.LOCSYMROOT,
4156+
BuiltinMacros.CCHROOT,
4157+
BuiltinMacros.CONFIGURATION_BUILD_DIR, BuiltinMacros.SHARED_PRECOMPS_DIR,
4158+
BuiltinMacros.CONFIGURATION_TEMP_DIR, BuiltinMacros.TARGET_TEMP_DIR, BuiltinMacros.TEMP_DIR,
4159+
BuiltinMacros.PROJECT_DIR, BuiltinMacros.BUILT_PRODUCTS_DIR
4160+
]
4161+
for macro in macrosToNormalize {
4162+
table.push(macro, literal: (project?.sourceRoot ?? workspaceContext.workspace.path.dirname).join(scope.evaluate(macro), normalize: true).str)
4163+
}
4164+
push(table, .exported)
4165+
}
4166+
4167+
4168+
// FIXME: Xcode also normalizes SDKROOT to an absolute path here, although native targets also do this (in a different place).
4169+
41394170
do {
41404171
// Also set each a build setting with the name of each architecture to `YES`.
41414172
// This is much more amenable to composable build setting names than using `ARCHS`.
@@ -4402,13 +4433,14 @@ private class SettingsBuilder: ProjectMatchLookup {
44024433
// Sort the archs for stability.
44034434
let sortedArchs = archs.sorted()
44044435

4405-
// Compute the base arch. The purpose of this is to always choose the same base arch from the same list of archs (if we don't have a preferredArch), and picking the first one from a sorted list is a simple way to do that.
4436+
// Compute the base arch. The purpose of this is to always choose the same base arch from the same list of archs in a stable manner.
4437+
// If the name of the arch cohort is in the list, then we prefer that one. Otherwise we pick the first one from the sorted list, which is a simple way to ensure stability.
4438+
// We don't want to use self.preferredArch here, because that can vary depending on context (for example different run destinations may provide different preferredArchs).
44064439
guard let firstArch = sortedArchs.first else {
44074440
// If there are no archs then we don't need to set up a cohort.
44084441
continue
44094442
}
4410-
let preferredBaseArch = self.preferredArch ?? cohortArch
4411-
let baseArch = archs.contains(preferredBaseArch) ? preferredBaseArch : firstArch
4443+
let baseArch = archs.contains(cohortArch) ? cohortArch : firstArch
44124444
let otherArchs = sortedArchs.filter({ $0 != baseArch })
44134445

44144446
// Set up the build settings for the cohort which CURRENT_ARCH is in. (They will of course be empty if there is no cohort - we won't even have gotten here.)
@@ -4464,6 +4496,12 @@ private class SettingsBuilder: ProjectMatchLookup {
44644496
// This will omit cohort archs, code for which is generated by the task for the base arch of the cohort.
44654497
table.push(BuiltinMacros.ARCHS_BASE, literal: archsBase)
44664498

4499+
if let onlyArch = archsBase.only {
4500+
table.push(BuiltinMacros.ONLY_ARCH, literal: onlyArch)
4501+
} else {
4502+
table.push(BuiltinMacros.ONLY_ARCH, literal: "multiple_archs")
4503+
}
4504+
44674505
// The set of Swift module-only architectures should be a set of valid architectures that's disjoint from the
44684506
// set of effective architectures. We don't necessarily care about these architectures being deprecated as this
44694507
// setting will primarily be used to support building Swift modules for deprecated (or at least unsupported)
@@ -4493,31 +4531,6 @@ private class SettingsBuilder: ProjectMatchLookup {
44934531
table.push(BuiltinMacros._LD_ARCH, literal: prefix)
44944532
}
44954533

4496-
// FIXME: There is a more random, but questionable stuff here. To be added in a test case driven fashion.
4497-
4498-
// Resolve some of the key path settings to be absolute.
4499-
//
4500-
// This is important, because they can be used to derive paths via xcspecs, and we want those paths to be absolute.
4501-
//
4502-
// FIXME: This is a bad way to enforce this, it doesn't have any type safety. What would be much better would be to introduce new macro evaluation features that let us write the specs to clearly demarcate paths.
4503-
//
4504-
// FIXME: <rdar://problem/41339901> In Xcode, this also does tilde expansion. We should support that (and test exactly where we want it to work).
4505-
// Now that these are paths not really required.
4506-
let macrosToNormalize = [
4507-
BuiltinMacros.SRCROOT, BuiltinMacros.SYMROOT, BuiltinMacros.OBJROOT, BuiltinMacros.DSTROOT,
4508-
BuiltinMacros.LOCROOT, BuiltinMacros.LOCSYMROOT,
4509-
BuiltinMacros.CCHROOT,
4510-
BuiltinMacros.CONFIGURATION_BUILD_DIR, BuiltinMacros.SHARED_PRECOMPS_DIR,
4511-
BuiltinMacros.CONFIGURATION_TEMP_DIR, BuiltinMacros.TARGET_TEMP_DIR, BuiltinMacros.TEMP_DIR,
4512-
BuiltinMacros.PROJECT_DIR, BuiltinMacros.BUILT_PRODUCTS_DIR
4513-
]
4514-
for macro in macrosToNormalize {
4515-
table.push(macro, literal: (project?.sourceRoot ?? workspaceContext.workspace.path.dirname).join(scope.evaluate(macro), normalize: true).str)
4516-
}
4517-
4518-
4519-
// FIXME: Xcode also normalizes SDKROOT to an absolute path here, although native targets also do this (in a different place).
4520-
45214534
// Compute the resolved value for GCC_VERSION, if not otherwise set.
45224535
if scope.evaluate(BuiltinMacros.GCC_VERSION).isEmpty {
45234536
table.push(BuiltinMacros.GCC_VERSION, literal: "com.apple.compilers.llvm.clang.1_0")

Sources/SWBMacro/MacroValueAssignmentTable.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ public struct MacroValueAssignmentTable: Serializable, Sendable {
342342
case 3: decl = delegate.namespace.lookupOrDeclareMacro(UserDefinedMacroDeclaration.self, name)
343343
case 4: decl = delegate.namespace.lookupOrDeclareMacro(PathMacroDeclaration.self, name)
344344
case 5: decl = delegate.namespace.lookupOrDeclareMacro(PathListMacroDeclaration.self, name)
345+
case 6: decl = delegate.namespace.lookupOrDeclareMacro(PathOrderedSetMacroDeclaration.self, name)
345346
default: throw DeserializerError.deserializationFailed("Unrecognized code for MacroType for MacroDeclaration \(name): \(typeCode)")
346347
}
347348
}

Sources/SWBTestSupport/PlatformFilter.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,9 @@ extension CoreBasedTests {
154154
results.checkNoTask(.matchTargetName("FwkTarget"))
155155
results.checkNoTask(.matchTargetName("PkgTarget"))
156156

157-
results.checkNote("Skipping '/tmp/Test/aProject/build/Debug\(runDestination.builtProductsDirSuffix)/FwkTarget.framework' because its platform filter (\(filtersString)) does not match the platform filter of the current context (\(runDestination.platformFilterString)). (in target 'AppTarget' from project 'aProject')")
157+
results.checkNote("Skipping '/tmp/Test/aProject/build/Debug\(runDestination.builtProductsDirSuffix(core: core))/FwkTarget.framework' because its platform filter (\(filtersString)) does not match the platform filter of the current context (\(runDestination.platformFilterString)). (in target 'AppTarget' from project 'aProject')")
158158

159-
results.checkNote("Skipping '/tmp/Test/Package/build/Debug\(runDestination.builtProductsDirSuffix)/PackageProduct::PkgTarget' because its platform filter (\(filtersString)) does not match the platform filter of the current context (\(runDestination.platformFilterString)). (in target 'AppTarget' from project 'aProject')")
159+
results.checkNote("Skipping '/tmp/Test/Package/build/Debug\(runDestination.builtProductsDirSuffix(core: core))/PackageProduct::PkgTarget' because its platform filter (\(filtersString)) does not match the platform filter of the current context (\(runDestination.platformFilterString)). (in target 'AppTarget' from project 'aProject')")
160160
} else {
161161
results.checkTasks(.matchTargetName("FwkTarget")) { tasks in
162162
#expect(tasks.count != 0, "Expected at least one task for dependent framework target, but the dependent target was incorrectly filtered out")

Sources/SWBTestSupport/RunDestinationTestSupport.swift

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package protocol _RunDestinationInfo {
2222
var platform: String { get }
2323
var sdk: String { get }
2424
var sdkVariant: String? { get }
25+
var targetArchitecture: String { get }
2526
}
2627

2728
extension _RunDestinationInfo {
@@ -377,15 +378,11 @@ extension _RunDestinationInfo {
377378
}
378379
}
379380

380-
package var builtProductsDirSuffix: String {
381-
switch platform {
382-
case "macosx" where sdk == "macosx" && sdkVariant == MacCatalystInfo.sdkVariantName:
381+
package func builtProductsDirSuffix(core: Core) -> String {
382+
if platform == "macosx" && sdkVariant == MacCatalystInfo.sdkVariantName {
383383
return MacCatalystInfo.publicSDKBuiltProductsDirSuffix
384-
case "macosx":
385-
return ""
386-
default:
387-
return "-\(platform)"
388384
}
385+
return core.effectivePlatformName(platformName: platform, archComponent: targetArchitecture)
389386
}
390387
}
391388

Sources/SwiftBuild/SWBBuildServiceConnection.swift

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ typealias swb_build_service_connection_message_handler_t = @Sendable (UInt64, SW
5757

5858
/// Track whether the channels have been cleared because the service has crashed.
5959
var channelsHaveBeenCleared = false
60+
61+
/// A nil handler for a known channel is expected, service crash or client closed the channel before the reply arrived.
62+
/// A nil handler for an unknown channel is unexpected.
63+
func isChannelKnown(_ channel: UInt64) -> Bool {
64+
return channel <= nextChannelID
65+
}
6066
}
6167

6268
/// An "unfair lock" that protects the channels state. Should be held only for very short periods of time.
@@ -303,14 +309,6 @@ typealias swb_build_service_connection_message_handler_t = @Sendable (UInt64, SW
303309
let channel = headerFrame.channel
304310
let msgSize = Int(headerFrame.messageSize)
305311

306-
// Look up the channel by its number.
307-
let handler = self.channelState.withLock({ $0.channels[channel] })
308-
if handler == nil {
309-
// It’s an error if we didn’t find a channel here.
310-
// FIXME: We need to handle this error in a better way.
311-
assertionFailure("no handler for channel: \(channel)")
312-
}
313-
314312
// If we don’t have all the data yet, we can go no further.
315313
if offset + msgSize > data.count {
316314
// Move the offset back to just before the header, so we’ll see it again next time around.
@@ -320,7 +318,15 @@ typealias swb_build_service_connection_message_handler_t = @Sendable (UInt64, SW
320318
}
321319

322320
// Otherwise, look up the channel by its number.
323-
handler?(channel, data.subdata(in: offset..<(offset + msgSize)))
321+
let (handler, isChannelKnown) = self.channelState.withLock {
322+
return ($0.channels[channel], $0.isChannelKnown(channel))
323+
}
324+
325+
if let handler {
326+
handler(channel, data.subdata(in: offset..<(offset + msgSize)))
327+
} else {
328+
assert(isChannelKnown, "Received reply for unknown channel: \(channel)")
329+
}
324330

325331
// Move on to the next message.
326332
offset += msgSize

0 commit comments

Comments
 (0)