Skip to content
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
2 changes: 2 additions & 0 deletions Sources/SWBCore/Settings/BuiltinMacros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -995,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 @@ -2164,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
21 changes: 15 additions & 6 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 Down Expand Up @@ -2178,13 +2178,22 @@ private class SettingsBuilder {
}
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) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this effectively evaluates to true in a debug build when we're not building as mergeable? cc @mhrawdon

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the difference between MERGEABLE_LIBRARY and MAKE_MERGEABLE.
If MERGEABLE_LIBRARY is true then this is a library that is opting into being mergeable (either automatically or manually), while MAKE_MERGEABLE is only true in optimized builds per the lines above.

So MERGEABLE_LIBRARY should be what we need here.

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
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
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ fileprivate struct MergeableLibrariesBuildOperationTests: CoreBasedTests {

// Check mergeable framework targets.
for targetName in ["FwkTarget1", "FwkTarget2"] {
results.checkTask(.matchTargetName(targetName), .matchRuleType("SwiftCompile"), .matchRuleItem("Compiling bundle_lookup_helper.swift")) { _ in }
results.checkTask(.matchTargetName(targetName), .matchRuleType("SwiftCompile")) { _ in }
results.checkTask(.matchTargetName(targetName), .matchRuleType("Ld")) { task in
task.checkCommandLineDoesNotContain("-make_mergeable")
Expand Down Expand Up @@ -410,6 +411,7 @@ fileprivate struct MergeableLibrariesBuildOperationTests: CoreBasedTests {

// Check mergeable framework targets
for targetName in ["FwkTarget1", "FwkTarget2"] {
results.checkTask(.matchTargetName(targetName), .matchRuleType("SwiftCompile"), .matchRuleItem("Compiling bundle_lookup_helper.swift")) { _ in }
results.checkTask(.matchTargetName(targetName), .matchRuleType("SwiftCompile")) { _ in }
results.checkTask(.matchTargetName(targetName), .matchRuleType("Ld")) { task in
task.checkCommandLineContains("-make_mergeable")
Expand Down Expand Up @@ -715,6 +717,7 @@ fileprivate struct MergeableLibrariesBuildOperationTests: CoreBasedTests {
// Check FwkTarget
do {
let targetName = "FwkTarget"
results.checkTask(.matchTargetName(targetName), .matchRuleType("SwiftCompile"), .matchRuleItem("Compiling bundle_lookup_helper.swift")) { _ in }
results.checkTask(.matchTargetName(targetName), .matchRuleType("SwiftCompile")) { _ in }
results.checkTask(.matchTargetName(targetName), .matchRuleType("Ld")) { task in
task.checkCommandLineDoesNotContain("-make_mergeable")
Expand Down Expand Up @@ -875,6 +878,7 @@ fileprivate struct MergeableLibrariesBuildOperationTests: CoreBasedTests {
// Check FwkTarget
do {
let targetName = "FwkTarget"
results.checkTask(.matchTargetName(targetName), .matchRuleType("SwiftCompile"), .matchRuleItem("Compiling bundle_lookup_helper.swift")) { _ in }
results.checkTask(.matchTargetName(targetName), .matchRuleType("SwiftCompile")) { _ in }
results.checkTask(.matchTargetName(targetName), .matchRuleType("Ld")) { task in
task.checkCommandLineContains("-make_mergeable")
Expand Down
Loading