diff --git a/.github/workflows/.java-version b/.github/workflows/.java-version index a45fd52cc5..aabe6ec390 100644 --- a/.github/workflows/.java-version +++ b/.github/workflows/.java-version @@ -1 +1 @@ -24 +21 diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 38cfc5ea54..1fc93fe1d5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -66,6 +66,8 @@ jobs: - macos-15 - ubuntu-24.04 - ubuntu-24.04-arm + - windows-2025 + - windows-11-arm runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v5 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f44a27770..69543710be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* New: Add JVM support for Windows (x64 and ARM). * In-development snapshots are now published to the Central Portal Snapshots repository at https://central.sonatype.com/repository/maven-snapshots/. diff --git a/zipline/build.zig b/zipline/build.zig index 3f97ef4c77..4e0acd62ae 100644 --- a/zipline/build.zig +++ b/zipline/build.zig @@ -9,6 +9,8 @@ pub fn build(b: *std.Build) !void { try setupTarget(b, &deleteLib.step, .linux, .x86_64, "amd64"); try setupTarget(b, &deleteLib.step, .macos, .aarch64, "aarch64"); try setupTarget(b, &deleteLib.step, .macos, .x86_64, "x86_64"); + try setupTarget(b, &deleteLib.step, .windows, .aarch64, "aarch64"); + try setupTarget(b, &deleteLib.step, .windows, .x86_64, "amd64"); } fn setupTarget(b: *std.Build, step: *std.Build.Step, tag: std.Target.Os.Tag, arch: std.Target.Cpu.Arch, dir: []const u8) !void { @@ -24,9 +26,9 @@ fn setupTarget(b: *std.Build, step: *std.Build.Step, tag: std.Target.Os.Tag, arc .optimize = .ReleaseSmall, }); - var version_buf: [11]u8 = undefined; + var version_buf: [64]u8 = undefined; const version = try readVersionFile(&version_buf); - var quoted_version_buf: [12]u8 = undefined; + var quoted_version_buf: [64]u8 = undefined; const quoted_version = try std.fmt.bufPrint("ed_version_buf, "\"{s}\"", .{ version }); lib.root_module.addCMacro("CONFIG_VERSION", quoted_version); @@ -81,15 +83,11 @@ fn setupTarget(b: *std.Build, step: *std.Build.Step, tag: std.Target.Os.Tag, arc step.dependOn(&install.step); } -fn readVersionFile(version_buf: []u8) ![]u8 { - const version_file = try std.fs.cwd().openFile( +fn readVersionFile(version_buf: []u8) ![]const u8 { + const version = try std.fs.cwd().readFile( "native/quickjs/VERSION", - .{ }, + version_buf, ); - defer version_file.close(); - var version_file_reader = std.io.bufferedReader(version_file.reader()); - var version_file_stream = version_file_reader.reader(); - const version = try version_file_stream.readUntilDelimiterOrEof(version_buf, '\n'); - return version.?; + return std.mem.trim(u8, version, "\r\n"); } diff --git a/zipline/src/jniTest/kotlin/app/cash/zipline/ThrowableSerializerTest.kt b/zipline/src/jniTest/kotlin/app/cash/zipline/ThrowableSerializerTest.kt index 7661b82eca..1dc7c5ed2c 100644 --- a/zipline/src/jniTest/kotlin/app/cash/zipline/ThrowableSerializerTest.kt +++ b/zipline/src/jniTest/kotlin/app/cash/zipline/ThrowableSerializerTest.kt @@ -18,6 +18,7 @@ package app.cash.zipline import app.cash.zipline.internal.bridge.ThrowableSerializer import assertk.assertThat import assertk.assertions.isEqualTo +import java.util.Locale.US import kotlin.coroutines.cancellation.CancellationException import kotlin.test.assertEquals import kotlinx.serialization.json.Json @@ -31,6 +32,17 @@ class ThrowableSerializerTest { contextual(Throwable::class, ThrowableSerializer) } } + private val newline: String + private val newlineEscape: String + init { + if ("windows" in System.getProperty("os.name").lowercase(US)) { + newline = "\r\n" + newlineEscape = "\\r\\n" + } else { + newline = "\n" + newlineEscape = "\\n" + } + } @Test fun happyPath() { val exception = Exception("boom") @@ -41,7 +53,7 @@ class ThrowableSerializerTest { val exceptionJson = """ |{ | "types": [], - | "stacktraceString": "java.lang.Exception: boom\n\tat ThrowableSerializerTest.goBoom(test.kt:5)" + | "stacktraceString": "java.lang.Exception: boom$newlineEscape\tat ThrowableSerializerTest.goBoom(test.kt:5)" |} """.trimMargin() @@ -53,10 +65,7 @@ class ThrowableSerializerTest { val decoded = json.decodeFromString(ThrowableSerializer, exceptionJson) assertEquals(ZiplineException::class, decoded::class) assertEquals( - """ - |java.lang.Exception: boom - |${'\t'}at ThrowableSerializerTest.goBoom(test.kt:5) - """.trimMargin(), + "java.lang.Exception: boom$newline\tat ThrowableSerializerTest.goBoom(test.kt:5)", decoded.message, ) } diff --git a/zipline/src/jvmMain/kotlin/app/cash/zipline/QuickJsNativeLoader.kt b/zipline/src/jvmMain/kotlin/app/cash/zipline/QuickJsNativeLoader.kt index dbeea79cb1..17bde4ee9f 100644 --- a/zipline/src/jvmMain/kotlin/app/cash/zipline/QuickJsNativeLoader.kt +++ b/zipline/src/jvmMain/kotlin/app/cash/zipline/QuickJsNativeLoader.kt @@ -29,6 +29,8 @@ internal actual fun loadNativeLibrary() { "/jni/$osArch/libquickjs.so" } else if (osName.contains("mac")) { "/jni/$osArch/libquickjs.dylib" + } else if (osName.contains("windows")) { + "/jni/$osArch/quickjs.dll" } else { throw IllegalStateException("Unsupported OS: $osName") }