diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml deleted file mode 100644 index 8994b624b..000000000 --- a/.github/workflows/compatibility.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Check compatibility -on: - pull_request: - push: - branches: [main] -jobs: - test: - name: Check source code compatibility - runs-on: ubuntu-latest - container: swift:6.0.3 - steps: - - name: Checkout - uses: actions/checkout@v4 - - uses: swiftwasm/setup-swiftwasm@v2 - - name: Run Test - run: | - set -eux - cd Examples/Basic - swift build --swift-sdk wasm32-unknown-wasi --static-swift-stdlib - swift build --swift-sdk wasm32-unknown-wasi -Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS --static-swift-stdlib diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c50de248a..486f7b6bf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,8 +38,10 @@ jobs: id: setup-swiftwasm with: target: ${{ matrix.entry.target }} - - name: Configure Swift SDK - run: echo "SWIFT_SDK_ID=${{ steps.setup-swiftwasm.outputs.swift-sdk-id }}" >> $GITHUB_ENV + - name: Configure environment variables + run: | + echo "SWIFT_SDK_ID=${{ steps.setup-swiftwasm.outputs.swift-sdk-id }}" >> $GITHUB_ENV + echo "SWIFT_PATH=$(dirname $(which swiftc))" >> $GITHUB_ENV - run: make bootstrap - run: make unittest # Skip unit tests with uwasi because its proc_exit throws @@ -49,6 +51,7 @@ jobs: run: | make regenerate_swiftpm_resources git diff --exit-code Sources/JavaScriptKit/Runtime + - run: swift test --package-path ./Plugins/PackageToJS native-build: # Check native build to make it easy to develop applications by Xcode @@ -64,19 +67,3 @@ jobs: - run: swift build env: DEVELOPER_DIR: /Applications/${{ matrix.xcode }}.app/Contents/Developer/ - - embedded-build: - name: Build for embedded target - runs-on: ubuntu-22.04 - strategy: - matrix: - entry: - - os: ubuntu-22.04 - toolchain: - download-url: https://download.swift.org/development/ubuntu2204/swift-DEVELOPMENT-SNAPSHOT-2025-02-26-a/swift-DEVELOPMENT-SNAPSHOT-2025-02-26-a-ubuntu22.04.tar.gz - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/install-swift - with: - download-url: ${{ matrix.entry.toolchain.download-url }} - - run: ./Examples/Embedded/build.sh diff --git a/Makefile b/Makefile index c8b79b4ab..a2ad1526a 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ SWIFT_BUILD_FLAGS := --swift-sdk $(SWIFT_SDK_ID) .PHONY: bootstrap bootstrap: npm ci + npx playwright install .PHONY: build build: diff --git a/Plugins/PackageToJS/Package.swift b/Plugins/PackageToJS/Package.swift index 1cc9318bd..57ccf3cf9 100644 --- a/Plugins/PackageToJS/Package.swift +++ b/Plugins/PackageToJS/Package.swift @@ -7,6 +7,10 @@ let package = Package( platforms: [.macOS(.v13)], targets: [ .target(name: "PackageToJS"), - .testTarget(name: "PackageToJSTests", dependencies: ["PackageToJS"]), + .testTarget( + name: "PackageToJSTests", + dependencies: ["PackageToJS"], + exclude: ["__Snapshots__"] + ), ] ) diff --git a/Plugins/PackageToJS/Sources/MiniMake.swift b/Plugins/PackageToJS/Sources/MiniMake.swift index 04e781690..3544a80f3 100644 --- a/Plugins/PackageToJS/Sources/MiniMake.swift +++ b/Plugins/PackageToJS/Sources/MiniMake.swift @@ -14,13 +14,13 @@ struct MiniMake { /// Information about a task enough to capture build /// graph changes - struct TaskInfo: Codable { + struct TaskInfo: Encodable { /// Input tasks not yet built let wants: [TaskKey] - /// Set of files that must be built before this task - let inputs: [String] - /// Output task name - let output: String + /// Set of file paths that must be built before this task + let inputs: [BuildPath] + /// Output file path + let output: BuildPath /// Attributes of the task let attributes: [TaskAttribute] /// Salt for the task, used to differentiate between otherwise identical tasks @@ -30,25 +30,23 @@ struct MiniMake { /// A task to build struct Task { let info: TaskInfo - /// Input tasks not yet built + /// Input tasks (files and phony tasks) not yet built let wants: Set /// Attributes of the task let attributes: Set - /// Display name of the task - let displayName: String /// Key of the task let key: TaskKey /// Build operation - let build: (Task) throws -> Void + let build: (_ task: Task, _ scope: VariableScope) throws -> Void /// Whether the task is done var isDone: Bool - var inputs: [String] { self.info.inputs } - var output: String { self.info.output } + var inputs: [BuildPath] { self.info.inputs } + var output: BuildPath { self.info.output } } /// A task key - struct TaskKey: Codable, Hashable, Comparable, CustomStringConvertible { + struct TaskKey: Encodable, Hashable, Comparable, CustomStringConvertible { let id: String var description: String { self.id } @@ -56,15 +54,45 @@ struct MiniMake { self.id = id } + func encode(to encoder: any Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.id) + } + static func < (lhs: TaskKey, rhs: TaskKey) -> Bool { lhs.id < rhs.id } } + struct VariableScope { + let variables: [String: String] + + func resolve(path: BuildPath) -> URL { + var components = [String]() + for component in path.components { + switch component { + case .prefix(let variable): + guard let value = variables[variable] else { + fatalError("Build path variable \"\(variable)\" not defined!") + } + components.append(value) + case .constant(let path): + components.append(path) + } + } + guard let first = components.first else { + fatalError("Build path is empty") + } + var url = URL(fileURLWithPath: first) + for component in components.dropFirst() { + url = url.appending(path: component) + } + return url + } + } + /// All tasks in the build system private var tasks: [TaskKey: Task] /// Whether to explain why tasks are built private var shouldExplain: Bool - /// Current working directory at the time the build started - private let buildCwd: String /// Prints progress of the build private var printProgress: ProgressPrinter.PrintProgress @@ -74,20 +102,16 @@ struct MiniMake { ) { self.tasks = [:] self.shouldExplain = explain - self.buildCwd = FileManager.default.currentDirectoryPath self.printProgress = printProgress } /// Adds a task to the build system mutating func addTask( - inputFiles: [String] = [], inputTasks: [TaskKey] = [], output: String, + inputFiles: [BuildPath] = [], inputTasks: [TaskKey] = [], output: BuildPath, attributes: [TaskAttribute] = [], salt: (any Encodable)? = nil, - build: @escaping (Task) throws -> Void + build: @escaping (_ task: Task, _ scope: VariableScope) throws -> Void ) -> TaskKey { - let displayName = - output.hasPrefix(self.buildCwd) - ? String(output.dropFirst(self.buildCwd.count + 1)) : output - let taskKey = TaskKey(id: output) + let taskKey = TaskKey(id: output.description) let saltData = try! salt.map { let encoder = JSONEncoder() encoder.outputFormatting = .sortedKeys @@ -99,7 +123,7 @@ struct MiniMake { ) self.tasks[taskKey] = Task( info: info, wants: Set(inputTasks), attributes: Set(attributes), - displayName: displayName, key: taskKey, build: build, isDone: false) + key: taskKey, build: build, isDone: false) return taskKey } @@ -107,9 +131,12 @@ struct MiniMake { /// /// This fingerprint must be stable across builds and must change /// if the build graph changes in any way. - func computeFingerprint(root: TaskKey) throws -> Data { + func computeFingerprint(root: TaskKey, prettyPrint: Bool = false) throws -> Data { let encoder = JSONEncoder() encoder.outputFormatting = .sortedKeys + if prettyPrint { + encoder.outputFormatting.insert(.prettyPrinted) + } let tasks = self.tasks.sorted { $0.key < $1.key }.map { $0.value.info } return try encoder.encode(tasks) } @@ -126,7 +153,13 @@ struct MiniMake { /// Prints progress of the build struct ProgressPrinter { - typealias PrintProgress = (_ subject: Task, _ total: Int, _ built: Int, _ message: String) -> Void + struct Context { + let subject: Task + let total: Int + let built: Int + let scope: VariableScope + } + typealias PrintProgress = (_ context: Context, _ message: String) -> Void /// Total number of tasks to build let total: Int @@ -145,17 +178,17 @@ struct MiniMake { private static var yellow: String { "\u{001B}[33m" } private static var reset: String { "\u{001B}[0m" } - mutating func started(_ task: Task) { - self.print(task, "\(Self.green)building\(Self.reset)") + mutating func started(_ task: Task, scope: VariableScope) { + self.print(task, scope, "\(Self.green)building\(Self.reset)") } - mutating func skipped(_ task: Task) { - self.print(task, "\(Self.yellow)skipped\(Self.reset)") + mutating func skipped(_ task: Task, scope: VariableScope) { + self.print(task, scope, "\(Self.yellow)skipped\(Self.reset)") } - private mutating func print(_ task: Task, _ message: @autoclosure () -> String) { + private mutating func print(_ task: Task, _ scope: VariableScope, _ message: @autoclosure () -> String) { guard !task.attributes.contains(.silent) else { return } - self.printProgress(task, self.total, self.built, message()) + self.printProgress(Context(subject: task, total: self.total, built: self.built, scope: scope), message()) self.built += 1 } } @@ -176,32 +209,32 @@ struct MiniMake { } /// Cleans all outputs of all tasks - func cleanEverything() { + func cleanEverything(scope: VariableScope) { for task in self.tasks.values { - try? FileManager.default.removeItem(atPath: task.output) + try? FileManager.default.removeItem(at: scope.resolve(path: task.output)) } } /// Starts building - func build(output: TaskKey) throws { + func build(output: TaskKey, scope: VariableScope) throws { /// Returns true if any of the task's inputs have a modification date later than the task's output func shouldBuild(task: Task) -> Bool { if task.attributes.contains(.phony) { return true } - let outputURL = URL(fileURLWithPath: task.output) - if !FileManager.default.fileExists(atPath: task.output) { + let outputURL = scope.resolve(path: task.output) + if !FileManager.default.fileExists(atPath: outputURL.path) { explain("Task \(task.output) should be built because it doesn't exist") return true } let outputMtime = try? outputURL.resourceValues(forKeys: [.contentModificationDateKey]) .contentModificationDate return task.inputs.contains { input in - let inputURL = URL(fileURLWithPath: input) + let inputURL = scope.resolve(path: input) // Ignore directory modification times var isDirectory: ObjCBool = false let fileExists = FileManager.default.fileExists( - atPath: input, isDirectory: &isDirectory) + atPath: inputURL.path, isDirectory: &isDirectory) if fileExists && isDirectory.boolValue { return false } @@ -238,10 +271,10 @@ struct MiniMake { } if shouldBuild(task: task) { - progressPrinter.started(task) - try task.build(task) + progressPrinter.started(task, scope: scope) + try task.build(task, scope) } else { - progressPrinter.skipped(task) + progressPrinter.skipped(task, scope: scope) } task.isDone = true tasks[taskKey] = task @@ -249,3 +282,45 @@ struct MiniMake { try runTask(taskKey: output) } } + +struct BuildPath: Encodable, Hashable, CustomStringConvertible { + enum Component: Hashable, CustomStringConvertible { + case prefix(variable: String) + case constant(String) + + var description: String { + switch self { + case .prefix(let variable): return "$\(variable)" + case .constant(let path): return path + } + } + } + fileprivate let components: [Component] + + var description: String { self.components.map(\.description).joined(separator: "/") } + + init(phony: String) { + self.components = [.constant(phony)] + } + + init(prefix: String, _ tail: String...) { + self.components = [.prefix(variable: prefix)] + tail.map(Component.constant) + } + + init(absolute: String) { + self.components = [.constant(absolute)] + } + + private init(components: [Component]) { + self.components = components + } + + func appending(path: String) -> BuildPath { + return BuildPath(components: self.components + [.constant(path)]) + } + + func encode(to encoder: any Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.description) + } +} diff --git a/Plugins/PackageToJS/Sources/PackageToJS.swift b/Plugins/PackageToJS/Sources/PackageToJS.swift index 44aaf532d..e54a2a910 100644 --- a/Plugins/PackageToJS/Sources/PackageToJS.swift +++ b/Plugins/PackageToJS/Sources/PackageToJS.swift @@ -9,7 +9,7 @@ struct PackageToJS { /// Whether to explain the build plan var explain: Bool = false /// Whether to use CDN for dependency packages - var useCDN: Bool + var useCDN: Bool = false } struct BuildOptions { @@ -39,6 +39,37 @@ struct PackageToJS { /// The options for packaging var packageOptions: PackageOptions } + + static func deriveBuildConfiguration(wasmProductArtifact: URL) -> (configuration: String, triple: String) { + // e.g. path/to/.build/wasm32-unknown-wasi/debug/Basic.wasm -> ("debug", "wasm32-unknown-wasi") + + // First, resolve symlink to get the actual path as SwiftPM 6.0 and earlier returns unresolved + // symlink path for product artifact. + let wasmProductArtifact = wasmProductArtifact.resolvingSymlinksInPath() + let buildConfiguration = wasmProductArtifact.deletingLastPathComponent().lastPathComponent + let triple = wasmProductArtifact.deletingLastPathComponent().deletingLastPathComponent().lastPathComponent + return (buildConfiguration, triple) + } + + static func runTest(testRunner: URL, currentDirectoryURL: URL, extraArguments: [String]) throws { + let node = try which("node") + let arguments = ["--experimental-wasi-unstable-preview1", testRunner.path] + extraArguments + print("Running test...") + logCommandExecution(node.path, arguments) + + let task = Process() + task.executableURL = node + task.arguments = arguments + task.currentDirectoryURL = currentDirectoryURL + try task.forwardTerminationSignals { + try task.run() + task.waitUntilExit() + } + // swift-testing returns EX_UNAVAILABLE (which is 69 in wasi-libc) for "no tests found" + guard task.terminationStatus == 0 || task.terminationStatus == 69 else { + throw PackageToJSError("Test failed with status \(task.terminationStatus)") + } + } } struct PackageToJSError: Swift.Error, CustomStringConvertible { @@ -49,45 +80,24 @@ struct PackageToJSError: Swift.Error, CustomStringConvertible { } } -/// Plans the build for packaging. -struct PackagingPlanner { - /// The options for packaging - let options: PackageToJS.PackageOptions - /// The package ID of the package that this plugin is running on - let packageId: String - /// The directory of the package that contains this plugin - let selfPackageDir: URL - /// The path of this file itself, used to capture changes of planner code - let selfPath: String - /// The directory for the final output - let outputDir: URL - /// The directory for intermediate files - let intermediatesDir: URL - /// The filename of the .wasm file - let wasmFilename = "main.wasm" - /// The path to the .wasm product artifact - let wasmProductArtifact: URL +protocol PackagingSystem { + func createDirectory(atPath: String) throws + func syncFile(from: String, to: String) throws + func writeFile(atPath: String, content: Data) throws - init( - options: PackageToJS.PackageOptions, - packageId: String, - pluginWorkDirectoryURL: URL, - selfPackageDir: URL, - outputDir: URL, - wasmProductArtifact: URL - ) { - self.options = options - self.packageId = packageId - self.selfPackageDir = selfPackageDir - self.outputDir = outputDir - self.intermediatesDir = pluginWorkDirectoryURL.appending(path: outputDir.lastPathComponent + ".tmp") - self.selfPath = String(#filePath) - self.wasmProductArtifact = wasmProductArtifact - } + func wasmOpt(_ arguments: [String], input: String, output: String) throws + func npmInstall(packageDir: String) throws +} - // MARK: - Primitive build operations +extension PackagingSystem { + func createDirectory(atPath: String) throws { + guard !FileManager.default.fileExists(atPath: atPath) else { return } + try FileManager.default.createDirectory( + atPath: atPath, withIntermediateDirectories: true, attributes: nil + ) + } - private static func syncFile(from: String, to: String) throws { + func syncFile(from: String, to: String) throws { if FileManager.default.fileExists(atPath: to) { try FileManager.default.removeItem(atPath: to) } @@ -97,14 +107,41 @@ struct PackagingPlanner { ) } - private static func createDirectory(atPath: String) throws { - guard !FileManager.default.fileExists(atPath: atPath) else { return } - try FileManager.default.createDirectory( - atPath: atPath, withIntermediateDirectories: true, attributes: nil - ) + func writeFile(atPath: String, content: Data) throws { + do { + try content.write(to: URL(fileURLWithPath: atPath)) + } catch { + throw PackageToJSError("Failed to write file \(atPath): \(error)") + } + } +} + +final class DefaultPackagingSystem: PackagingSystem { + + private let printWarning: (String) -> Void + + init(printWarning: @escaping (String) -> Void) { + self.printWarning = printWarning + } + + func npmInstall(packageDir: String) throws { + try runCommand(try which("npm"), ["-C", packageDir, "install"]) } - private static func runCommand(_ command: URL, _ arguments: [String]) throws { + lazy var warnMissingWasmOpt: () = { + self.printWarning("Warning: wasm-opt is not installed, optimizations will not be applied") + }() + + func wasmOpt(_ arguments: [String], input: String, output: String) throws { + guard let wasmOpt = try? which("wasm-opt") else { + _ = warnMissingWasmOpt + try FileManager.default.copyItem(atPath: input, toPath: output) + return + } + try runCommand(wasmOpt, arguments + ["-o", output, input]) + } + + private func runCommand(_ command: URL, _ arguments: [String]) throws { let task = Process() task.executableURL = command task.arguments = arguments @@ -115,6 +152,73 @@ struct PackagingPlanner { throw PackageToJSError("Command failed with status \(task.terminationStatus)") } } +} + +internal func which(_ executable: String) throws -> URL { + let pathSeparator: Character + #if os(Windows) + pathSeparator = ";" + #else + pathSeparator = ":" + #endif + let paths = ProcessInfo.processInfo.environment["PATH"]!.split(separator: pathSeparator) + for path in paths { + let url = URL(fileURLWithPath: String(path)).appendingPathComponent(executable) + if FileManager.default.isExecutableFile(atPath: url.path) { + return url + } + } + throw PackageToJSError("Executable \(executable) not found in PATH") +} + +/// Plans the build for packaging. +struct PackagingPlanner { + /// The options for packaging + let options: PackageToJS.PackageOptions + /// The package ID of the package that this plugin is running on + let packageId: String + /// The directory of the package that contains this plugin + let selfPackageDir: BuildPath + /// The path of this file itself, used to capture changes of planner code + let selfPath: BuildPath + /// The directory for the final output + let outputDir: BuildPath + /// The directory for intermediate files + let intermediatesDir: BuildPath + /// The filename of the .wasm file + let wasmFilename = "main.wasm" + /// The path to the .wasm product artifact + let wasmProductArtifact: BuildPath + /// The build configuration + let configuration: String + /// The target triple + let triple: String + /// The system interface to use + let system: any PackagingSystem + + init( + options: PackageToJS.PackageOptions, + packageId: String, + intermediatesDir: BuildPath, + selfPackageDir: BuildPath, + outputDir: BuildPath, + wasmProductArtifact: BuildPath, + configuration: String, + triple: String, + selfPath: BuildPath = BuildPath(absolute: #filePath), + system: any PackagingSystem + ) { + self.options = options + self.packageId = packageId + self.selfPackageDir = selfPackageDir + self.outputDir = outputDir + self.intermediatesDir = intermediatesDir + self.selfPath = selfPath + self.wasmProductArtifact = wasmProductArtifact + self.configuration = configuration + self.triple = triple + self.system = system + } // MARK: - Build plans @@ -123,23 +227,12 @@ struct PackagingPlanner { make: inout MiniMake, buildOptions: PackageToJS.BuildOptions ) throws -> MiniMake.TaskKey { - let (allTasks, _, _) = try planBuildInternal( + let (allTasks, _, _, _) = try planBuildInternal( make: &make, splitDebug: buildOptions.splitDebug, noOptimize: buildOptions.noOptimize ) return make.addTask( - inputTasks: allTasks, output: "all", attributes: [.phony, .silent] - ) { _ in } - } - - func deriveBuildConfiguration() -> (configuration: String, triple: String) { - // e.g. path/to/.build/wasm32-unknown-wasi/debug/Basic.wasm -> ("debug", "wasm32-unknown-wasi") - - // First, resolve symlink to get the actual path as SwiftPM 6.0 and earlier returns unresolved - // symlink path for product artifact. - let wasmProductArtifact = self.wasmProductArtifact.resolvingSymlinksInPath() - let buildConfiguration = wasmProductArtifact.deletingLastPathComponent().lastPathComponent - let triple = wasmProductArtifact.deletingLastPathComponent().deletingLastPathComponent().lastPathComponent - return (buildConfiguration, triple) + inputTasks: allTasks, output: BuildPath(phony: "all"), attributes: [.phony, .silent] + ) { _, _ in } } private func planBuildInternal( @@ -148,55 +241,49 @@ struct PackagingPlanner { ) throws -> ( allTasks: [MiniMake.TaskKey], outputDirTask: MiniMake.TaskKey, + intermediatesDirTask: MiniMake.TaskKey, packageJsonTask: MiniMake.TaskKey ) { // Prepare output directory let outputDirTask = make.addTask( - inputFiles: [selfPath], output: outputDir.path, attributes: [.silent] + inputFiles: [selfPath], output: outputDir, attributes: [.silent] ) { - try Self.createDirectory(atPath: $0.output) + try system.createDirectory(atPath: $1.resolve(path: $0.output).path) } var packageInputs: [MiniMake.TaskKey] = [] // Guess the build configuration from the parent directory name of .wasm file - let (buildConfiguration, _) = deriveBuildConfiguration() let wasm: MiniMake.TaskKey let shouldOptimize: Bool - let wasmOptPath = try? which("wasm-opt") - if buildConfiguration == "debug" { + if self.configuration == "debug" { shouldOptimize = false } else { - if wasmOptPath != nil { - shouldOptimize = !noOptimize - } else { - print("Warning: wasm-opt not found in PATH, skipping optimizations") - shouldOptimize = false - } + shouldOptimize = !noOptimize } let intermediatesDirTask = make.addTask( - inputFiles: [selfPath], output: intermediatesDir.path, attributes: [.silent] + inputFiles: [selfPath], output: intermediatesDir, attributes: [.silent] ) { - try Self.createDirectory(atPath: $0.output) + try system.createDirectory(atPath: $1.resolve(path: $0.output).path) } - let finalWasmPath = outputDir.appending(path: wasmFilename).path + let finalWasmPath = outputDir.appending(path: wasmFilename) - if let wasmOptPath = wasmOptPath, shouldOptimize { + if shouldOptimize { // Optimize the wasm in release mode // If splitDebug is true, we need to place the DWARF-stripped wasm file (but "name" section remains) // in the output directory. - let stripWasmPath = (splitDebug ? outputDir : intermediatesDir).appending(path: wasmFilename + ".debug").path + let stripWasmPath = (splitDebug ? outputDir : intermediatesDir).appending(path: wasmFilename + ".debug") // First, strip DWARF sections as their existence enables DWARF preserving mode in wasm-opt let stripWasm = make.addTask( - inputFiles: [selfPath, wasmProductArtifact.path], inputTasks: [outputDirTask, intermediatesDirTask], + inputFiles: [selfPath, wasmProductArtifact], inputTasks: [outputDirTask, intermediatesDirTask], output: stripWasmPath ) { print("Stripping DWARF debug info...") - try Self.runCommand(wasmOptPath, [wasmProductArtifact.path, "--strip-dwarf", "--debuginfo", "-o", $0.output]) + try system.wasmOpt(["--strip-dwarf", "--debuginfo"], input: $1.resolve(path: wasmProductArtifact).path, output: $1.resolve(path: $0.output).path) } // Then, run wasm-opt with all optimizations wasm = make.addTask( @@ -204,15 +291,15 @@ struct PackagingPlanner { output: finalWasmPath ) { print("Optimizing the wasm file...") - try Self.runCommand(wasmOptPath, [stripWasmPath, "-Os", "-o", $0.output]) + try system.wasmOpt(["-Os"], input: $1.resolve(path: stripWasmPath).path, output: $1.resolve(path: $0.output).path) } } else { // Copy the wasm product artifact wasm = make.addTask( - inputFiles: [selfPath, wasmProductArtifact.path], inputTasks: [outputDirTask], + inputFiles: [selfPath, wasmProductArtifact], inputTasks: [outputDirTask], output: finalWasmPath ) { - try Self.syncFile(from: wasmProductArtifact.path, to: $0.output) + try system.syncFile(from: $1.resolve(path: wasmProductArtifact).path, to: $1.resolve(path: $0.output).path) } } packageInputs.append(wasm) @@ -220,22 +307,24 @@ struct PackagingPlanner { let wasmImportsPath = intermediatesDir.appending(path: "wasm-imports.json") let wasmImportsTask = make.addTask( inputFiles: [selfPath, finalWasmPath], inputTasks: [outputDirTask, intermediatesDirTask, wasm], - output: wasmImportsPath.path + output: wasmImportsPath ) { - let metadata = try parseImports(moduleBytes: Array(try Data(contentsOf: URL(fileURLWithPath: finalWasmPath)))) + let metadata = try parseImports( + moduleBytes: try Data(contentsOf: URL(fileURLWithPath: $1.resolve(path: finalWasmPath).path)) + ) let jsonEncoder = JSONEncoder() jsonEncoder.outputFormatting = .prettyPrinted let jsonData = try jsonEncoder.encode(metadata) - try jsonData.write(to: URL(fileURLWithPath: $0.output)) + try system.writeFile(atPath: $1.resolve(path: $0.output).path, content: jsonData) } packageInputs.append(wasmImportsTask) let platformsDir = outputDir.appending(path: "platforms") let platformsDirTask = make.addTask( - inputFiles: [selfPath], output: platformsDir.path, attributes: [.silent] + inputFiles: [selfPath], output: platformsDir, attributes: [.silent] ) { - try Self.createDirectory(atPath: $0.output) + try system.createDirectory(atPath: $1.resolve(path: $0.output).path) } let packageJsonTask = planCopyTemplateFile( @@ -259,40 +348,39 @@ struct PackagingPlanner { ] { packageInputs.append(planCopyTemplateFile( make: &make, file: file, output: output, outputDirTask: outputDirTask, - inputFiles: [wasmImportsPath.path], inputTasks: [platformsDirTask, wasmImportsTask], - wasmImportsPath: wasmImportsPath.path + inputFiles: [wasmImportsPath], inputTasks: [platformsDirTask, wasmImportsTask], + wasmImportsPath: wasmImportsPath )) } - return (packageInputs, outputDirTask, packageJsonTask) + return (packageInputs, outputDirTask, intermediatesDirTask, packageJsonTask) } /// Construct the test build plan and return the root task key func planTestBuild( make: inout MiniMake - ) throws -> (rootTask: MiniMake.TaskKey, binDir: URL) { - var (allTasks, outputDirTask, packageJsonTask) = try planBuildInternal( + ) throws -> (rootTask: MiniMake.TaskKey, binDir: BuildPath) { + var (allTasks, outputDirTask, intermediatesDirTask, packageJsonTask) = try planBuildInternal( make: &make, splitDebug: false, noOptimize: false ) // Install npm dependencies used in the test harness - let npm = try which("npm") allTasks.append(make.addTask( inputFiles: [ selfPath, - outputDir.appending(path: "package.json").path, - ], inputTasks: [outputDirTask, packageJsonTask], - output: intermediatesDir.appending(path: "npm-install.stamp").path + outputDir.appending(path: "package.json"), + ], inputTasks: [intermediatesDirTask, packageJsonTask], + output: intermediatesDir.appending(path: "npm-install.stamp") ) { - try Self.runCommand(npm, ["-C", outputDir.path, "install"]) - _ = FileManager.default.createFile(atPath: $0.output, contents: Data(), attributes: nil) + try system.npmInstall(packageDir: $1.resolve(path: outputDir).path) + try system.writeFile(atPath: $1.resolve(path: $0.output).path, content: Data()) }) let binDir = outputDir.appending(path: "bin") let binDirTask = make.addTask( inputFiles: [selfPath], inputTasks: [outputDirTask], - output: binDir.path + output: binDir ) { - try Self.createDirectory(atPath: $0.output) + try system.createDirectory(atPath: $1.resolve(path: $0.output).path) } allTasks.append(binDirTask) @@ -309,8 +397,8 @@ struct PackagingPlanner { )) } let rootTask = make.addTask( - inputTasks: allTasks, output: "all", attributes: [.phony, .silent] - ) { _ in } + inputTasks: allTasks, output: BuildPath(phony: "all"), attributes: [.phony, .silent] + ) { _, _ in } return (rootTask, binDir) } @@ -319,9 +407,9 @@ struct PackagingPlanner { file: String, output: String, outputDirTask: MiniMake.TaskKey, - inputFiles: [String], + inputFiles: [BuildPath], inputTasks: [MiniMake.TaskKey], - wasmImportsPath: String? = nil + wasmImportsPath: BuildPath? = nil ) -> MiniMake.TaskKey { struct Salt: Encodable { @@ -330,7 +418,6 @@ struct PackagingPlanner { } let inputPath = selfPackageDir.appending(path: file) - let (_, triple) = deriveBuildConfiguration() let conditions = [ "USE_SHARED_MEMORY": triple == "wasm32-unknown-wasip1-threads", "IS_WASI": triple.hasPrefix("wasm32-unknown-wasi"), @@ -343,13 +430,14 @@ struct PackagingPlanner { let salt = Salt(conditions: conditions, substitutions: constantSubstitutions) return make.addTask( - inputFiles: [selfPath, inputPath.path] + inputFiles, inputTasks: [outputDirTask] + inputTasks, - output: outputDir.appending(path: output).path, salt: salt + inputFiles: [selfPath, inputPath] + inputFiles, inputTasks: [outputDirTask] + inputTasks, + output: outputDir.appending(path: output), salt: salt ) { var substitutions = constantSubstitutions if let wasmImportsPath = wasmImportsPath { - let importEntries = try JSONDecoder().decode([ImportEntry].self, from: Data(contentsOf: URL(fileURLWithPath: wasmImportsPath))) + let wasmImportsPath = $1.resolve(path: wasmImportsPath) + let importEntries = try JSONDecoder().decode([ImportEntry].self, from: Data(contentsOf: wasmImportsPath)) let memoryImport = importEntries.first { $0.module == "env" && $0.name == "memory" } if case .memory(let type) = memoryImport?.kind { substitutions["PACKAGE_TO_JS_MEMORY_INITIAL"] = "\(type.minimum)" @@ -358,33 +446,17 @@ struct PackagingPlanner { } } + let inputPath = $1.resolve(path: inputPath) var content = try String(contentsOf: inputPath, encoding: .utf8) let options = PreprocessOptions(conditions: conditions, substitutions: substitutions) - content = try preprocess(source: content, file: file, options: options) - try content.write(toFile: $0.output, atomically: true, encoding: .utf8) + content = try preprocess(source: content, file: inputPath.path, options: options) + try system.writeFile(atPath: $1.resolve(path: $0.output).path, content: Data(content.utf8)) } } } // MARK: - Utilities -func which(_ executable: String) throws -> URL { - let pathSeparator: Character - #if os(Windows) - pathSeparator = ";" - #else - pathSeparator = ":" - #endif - let paths = ProcessInfo.processInfo.environment["PATH"]!.split(separator: pathSeparator) - for path in paths { - let url = URL(fileURLWithPath: String(path)).appendingPathComponent(executable) - if FileManager.default.isExecutableFile(atPath: url.path) { - return url - } - } - throw PackageToJSError("Executable \(executable) not found in PATH") -} - func logCommandExecution(_ command: String, _ arguments: [String]) { var fullArguments = [command] fullArguments.append(contentsOf: arguments) diff --git a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift index 7e12eb94f..4074a8218 100644 --- a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift +++ b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift @@ -122,7 +122,8 @@ struct PackageToJSPlugin: CommandPlugin { make: &make, buildOptions: buildOptions) cleanIfBuildGraphChanged(root: rootTask, make: make, context: context) print("Packaging...") - try make.build(output: rootTask) + let scope = MiniMake.VariableScope(variables: [:]) + try make.build(output: rootTask, scope: scope) print("Packaging finished") } @@ -159,7 +160,8 @@ struct PackageToJSPlugin: CommandPlugin { // not worth the overhead) var productArtifact: URL? for fileExtension in ["wasm", "xctest"] { - let path = ".build/debug/\(productName).\(fileExtension)" + let packageDir = context.package.directoryURL + let path = packageDir.appending(path: ".build/debug/\(productName).\(fileExtension)").path if FileManager.default.fileExists(atPath: path) { productArtifact = URL(fileURLWithPath: path) break @@ -192,10 +194,11 @@ struct PackageToJSPlugin: CommandPlugin { make: &make) cleanIfBuildGraphChanged(root: rootTask, make: make, context: context) print("Packaging tests...") - try make.build(output: rootTask) + let scope = MiniMake.VariableScope(variables: [:]) + try make.build(output: rootTask, scope: scope) print("Packaging tests finished") - let testRunner = binDir.appending(path: "test.js") + let testRunner = scope.resolve(path: binDir.appending(path: "test.js")) if !testOptions.buildOnly { var testJsArguments: [String] = [] var testFrameworkArguments: [String] = [] @@ -212,38 +215,18 @@ struct PackageToJSPlugin: CommandPlugin { if testOptions.inspect { testJsArguments += ["--inspect"] } - try runTest( - testRunner: testRunner, context: context, + try PackageToJS.runTest( + testRunner: testRunner, currentDirectoryURL: context.pluginWorkDirectoryURL, extraArguments: testJsArguments + ["--"] + testFrameworkArguments + testOptions.filter ) - try runTest( - testRunner: testRunner, context: context, + try PackageToJS.runTest( + testRunner: testRunner, currentDirectoryURL: context.pluginWorkDirectoryURL, extraArguments: testJsArguments + ["--", "--testing-library", "swift-testing"] + testFrameworkArguments + testOptions.filter.flatMap { ["--filter", $0] } ) } } - private func runTest(testRunner: URL, context: PluginContext, extraArguments: [String]) throws { - let node = try which("node") - let arguments = ["--experimental-wasi-unstable-preview1", testRunner.path] + extraArguments - print("Running test...") - logCommandExecution(node.path, arguments) - - let task = Process() - task.executableURL = node - task.arguments = arguments - task.currentDirectoryURL = context.pluginWorkDirectoryURL - try task.forwardTerminationSignals { - try task.run() - task.waitUntilExit() - } - // swift-testing returns EX_UNAVAILABLE (which is 69 in wasi-libc) for "no tests found" - guard task.terminationStatus == 0 || task.terminationStatus == 69 else { - throw PackageToJSError("Test failed with status \(task.terminationStatus)") - } - } - private func buildWasm(productName: String, context: PluginContext) throws -> PackageManager.BuildResult { @@ -282,14 +265,18 @@ struct PackageToJSPlugin: CommandPlugin { let lastBuildFingerprint = try? Data(contentsOf: buildFingerprint) let currentBuildFingerprint = try? make.computeFingerprint(root: root) if lastBuildFingerprint != currentBuildFingerprint { - print("Build graph changed, cleaning...") - make.cleanEverything() + printStderr("Build graph changed, cleaning...") + make.cleanEverything(scope: MiniMake.VariableScope(variables: [:])) } try? currentBuildFingerprint?.write(to: buildFingerprint) } - private func printProgress(task: MiniMake.Task, total: Int, built: Int, message: String) { - printStderr("[\(built + 1)/\(total)] \(task.displayName): \(message)") + private func printProgress(context: MiniMake.ProgressPrinter.Context, message: String) { + let buildCwd = FileManager.default.currentDirectoryPath + let outputPath = context.scope.resolve(path: context.subject.output).path + let displayName = outputPath.hasPrefix(buildCwd) + ? String(outputPath.dropFirst(buildCwd.count + 1)) : outputPath + printStderr("[\(context.built + 1)/\(context.total)] \(displayName): \(message)") } } @@ -457,13 +444,19 @@ extension PackagingPlanner { outputDir: URL, wasmProductArtifact: URL ) { + let outputBaseName = outputDir.lastPathComponent + let (configuration, triple) = PackageToJS.deriveBuildConfiguration(wasmProductArtifact: wasmProductArtifact) + let system = DefaultPackagingSystem(printWarning: printStderr) self.init( options: options, packageId: context.package.id, - pluginWorkDirectoryURL: context.pluginWorkDirectoryURL, - selfPackageDir: selfPackage.directoryURL, - outputDir: outputDir, - wasmProductArtifact: wasmProductArtifact + intermediatesDir: BuildPath(absolute: context.pluginWorkDirectoryURL.appending(path: outputBaseName + ".tmp").path), + selfPackageDir: BuildPath(absolute: selfPackage.directoryURL.path), + outputDir: BuildPath(absolute: outputDir.path), + wasmProductArtifact: BuildPath(absolute: wasmProductArtifact.path), + configuration: configuration, + triple: triple, + system: system ) } } diff --git a/Plugins/PackageToJS/Sources/ParseWasm.swift b/Plugins/PackageToJS/Sources/ParseWasm.swift index 1cec9e43f..a35b69561 100644 --- a/Plugins/PackageToJS/Sources/ParseWasm.swift +++ b/Plugins/PackageToJS/Sources/ParseWasm.swift @@ -1,3 +1,5 @@ +import struct Foundation.Data + /// Represents the type of value in WebAssembly enum ValueType: String, Codable { case i32 @@ -62,10 +64,10 @@ struct ImportEntry: Codable { /// Parse state for WebAssembly parsing private class ParseState { - private let moduleBytes: [UInt8] + private let moduleBytes: Data private var offset: Int - init(moduleBytes: [UInt8]) { + init(moduleBytes: Data) { self.moduleBytes = moduleBytes self.offset = 0 } @@ -158,7 +160,7 @@ enum ParseError: Error { /// - Parameter moduleBytes: The WebAssembly module bytes /// - Returns: Array of import entries /// - Throws: ParseError if the module bytes are invalid -func parseImports(moduleBytes: [UInt8]) throws -> [ImportEntry] { +func parseImports(moduleBytes: Data) throws -> [ImportEntry] { let parseState = ParseState(moduleBytes: moduleBytes) try parseMagicNumber(parseState) try parseVersion(parseState) diff --git a/Plugins/PackageToJS/Tests/ExampleProjectTests.swift b/Plugins/PackageToJS/Tests/ExampleProjectTests.swift deleted file mode 100644 index 1bcc25d48..000000000 --- a/Plugins/PackageToJS/Tests/ExampleProjectTests.swift +++ /dev/null @@ -1,6 +0,0 @@ -import Testing - -@Suite struct ExampleProjectTests { - @Test func example() throws { - } -} diff --git a/Plugins/PackageToJS/Tests/ExampleTests.swift b/Plugins/PackageToJS/Tests/ExampleTests.swift new file mode 100644 index 000000000..be5d8e60b --- /dev/null +++ b/Plugins/PackageToJS/Tests/ExampleTests.swift @@ -0,0 +1,185 @@ +import Foundation +import Testing + +@testable import PackageToJS + +extension Trait where Self == ConditionTrait { + static var requireSwiftSDK: ConditionTrait { + .enabled( + if: ProcessInfo.processInfo.environment["SWIFT_SDK_ID"] != nil + && ProcessInfo.processInfo.environment["SWIFT_PATH"] != nil, + "Requires SWIFT_SDK_ID and SWIFT_PATH environment variables" + ) + } + + static func requireSwiftSDK(triple: String) -> ConditionTrait { + .enabled( + if: ProcessInfo.processInfo.environment["SWIFT_SDK_ID"] != nil + && ProcessInfo.processInfo.environment["SWIFT_PATH"] != nil + && ProcessInfo.processInfo.environment["SWIFT_SDK_ID"]!.hasSuffix(triple), + "Requires SWIFT_SDK_ID and SWIFT_PATH environment variables" + ) + } + + static var requireEmbeddedSwift: ConditionTrait { + // Check if $SWIFT_PATH/../lib/swift/embedded/wasm32-unknown-none-wasm/ exists + return .enabled( + if: { + guard let swiftPath = ProcessInfo.processInfo.environment["SWIFT_PATH"] else { + return false + } + let embeddedPath = URL(fileURLWithPath: swiftPath).deletingLastPathComponent() + .appending(path: "lib/swift/embedded/wasm32-unknown-none-wasm") + return FileManager.default.fileExists(atPath: embeddedPath.path) + }(), + "Requires embedded Swift SDK under $SWIFT_PATH/../lib/swift/embedded" + ) + } +} + +@Suite struct ExampleTests { + static func getSwiftSDKID() -> String? { + ProcessInfo.processInfo.environment["SWIFT_SDK_ID"] + } + + static let repoPath = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + + static func copyRepository(to destination: URL) throws { + try FileManager.default.createDirectory( + atPath: destination.path, withIntermediateDirectories: true, attributes: nil) + let ignore = [ + ".git", + ".vscode", + ".build", + "node_modules", + ] + + let enumerator = FileManager.default.enumerator(atPath: repoPath.path)! + while let file = enumerator.nextObject() as? String { + let sourcePath = repoPath.appending(path: file) + let destinationPath = destination.appending(path: file) + if ignore.contains(where: { file.hasSuffix($0) }) { + enumerator.skipDescendants() + continue + } + // Skip directories + var isDirectory: ObjCBool = false + if FileManager.default.fileExists(atPath: sourcePath.path, isDirectory: &isDirectory) { + if isDirectory.boolValue { + continue + } + } + + do { + try FileManager.default.createDirectory( + at: destinationPath.deletingLastPathComponent(), + withIntermediateDirectories: true, attributes: nil) + try FileManager.default.copyItem(at: sourcePath, to: destinationPath) + } catch { + print("Failed to copy \(sourcePath) to \(destinationPath): \(error)") + throw error + } + } + } + + typealias RunSwift = (_ args: [String], _ env: [String: String]) throws -> Void + + func withPackage(at path: String, body: (URL, _ runSwift: RunSwift) throws -> Void) throws { + try withTemporaryDirectory { tempDir, retain in + let destination = tempDir.appending(path: Self.repoPath.lastPathComponent) + try Self.copyRepository(to: destination) + try body(destination.appending(path: path)) { args, env in + let process = Process() + process.executableURL = URL( + fileURLWithPath: "swift", + relativeTo: URL( + fileURLWithPath: ProcessInfo.processInfo.environment["SWIFT_PATH"]!)) + process.arguments = args + process.currentDirectoryURL = destination.appending(path: path) + process.environment = ProcessInfo.processInfo.environment.merging(env) { _, new in + new + } + let stdoutPath = tempDir.appending(path: "stdout.txt") + let stderrPath = tempDir.appending(path: "stderr.txt") + _ = FileManager.default.createFile(atPath: stdoutPath.path, contents: nil) + _ = FileManager.default.createFile(atPath: stderrPath.path, contents: nil) + process.standardOutput = try FileHandle(forWritingTo: stdoutPath) + process.standardError = try FileHandle(forWritingTo: stderrPath) + + try process.run() + process.waitUntilExit() + if process.terminationStatus != 0 { + retain = true + } + try #require( + process.terminationStatus == 0, + """ + Swift package should build successfully, check \(destination.appending(path: path).path) for details + stdout: \(stdoutPath.path) + stderr: \(stderrPath.path) + + \((try? String(contentsOf: stdoutPath, encoding: .utf8)) ?? "<>") + \((try? String(contentsOf: stderrPath, encoding: .utf8)) ?? "<>") + """ + ) + } + } + } + + @Test(.requireSwiftSDK) + func basic() throws { + let swiftSDKID = try #require(Self.getSwiftSDKID()) + try withPackage(at: "Examples/Basic") { packageDir, runSwift in + try runSwift(["package", "--swift-sdk", swiftSDKID, "js"], [:]) + try runSwift(["package", "--swift-sdk", swiftSDKID, "-Xswiftc", "-DJAVASCRIPTKIT_WITHOUT_WEAKREFS", "js"], [:]) + } + } + + @Test(.requireSwiftSDK) + func testing() throws { + let swiftSDKID = try #require(Self.getSwiftSDKID()) + try withPackage(at: "Examples/Testing") { packageDir, runSwift in + try runSwift(["package", "--swift-sdk", swiftSDKID, "js", "test"], [:]) + try runSwift(["package", "--swift-sdk", swiftSDKID, "js", "test", "--environment", "browser"], [:]) + } + } + + @Test(.requireSwiftSDK(triple: "wasm32-unknown-wasip1-threads")) + func multithreading() throws { + let swiftSDKID = try #require(Self.getSwiftSDKID()) + try withPackage(at: "Examples/Multithreading") { packageDir, runSwift in + try runSwift(["package", "--swift-sdk", swiftSDKID, "js"], [:]) + } + } + + @Test(.requireSwiftSDK(triple: "wasm32-unknown-wasip1-threads")) + func offscreenCanvas() throws { + let swiftSDKID = try #require(Self.getSwiftSDKID()) + try withPackage(at: "Examples/OffscrenCanvas") { packageDir, runSwift in + try runSwift(["package", "--swift-sdk", swiftSDKID, "js"], [:]) + } + } + + @Test(.requireSwiftSDK(triple: "wasm32-unknown-wasip1-threads")) + func actorOnWebWorker() throws { + let swiftSDKID = try #require(Self.getSwiftSDKID()) + try withPackage(at: "Examples/ActorOnWebWorker") { packageDir, runSwift in + try runSwift(["package", "--swift-sdk", swiftSDKID, "js"], [:]) + } + } + + @Test(.requireEmbeddedSwift) func embedded() throws { + try withPackage(at: "Examples/Embedded") { packageDir, runSwift in + try runSwift( + ["package", "--triple", "wasm32-unknown-none-wasm", "-c", "release", "js"], + [ + "JAVASCRIPTKIT_EXPERIMENTAL_EMBEDDED_WASM": "true" + ] + ) + } + } +} diff --git a/Plugins/PackageToJS/Tests/MiniMakeTests.swift b/Plugins/PackageToJS/Tests/MiniMakeTests.swift index bb097115c..b15a87607 100644 --- a/Plugins/PackageToJS/Tests/MiniMakeTests.swift +++ b/Plugins/PackageToJS/Tests/MiniMakeTests.swift @@ -6,65 +6,75 @@ import Testing @Suite struct MiniMakeTests { // Test basic task management functionality @Test func basicTaskManagement() throws { - try withTemporaryDirectory { tempDir in - var make = MiniMake(printProgress: { _, _, _, _ in }) - let outputPath = tempDir.appendingPathComponent("output.txt").path + try withTemporaryDirectory { tempDir, _ in + var make = MiniMake(printProgress: { _, _ in }) + let outDir = BuildPath(prefix: "OUTPUT") - let task = make.addTask(output: outputPath) { task in - try "Hello".write(toFile: task.output, atomically: true, encoding: .utf8) + let task = make.addTask(output: outDir.appending(path: "output.txt")) { + try "Hello".write(toFile: $1.resolve(path: $0.output).path, atomically: true, encoding: .utf8) } - try make.build(output: task) - let content = try String(contentsOfFile: outputPath, encoding: .utf8) + try make.build(output: task, scope: MiniMake.VariableScope(variables: [ + "OUTPUT": tempDir.path, + ])) + let content = try String(contentsOfFile: tempDir.appendingPathComponent("output.txt").path, encoding: .utf8) #expect(content == "Hello") } } // Test that task dependencies are handled correctly @Test func taskDependencies() throws { - try withTemporaryDirectory { tempDir in - var make = MiniMake(printProgress: { _, _, _, _ in }) - let input = tempDir.appendingPathComponent("input.txt").path - let intermediate = tempDir.appendingPathComponent("intermediate.txt").path - let output = tempDir.appendingPathComponent("output.txt").path - - try "Input".write(toFile: input, atomically: true, encoding: .utf8) - - let intermediateTask = make.addTask(inputFiles: [input], output: intermediate) { task in - let content = try String(contentsOfFile: task.inputs[0], encoding: .utf8) + try withTemporaryDirectory { tempDir, _ in + var make = MiniMake(printProgress: { _, _ in }) + let prefix = BuildPath(prefix: "PREFIX") + let scope = MiniMake.VariableScope(variables: [ + "PREFIX": tempDir.path, + ]) + let input = prefix.appending(path: "input.txt") + let intermediate = prefix.appending(path: "intermediate.txt") + let output = prefix.appending(path: "output.txt") + + try "Input".write(toFile: scope.resolve(path: input).path, atomically: true, encoding: .utf8) + + let intermediateTask = make.addTask(inputFiles: [input], output: intermediate) { task, outputURL in + let content = try String(contentsOfFile: scope.resolve(path: task.inputs[0]).path, encoding: .utf8) try (content + " processed").write( - toFile: task.output, atomically: true, encoding: .utf8) + toFile: scope.resolve(path: task.output).path, atomically: true, encoding: .utf8) } let finalTask = make.addTask( inputFiles: [intermediate], inputTasks: [intermediateTask], output: output - ) { task in - let content = try String(contentsOfFile: task.inputs[0], encoding: .utf8) + ) { task, scope in + let content = try String(contentsOfFile: scope.resolve(path: task.inputs[0]).path, encoding: .utf8) try (content + " final").write( - toFile: task.output, atomically: true, encoding: .utf8) + toFile: scope.resolve(path: task.output).path, atomically: true, encoding: .utf8) } - try make.build(output: finalTask) - let content = try String(contentsOfFile: output, encoding: .utf8) + try make.build(output: finalTask, scope: scope) + let content = try String(contentsOfFile: scope.resolve(path: output).path, encoding: .utf8) #expect(content == "Input processed final") } } // Test that phony tasks are always rebuilt @Test func phonyTask() throws { - try withTemporaryDirectory { tempDir in - var make = MiniMake(printProgress: { _, _, _, _ in }) - let outputPath = tempDir.appendingPathComponent("phony.txt").path - try "Hello".write(toFile: outputPath, atomically: true, encoding: .utf8) + try withTemporaryDirectory { tempDir, _ in + var make = MiniMake(printProgress: { _, _ in }) + let phonyName = "phony.txt" + let outputPath = BuildPath(prefix: "OUTPUT").appending(path: phonyName) + try "Hello".write(toFile: tempDir.appendingPathComponent(phonyName).path, atomically: true, encoding: .utf8) var buildCount = 0 - let task = make.addTask(output: outputPath, attributes: [.phony]) { task in + let task = make.addTask(output: outputPath, attributes: [.phony]) { task, scope in buildCount += 1 - try String(buildCount).write(toFile: task.output, atomically: true, encoding: .utf8) + try String(buildCount).write(toFile: scope.resolve(path: task.output).path, atomically: true, encoding: .utf8) } - try make.build(output: task) - try make.build(output: task) + let scope = MiniMake.VariableScope(variables: [ + "OUTPUT": tempDir.path, + ]) + try make.build(output: task, scope: scope) + try make.build(output: task, scope: scope) #expect(buildCount == 2, "Phony task should always rebuild") } @@ -72,13 +82,13 @@ import Testing // Test that the same build graph produces stable fingerprints @Test func fingerprintStability() throws { - var make1 = MiniMake(printProgress: { _, _, _, _ in }) - var make2 = MiniMake(printProgress: { _, _, _, _ in }) + var make1 = MiniMake(printProgress: { _, _ in }) + var make2 = MiniMake(printProgress: { _, _ in }) - let output1 = "output1.txt" + let output1 = BuildPath(prefix: "OUTPUT") - let task1 = make1.addTask(output: output1) { _ in } - let task2 = make2.addTask(output: output1) { _ in } + let task1 = make1.addTask(output: output1) { _, _ in } + let task2 = make2.addTask(output: output1) { _, _ in } let fingerprint1 = try make1.computeFingerprint(root: task1) let fingerprint2 = try make2.computeFingerprint(root: task2) @@ -88,114 +98,130 @@ import Testing // Test that rebuilds are controlled by timestamps @Test func timestampBasedRebuild() throws { - try withTemporaryDirectory { tempDir in - var make = MiniMake(printProgress: { _, _, _, _ in }) - let input = tempDir.appendingPathComponent("input.txt").path - let output = tempDir.appendingPathComponent("output.txt").path + try withTemporaryDirectory { tempDir, _ in + var make = MiniMake(printProgress: { _, _ in }) + let prefix = BuildPath(prefix: "PREFIX") + let scope = MiniMake.VariableScope(variables: [ + "PREFIX": tempDir.path, + ]) + let input = prefix.appending(path: "input.txt") + let output = prefix.appending(path: "output.txt") var buildCount = 0 - try "Initial".write(toFile: input, atomically: true, encoding: .utf8) + try "Initial".write(toFile: scope.resolve(path: input).path, atomically: true, encoding: .utf8) - let task = make.addTask(inputFiles: [input], output: output) { task in + let task = make.addTask(inputFiles: [input], output: output) { task, scope in buildCount += 1 - let content = try String(contentsOfFile: task.inputs[0], encoding: .utf8) - try content.write(toFile: task.output, atomically: true, encoding: .utf8) + let content = try String(contentsOfFile: scope.resolve(path: task.inputs[0]).path, encoding: .utf8) + try content.write(toFile: scope.resolve(path: task.output).path, atomically: true, encoding: .utf8) } // First build - try make.build(output: task) + try make.build(output: task, scope: scope) #expect(buildCount == 1, "First build should occur") // Second build without changes - try make.build(output: task) + try make.build(output: task, scope: scope) #expect(buildCount == 1, "No rebuild should occur if input is not modified") // Modify input and rebuild - try "Modified".write(toFile: input, atomically: true, encoding: .utf8) - try make.build(output: task) + try "Modified".write(toFile: scope.resolve(path: input).path, atomically: true, encoding: .utf8) + try make.build(output: task, scope: scope) #expect(buildCount == 2, "Should rebuild when input is modified") } } // Test that silent tasks execute without output @Test func silentTask() throws { - try withTemporaryDirectory { tempDir in + try withTemporaryDirectory { tempDir, _ in var messages: [(String, Int, Int, String)] = [] var make = MiniMake( - printProgress: { task, total, built, message in - messages.append((URL(fileURLWithPath: task.output).lastPathComponent, total, built, message)) + printProgress: { ctx, message in + messages.append((ctx.subject.output.description, ctx.total, ctx.built, message)) } ) - let silentOutputPath = tempDir.appendingPathComponent("silent.txt").path - let silentTask = make.addTask(output: silentOutputPath, attributes: [.silent]) { task in - try "Silent".write(toFile: task.output, atomically: true, encoding: .utf8) + let prefix = BuildPath(prefix: "PREFIX") + let scope = MiniMake.VariableScope(variables: [ + "PREFIX": tempDir.path, + ]) + let silentOutputPath = prefix.appending(path: "silent.txt") + let silentTask = make.addTask(output: silentOutputPath, attributes: [.silent]) { task, scope in + try "Silent".write(toFile: scope.resolve(path: task.output).path, atomically: true, encoding: .utf8) } - let finalOutputPath = tempDir.appendingPathComponent("output.txt").path + let finalOutputPath = prefix.appending(path: "output.txt") let task = make.addTask( inputTasks: [silentTask], output: finalOutputPath - ) { task in - try "Hello".write(toFile: task.output, atomically: true, encoding: .utf8) + ) { task, scope in + try "Hello".write(toFile: scope.resolve(path: task.output).path, atomically: true, encoding: .utf8) } - try make.build(output: task) - #expect(FileManager.default.fileExists(atPath: silentOutputPath), "Silent task should still create output file") - #expect(FileManager.default.fileExists(atPath: finalOutputPath), "Final task should create output file") + try make.build(output: task, scope: scope) + #expect(FileManager.default.fileExists(atPath: scope.resolve(path: silentOutputPath).path), "Silent task should still create output file") + #expect(FileManager.default.fileExists(atPath: scope.resolve(path: finalOutputPath).path), "Final task should create output file") try #require(messages.count == 1, "Should print progress for the final task") - #expect(messages[0] == ("output.txt", 1, 0, "\u{1B}[32mbuilding\u{1B}[0m")) + #expect(messages[0] == ("$PREFIX/output.txt", 1, 0, "\u{1B}[32mbuilding\u{1B}[0m")) } } // Test that error cases are handled appropriately @Test func errorWhileBuilding() throws { struct BuildError: Error {} - try withTemporaryDirectory { tempDir in - var make = MiniMake(printProgress: { _, _, _, _ in }) - let output = tempDir.appendingPathComponent("error.txt").path - - let task = make.addTask(output: output) { task in + try withTemporaryDirectory { tempDir, _ in + var make = MiniMake(printProgress: { _, _ in }) + let prefix = BuildPath(prefix: "PREFIX") + let scope = MiniMake.VariableScope(variables: [ + "PREFIX": tempDir.path, + ]) + let output = prefix.appending(path: "error.txt") + + let task = make.addTask(output: output) { task, scope in throw BuildError() } #expect(throws: BuildError.self) { - try make.build(output: task) + try make.build(output: task, scope: scope) } } } // Test that cleanup functionality works correctly @Test func cleanup() throws { - try withTemporaryDirectory { tempDir in - var make = MiniMake(printProgress: { _, _, _, _ in }) + try withTemporaryDirectory { tempDir, _ in + var make = MiniMake(printProgress: { _, _ in }) + let prefix = BuildPath(prefix: "PREFIX") + let scope = MiniMake.VariableScope(variables: [ + "PREFIX": tempDir.path, + ]) let outputs = [ - tempDir.appendingPathComponent("clean1.txt").path, - tempDir.appendingPathComponent("clean2.txt").path, + prefix.appending(path: "clean1.txt"), + prefix.appending(path: "clean2.txt"), ] // Create tasks and build them let tasks = outputs.map { output in - make.addTask(output: output) { task in - try "Content".write(toFile: task.output, atomically: true, encoding: .utf8) + make.addTask(output: output) { task, scope in + try "Content".write(toFile: scope.resolve(path: task.output).path, atomically: true, encoding: .utf8) } } for task in tasks { - try make.build(output: task) + try make.build(output: task, scope: scope) } // Verify files exist for output in outputs { #expect( - FileManager.default.fileExists(atPath: output), + FileManager.default.fileExists(atPath: scope.resolve(path: output).path), "Output file should exist before cleanup") } // Clean everything - make.cleanEverything() + make.cleanEverything(scope: scope) // Verify files are removed for output in outputs { #expect( - !FileManager.default.fileExists(atPath: output), + !FileManager.default.fileExists(atPath: scope.resolve(path: output).path), "Output file should not exist after cleanup") } } diff --git a/Plugins/PackageToJS/Tests/PackagingPlannerTests.swift b/Plugins/PackageToJS/Tests/PackagingPlannerTests.swift new file mode 100644 index 000000000..7269bea2d --- /dev/null +++ b/Plugins/PackageToJS/Tests/PackagingPlannerTests.swift @@ -0,0 +1,92 @@ +import Foundation +import Testing + +@testable import PackageToJS + +@Suite struct PackagingPlannerTests { + struct BuildSnapshot: Codable, Equatable { + let npmInstalls: [String] + } + class TestPackagingSystem: PackagingSystem { + var npmInstallCalls: [String] = [] + func npmInstall(packageDir: String) throws { + npmInstallCalls.append(packageDir) + } + + func wasmOpt(_ arguments: [String], input: String, output: String) throws { + try FileManager.default.copyItem( + at: URL(fileURLWithPath: input), to: URL(fileURLWithPath: output)) + } + } + + func snapshotBuildPlan( + filePath: String = #filePath, function: String = #function, + sourceLocation: SourceLocation = #_sourceLocation, + variant: String? = nil, + body: (inout MiniMake) throws -> MiniMake.TaskKey + ) throws { + var make = MiniMake(explain: false, printProgress: { _, _ in }) + let rootKey = try body(&make) + let fingerprint = try make.computeFingerprint(root: rootKey, prettyPrint: true) + try assertSnapshot( + filePath: filePath, function: function, sourceLocation: sourceLocation, + variant: variant, input: fingerprint + ) + } + + @Test(arguments: [ + (variant: "debug", configuration: "debug", splitDebug: false, noOptimize: false), + (variant: "release", configuration: "release", splitDebug: false, noOptimize: false), + (variant: "release_split_debug", configuration: "release", splitDebug: true, noOptimize: false), + (variant: "release_no_optimize", configuration: "release", splitDebug: false, noOptimize: true), + ]) + func planBuild(variant: String, configuration: String, splitDebug: Bool, noOptimize: Bool) throws { + let options = PackageToJS.PackageOptions() + let system = TestPackagingSystem() + let planner = PackagingPlanner( + options: options, + packageId: "test", + intermediatesDir: BuildPath(prefix: "INTERMEDIATES"), + selfPackageDir: BuildPath(prefix: "SELF_PACKAGE"), + outputDir: BuildPath(prefix: "OUTPUT"), + wasmProductArtifact: BuildPath(prefix: "WASM_PRODUCT_ARTIFACT"), + configuration: configuration, + triple: "wasm32-unknown-wasi", + selfPath: BuildPath(prefix: "PLANNER_SOURCE_PATH"), + system: system + ) + try snapshotBuildPlan(variant: variant) { make in + try planner.planBuild( + make: &make, + buildOptions: PackageToJS.BuildOptions( + product: "test", + splitDebug: splitDebug, + noOptimize: noOptimize, + packageOptions: options + ) + ) + } + } + + @Test func planTestBuild() throws { + let options = PackageToJS.PackageOptions() + let system = TestPackagingSystem() + let planner = PackagingPlanner( + options: options, + packageId: "test", + intermediatesDir: BuildPath(prefix: "INTERMEDIATES"), + selfPackageDir: BuildPath(prefix: "SELF_PACKAGE"), + outputDir: BuildPath(prefix: "OUTPUT"), + wasmProductArtifact: BuildPath(prefix: "WASM_PRODUCT_ARTIFACT"), + configuration: "debug", + triple: "wasm32-unknown-wasi", + selfPath: BuildPath(prefix: "PLANNER_SOURCE_PATH"), + system: system + ) + try snapshotBuildPlan() { make in + let (root, binDir) = try planner.planTestBuild(make: &make) + #expect(binDir.description == "$OUTPUT/bin") + return root + } + } +} diff --git a/Plugins/PackageToJS/Tests/SnapshotTesting.swift b/Plugins/PackageToJS/Tests/SnapshotTesting.swift new file mode 100644 index 000000000..8e556357b --- /dev/null +++ b/Plugins/PackageToJS/Tests/SnapshotTesting.swift @@ -0,0 +1,34 @@ +import Testing +import Foundation + +func assertSnapshot( + filePath: String = #filePath, function: String = #function, + sourceLocation: SourceLocation = #_sourceLocation, + variant: String? = nil, + input: Data +) throws { + let testFileName = URL(fileURLWithPath: filePath).deletingPathExtension().lastPathComponent + let snapshotDir = URL(fileURLWithPath: filePath) + .deletingLastPathComponent() + .appendingPathComponent("__Snapshots__") + .appendingPathComponent(testFileName) + try FileManager.default.createDirectory(at: snapshotDir, withIntermediateDirectories: true) + let snapshotFileName: String = "\(function[.. Comment { + "Snapshot mismatch: \(actualFilePath) \(snapshotPath.path)" + } + if !ok { + try input.write(to: URL(fileURLWithPath: actualFilePath)) + } + #expect(ok, buildComment(), sourceLocation: sourceLocation) + } else { + try input.write(to: snapshotPath) + #expect(Bool(false), "Snapshot created at \(snapshotPath.path)", sourceLocation: sourceLocation) + } +} diff --git a/Plugins/PackageToJS/Tests/TemporaryDirectory.swift b/Plugins/PackageToJS/Tests/TemporaryDirectory.swift index 4aa543bbf..199380fac 100644 --- a/Plugins/PackageToJS/Tests/TemporaryDirectory.swift +++ b/Plugins/PackageToJS/Tests/TemporaryDirectory.swift @@ -4,7 +4,7 @@ struct MakeTemporaryDirectoryError: Error { let error: CInt } -internal func withTemporaryDirectory(body: (URL) throws -> T) throws -> T { +internal func withTemporaryDirectory(body: (URL, _ retain: inout Bool) throws -> T) throws -> T { // Create a temporary directory using mkdtemp var template = FileManager.default.temporaryDirectory.appendingPathComponent("PackageToJSTests.XXXXXX").path return try template.withUTF8 { template in @@ -16,9 +16,12 @@ internal func withTemporaryDirectory(body: (URL) throws -> T) throws -> T { throw MakeTemporaryDirectoryError(error: errno) } let tempDir = URL(fileURLWithPath: String(cString: result)) + var retain = false defer { - try? FileManager.default.removeItem(at: tempDir) + if !retain { + try? FileManager.default.removeItem(at: tempDir) + } } - return try body(tempDir) + return try body(tempDir, &retain) } -} \ No newline at end of file +} diff --git a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_debug.json b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_debug.json new file mode 100644 index 000000000..0b1b2ac80 --- /dev/null +++ b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_debug.json @@ -0,0 +1,275 @@ +[ + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$INTERMEDIATES", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$OUTPUT\/main.wasm" + ], + "output" : "$INTERMEDIATES\/wasm-imports.json", + "wants" : [ + "$OUTPUT", + "$INTERMEDIATES", + "$OUTPUT\/main.wasm" + ] + }, + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$OUTPUT", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/index.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/index.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/index.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/index.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/instantiate.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/instantiate.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/instantiate.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/instantiate.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$WASM_PRODUCT_ARTIFACT" + ], + "output" : "$OUTPUT\/main.wasm", + "wants" : [ + "$OUTPUT" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/package.json" + ], + "output" : "$OUTPUT\/package.json", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT" + ] + }, + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$OUTPUT\/platforms", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.worker.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.worker.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/node.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/node.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/node.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/node.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Sources\/JavaScriptKit\/Runtime\/index.mjs", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/runtime.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + "phony", + "silent" + ], + "inputs" : [ + + ], + "output" : "all", + "wants" : [ + "$OUTPUT\/main.wasm", + "$INTERMEDIATES\/wasm-imports.json", + "$OUTPUT\/package.json", + "$OUTPUT\/index.js", + "$OUTPUT\/index.d.ts", + "$OUTPUT\/instantiate.js", + "$OUTPUT\/instantiate.d.ts", + "$OUTPUT\/platforms\/browser.js", + "$OUTPUT\/platforms\/browser.d.ts", + "$OUTPUT\/platforms\/browser.worker.js", + "$OUTPUT\/platforms\/node.js", + "$OUTPUT\/platforms\/node.d.ts", + "$OUTPUT\/runtime.js" + ] + } +] \ No newline at end of file diff --git a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release.json b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release.json new file mode 100644 index 000000000..bb2c3f74b --- /dev/null +++ b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release.json @@ -0,0 +1,290 @@ +[ + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$INTERMEDIATES", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$WASM_PRODUCT_ARTIFACT" + ], + "output" : "$INTERMEDIATES\/main.wasm.debug", + "wants" : [ + "$OUTPUT", + "$INTERMEDIATES" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$OUTPUT\/main.wasm" + ], + "output" : "$INTERMEDIATES\/wasm-imports.json", + "wants" : [ + "$OUTPUT", + "$INTERMEDIATES", + "$OUTPUT\/main.wasm" + ] + }, + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$OUTPUT", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/index.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/index.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/index.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/index.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/instantiate.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/instantiate.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/instantiate.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/instantiate.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$INTERMEDIATES\/main.wasm.debug" + ], + "output" : "$OUTPUT\/main.wasm", + "wants" : [ + "$OUTPUT", + "$INTERMEDIATES\/main.wasm.debug" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/package.json" + ], + "output" : "$OUTPUT\/package.json", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT" + ] + }, + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$OUTPUT\/platforms", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.worker.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.worker.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/node.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/node.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/node.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/node.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Sources\/JavaScriptKit\/Runtime\/index.mjs", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/runtime.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + "phony", + "silent" + ], + "inputs" : [ + + ], + "output" : "all", + "wants" : [ + "$OUTPUT\/main.wasm", + "$INTERMEDIATES\/wasm-imports.json", + "$OUTPUT\/package.json", + "$OUTPUT\/index.js", + "$OUTPUT\/index.d.ts", + "$OUTPUT\/instantiate.js", + "$OUTPUT\/instantiate.d.ts", + "$OUTPUT\/platforms\/browser.js", + "$OUTPUT\/platforms\/browser.d.ts", + "$OUTPUT\/platforms\/browser.worker.js", + "$OUTPUT\/platforms\/node.js", + "$OUTPUT\/platforms\/node.d.ts", + "$OUTPUT\/runtime.js" + ] + } +] \ No newline at end of file diff --git a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_no_optimize.json b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_no_optimize.json new file mode 100644 index 000000000..0b1b2ac80 --- /dev/null +++ b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_no_optimize.json @@ -0,0 +1,275 @@ +[ + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$INTERMEDIATES", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$OUTPUT\/main.wasm" + ], + "output" : "$INTERMEDIATES\/wasm-imports.json", + "wants" : [ + "$OUTPUT", + "$INTERMEDIATES", + "$OUTPUT\/main.wasm" + ] + }, + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$OUTPUT", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/index.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/index.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/index.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/index.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/instantiate.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/instantiate.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/instantiate.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/instantiate.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$WASM_PRODUCT_ARTIFACT" + ], + "output" : "$OUTPUT\/main.wasm", + "wants" : [ + "$OUTPUT" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/package.json" + ], + "output" : "$OUTPUT\/package.json", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT" + ] + }, + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$OUTPUT\/platforms", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.worker.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.worker.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/node.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/node.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/node.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/node.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Sources\/JavaScriptKit\/Runtime\/index.mjs", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/runtime.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + "phony", + "silent" + ], + "inputs" : [ + + ], + "output" : "all", + "wants" : [ + "$OUTPUT\/main.wasm", + "$INTERMEDIATES\/wasm-imports.json", + "$OUTPUT\/package.json", + "$OUTPUT\/index.js", + "$OUTPUT\/index.d.ts", + "$OUTPUT\/instantiate.js", + "$OUTPUT\/instantiate.d.ts", + "$OUTPUT\/platforms\/browser.js", + "$OUTPUT\/platforms\/browser.d.ts", + "$OUTPUT\/platforms\/browser.worker.js", + "$OUTPUT\/platforms\/node.js", + "$OUTPUT\/platforms\/node.d.ts", + "$OUTPUT\/runtime.js" + ] + } +] \ No newline at end of file diff --git a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_split_debug.json b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_split_debug.json new file mode 100644 index 000000000..b18680f8d --- /dev/null +++ b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_split_debug.json @@ -0,0 +1,290 @@ +[ + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$INTERMEDIATES", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$OUTPUT\/main.wasm" + ], + "output" : "$INTERMEDIATES\/wasm-imports.json", + "wants" : [ + "$OUTPUT", + "$INTERMEDIATES", + "$OUTPUT\/main.wasm" + ] + }, + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$OUTPUT", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/index.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/index.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/index.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/index.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/instantiate.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/instantiate.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/instantiate.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/instantiate.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$OUTPUT\/main.wasm.debug" + ], + "output" : "$OUTPUT\/main.wasm", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/main.wasm.debug" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$WASM_PRODUCT_ARTIFACT" + ], + "output" : "$OUTPUT\/main.wasm.debug", + "wants" : [ + "$OUTPUT", + "$INTERMEDIATES" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/package.json" + ], + "output" : "$OUTPUT\/package.json", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT" + ] + }, + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$OUTPUT\/platforms", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.worker.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.worker.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/node.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/node.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/node.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/node.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Sources\/JavaScriptKit\/Runtime\/index.mjs", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/runtime.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + "phony", + "silent" + ], + "inputs" : [ + + ], + "output" : "all", + "wants" : [ + "$OUTPUT\/main.wasm", + "$INTERMEDIATES\/wasm-imports.json", + "$OUTPUT\/package.json", + "$OUTPUT\/index.js", + "$OUTPUT\/index.d.ts", + "$OUTPUT\/instantiate.js", + "$OUTPUT\/instantiate.d.ts", + "$OUTPUT\/platforms\/browser.js", + "$OUTPUT\/platforms\/browser.d.ts", + "$OUTPUT\/platforms\/browser.worker.js", + "$OUTPUT\/platforms\/node.js", + "$OUTPUT\/platforms\/node.d.ts", + "$OUTPUT\/runtime.js" + ] + } +] \ No newline at end of file diff --git a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planTestBuild.json b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planTestBuild.json new file mode 100644 index 000000000..59e5bb4ad --- /dev/null +++ b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planTestBuild.json @@ -0,0 +1,367 @@ +[ + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$INTERMEDIATES", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$OUTPUT\/package.json" + ], + "output" : "$INTERMEDIATES\/npm-install.stamp", + "wants" : [ + "$INTERMEDIATES", + "$OUTPUT\/package.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$OUTPUT\/main.wasm" + ], + "output" : "$INTERMEDIATES\/wasm-imports.json", + "wants" : [ + "$OUTPUT", + "$INTERMEDIATES", + "$OUTPUT\/main.wasm" + ] + }, + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$OUTPUT", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$OUTPUT\/bin", + "wants" : [ + "$OUTPUT" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/bin\/test.js" + ], + "output" : "$OUTPUT\/bin\/test.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/bin" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/index.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/index.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/index.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/index.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/instantiate.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/instantiate.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/instantiate.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/instantiate.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$WASM_PRODUCT_ARTIFACT" + ], + "output" : "$OUTPUT\/main.wasm", + "wants" : [ + "$OUTPUT" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/package.json" + ], + "output" : "$OUTPUT\/package.json", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT" + ] + }, + { + "attributes" : [ + "silent" + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH" + ], + "output" : "$OUTPUT\/platforms", + "wants" : [ + + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/browser.worker.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/browser.worker.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/node.d.ts", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/node.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/platforms\/node.js", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/platforms\/node.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Sources\/JavaScriptKit\/Runtime\/index.mjs", + "$INTERMEDIATES\/wasm-imports.json" + ], + "output" : "$OUTPUT\/runtime.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/platforms", + "$INTERMEDIATES\/wasm-imports.json" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/test.browser.html" + ], + "output" : "$OUTPUT\/test.browser.html", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/bin" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/test.d.ts" + ], + "output" : "$OUTPUT\/test.d.ts", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/bin" + ] + }, + { + "attributes" : [ + + ], + "inputs" : [ + "$PLANNER_SOURCE_PATH", + "$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/test.js" + ], + "output" : "$OUTPUT\/test.js", + "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==", + "wants" : [ + "$OUTPUT", + "$OUTPUT\/bin" + ] + }, + { + "attributes" : [ + "phony", + "silent" + ], + "inputs" : [ + + ], + "output" : "all", + "wants" : [ + "$OUTPUT\/main.wasm", + "$INTERMEDIATES\/wasm-imports.json", + "$OUTPUT\/package.json", + "$OUTPUT\/index.js", + "$OUTPUT\/index.d.ts", + "$OUTPUT\/instantiate.js", + "$OUTPUT\/instantiate.d.ts", + "$OUTPUT\/platforms\/browser.js", + "$OUTPUT\/platforms\/browser.d.ts", + "$OUTPUT\/platforms\/browser.worker.js", + "$OUTPUT\/platforms\/node.js", + "$OUTPUT\/platforms\/node.d.ts", + "$OUTPUT\/runtime.js", + "$INTERMEDIATES\/npm-install.stamp", + "$OUTPUT\/bin", + "$OUTPUT\/test.js", + "$OUTPUT\/test.d.ts", + "$OUTPUT\/test.browser.html", + "$OUTPUT\/bin\/test.js" + ] + } +] \ No newline at end of file diff --git a/Tests/JavaScriptEventLoopTests/JavaScriptEventLoopTests.swift b/Tests/JavaScriptEventLoopTests/JavaScriptEventLoopTests.swift index 029876904..5d610aa48 100644 --- a/Tests/JavaScriptEventLoopTests/JavaScriptEventLoopTests.swift +++ b/Tests/JavaScriptEventLoopTests/JavaScriptEventLoopTests.swift @@ -197,7 +197,7 @@ final class JavaScriptEventLoopTests: XCTestCase { let result = try await catchPromise2.value XCTAssertEqual(result.object?.message, .string("test")) } - XCTAssertGreaterThanOrEqual(catchDiff, 200) + XCTAssertGreaterThanOrEqual(catchDiff, 150) } // MARK: - Continuation Tests diff --git a/package-lock.json b/package-lock.json index 18415649f..bb5718d1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "devDependencies": { "@rollup/plugin-typescript": "^8.3.1", + "playwright": "^1.51.0", "prettier": "2.6.1", "rollup": "^2.70.0", "tslib": "^2.3.1", @@ -125,6 +126,38 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.0.tgz", + "integrity": "sha512-442pTfGM0xxfCYxuBa/Pu6B2OqxqqaYq39JS8QDMGThUvIOCd6s0ANDog3uwA0cHavVlnTQzGCN7Id2YekDSXA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.51.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.0.tgz", + "integrity": "sha512-x47yPE3Zwhlil7wlNU/iktF7t2r/URR3VLbH6EknJd/04Qc/PSJ0EY3CMXipmglLG+zyRxW6HNo2EGbKLHPWMg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/prettier": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.1.tgz", @@ -281,6 +314,22 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "playwright": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.0.tgz", + "integrity": "sha512-442pTfGM0xxfCYxuBa/Pu6B2OqxqqaYq39JS8QDMGThUvIOCd6s0ANDog3uwA0cHavVlnTQzGCN7Id2YekDSXA==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.51.0" + } + }, + "playwright-core": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.0.tgz", + "integrity": "sha512-x47yPE3Zwhlil7wlNU/iktF7t2r/URR3VLbH6EknJd/04Qc/PSJ0EY3CMXipmglLG+zyRxW6HNo2EGbKLHPWMg==", + "dev": true + }, "prettier": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.1.tgz", diff --git a/package.json b/package.json index e25d0a17b..0c67b2705 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "license": "MIT", "devDependencies": { "@rollup/plugin-typescript": "^8.3.1", + "playwright": "^1.51.0", "prettier": "2.6.1", "rollup": "^2.70.0", "tslib": "^2.3.1",