Skip to content

Merge release/6.2 into main #623

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 30, 2025
Merged
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
4 changes: 4 additions & 0 deletions Sources/SWBCore/Settings/BuiltinMacros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ public final class BuiltinMacros {

// MARK: Mergeable libraries macros

public static let ADD_MERGEABLE_DEBUG_HOOK = BuiltinMacros.declareBooleanMacro("ADD_MERGEABLE_DEBUG_HOOK")
public static let AUTOMATICALLY_MERGE_DEPENDENCIES = BuiltinMacros.declareBooleanMacro("AUTOMATICALLY_MERGE_DEPENDENCIES")
public static let MERGEABLE_LIBRARY = BuiltinMacros.declareBooleanMacro("MERGEABLE_LIBRARY")
public static let DONT_EMBED_REEXPORTED_MERGEABLE_LIBRARIES = BuiltinMacros.declareBooleanMacro("DONT_EMBED_REEXPORTED_MERGEABLE_LIBRARIES")
Expand Down Expand Up @@ -994,6 +995,7 @@ public final class BuiltinMacros {
public static let SWIFT_ABI_CHECKER_EXCEPTIONS_FILE = BuiltinMacros.declareStringMacro("SWIFT_ABI_CHECKER_EXCEPTIONS_FILE")
public static let SWIFT_ABI_GENERATION_TOOL_OUTPUT_DIR = BuiltinMacros.declareStringMacro("SWIFT_ABI_GENERATION_TOOL_OUTPUT_DIR")
public static let SWIFT_ACCESS_NOTES_PATH = BuiltinMacros.declareStringMacro("SWIFT_ACCESS_NOTES_PATH")
public static let SWIFT_ACTIVE_COMPILATION_CONDITIONS = BuiltinMacros.declareStringListMacro("SWIFT_ACTIVE_COMPILATION_CONDITIONS")
public static let SWIFT_ALLOW_INSTALL_OBJC_HEADER = BuiltinMacros.declareBooleanMacro("SWIFT_ALLOW_INSTALL_OBJC_HEADER")
public static let __SWIFT_ALLOW_INSTALL_OBJC_HEADER_MESSAGE = BuiltinMacros.declareStringMacro("__SWIFT_ALLOW_INSTALL_OBJC_HEADER_MESSAGE")
public static let SWIFT_COMPILATION_MODE = BuiltinMacros.declareStringMacro("SWIFT_COMPILATION_MODE")
Expand Down Expand Up @@ -1371,6 +1373,7 @@ public final class BuiltinMacros {
ADDITIONAL_SDK_DIRS,
AD_HOC_CODE_SIGNING_ALLOWED,
__AD_HOC_CODE_SIGNING_NOT_ALLOWED_SUPPLEMENTAL_MESSAGE,
ADD_MERGEABLE_DEBUG_HOOK,
AGGREGATE_TRACKED_DOMAINS,
ALLOW_BUILD_REQUEST_OVERRIDES,
ALLOW_DISJOINTED_DIRECTORIES_AS_DEPENDENCIES,
Expand Down Expand Up @@ -2162,6 +2165,7 @@ public final class BuiltinMacros {
SWIFT_ABI_CHECKER_EXCEPTIONS_FILE,
SWIFT_ABI_GENERATION_TOOL_OUTPUT_DIR,
SWIFT_ACCESS_NOTES_PATH,
SWIFT_ACTIVE_COMPILATION_CONDITIONS,
SWIFT_ALLOW_INSTALL_OBJC_HEADER,
__SWIFT_ALLOW_INSTALL_OBJC_HEADER_MESSAGE,
SWIFT_COMPILATION_MODE,
Expand Down
54 changes: 36 additions & 18 deletions Sources/SWBCore/Settings/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1554,7 +1554,7 @@ private class SettingsBuilder {
}

// Push the target derived overriding settings.
addTargetDerivedSettings(self.target, boundProperties.platform, boundProperties.sdk, boundProperties.sdkVariant)
addTargetDerivedSettings(self.target, boundProperties.platform, boundProperties.sdk, boundProperties.sdkVariant, specLookupContext)

if boundDeploymentTarget.platformDeploymentTargetMacro == BuiltinMacros.DRIVERKIT_DEPLOYMENT_TARGET, let deploymentTarget = boundDeploymentTarget.platformDeploymentTarget, deploymentTarget < Version(20) {
var table = MacroValueAssignmentTable(namespace: userNamespace)
Expand Down Expand Up @@ -2039,13 +2039,13 @@ private class SettingsBuilder {
/// Add the derived overriding settings for the target. These are settings whose values depend on the whole stack of build settings, and include settings which are forced to a value under certain conditions, and settings whose value is wholly derived from other settings. They override settings from all lower levels, and thus cannot be overridden by (for example) xcodebuild or run destination overrides, so settings should only be assigned here when they represent true boundary conditions which users should never want to or be able to override.
///
/// These are only added if we're constructing settings for a target.
func addTargetDerivedSettings(_ target: Target?, _ platform: Platform?, _ sdk: SDK?, _ sdkVariant: SDKVariant?) {
func addTargetDerivedSettings(_ target: Target?, _ platform: Platform?, _ sdk: SDK?, _ sdkVariant: SDKVariant?, _ specLookupContext: any SpecLookupContext) {
guard target != nil else {
return
}

push(getTargetDerivedSettings(platform, sdk, sdkVariant), .exportedForNative)
addSecondaryTargetDerivedSettings(sdk)
addSecondaryTargetDerivedSettings(sdk, specLookupContext)
}

/// Add the core derived overriding settings for the target.
Expand Down Expand Up @@ -2138,7 +2138,7 @@ private class SettingsBuilder {
/// Add derived settings for the target which are themselves derived from the core target derived settings computed above. (Whee!)
///
/// This is called from `addTargetDerivedSettings().
func addSecondaryTargetDerivedSettings(_ sdk: SDK?) {
func addSecondaryTargetDerivedSettings(_ sdk: SDK?, _ specLookupContext: any SpecLookupContext) {
// Mergeable library/merged binary support.
do {
let scope = createScope(sdkToUse: sdk)
Expand All @@ -2164,22 +2164,36 @@ private class SettingsBuilder {
table.push(BuiltinMacros.MERGE_LINKED_LIBRARIES, literal: true)
}

// If this is a mergeable library, then build it as mergeable if this is a release build.
if scope.evaluate(BuiltinMacros.MERGEABLE_LIBRARY), !scope.evaluate(BuiltinMacros.IS_UNOPTIMIZED_BUILD) {
table.push(BuiltinMacros.MAKE_MERGEABLE, literal: true)
// If this is a mergeable library, for a release build we want to build it as mergeable, whereas for a debug build we want to add the mergeable debug hook.
if scope.evaluate(BuiltinMacros.MERGEABLE_LIBRARY) {
if scope.evaluate(BuiltinMacros.IS_UNOPTIMIZED_BUILD) {
table.push(BuiltinMacros.ADD_MERGEABLE_DEBUG_HOOK, literal: true)
}
else {
table.push(BuiltinMacros.MAKE_MERGEABLE, literal: true)
}
}

push(table, .exportedForNative)
}
do {
let scope = createScope(sdkToUse: sdk)
var table = MacroValueAssignmentTable(namespace: core.specRegistry.internalMacroNamespace)

// If the product is being built as mergeable, then that overrides certain other settings.
if scope.evaluate(BuiltinMacros.MAKE_MERGEABLE) {
var table = MacroValueAssignmentTable(namespace: core.specRegistry.internalMacroNamespace)
table.push(BuiltinMacros.STRIP_INSTALLED_PRODUCT, literal: false)
push(table, .exportedForNative)
}

// Even if not being merged in this build, a mergeable library still uses a generated bundle lookup helper to power #bundle support.
if scope.evaluate(BuiltinMacros.MERGEABLE_LIBRARY) {
let pathResolver = FilePathResolver(scope: scope)
if (target as? StandardTarget)?.sourcesBuildPhase?.containsSwiftSources(workspaceContext.workspace, specLookupContext, scope, pathResolver) ?? false {
table.push(BuiltinMacros.SWIFT_ACTIVE_COMPILATION_CONDITIONS, BuiltinMacros.namespace.parseStringList(["$(inherited)", "SWIFT_BUNDLE_LOOKUP_HELPER_AVAILABLE"]))
}
}

push(table, .exportedForNative)
}
}

Expand Down Expand Up @@ -4274,17 +4288,20 @@ private class SettingsBuilder {
}

let toolchainPath = Path(scope.evaluateAsString(BuiltinMacros.TOOLCHAIN_DIR))
guard let toolchain = core.toolchainRegistry.toolchains.first(where: { $0.path == toolchainPath }) else {
guard let toolchain = core.toolchainRegistry.toolchains.first(where: { $0.path == toolchainPath }),
let defaultToolchain = core.toolchainRegistry.defaultToolchain
else {
return []
}

enum ToolchainStyle {
case xcodeDefault
case xcode(isDefault: Bool)
case other

init(_ toolchain: Toolchain) {
if toolchain.identifier == ToolchainRegistry.defaultToolchainIdentifier {
self = .xcodeDefault
if toolchain.identifier.hasPrefix(ToolchainRegistry.appleToolchainIdentifierPrefix) {
let isDefault = toolchain.identifier == ToolchainRegistry.defaultToolchainIdentifier
self = .xcode(isDefault: isDefault)
} else {
self = .other
}
Expand All @@ -4293,11 +4310,12 @@ private class SettingsBuilder {

let testingPluginsPath = "/usr/lib/swift/host/plugins/testing"
switch (ToolchainStyle(toolchain)) {
case .xcodeDefault:
// This target is building using the same toolchain as the one used
// to build the testing libraries which it is using, so it can use
// non-external plugin flags.
return ["-plugin-path", "$(TOOLCHAIN_DIR)\(testingPluginsPath)"]
case let .xcode(isDefault):
// This target is using a built-in Xcode toolchain, and that should
// match the toolchain which was used to build the testing libraries
// this target is using, so it can use non-external plugin flags.
let toolchainPathPrefix = isDefault ? "$(TOOLCHAIN_DIR)" : defaultToolchain.path.str
return ["-plugin-path", "\(toolchainPathPrefix)\(testingPluginsPath)"]
case .other:
// This target is using the testing libraries from Xcode,
// which were built using the XcodeDefault toolchain, but it's using
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,9 @@ final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, FilesBase
let packageTargetBundleAccessorResult = await generatePackageTargetBundleAccessorResult(scope)
tasks += packageTargetBundleAccessorResult?.tasks ?? []

let bundleLookupHelperResult = await generateBundleLookupHelper(scope)
tasks += bundleLookupHelperResult?.tasks ?? []

let embedInCodeAccessorResult: GeneratedResourceAccessorResult?
if scope.evaluate(BuiltinMacros.GENERATE_EMBED_IN_CODE_ACCESSORS), let configuredTarget = context.configuredTarget, buildPhase.containsSwiftSources(context.workspaceContext.workspace, context, scope, context.filePathResolver) {
let ownTargetBuildFilesToEmbed = ((context.workspaceContext.workspace.target(for: configuredTarget.target.guid) as? StandardTarget)?.buildPhases.compactMap { $0 as? BuildPhaseWithBuildFiles }.flatMap { $0.buildFiles }.filter { $0.resourceRule == .embedInCode }) ?? []
Expand Down Expand Up @@ -868,6 +871,10 @@ final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, FilesBase
result.append((packageTargetBundleAccessorResult.fileToBuild, packageTargetBundleAccessorResult.fileToBuildFileType, /* shouldUsePrefixHeader */ false))
}

if let bundleLookupHelperResult {
result.append((bundleLookupHelperResult.fileToBuild, bundleLookupHelperResult.fileToBuildFileType, /* shouldUsePrefixHeader */ false))
}

if let embedInCodeAccessorResult {
result.append((embedInCodeAccessorResult.fileToBuild, embedInCodeAccessorResult.fileToBuildFileType, /* shouldUsePrefixHeader */ false))
}
Expand Down Expand Up @@ -1192,11 +1199,17 @@ final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, FilesBase
let buildFilesContext = BuildFilesProcessingContext(scope, belongsToPreferredArch: preferredArch == nil || preferredArch == arch, currentArchSpec: currentArchSpec)
var perArchTasks: [any PlannedTask] = []
await groupAndAddTasksForFiles(self, buildFilesContext, scope, filterToAPIRules: isForAPI, filterToHeaderRules: isForHeaders, &perArchTasks, extraResolvedBuildFiles: {
var result: [(Path, FileTypeSpec, Bool)] = []

if let packageTargetBundleAccessorResult {
return [(packageTargetBundleAccessorResult.fileToBuild, packageTargetBundleAccessorResult.fileToBuildFileType, /* shouldUsePrefixHeader */ false)]
} else {
return []
result.append((packageTargetBundleAccessorResult.fileToBuild, packageTargetBundleAccessorResult.fileToBuildFileType, /* shouldUsePrefixHeader */ false))
}

if let bundleLookupHelperResult {
result.append((bundleLookupHelperResult.fileToBuild, bundleLookupHelperResult.fileToBuildFileType, /* shouldUsePrefixHeader */ false))
}

return result
}())

// Add all the collected per-arch tasks.
Expand Down Expand Up @@ -1725,7 +1738,43 @@ final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, FilesBase
return GeneratedResourceAccessorResult(tasks: tasks, fileToBuild: filePath, fileToBuildFileType: context.lookupFileType(fileName: "sourcecode.swift")!)
}

/// Generates a task for creating the `__BundleLookupHelper` class to enable `#bundle` support in mergeable libraries.
private func generateBundleLookupHelper(_ scope: MacroEvaluationScope) async -> GeneratedResourceAccessorResult? {
// We generate a __BundleLookupHelper class that Foundation's #bundle macro can use to lookup the resource bundle.
// ld will inject a mapping of class pointers to the correct resource bundle so that BundleForClass works at runtime.

// We only need this treatment for mergeable libraries at this time.
// Package targets do something similar but they generate the Bundle.module extensions and #bundle calls that.

// We need to do this for all mergeable libraries, even if it will just be re-exported in this build.
guard scope.evaluate(BuiltinMacros.MERGEABLE_LIBRARY) else {
return nil
}

let workspace = self.context.workspaceContext.workspace

// #bundle is a Swift macro, so this is only needed for Swift code.
guard buildPhase.containsSwiftSources(workspace, context, scope, context.filePathResolver) else {
return nil
}

let filePath = scope.evaluate(BuiltinMacros.DERIVED_SOURCES_DIR).join("bundle_lookup_helper.swift")

// We need one class with a relatively unique name that #bundle can use for bundle lookup.
// It cannot be less visible than internal since the #bundle expansion needs to be able to resolve it AND so ld will record the class->bundle mapping.
// We intentionally do not want a Foundation dependency in this generated code, so don't import Foundation.
let content = "internal class __BundleLookupHelper {}"

var tasks = [any PlannedTask]()
await appendGeneratedTasks(&tasks) { delegate in
context.writeFileSpec.constructFileTasks(CommandBuildContext(producer: context, scope: context.settings.globalScope, inputs: [], output: filePath), delegate, contents: ByteString(encodingAsUTF8: content), permissions: nil, preparesForIndexing: true, additionalTaskOrderingOptions: [.immediate, .ignorePhaseOrdering])
}
return GeneratedResourceAccessorResult(tasks: tasks, fileToBuild: filePath, fileToBuildFileType: context.lookupFileType(fileName: "sourcecode.swift")!)
}

/// Generates a task for creating the resource bundle accessor for package targets.
///
/// This produces the `Bundle.module` accessor.
private func generatePackageTargetBundleAccessorResult(_ scope: MacroEvaluationScope) async -> GeneratedResourceAccessorResult? {
let bundleName = scope.evaluate(BuiltinMacros.PACKAGE_RESOURCE_BUNDLE_NAME)
let isRegularPackage = scope.evaluate(BuiltinMacros.PACKAGE_RESOURCE_TARGET_KIND) == .regular
Expand Down
Loading