Skip to content

Commit 40c79f7

Browse files
Fix PackageToJS plugin wasm-opt fallback when output file exists
When wasm-opt is not installed, DefaultPackagingSystem falls back to copying the input Wasm file directly to output. However, FileManager.copyItem fails if the destination file already exists, while wasm-opt overwrites existing files. Changes: - Remove existing output file before copying in wasm-opt fallback - Add focused tests for DefaultPackagingSystem fallback behavior - Test both new and existing output file scenarios Fixes issue where builds fail with 'File exists' error when wasm-opt is unavailable and output file already exists from previous builds.
1 parent 9968357 commit 40c79f7

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed

Plugins/PackageToJS/Sources/PackageToJS.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,10 @@ final class DefaultPackagingSystem: PackagingSystem {
309309
func wasmOpt(_ arguments: [String], input: String, output: String) throws {
310310
guard let wasmOpt = try? which("wasm-opt") else {
311311
_ = warnMissingWasmOpt
312+
// Remove existing output file if it exists (to match wasm-opt behavior)
313+
if FileManager.default.fileExists(atPath: output) {
314+
try FileManager.default.removeItem(atPath: output)
315+
}
312316
try FileManager.default.copyItem(atPath: input, toPath: output)
313317
return
314318
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import Foundation
2+
import Testing
3+
4+
@testable import PackageToJS
5+
6+
@Suite struct DefaultPackagingSystemTests {
7+
8+
@Test func wasmOptFallbackHandlesNewOutputFile() throws {
9+
try withTemporaryDirectory { tempDir, _ in
10+
let inputFile = tempDir.appendingPathComponent("input.wasm")
11+
let outputFile = tempDir.appendingPathComponent("output.wasm")
12+
let inputContent = Data("input wasm content".utf8)
13+
try inputContent.write(to: inputFile)
14+
15+
var warnings: [String] = []
16+
let system = DefaultPackagingSystem { warning in
17+
warnings.append(warning)
18+
}
19+
20+
// Temporarily remove wasm-opt to force fallback behavior
21+
let wasmOptPath = "/opt/homebrew/bin/wasm-opt"
22+
let backupPath = "/tmp/wasm-opt-backup-test"
23+
let wasmOptExists = FileManager.default.fileExists(atPath: wasmOptPath)
24+
25+
if wasmOptExists {
26+
try? FileManager.default.moveItem(atPath: wasmOptPath, toPath: backupPath)
27+
}
28+
29+
defer {
30+
if wasmOptExists {
31+
try? FileManager.default.moveItem(atPath: backupPath, toPath: wasmOptPath)
32+
}
33+
}
34+
35+
// This should work - fallback should copy file
36+
try system.wasmOpt(["-Os"], input: inputFile.path, output: outputFile.path)
37+
38+
// Verify the output file was created with input content
39+
let finalContent = try Data(contentsOf: outputFile)
40+
#expect(finalContent == inputContent)
41+
#expect(warnings.contains { $0.contains("wasm-opt is not installed") })
42+
}
43+
}
44+
45+
@Test func wasmOptFallbackHandlesExistingOutputFile() throws {
46+
try withTemporaryDirectory { tempDir, _ in
47+
let inputFile = tempDir.appendingPathComponent("input.wasm")
48+
let outputFile = tempDir.appendingPathComponent("output.wasm")
49+
let inputContent = Data("input wasm content".utf8)
50+
let existingContent = Data("existing output content".utf8)
51+
52+
// Create input file and existing output file
53+
try inputContent.write(to: inputFile)
54+
try existingContent.write(to: outputFile)
55+
56+
var warnings: [String] = []
57+
let system = DefaultPackagingSystem { warning in
58+
warnings.append(warning)
59+
}
60+
61+
// Temporarily remove wasm-opt to force fallback behavior
62+
let wasmOptPath = "/opt/homebrew/bin/wasm-opt"
63+
let backupPath = "/tmp/wasm-opt-backup-test"
64+
let wasmOptExists = FileManager.default.fileExists(atPath: wasmOptPath)
65+
66+
if wasmOptExists {
67+
try? FileManager.default.moveItem(atPath: wasmOptPath, toPath: backupPath)
68+
}
69+
70+
defer {
71+
if wasmOptExists {
72+
try? FileManager.default.moveItem(atPath: backupPath, toPath: wasmOptPath)
73+
}
74+
}
75+
76+
// This should work - fallback should overwrite existing file
77+
try system.wasmOpt(["-Os"], input: inputFile.path, output: outputFile.path)
78+
79+
// Verify the output file was overwritten with input content
80+
let finalContent = try Data(contentsOf: outputFile)
81+
#expect(finalContent == inputContent)
82+
#expect(finalContent != existingContent)
83+
#expect(warnings.contains { $0.contains("wasm-opt is not installed") })
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)