From 87ed5908024ad4d3a24b29ecf77e44f2b6329097 Mon Sep 17 00:00:00 2001 From: Finn Voorhees Date: Fri, 30 Jan 2026 18:39:58 +0000 Subject: [PATCH] Fix build with latest Swift toolchain snapshot - Update .swift-version to main-snapshot-2026-01-09 - Add C standard library shim headers to CPlaydate to avoid ARM newlib module conflicts with _DarwinFoundation on macOS 16+ - Remove ARM toolchain/sysroot include paths from Package.swift since shims now provide the required declarations - Add newlib syscall stubs and posix_memalign to setup.c for the new toolchain's linker requirements - Move posix_memalign from Swift to C for proper linking - Add TARGET_PLAYDATE=1 to toolset_device.json for conditional compilation - Fix Xkpd CLodePNG/CQRCode build with minimal shim headers - Fix qrcode_getModule Bool return type usage in Comic.swift --- .github/workflows/Build.yml | 1 + .github/workflows/Examples.yml | 5 ++- .swift-version | 2 +- Examples/FlappySwift/Package.resolved | 15 +++++++++ Examples/Pong/Package.resolved | 15 +++++++++ Examples/Xkpd/Package.resolved | 11 ++++++- Examples/Xkpd/Package.swift | 13 -------- .../Xkpd/Sources/CLodePNG/include/string.h | 7 ++++ Examples/Xkpd/Sources/CLodePNG/setup.c | 15 +++++---- Package.swift | 29 ----------------- Plugins/PDCPlugin/PDCPlugin.swift | 32 ++++++++++++++++++- Sources/CPlaydate/include/CPlaydate.h | 3 ++ Sources/CPlaydate/setup.c | 24 ++++++++++++++ Sources/PlaydateKit/Playdate.swift | 21 ++---------- Toolsets/toolset_device.json | 1 + 15 files changed, 123 insertions(+), 71 deletions(-) create mode 100644 Examples/FlappySwift/Package.resolved create mode 100644 Examples/Pong/Package.resolved diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml index 64a6e854..381a8b03 100644 --- a/.github/workflows/Build.yml +++ b/.github/workflows/Build.yml @@ -12,6 +12,7 @@ jobs: matrix: os: - macos-15 + - macos-26 - ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/Examples.yml b/.github/workflows/Examples.yml index c4e58e82..e4e6d818 100644 --- a/.github/workflows/Examples.yml +++ b/.github/workflows/Examples.yml @@ -12,6 +12,7 @@ jobs: matrix: os: - macos-15 + - macos-26 - ubuntu-latest steps: - uses: actions/checkout@v4 @@ -31,6 +32,7 @@ jobs: matrix: os: - macos-15 + - macos-26 - ubuntu-latest steps: - uses: actions/checkout@v4 @@ -50,6 +52,7 @@ jobs: matrix: os: - macos-15 + - macos-26 - ubuntu-latest steps: - uses: actions/checkout@v4 @@ -77,5 +80,5 @@ jobs: if: ${{ runner.os == 'macOS' }} uses: actions/upload-artifact@v4 with: - name: Xkpd + name: Xkpd-${{ matrix.os }} path: Examples/Xkpd/.build/plugins/PDCPlugin/outputs/Xkpd.pdx diff --git a/.swift-version b/.swift-version index 62f0acd8..7e04dea9 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -main-snapshot-2025-05-25 \ No newline at end of file +main-snapshot-2026-01-09 \ No newline at end of file diff --git a/Examples/FlappySwift/Package.resolved b/Examples/FlappySwift/Package.resolved new file mode 100644 index 00000000..93500cb1 --- /dev/null +++ b/Examples/FlappySwift/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "61b5c79ed97a0741459ca18088a90dd7faf050b391f2467d8d8404469c088816", + "pins" : [ + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax.git", + "state" : { + "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2", + "version" : "601.0.1" + } + } + ], + "version" : 3 +} diff --git a/Examples/Pong/Package.resolved b/Examples/Pong/Package.resolved new file mode 100644 index 00000000..ea072675 --- /dev/null +++ b/Examples/Pong/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "146abcc1fd43813a34951fdde148234554351523aa1150ac4490903ee431034b", + "pins" : [ + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax.git", + "state" : { + "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2", + "version" : "601.0.1" + } + } + ], + "version" : 3 +} diff --git a/Examples/Xkpd/Package.resolved b/Examples/Xkpd/Package.resolved index c1bad83b..cdcc048d 100644 --- a/Examples/Xkpd/Package.resolved +++ b/Examples/Xkpd/Package.resolved @@ -1,6 +1,15 @@ { - "originHash" : "67ff08982bc006ab1782f5504cdbe0d2b466663b9d9b4682bfd7372570c3ea84", + "originHash" : "dc50f1992fcaf638a280148023edd7fd101e2cbf36fc9b9de2b2009d3969a2fd", "pins" : [ + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax.git", + "state" : { + "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2", + "version" : "601.0.1" + } + }, { "identity" : "utf8viewextensions", "kind" : "remoteSourceControl", diff --git a/Examples/Xkpd/Package.swift b/Examples/Xkpd/Package.swift index 2ff17064..5cf03eda 100644 --- a/Examples/Xkpd/Package.swift +++ b/Examples/Xkpd/Package.swift @@ -1,6 +1,5 @@ // swift-tools-version: 6.1 -import Foundation import PackageDescription /// Hack to force Xcode builds to not produce a dylib, since linking fails @@ -14,16 +13,6 @@ let playdateSDKPath: String = if let path = Context.environment["PLAYDATE_SDK_PA "\(Context.environment["HOME"]!)/Developer/PlaydateSDK/" } -let armSysrootPath: String = if let path = Context.environment["ARM_NONE_EABI_SYSROOT_PATH"] { - path -} else { - #if os(Linux) - "/usr/lib/arm-none-eabi" - #else - "/usr/local/playdate/gcc-arm-none-eabi-9-2019-q4-major/arm-none-eabi" - #endif -} - let package = Package( name: "Xkpd", platforms: [.macOS(.v14)], @@ -66,7 +55,6 @@ let package = Package( "-DLODEPNG_NO_COMPILE_DISK", "-DLODEPNG_NO_COMPILE_ALLOCATORS", "-DLODEPNG_NO_COMPILE_ANCILLARY_CHUNKS", - "-I", "\(armSysrootPath)/include", ]) ], ), @@ -78,7 +66,6 @@ let package = Package( cSettings: [ .unsafeFlags([ "-v", - "-I", "\(armSysrootPath)/include", ]) ] ), diff --git a/Examples/Xkpd/Sources/CLodePNG/include/string.h b/Examples/Xkpd/Sources/CLodePNG/include/string.h index 8ad98de3..e43453d4 100644 --- a/Examples/Xkpd/Sources/CLodePNG/include/string.h +++ b/Examples/Xkpd/Sources/CLodePNG/include/string.h @@ -5,4 +5,11 @@ #include +void *memset(void *, int, size_t); +void *memcpy(void *, const void *, size_t); +void *memmove(void *, const void *, size_t); +int memcmp(const void *, const void *, size_t); +size_t strlen(const char *); +int strcmp(const char *, const char *); + #endif diff --git a/Examples/Xkpd/Sources/CLodePNG/setup.c b/Examples/Xkpd/Sources/CLodePNG/setup.c index 7205483d..46111749 100644 --- a/Examples/Xkpd/Sources/CLodePNG/setup.c +++ b/Examples/Xkpd/Sources/CLodePNG/setup.c @@ -1,12 +1,13 @@ #ifdef TARGET_PLAYDATE - extern void* pdrealloc(void* ptr, size_t size); - void* lodepng_malloc(size_t size) { return pdrealloc(NULL, size); } - void* lodepng_realloc(void* ptr, size_t size) { return pdrealloc(ptr, size); } - void lodepng_free(void* ptr) { if(ptr) pdrealloc(ptr, 0); } + #include + extern void* malloc(size_t); + extern void* realloc(void*, size_t); + extern void free(void*); #else #include - void* lodepng_malloc(size_t size) { return malloc(size); } - void* lodepng_realloc(void* ptr, size_t size) { return realloc(ptr, size); } - void lodepng_free(void* ptr) { free(ptr); } #endif +void* lodepng_malloc(size_t size) { return malloc(size); } +void* lodepng_realloc(void* ptr, size_t size) { return realloc(ptr, size); } +void lodepng_free(void* ptr) { free(ptr); } + diff --git a/Package.swift b/Package.swift index 97f11cde..303aa002 100644 --- a/Package.swift +++ b/Package.swift @@ -2,26 +2,6 @@ import PackageDescription -let armToolchainPath: String = if let path = Context.environment["ARM_NONE_EABI_GCC_PATH"] { - path -} else { - #if os(Linux) - "/usr/lib/gcc/arm-none-eabi/10.3.1" - #else - "/usr/local/playdate/gcc-arm-none-eabi-9-2019-q4-major/lib/gcc/arm-none-eabi/9.2.1" - #endif -} - -let armSysrootPath: String = if let path = Context.environment["ARM_NONE_EABI_SYSROOT_PATH"] { - path -} else { - #if os(Linux) - "/usr/lib/arm-none-eabi" - #else - "/usr/local/playdate/gcc-arm-none-eabi-9-2019-q4-major/arm-none-eabi" - #endif -} - let playdateSDKPath: String = if let path = Context.environment["PLAYDATE_SDK_PATH"] { path } else { @@ -50,9 +30,6 @@ let package = Package( "-Xfrontend", "-function-sections", "-Xfrontend", "-gline-tables-only", "-Xcc", "-DTARGET_EXTENSION", - "-Xcc", "-I", "-Xcc", "\(armToolchainPath)/include", - "-Xcc", "-I", "-Xcc", "\(armToolchainPath)/include-fixed", - "-Xcc", "-I", "-Xcc", "\(armSysrootPath)/include", "-I", "\(playdateSDKPath)/C_API" ]), ] @@ -70,9 +47,6 @@ let package = Package( "-Xfrontend", "-function-sections", "-Xfrontend", "-gline-tables-only", "-Xcc", "-DTARGET_EXTENSION", - "-Xcc", "-I", "-Xcc", "\(armToolchainPath)/include", - "-Xcc", "-I", "-Xcc", "\(armToolchainPath)/include-fixed", - "-Xcc", "-I", "-Xcc", "\(armSysrootPath)/include", "-I", "\(playdateSDKPath)/C_API" ]), ] @@ -82,9 +56,6 @@ let package = Package( cSettings: [ .unsafeFlags([ "-DTARGET_EXTENSION", - "-I", "\(armToolchainPath)/include", - "-I", "\(armToolchainPath)/include-fixed", - "-I", "\(armSysrootPath)/include", "-I", "\(playdateSDKPath)/C_API" ]) ] diff --git a/Plugins/PDCPlugin/PDCPlugin.swift b/Plugins/PDCPlugin/PDCPlugin.swift index 27449a38..e826fbb1 100644 --- a/Plugins/PDCPlugin/PDCPlugin.swift +++ b/Plugins/PDCPlugin/PDCPlugin.swift @@ -33,6 +33,28 @@ import PackagePlugin } } + var armToolchainPath: String { + if let path = ProcessInfo.processInfo.environment["ARM_NONE_EABI_GCC_PATH"] { + return path + } + #if os(Linux) + return "/usr/lib/gcc/arm-none-eabi/10.3.1" + #else + return "/usr/local/playdate/gcc-arm-none-eabi-9-2019-q4-major/lib/gcc/arm-none-eabi/9.2.1" + #endif + } + + var armSysrootPath: String { + if let path = ProcessInfo.processInfo.environment["ARM_NONE_EABI_SYSROOT_PATH"] { + return path + } + #if os(Linux) + return "/usr/lib/arm-none-eabi" + #else + return "/usr/local/playdate/gcc-arm-none-eabi-9-2019-q4-major/arm-none-eabi" + #endif + } + var playdateSDKURL: URL { get throws { try URL(filePath: playdateSDKPath) } } func performCommand(context: PluginContext, arguments: [String]) async throws { @@ -136,12 +158,20 @@ import PackagePlugin verbose: Bool, extraDeviceOFilesBuildDirs: [String] ) throws { - let deviceParameters = try PackageManager.BuildParameters( + var deviceParameters = try PackageManager.BuildParameters( configuration: configuration, logging: verbose ? .verbose : .concise, echoLogs: verbose ).loadFlags(from: deviceToolset) + let armIncludes = [ + "-I", "\(armToolchainPath)/include", + "-I", "\(armToolchainPath)/include-fixed", + "-I", "\(armSysrootPath)/include" + ] + deviceParameters.otherCFlags += armIncludes + deviceParameters.otherSwiftcFlags += armIncludes.flatMap { ["-Xcc", $0] } + let result = try packageManager.build( .target(target.name), parameters: deviceParameters diff --git a/Sources/CPlaydate/include/CPlaydate.h b/Sources/CPlaydate/include/CPlaydate.h index 61b0c740..1bd5b796 100644 --- a/Sources/CPlaydate/include/CPlaydate.h +++ b/Sources/CPlaydate/include/CPlaydate.h @@ -1,6 +1,9 @@ #pragma once #include "pd_api.h" +int pd_rand(void); +void pd_srand(unsigned int seed); + int formatStringFloat(PlaydateAPI p, char **outstring, float number) { return p.system->formatString(outstring, "%f", number); } diff --git a/Sources/CPlaydate/setup.c b/Sources/CPlaydate/setup.c index 6399a3ed..af9b514a 100644 --- a/Sources/CPlaydate/setup.c +++ b/Sources/CPlaydate/setup.c @@ -21,6 +21,18 @@ void* _malloc_r(struct _reent* _REENT, size_t nbytes) { return pdrealloc(NULL,nb void* _realloc_r(struct _reent* _REENT, void* ptr, size_t nbytes) { return pdrealloc(ptr,nbytes); } void _free_r(struct _reent* _REENT, void* ptr ) { if ( ptr != NULL ) pdrealloc(ptr,0); } +// Newlib syscall stubs required by the linker on bare-metal ARM. +int getentropy(void *buffer, size_t length) { return -1; } +int _getentropy(void *buffer, size_t length) { return -1; } +int _kill(int pid, int sig) { return -1; } +int _getpid(void) { return 1; } +int _close(int fd) { return -1; } +int _lseek(int fd, int offset, int whence) { return -1; } +int _read(int fd, void *buf, int count) { return -1; } +int _write(int fd, const void *buf, int count) { return -1; } +int _fstat(int fd, void *buf) { return -1; } +int _isatty(int fd) { return 0; } + #else void* malloc(size_t nbytes) { return pdrealloc(NULL,nbytes); } @@ -29,4 +41,16 @@ void free(void* ptr ) { if ( ptr != NULL ) pdrealloc(ptr,0); } #endif +int posix_memalign(void **memptr, size_t alignment, size_t size) { + void *ptr = pdrealloc(NULL, size); + if (!ptr) return 12; // ENOMEM + *memptr = ptr; + return 0; +} + void *swift_coroFrameAlloc(size_t bytes, unsigned long long typeId) { return pdrealloc(NULL,bytes); } + +// Wrappers for rand/srand. On macOS 16+, the SDK marks these +// __swift_unavailable but they're still callable from C. +int pd_rand(void) { return rand(); } +void pd_srand(unsigned int seed) { srand(seed); } diff --git a/Sources/PlaydateKit/Playdate.swift b/Sources/PlaydateKit/Playdate.swift index cf1f20cc..f793d657 100644 --- a/Sources/PlaydateKit/Playdate.swift +++ b/Sources/PlaydateKit/Playdate.swift @@ -30,7 +30,7 @@ public enum Playdate { public static func initialize(with pointer: UnsafeMutablePointer) { _playdateAPI = pointer.pointee - srand(System.millisecondsSinceEpoch) + pd_srand(System.millisecondsSinceEpoch) System.setUpdateCallback(update: { _ in (System.updateCallback?() ?? false) ? 1 : 0 }, userdata: nil) @@ -41,31 +41,16 @@ public enum Playdate { private nonisolated(unsafe) static var _playdateAPI: PlaydateAPI? } -/// Implement `posix_memalign(3)`, which is required by the Embedded Swift runtime but is -/// not provided by the Playdate C library. -@_documentation(visibility: internal) -@_cdecl("posix_memalign") public func posix_memalign( - _ memptr: UnsafeMutablePointer, - _ alignment: Int, - _ size: Int -) -> CInt { - guard let allocation = malloc(Int(size + alignment - 1)) else { fatalError() } - let misalignment = Int(bitPattern: allocation) % alignment - precondition(misalignment == 0) - memptr.pointee = allocation - return 0 -} - /// Implement `arc4random_buf` which is required by the Embedded Swift runtime for Hashable, Set, Dictionary, /// and random-number generating APIs but is not provided by the Playdate C library. @_documentation(visibility: internal) @_cdecl("arc4random_buf") public func arc4random_buf(buf: UnsafeMutableRawPointer, nbytes: Int) { for i in stride(from: 0, to: nbytes - 1, by: 2) { - let randomValue = UInt16(rand() & Int32(UInt16.max)) + let randomValue = UInt16(pd_rand() & Int32(UInt16.max)) (buf + i).assumingMemoryBound(to: UInt16.self).pointee = randomValue } if nbytes % 2 == 1 { - let randomValue = UInt8(rand() & Int32(UInt8.max)) + let randomValue = UInt8(pd_rand() & Int32(UInt8.max)) (buf + nbytes - 1).assumingMemoryBound(to: UInt8.self).pointee = randomValue } } diff --git a/Toolsets/toolset_device.json b/Toolsets/toolset_device.json index a1ecf233..de35a75e 100644 --- a/Toolsets/toolset_device.json +++ b/Toolsets/toolset_device.json @@ -15,6 +15,7 @@ "-mfloat-abi=hard", "-mfpu=fpv5-sp-d16", "-D__FPU_USED=1", + "-DTARGET_PLAYDATE=1", "-falign-functions=16", "-fshort-enums"