Skip to content

Commit c047d29

Browse files
committed
Use access level on import in resource_bundle_accessor.swift
When a Swift package specifies a resource as part of a build target, SwiftPM synthesizes a resource_bundle_accessor.swift to provide runtime hooks to access the resource. This generated file imports Foundation but does not specify an access level. With Swift 6.0, access-level-on-import is part of the language mode, meaning that if a target specified a resource and contained a non-public import of Foundation, the build would fail with: ``` error: ambiguous implicit access level for import of 'Foundation' ``` This patch updates the generation logic to include an explicit `public` access level modifier for the Foundation import in `bundle_resource_accessor.swift` if the Swift version is >= 6.0.
1 parent af813e1 commit c047d29

2 files changed

Lines changed: 69 additions & 4 deletions

File tree

Sources/SWBTaskConstruction/TaskProducers/BuildPhaseTaskProducers/SourcesTaskProducer.swift

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2021,8 +2021,18 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
20212021
private func generatePackageTargetBundleAccessorForSwift(_ scope: MacroEvaluationScope, bundleName: String) async -> GeneratedSourceCodeResult {
20222022
let filePath = scope.evaluate(BuiltinMacros.DERIVED_SOURCES_DIR).join("resource_bundle_accessor.swift")
20232023

2024+
// Emit an explicit access level on imports if access-level-on-import is part of the language mode (>= 6.0).
2025+
// Without this, targets that use access levels on imports will fail to compile.
2026+
let importPrefix: String = {
2027+
guard let swiftVersion = try? Version(scope.evaluate(BuiltinMacros.SWIFT_VERSION)),
2028+
swiftVersion >= Version(6) else {
2029+
return ""
2030+
}
2031+
return "public "
2032+
}()
2033+
20242034
let contents = bundleName.isEmpty ? """
2025-
import class Foundation.Bundle
2035+
\(importPrefix)import class Foundation.Bundle
20262036
20272037
extension Foundation.Bundle {
20282038
static let module = {
@@ -2031,9 +2041,9 @@ package final class SourcesTaskProducer: FilesBasedBuildPhaseTaskProducerBase, F
20312041
}()
20322042
}
20332043
""" : """
2034-
import class Foundation.Bundle
2035-
import class Foundation.ProcessInfo
2036-
import struct Foundation.URL
2044+
\(importPrefix)import class Foundation.Bundle
2045+
\(importPrefix)import class Foundation.ProcessInfo
2046+
\(importPrefix)import struct Foundation.URL
20372047
20382048
private class BundleFinder {}
20392049

Tests/SWBTaskConstructionTests/PackageProductConstructionTests.swift

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,9 @@ fileprivate struct PackageProductConstructionTests: CoreBasedTests {
10761076
results.checkWriteAuxiliaryFileTask(.matchTarget(target), .matchRuleType("WriteAuxiliaryFile"), .matchRuleItemBasename("resource_bundle_accessor.swift")) { task, contents in
10771077
XCTAssertMatch(contents.unsafeStringValue, .contains("static nonisolated let module: Bundle"))
10781078
XCTAssertMatch(contents.unsafeStringValue, .contains("let bundleName = \"tool_resources\""))
1079+
// For SWIFT_VERSION < 6.0, access-level-on-import is not part of the language mode.
1080+
// Imports in the generated file must not specify an access level in this case.
1081+
XCTAssertMatch(contents.unsafeStringValue, (.regex(#/^import class Foundation\.Bundle/#)))
10791082
}
10801083
}
10811084

@@ -1129,6 +1132,58 @@ fileprivate struct PackageProductConstructionTests: CoreBasedTests {
11291132
}
11301133
}
11311134

1135+
/// For SWIFT_VERSION >= 6.0, access-level-on-import is part of the language mode.
1136+
/// If any source file in the target uses an access level on an import, then
1137+
/// every import in the module must specify an access level, including imports
1138+
/// in generated files.
1139+
@Test(.requireSDKs(.macOS))
1140+
func resourceBundleAccessorEmitsAccessLevelImportForSwift6() async throws {
1141+
let testProject = try await TestPackageProject(
1142+
"aProject",
1143+
groupTree: TestGroup(
1144+
"SomeFiles",
1145+
children: [
1146+
TestFile("main.swift"),
1147+
]),
1148+
buildConfigurations: [
1149+
TestBuildConfiguration("Debug", buildSettings: [
1150+
"SWIFT_EXEC": swiftCompilerPath.str,
1151+
"SWIFT_VERSION": "6",
1152+
"GENERATE_INFOPLIST_FILE": "YES",
1153+
"PRODUCT_NAME": "$(TARGET_NAME)",
1154+
"GENERATE_RESOURCE_ACCESSORS": "YES",
1155+
"USE_HEADERMAP": "NO",
1156+
]),
1157+
],
1158+
targets: [
1159+
TestStandardTarget(
1160+
"tool", type: .application,
1161+
buildConfigurations: [
1162+
TestBuildConfiguration("Debug", buildSettings: [
1163+
"PRODUCT_NAME": "$(TARGET_NAME)",
1164+
"USE_HEADERMAP": "NO",
1165+
"DEFINES_MODULE": "YES",
1166+
"PACKAGE_RESOURCE_BUNDLE_NAME": "tool_resources",
1167+
"CODE_SIGNING_ALLOWED": "NO",
1168+
]),
1169+
],
1170+
buildPhases: [
1171+
TestSourcesBuildPhase(["main.swift"]),
1172+
]
1173+
),
1174+
])
1175+
let tester = try await TaskConstructionTester(getCore(), testProject)
1176+
1177+
await tester.checkBuild(runDestination: .macOS) { results in
1178+
results.checkNoDiagnostics()
1179+
results.checkTarget("tool") { target in
1180+
results.checkWriteAuxiliaryFileTask(.matchTarget(target), .matchRuleType("WriteAuxiliaryFile"), .matchRuleItemBasename("resource_bundle_accessor.swift")) { task, contents in
1181+
XCTAssertMatch(contents.unsafeStringValue, .contains("public import class Foundation.Bundle"))
1182+
}
1183+
}
1184+
}
1185+
}
1186+
11321187
@Test(.requireSDKs(.macOS, .iOS))
11331188
func packageResourceBundleEmbedding() async throws {
11341189
let testProject = try await TestProject(

0 commit comments

Comments
 (0)