From 34bdfe1c266fcc3d7ddb1de9893bcbaa73454693 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sun, 24 Oct 2021 18:40:54 +0100 Subject: [PATCH 1/3] Fix `generateResourceAccessor` regression --- Sources/Build/BuildPlan.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Sources/Build/BuildPlan.swift b/Sources/Build/BuildPlan.swift index f8716c60afe..5bb8145936f 100644 --- a/Sources/Build/BuildPlan.swift +++ b/Sources/Build/BuildPlan.swift @@ -658,13 +658,21 @@ public final class SwiftTargetBuildDescription { // Do nothing if we're not generating a bundle. guard let bundlePath = self.bundlePath else { return } + let mainPathSubstitution: String + if buildParameters.triple.isWASI() { + let mainPath = AbsolutePath(Bundle.main.bundlePath).appending(component: bundlePath.basename).pathString + mainPathSubstitution = #""\#(mainPath.asSwiftStringLiteralConstant)""# + } else { + mainPathSubstitution = #"Bundle.main.bundleURL.appendingPathComponent("\#(bundlePath.basename.asSwiftStringLiteralConstant)").path"# + } + let stream = BufferedOutputByteStream() stream <<< """ import class Foundation.Bundle extension Foundation.Bundle { static var module: Bundle = { - let mainPath = Bundle.main.bundleURL.appendingPathComponent("\(bundlePath.basename.asSwiftStringLiteralConstant)").path + let mainPath = \(mainPathSubstitution) let buildPath = "\(bundlePath.pathString.asSwiftStringLiteralConstant)" let preferredBundle = Bundle(path: mainPath) From 92c6e751af947edde2485c0a5b85307bcb0f0607 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sun, 24 Oct 2021 18:47:58 +0100 Subject: [PATCH 2/3] Add testSwiftWASIBundleAccessor to BuildPlanTests --- Tests/BuildTests/BuildPlanTests.swift | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/Tests/BuildTests/BuildPlanTests.swift b/Tests/BuildTests/BuildPlanTests.swift index 8d25e976959..ecb86fc0edb 100644 --- a/Tests/BuildTests/BuildPlanTests.swift +++ b/Tests/BuildTests/BuildPlanTests.swift @@ -2652,6 +2652,70 @@ final class BuildPlanTests: XCTestCase { ]) } + func testSwiftWASIBundleAccessor() throws { + // This has a Swift and ObjC target in the same package. + let fs = InMemoryFileSystem(emptyFiles: + "/PkgA/Sources/Foo/Foo.swift", + "/PkgA/Sources/Foo/foo.txt", + "/PkgA/Sources/Foo/bar.txt", + "/PkgA/Sources/Bar/Bar.swift" + ) + + let observability = ObservabilitySystem.makeForTesting() + + let graph = try loadPackageGraph( + fs: fs, + manifests: [ + Manifest.createRootManifest( + name: "PkgA", + path: .init("/PkgA"), + toolsVersion: .v5_2, + targets: [ + TargetDescription( + name: "Foo", + resources: [ + .init(rule: .copy, path: "foo.txt"), + .init(rule: .process, path: "bar.txt"), + ] + ), + TargetDescription( + name: "Bar" + ), + ] + ) + ], + observabilityScope: observability.topScope + ) + + XCTAssertNoDiagnostics(observability.diagnostics) + + let plan = try BuildPlan( + buildParameters: mockBuildParameters(destinationTriple: .wasi), + graph: graph, + fileSystem: fs, + observabilityScope: observability.topScope + ) + let result = BuildPlanResult(plan: plan) + + let fooTarget = try result.target(for: "Foo").swiftTarget() + XCTAssertEqual(fooTarget.objects.map{ $0.pathString }, [ + "/path/to/build/debug/Foo.build/Foo.swift.o", + "/path/to/build/debug/Foo.build/resource_bundle_accessor.swift.o" + ]) + + let resourceAccessor = fooTarget.sources.first{ $0.basename == "resource_bundle_accessor.swift" }! + let contents = try fs.readFileContents(resourceAccessor).cString + XCTAssertMatch(contents, .contains("extension Foundation.Bundle")) + // Assert that `Bundle.main` is executed in the compiled binary (and not during compilation) + // See https://bugs.swift.org/browse/SR-14555 and https://github.com/apple/swift-package-manager/pull/2972/files#r623861646 + XCTAssertMatch(contents, .contains("let mainPath = \"")) + + let barTarget = try result.target(for: "Bar").swiftTarget() + XCTAssertEqual(barTarget.objects.map{ $0.pathString }, [ + "/path/to/build/debug/Bar.build/Bar.swift.o", + ]) + } + func testShouldLinkStaticSwiftStdlib() throws { let fs = InMemoryFileSystem(emptyFiles: "/Pkg/Sources/exe/main.swift", From a1cab7088693f85f81bd8b2e6e1030722c46035a Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 25 Oct 2021 19:12:14 +0100 Subject: [PATCH 3/3] Add clarifying comment to `isWASI` branch of `generateResourceAccessor` --- Sources/Build/BuildPlan.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/Build/BuildPlan.swift b/Sources/Build/BuildPlan.swift index 5bb8145936f..85c5a813de8 100644 --- a/Sources/Build/BuildPlan.swift +++ b/Sources/Build/BuildPlan.swift @@ -660,6 +660,11 @@ public final class SwiftTargetBuildDescription { let mainPathSubstitution: String if buildParameters.triple.isWASI() { + // We prefer compile-time evaluation of the bundle path here for WASI. There's no benefit in evaluating this at runtime, + // especially as Bundle support in WASI Foundation is partial. We expect all resource paths to evaluate to + // `/\(resourceBundleName)/\(resourcePath)`, which allows us to pass this path to JS APIs like `fetch` directly, or to + // `