From cfb5a0220a2c859e8f2f58d52de98fc2171e3cfb Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 2 Aug 2024 00:50:27 +0900 Subject: [PATCH 01/12] Add explicit include of `wasi/libc-environ.h` (#786) This is necessary to get the `__wasilibc_get_environ` function declaration. (cherry picked from commit 243066f12d0b6f1ab8ad9fefd8526b2383641892) --- Sources/_FoundationCShims/platform_shims.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/_FoundationCShims/platform_shims.c b/Sources/_FoundationCShims/platform_shims.c index 5a400a468..6f792241c 100644 --- a/Sources/_FoundationCShims/platform_shims.c +++ b/Sources/_FoundationCShims/platform_shims.c @@ -21,6 +21,10 @@ extern char **environ; #endif +#if __wasi__ +#include // for __wasilibc_get_environ +#endif + #if __has_include() #import void _platform_shims_lock_environ() { From 189cfd856357620038849645f5ca9ba077837266 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 2 Aug 2024 01:02:11 +0900 Subject: [PATCH 02/12] Add explicit void type parameter to C functions without parameters (#775) C functions with `()` as parameter list can take any number of parameters. But WebAssembly requires static signature information for every function call, so we need to explicitly specify `(void)` to indicate that the function takes no parameters. (cherry picked from commit 8f34f38f30858f7b9dfa9a40b07c18fd7a7b93ae) --- .../_FoundationCShims/include/platform_shims.h | 12 ++++++------ Sources/_FoundationCShims/platform_shims.c | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Sources/_FoundationCShims/include/platform_shims.h b/Sources/_FoundationCShims/include/platform_shims.h index 911fc9e7a..f8048e678 100644 --- a/Sources/_FoundationCShims/include/platform_shims.h +++ b/Sources/_FoundationCShims/include/platform_shims.h @@ -31,19 +31,19 @@ #include #endif -INTERNAL char * _Nullable * _Nullable _platform_shims_get_environ(); +INTERNAL char * _Nullable * _Nullable _platform_shims_get_environ(void); -INTERNAL void _platform_shims_lock_environ(); -INTERNAL void _platform_shims_unlock_environ(); +INTERNAL void _platform_shims_lock_environ(void); +INTERNAL void _platform_shims_unlock_environ(void); #if __has_include() #include -INTERNAL vm_size_t _platform_shims_vm_size(); +INTERNAL vm_size_t _platform_shims_vm_size(void); #endif #if __has_include() #include -INTERNAL mach_port_t _platform_mach_task_self(); +INTERNAL mach_port_t _platform_mach_task_self(void); #endif #if __has_include() @@ -65,7 +65,7 @@ typedef enum { } _platform_shims_OSThermalPressureLevel; -INTERNAL const char * _Nonnull _platform_shims_kOSThermalNotificationPressureLevelName(); +INTERNAL const char * _Nonnull _platform_shims_kOSThermalNotificationPressureLevelName(void); #endif #endif /* CSHIMS_PLATFORM_SHIMS */ diff --git a/Sources/_FoundationCShims/platform_shims.c b/Sources/_FoundationCShims/platform_shims.c index 6f792241c..556bc9416 100644 --- a/Sources/_FoundationCShims/platform_shims.c +++ b/Sources/_FoundationCShims/platform_shims.c @@ -27,19 +27,19 @@ extern char **environ; #if __has_include() #import -void _platform_shims_lock_environ() { +void _platform_shims_lock_environ(void) { environ_lock_np(); } -void _platform_shims_unlock_environ() { +void _platform_shims_unlock_environ(void) { environ_unlock_np(); } #else -void _platform_shims_lock_environ() { /* noop */ } -void _platform_shims_unlock_environ() { /* noop */ } +void _platform_shims_lock_environ(void) { /* noop */ } +void _platform_shims_unlock_environ(void) { /* noop */ } #endif -char ** _platform_shims_get_environ() { +char ** _platform_shims_get_environ(void) { #if __has_include() return *_NSGetEnviron(); #elif defined(_WIN32) @@ -52,20 +52,20 @@ char ** _platform_shims_get_environ() { } #if __has_include() -const char * _platform_shims_kOSThermalNotificationPressureLevelName() { +const char * _platform_shims_kOSThermalNotificationPressureLevelName(void) { return kOSThermalNotificationPressureLevelName; } #endif #if __has_include() -vm_size_t _platform_shims_vm_size() { +vm_size_t _platform_shims_vm_size(void) { // This shim exists because vm_page_size is not marked const, and therefore looks like global mutable state to Swift. return vm_page_size; } #endif #if __has_include() -mach_port_t _platform_mach_task_self() { +mach_port_t _platform_mach_task_self(void) { // This shim exists because mach_task_self_ is not marked const, and therefore looks like global mutable state to Swift. return mach_task_self(); } From 9f3ab600d3062e8de6c79d306449faad8a194449 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 2 Aug 2024 01:06:57 +0900 Subject: [PATCH 03/12] Exclude EREMOTE definition for WASI platform (#778) WASI does not define the EREMOTE error code. (cherry picked from commit 6bb5ff7b29ed65a722119057d406ffd4bdcdf1b9) --- Sources/FoundationEssentials/Error/ErrorCodes+POSIX.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/FoundationEssentials/Error/ErrorCodes+POSIX.swift b/Sources/FoundationEssentials/Error/ErrorCodes+POSIX.swift index a7e01952c..d6977ba47 100644 --- a/Sources/FoundationEssentials/Error/ErrorCodes+POSIX.swift +++ b/Sources/FoundationEssentials/Error/ErrorCodes+POSIX.swift @@ -465,11 +465,13 @@ extension POSIXError { return .ESTALE } + #if !os(WASI) /// Too many levels of remote in path. public static var EREMOTE: POSIXErrorCode { return .EREMOTE } #endif + #endif #if canImport(Darwin) /// RPC struct is bad. From 9c6d53cb0b49bb7b79eda03b7dcd4aacee064118 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 2 Aug 2024 01:12:39 +0900 Subject: [PATCH 04/12] Throw `.featureUnsupported` when attempting to create temp files on WASI (#779) WASI does not have temp directory concept, and does not provide mktemp family of functions, so attempting to create a temporary file should be considered a feature unsupported. (cherry picked from commit fb11420dc6f546e67fce249c15bb17e208c40aff) --- Sources/FoundationEssentials/Data/Data+Writing.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/FoundationEssentials/Data/Data+Writing.swift b/Sources/FoundationEssentials/Data/Data+Writing.swift index 4f92cb1a6..44cf53d64 100644 --- a/Sources/FoundationEssentials/Data/Data+Writing.swift +++ b/Sources/FoundationEssentials/Data/Data+Writing.swift @@ -127,6 +127,10 @@ private func cleanupTemporaryDirectory(at inPath: String?) { /// Caller is responsible for calling `close` on the `Int32` file descriptor. private func createTemporaryFile(at destinationPath: String, inPath: PathOrURL, prefix: String, options: Data.WritingOptions) throws -> (Int32, String) { +#if os(WASI) + // WASI does not have temp directories + throw CocoaError(.featureUnsupported) +#else var directoryPath = destinationPath if !directoryPath.isEmpty && directoryPath.last! != "/" { directoryPath.append("/") @@ -181,6 +185,7 @@ private func createTemporaryFile(at destinationPath: String, inPath: PathOrURL, } } } while true +#endif // os(WASI) } /// Returns `(file descriptor, temporary file path, temporary directory path)` From 1b8c399cd90a8d6e960a18c5edfda427ec2ba461 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 2 Aug 2024 01:15:23 +0900 Subject: [PATCH 05/12] Fix `operatingSystemVersion` on WASI (#782) The `operatingSystemVersion` property type is a tuple but the it was returning an `OperatingSystemVersion` instance on unknown platforms. (cherry picked from commit a8f12255dbf98e0899af84d0e674ab71be30bd85) --- Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift b/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift index 302d3c62d..a3e6b6c60 100644 --- a/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift +++ b/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift @@ -389,7 +389,7 @@ extension _ProcessInfo { patch: Int(osVersionInfo.dwBuildNumber) ) #else - return OperatingSystemVersion(majorVersion: -1, minorVersion: 0, patchVersion: 0) + return (major: -1, minor: 0, patch: 0) #endif } From 0f1c377bc3935e2060848f234a20ac89ef085040 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 2 Aug 2024 01:20:05 +0900 Subject: [PATCH 06/12] Guard out extended or fs attributes related code on WASI (#784) This commit guards out the extended attributes and file system attributes related code on WASI as WASI does not support these features. Just return nothing or ignore the set request. (cherry picked from commit fab7195ea2503d296ce80ff6bc5cdb6da8c71b9c) --- .../FileManager/FileManager+Files.swift | 13 +++++++++++-- .../FileManager/FileManager+Utilities.swift | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Sources/FoundationEssentials/FileManager/FileManager+Files.swift b/Sources/FoundationEssentials/FileManager/FileManager+Files.swift index 72e05f937..cf5cb79a4 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Files.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Files.swift @@ -484,7 +484,7 @@ extension _FileManagerImpl { #endif } -#if !os(Windows) +#if !os(Windows) && !os(WASI) private func _extendedAttribute(_ key: UnsafePointer, at path: UnsafePointer, followSymlinks: Bool) throws -> Data? { #if canImport(Darwin) var size = getxattr(path, key, nil, 0, 0, followSymlinks ? 0 : XATTR_NOFOLLOW) @@ -638,10 +638,11 @@ extension _FileManagerImpl { var attributes = statAtPath.fileAttributes try? Self._catInfo(for: URL(filePath: path, directoryHint: .isDirectory), statInfo: statAtPath, into: &attributes) - + #if !os(WASI) // WASI does not support extended attributes if let extendedAttrs = try? _extendedAttributes(at: fsRep, followSymlinks: false) { attributes[._extendedAttributes] = extendedAttrs } + #endif #if !targetEnvironment(simulator) && FOUNDATION_FRAMEWORK if statAtPath.isRegular || statAtPath.isDirectory { @@ -703,6 +704,9 @@ extension _FileManagerImpl { ] } } +#elseif os(WASI) + // WASI does not support file system attributes + return [:] #else try fileManager.withFileSystemRepresentation(for: path) { rep in guard let rep else { @@ -930,7 +934,12 @@ extension _FileManagerImpl { try Self._setCatInfoAttributes(attributes, path: path) if let extendedAttrs = attributes[.init("NSFileExtendedAttributes")] as? [String : Data] { + #if os(WASI) + // WASI does not support extended attributes + throw CocoaError.errorWithFilePath(.featureUnsupported, path) + #else try Self._setAttributes(extendedAttrs, at: fileSystemRepresentation, followSymLinks: false) + #endif } if let date = attributes[.modificationDate] as? Date { diff --git a/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift b/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift index bba8ed5c9..eb430baaf 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift @@ -173,7 +173,7 @@ extension _FileManagerImpl { #endif } -#if !os(Windows) +#if !os(Windows) && !os(WASI) static func _setAttribute(_ key: UnsafePointer, value: Data, at path: UnsafePointer, followSymLinks: Bool) throws { try value.withUnsafeBytes { buffer in #if canImport(Darwin) From 0e3174f872f9d1a48daf6aa9e6f80adda77e16f3 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 2 Aug 2024 01:23:13 +0900 Subject: [PATCH 07/12] Guard out user/group related code on WASI (#783) * Guard out user/group related code on WASI This change guards out the user/group related code on WASI, as WASI does not have the concept of users or groups. * Throw explicit unsupported error if trying to set user or group on WASI Instead of implicitly ignoring user-given values, we should throw exception to make it clear that those values cannot be set on WASI. (cherry picked from commit 0b3974d35103fd1e3e5213f2cdcefc1fd7fa84f4) --- Sources/FoundationEssentials/Data/Data+Writing.swift | 2 ++ .../FoundationEssentials/FileManager/FileManager+Files.swift | 5 +++++ .../FileManager/FileManager+Utilities.swift | 2 +- .../FoundationEssentials/FileManager/FileOperations.swift | 2 ++ Sources/FoundationEssentials/Platform.swift | 4 ++-- Sources/FoundationEssentials/String/String+Path.swift | 2 ++ 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Sources/FoundationEssentials/Data/Data+Writing.swift b/Sources/FoundationEssentials/Data/Data+Writing.swift index 44cf53d64..c86f27bc3 100644 --- a/Sources/FoundationEssentials/Data/Data+Writing.swift +++ b/Sources/FoundationEssentials/Data/Data+Writing.swift @@ -519,6 +519,7 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint cleanupTemporaryDirectory(at: temporaryDirectoryPath) +#if !os(WASI) // WASI does not support fchmod for now if let mode { // Try to change the mode if the path has not changed. Do our best, but don't report an error. #if FOUNDATION_FRAMEWORK @@ -542,6 +543,7 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint fchmod(fd, mode) #endif } +#endif // os(WASI) } } } diff --git a/Sources/FoundationEssentials/FileManager/FileManager+Files.swift b/Sources/FoundationEssentials/FileManager/FileManager+Files.swift index cf5cb79a4..acf7d3c51 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Files.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Files.swift @@ -922,6 +922,10 @@ extension _FileManagerImpl { let groupID = _readFileAttributePrimitive(attributes[.groupOwnerAccountID], as: UInt.self) if user != nil || userID != nil || group != nil || groupID != nil { + #if os(WASI) + // WASI does not have the concept of users or groups + throw CocoaError.errorWithFilePath(.featureUnsupported, path) + #else // Bias toward userID & groupID - try to prevent round trips to getpwnam if possible. var leaveUnchanged: UInt32 { UInt32(bitPattern: -1) } let rawUserID = userID.flatMap(uid_t.init) ?? user.flatMap(Self._userAccountNameToNumber) ?? leaveUnchanged @@ -929,6 +933,7 @@ extension _FileManagerImpl { if chown(fileSystemRepresentation, rawUserID, rawGroupID) != 0 { throw CocoaError.errorWithFilePath(path, errno: errno, reading: false) } + #endif } try Self._setCatInfoAttributes(attributes, path: path) diff --git a/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift b/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift index eb430baaf..45f498937 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift @@ -271,7 +271,7 @@ extension _FileManagerImpl { } #endif -#if !os(Windows) +#if !os(Windows) && !os(WASI) static func _userAccountNameToNumber(_ name: String) -> uid_t? { name.withCString { ptr in getpwnam(ptr)?.pointee.pw_uid diff --git a/Sources/FoundationEssentials/FileManager/FileOperations.swift b/Sources/FoundationEssentials/FileManager/FileOperations.swift index 8e67a771f..130a07ba4 100644 --- a/Sources/FoundationEssentials/FileManager/FileOperations.swift +++ b/Sources/FoundationEssentials/FileManager/FileOperations.swift @@ -855,12 +855,14 @@ enum _FileOperations { } defer { close(dstfd) } + #if !os(WASI) // WASI doesn't have fchmod for now // Set the file permissions using fchmod() instead of when open()ing to avoid umask() issues let permissions = fileInfo.st_mode & ~S_IFMT guard fchmod(dstfd, permissions) == 0 else { try delegate.throwIfNecessary(errno, String(cString: srcPtr), String(cString: dstPtr)) return } + #endif if fileInfo.st_size == 0 { // no copying required diff --git a/Sources/FoundationEssentials/Platform.swift b/Sources/FoundationEssentials/Platform.swift index 20dc561af..bf2e652d3 100644 --- a/Sources/FoundationEssentials/Platform.swift +++ b/Sources/FoundationEssentials/Platform.swift @@ -113,7 +113,7 @@ private let _cachedUGIDs: (uid_t, gid_t) = { }() #endif -#if !os(Windows) +#if !os(Windows) && !os(WASI) extension Platform { private static var ROOT_USER: UInt32 { 0 } static func getUGIDs(allowEffectiveRootUID: Bool = true) -> (uid: UInt32, gid: UInt32) { @@ -174,7 +174,7 @@ extension Platform { // FIXME: bionic implements this as `return 0;` and does not expose the // function via headers. We should be able to shim this and use the call // if it is available. -#if !os(Android) +#if !os(Android) && !os(WASI) guard issetugid() == 0 else { return nil } #endif if let value = getenv(name) { diff --git a/Sources/FoundationEssentials/String/String+Path.swift b/Sources/FoundationEssentials/String/String+Path.swift index 79bcbfa76..5e42aef4c 100644 --- a/Sources/FoundationEssentials/String/String+Path.swift +++ b/Sources/FoundationEssentials/String/String+Path.swift @@ -450,6 +450,7 @@ extension String { return envVar.standardizingPath } + #if !os(WASI) // WASI does not have user concept // Next, attempt to find the home directory via getpwnam/getpwuid var pass: UnsafeMutablePointer? if let user { @@ -463,6 +464,7 @@ extension String { if let dir = pass?.pointee.pw_dir { return String(cString: dir).standardizingPath } + #endif // Fallback to HOME for the current user if possible if user == nil, let home = getenv("HOME") { From 05fc5ebc4c0b92438498ce8a4e38270761b0545f Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 2 Aug 2024 01:30:17 +0900 Subject: [PATCH 08/12] Skip sticky-bit check in `isDeletableFile` on WASI (#785) WASI does not surface the sticky bit and getuid, so we cannot check whether the file is actually deletable before attempting to delete it. (cherry picked from commit e90b6c3f90e52f840fc087b05468f430eae1d05a) --- .../FoundationEssentials/FileManager/FileManager+Files.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/FoundationEssentials/FileManager/FileManager+Files.swift b/Sources/FoundationEssentials/FileManager/FileManager+Files.swift index acf7d3c51..99f1b32c6 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Files.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Files.swift @@ -461,7 +461,7 @@ extension _FileManagerImpl { parent = fileManager.currentDirectoryPath } -#if os(Windows) +#if os(Windows) || os(WASI) return fileManager.isWritableFile(atPath: parent) && fileManager.isWritableFile(atPath: path) #else guard fileManager.isWritableFile(atPath: parent), From 779e11a4c35584a7a5e43fbc4ca88bfd8a570aca Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 2 Aug 2024 02:50:04 +0900 Subject: [PATCH 09/12] Implement `_copyRegularFile` for WASI without `sendfile` (#787) WASI doesn't have `sendfile`, so we need to implement the copy in user space with `read` and `write`. It's not as efficient as `sendfile`, but it's the best we can do. (cherry picked from commit 2a6afeb50aecc5fdfaa7b739399afff1eca024d1) --- .../FileManager/FileOperations.swift | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Sources/FoundationEssentials/FileManager/FileOperations.swift b/Sources/FoundationEssentials/FileManager/FileOperations.swift index 130a07ba4..51f4f7ea4 100644 --- a/Sources/FoundationEssentials/FileManager/FileOperations.swift +++ b/Sources/FoundationEssentials/FileManager/FileOperations.swift @@ -873,12 +873,31 @@ enum _FileOperations { let chunkSize: Int = Int(fileInfo.st_blksize) var current: off_t = 0 + #if os(WASI) + // WASI doesn't have sendfile, so we need to do it in user space with read/write + try withUnsafeTemporaryAllocation(of: UInt8.self, capacity: chunkSize) { buffer in + while current < total { + let readSize = Swift.min(total - Int(current), chunkSize) + let bytesRead = read(srcfd, buffer.baseAddress, readSize) + guard bytesRead >= 0 else { + try delegate.throwIfNecessary(errno, String(cString: srcPtr), String(cString: dstPtr)) + return + } + guard write(dstfd, buffer.baseAddress, bytesRead) == bytesRead else { + try delegate.throwIfNecessary(errno, String(cString: srcPtr), String(cString: dstPtr)) + return + } + current += off_t(bytesRead) + } + } + #else while current < total { guard sendfile(dstfd, srcfd, ¤t, Swift.min(total - Int(current), chunkSize)) != -1 else { try delegate.throwIfNecessary(errno, String(cString: srcPtr), String(cString: dstPtr)) return } } + #endif } #endif From 03c088435bf1300b0b5fe914a7d100d756c19234 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Fri, 2 Aug 2024 05:57:20 +0900 Subject: [PATCH 10/12] Port `LockedState` and `_ThreadLocal` to WASI without any locking (#780) (cherry picked from commit aa68eebebcb8dd16b9636c54e580e0ad32bb57e3) --- Sources/FoundationEssentials/LockedState.swift | 9 +++++++++ Sources/FoundationEssentials/_ThreadLocal.swift | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/Sources/FoundationEssentials/LockedState.swift b/Sources/FoundationEssentials/LockedState.swift index ad432c704..59fbd8b8e 100644 --- a/Sources/FoundationEssentials/LockedState.swift +++ b/Sources/FoundationEssentials/LockedState.swift @@ -33,6 +33,9 @@ package struct LockedState { typealias Primitive = pthread_mutex_t #elseif canImport(WinSDK) typealias Primitive = SRWLOCK +#elseif os(WASI) + // WASI is single-threaded, so we don't need a lock. + typealias Primitive = Void #endif typealias PlatformLock = UnsafeMutablePointer @@ -45,6 +48,8 @@ package struct LockedState { pthread_mutex_init(platformLock, nil) #elseif canImport(WinSDK) InitializeSRWLock(platformLock) +#elseif os(WASI) + // no-op #endif } @@ -62,6 +67,8 @@ package struct LockedState { pthread_mutex_lock(platformLock) #elseif canImport(WinSDK) AcquireSRWLockExclusive(platformLock) +#elseif os(WASI) + // no-op #endif } @@ -72,6 +79,8 @@ package struct LockedState { pthread_mutex_unlock(platformLock) #elseif canImport(WinSDK) ReleaseSRWLockExclusive(platformLock) +#elseif os(WASI) + // no-op #endif } } diff --git a/Sources/FoundationEssentials/_ThreadLocal.swift b/Sources/FoundationEssentials/_ThreadLocal.swift index 67f5dfaae..df5bad8d7 100644 --- a/Sources/FoundationEssentials/_ThreadLocal.swift +++ b/Sources/FoundationEssentials/_ThreadLocal.swift @@ -30,6 +30,8 @@ struct _ThreadLocal { fileprivate typealias PlatformKey = tss_t #elseif canImport(WinSDK) fileprivate typealias PlatformKey = DWORD +#elseif os(WASI) + fileprivate typealias PlatformKey = UnsafeMutablePointer #endif struct Key { @@ -46,6 +48,8 @@ struct _ThreadLocal { self.key = key #elseif canImport(WinSDK) key = FlsAlloc(nil) +#elseif os(WASI) + key = UnsafeMutablePointer.allocate(capacity: 1) #endif } } @@ -58,6 +62,8 @@ struct _ThreadLocal { tss_get(key) #elseif canImport(WinSDK) FlsGetValue(key) +#elseif os(WASI) + key.pointee #endif } @@ -68,6 +74,8 @@ struct _ThreadLocal { tss_set(key, newValue) #elseif canImport(WinSDK) FlsSetValue(key, newValue) +#elseif os(WASI) + key.pointee = newValue #endif } } From 2ca6906f97be3110a101a23c452d3389432a98ff Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 3 Aug 2024 01:55:56 +0900 Subject: [PATCH 11/12] Add WASI platform conditions for libc imports and word size (#776) * Add `import WASILibc` statements to libc import chains * Declare wasm32 arch as 32-bit environment * Switch to _pointerBitWidth for architecture checks This change switches the architecture checks in Data.swift to use the _pointerBitWidth instead of the arch() checks for consistency with newer platforms. (cherry picked from commit c82d1673eb112ac62e4f770947c8a238a7659163) --- .../FoundationEssentials/Calendar/Calendar.swift | 2 ++ .../Calendar/Calendar_Gregorian.swift | 2 ++ .../FoundationEssentials/Data/Data+Reading.swift | 2 ++ .../FoundationEssentials/Data/Data+Writing.swift | 2 ++ Sources/FoundationEssentials/Data/Data.swift | 14 ++++++++------ Sources/FoundationEssentials/Date.swift | 2 ++ .../Decimal/Decimal+Math.swift | 2 ++ .../Error/CocoaError+FilePath.swift | 2 ++ .../Error/ErrorCodes+POSIX.swift | 2 ++ .../FileManager/FileManager+Basics.swift | 2 ++ .../FileManager/FileManager+Directories.swift | 2 ++ .../FileManager/FileManager+Files.swift | 3 +++ .../FileManager/FileManager+SymbolicLinks.swift | 2 ++ .../FileManager/FileManager+Utilities.swift | 2 ++ .../FileManager/FileOperations.swift | 2 ++ ...BinaryInteger+NumericStringRepresentation.swift | 2 ++ .../ProcessInfo/ProcessInfo.swift | 2 ++ .../PropertyList/OpenStepPlist.swift | 2 ++ .../FoundationEssentials/String/String+Path.swift | 2 ++ .../Calendar/Calendar_ICU.swift | 2 ++ .../Formatting/Duration+Formatting.swift | 2 ++ 21 files changed, 49 insertions(+), 6 deletions(-) diff --git a/Sources/FoundationEssentials/Calendar/Calendar.swift b/Sources/FoundationEssentials/Calendar/Calendar.swift index 99d72e161..94a67d60a 100644 --- a/Sources/FoundationEssentials/Calendar/Calendar.swift +++ b/Sources/FoundationEssentials/Calendar/Calendar.swift @@ -18,6 +18,8 @@ import Android import Glibc #elseif canImport(CRT) import CRT +#elseif os(WASI) +import WASILibc #endif #if FOUNDATION_FRAMEWORK diff --git a/Sources/FoundationEssentials/Calendar/Calendar_Gregorian.swift b/Sources/FoundationEssentials/Calendar/Calendar_Gregorian.swift index fd863cef8..a7cc7386f 100644 --- a/Sources/FoundationEssentials/Calendar/Calendar_Gregorian.swift +++ b/Sources/FoundationEssentials/Calendar/Calendar_Gregorian.swift @@ -18,6 +18,8 @@ import Android import Glibc #elseif canImport(CRT) import CRT +#elseif os(WASI) +import WASILibc #endif diff --git a/Sources/FoundationEssentials/Data/Data+Reading.swift b/Sources/FoundationEssentials/Data/Data+Reading.swift index eb66e3f7c..4c893f6cb 100644 --- a/Sources/FoundationEssentials/Data/Data+Reading.swift +++ b/Sources/FoundationEssentials/Data/Data+Reading.swift @@ -25,6 +25,8 @@ import Glibc #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +import WASILibc #endif func _fgetxattr(_ fd: Int32, _ name: UnsafePointer!, _ value: UnsafeMutableRawPointer!, _ size: Int, _ position: UInt32, _ options: Int32) -> Int { diff --git a/Sources/FoundationEssentials/Data/Data+Writing.swift b/Sources/FoundationEssentials/Data/Data+Writing.swift index c86f27bc3..7150157aa 100644 --- a/Sources/FoundationEssentials/Data/Data+Writing.swift +++ b/Sources/FoundationEssentials/Data/Data+Writing.swift @@ -27,6 +27,8 @@ import Glibc #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +import WASILibc #endif #if !NO_FILESYSTEM diff --git a/Sources/FoundationEssentials/Data/Data.swift b/Sources/FoundationEssentials/Data/Data.swift index a88478261..40fee4a2c 100644 --- a/Sources/FoundationEssentials/Data/Data.swift +++ b/Sources/FoundationEssentials/Data/Data.swift @@ -76,6 +76,8 @@ import Glibc import Musl #elseif canImport(ucrt) import ucrt +#elseif canImport(WASILibc) +import WASILibc #endif #if os(Windows) @@ -580,11 +582,11 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect @usableFromInline @frozen internal struct InlineData : Sendable { -#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le) +#if _pointerBitWidth(_64) @usableFromInline typealias Buffer = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) //len //enum @usableFromInline var bytes: Buffer -#elseif arch(i386) || arch(arm) || arch(arm64_32) +#elseif _pointerBitWidth(_32) @usableFromInline typealias Buffer = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) //len //enum @usableFromInline var bytes: Buffer @@ -615,9 +617,9 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect @inlinable // This is @inlinable as a trivial initializer. init(count: Int = 0) { assert(count <= MemoryLayout.size) -#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le) +#if _pointerBitWidth(_64) bytes = (UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0)) -#elseif arch(i386) || arch(arm) || arch(arm64_32) +#elseif _pointerBitWidth(_32) bytes = (UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0)) #else #error ("Unsupported architecture: initialization for Buffer is required for this architecture") @@ -802,9 +804,9 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect } } -#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le) +#if _pointerBitWidth(_64) @usableFromInline internal typealias HalfInt = Int32 -#elseif arch(i386) || arch(arm) || arch(arm64_32) +#elseif _pointerBitWidth(_32) @usableFromInline internal typealias HalfInt = Int16 #else #error ("Unsupported architecture: a definition of half of the pointer sized Int needs to be defined for this architecture") diff --git a/Sources/FoundationEssentials/Date.swift b/Sources/FoundationEssentials/Date.swift index 8811aa433..66e778c1c 100644 --- a/Sources/FoundationEssentials/Date.swift +++ b/Sources/FoundationEssentials/Date.swift @@ -18,6 +18,8 @@ import Bionic import Glibc #elseif canImport(WinSDK) import WinSDK +#elseif os(WASI) +import WASILibc #endif #if !FOUNDATION_FRAMEWORK diff --git a/Sources/FoundationEssentials/Decimal/Decimal+Math.swift b/Sources/FoundationEssentials/Decimal/Decimal+Math.swift index 172454463..38ef0cf5e 100644 --- a/Sources/FoundationEssentials/Decimal/Decimal+Math.swift +++ b/Sources/FoundationEssentials/Decimal/Decimal+Math.swift @@ -18,6 +18,8 @@ import Android import Glibc #elseif canImport(CRT) import CRT +#elseif os(WASI) +import WASILibc #endif private let powerOfTen: [Decimal.VariableLengthInteger] = [ diff --git a/Sources/FoundationEssentials/Error/CocoaError+FilePath.swift b/Sources/FoundationEssentials/Error/CocoaError+FilePath.swift index fbce0f6f8..3b60afe22 100644 --- a/Sources/FoundationEssentials/Error/CocoaError+FilePath.swift +++ b/Sources/FoundationEssentials/Error/CocoaError+FilePath.swift @@ -22,6 +22,8 @@ import Glibc #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +import WASILibc #endif extension CocoaError.Code { diff --git a/Sources/FoundationEssentials/Error/ErrorCodes+POSIX.swift b/Sources/FoundationEssentials/Error/ErrorCodes+POSIX.swift index d6977ba47..9fae00120 100644 --- a/Sources/FoundationEssentials/Error/ErrorCodes+POSIX.swift +++ b/Sources/FoundationEssentials/Error/ErrorCodes+POSIX.swift @@ -19,6 +19,8 @@ #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +import WASILibc #endif #if FOUNDATION_FRAMEWORK diff --git a/Sources/FoundationEssentials/FileManager/FileManager+Basics.swift b/Sources/FoundationEssentials/FileManager/FileManager+Basics.swift index 03ca025a2..d7a4de859 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Basics.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Basics.swift @@ -19,6 +19,8 @@ import Glibc #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +import WASILibc #endif #if os(Windows) diff --git a/Sources/FoundationEssentials/FileManager/FileManager+Directories.swift b/Sources/FoundationEssentials/FileManager/FileManager+Directories.swift index 89e3f1f97..a29b31195 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Directories.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Directories.swift @@ -26,6 +26,8 @@ import Glibc #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +import WASILibc #endif internal import _FoundationCShims diff --git a/Sources/FoundationEssentials/FileManager/FileManager+Files.swift b/Sources/FoundationEssentials/FileManager/FileManager+Files.swift index 99f1b32c6..5d7c4a2c0 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Files.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Files.swift @@ -26,6 +26,9 @@ internal import _FoundationCShims #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +internal import _FoundationCShims +import WASILibc #endif extension Date { diff --git a/Sources/FoundationEssentials/FileManager/FileManager+SymbolicLinks.swift b/Sources/FoundationEssentials/FileManager/FileManager+SymbolicLinks.swift index fc9b8f70e..92eb3accb 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+SymbolicLinks.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+SymbolicLinks.swift @@ -21,6 +21,8 @@ import Glibc import CRT import WinSDK internal import _FoundationCShims +#elseif os(WASI) +import WASILibc #endif extension _FileManagerImpl { diff --git a/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift b/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift index 45f498937..870e453ff 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift @@ -31,6 +31,8 @@ internal import _FoundationCShims #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +import WASILibc #endif #if os(Windows) diff --git a/Sources/FoundationEssentials/FileManager/FileOperations.swift b/Sources/FoundationEssentials/FileManager/FileOperations.swift index 51f4f7ea4..d3fc7b2d5 100644 --- a/Sources/FoundationEssentials/FileManager/FileOperations.swift +++ b/Sources/FoundationEssentials/FileManager/FileOperations.swift @@ -19,6 +19,8 @@ import Glibc #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +import WASILibc #endif #if FOUNDATION_FRAMEWORK diff --git a/Sources/FoundationEssentials/Formatting/BinaryInteger+NumericStringRepresentation.swift b/Sources/FoundationEssentials/Formatting/BinaryInteger+NumericStringRepresentation.swift index db02789e2..b9a60576a 100644 --- a/Sources/FoundationEssentials/Formatting/BinaryInteger+NumericStringRepresentation.swift +++ b/Sources/FoundationEssentials/Formatting/BinaryInteger+NumericStringRepresentation.swift @@ -18,6 +18,8 @@ import Android import Glibc #elseif os(Windows) import CRT +#elseif os(WASI) +import WASILibc #endif // MARK: - BinaryInteger + Numeric string representation diff --git a/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift b/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift index a3e6b6c60..bd3f042fe 100644 --- a/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift +++ b/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift @@ -21,6 +21,8 @@ import unistd import Glibc #elseif os(Windows) import WinSDK +#elseif os(WASI) +import WASILibc #endif #if !NO_PROCESS diff --git a/Sources/FoundationEssentials/PropertyList/OpenStepPlist.swift b/Sources/FoundationEssentials/PropertyList/OpenStepPlist.swift index 174a6edda..42982deeb 100644 --- a/Sources/FoundationEssentials/PropertyList/OpenStepPlist.swift +++ b/Sources/FoundationEssentials/PropertyList/OpenStepPlist.swift @@ -16,6 +16,8 @@ import Darwin import Bionic #elseif canImport(Glibc) import Glibc +#elseif os(WASI) +import WASILibc #endif #if canImport(CRT) diff --git a/Sources/FoundationEssentials/String/String+Path.swift b/Sources/FoundationEssentials/String/String+Path.swift index 5e42aef4c..5d4207fb4 100644 --- a/Sources/FoundationEssentials/String/String+Path.swift +++ b/Sources/FoundationEssentials/String/String+Path.swift @@ -18,6 +18,8 @@ import Android import Glibc #elseif os(Windows) import WinSDK +#elseif os(WASI) +import WASILibc #endif internal import _FoundationCShims diff --git a/Sources/FoundationInternationalization/Calendar/Calendar_ICU.swift b/Sources/FoundationInternationalization/Calendar/Calendar_ICU.swift index 0f0b97386..86d1d8d6d 100644 --- a/Sources/FoundationInternationalization/Calendar/Calendar_ICU.swift +++ b/Sources/FoundationInternationalization/Calendar/Calendar_ICU.swift @@ -22,6 +22,8 @@ import Glibc import CRT #elseif canImport(Darwin) import Darwin +#elseif os(WASI) +import WASILibc #endif internal import _FoundationICU diff --git a/Sources/FoundationInternationalization/Formatting/Duration+Formatting.swift b/Sources/FoundationInternationalization/Formatting/Duration+Formatting.swift index ccfea7873..41d14b388 100644 --- a/Sources/FoundationInternationalization/Formatting/Duration+Formatting.swift +++ b/Sources/FoundationInternationalization/Formatting/Duration+Formatting.swift @@ -22,6 +22,8 @@ import Android import Glibc #elseif os(Windows) import CRT +#elseif os(WASI) +import WASILibc #endif @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) From fafdf905c4b70652d3a1573ed0e537135c52d891 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 6 Aug 2024 02:20:54 +0900 Subject: [PATCH 12/12] Enable wasi-libc emulation features (#777) * Enable wasi-libc emulation features Those features require explicit macro definitions to be enabled, so add them to the package definition. Only affects WASI builds. * Prefer `TARGET_OS_WASI` over `__wasi__` And explain why we need definition checks for `signal.h` and `sys/mman.h` (cherry picked from commit c86692f7e7b6d7cb1625d66ee6ff2618011f22f1) --- CMakeLists.txt | 8 +++++ Package.swift | 35 ++++++++++++++----- Sources/FoundationEssentials/CMakeLists.txt | 1 + .../CMakeLists.txt | 1 + Sources/_FoundationCShims/include/_CStdlib.h | 16 ++++++++- 5 files changed, 51 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b99fcfa8f..2afb0787e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,6 +104,14 @@ foreach(version ${_SwiftFoundation_versions}) endforeach() endforeach() +# wasi-libc emulation feature flags +set(_SwiftFoundation_wasi_libc_flags) +if(CMAKE_SYSTEM_NAME STREQUAL "WASI") + list(APPEND _SwiftFoundation_wasi_libc_flags + "SHELL:$<$:-Xcc -D_WASI_EMULATED_SIGNAL>" + "SHELL:$<$:-Xcc -D_WASI_EMULATED_MMAN>") +endif() + include(GNUInstallDirs) include(SwiftFoundationSwiftSupport) diff --git a/Package.swift b/Package.swift index 70f77537b..6477b7ffb 100644 --- a/Package.swift +++ b/Package.swift @@ -70,6 +70,11 @@ var dependencies: [Package.Dependency] { } } +let wasiLibcCSettings: [CSetting] = [ + .define("_WASI_EMULATED_SIGNAL", .when(platforms: [.wasi])), + .define("_WASI_EMULATED_MMAN", .when(platforms: [.wasi])), +] + let package = Package( name: "FoundationPreview", platforms: [.macOS("13.3"), .iOS("16.4"), .tvOS("16.4"), .watchOS("9.4")], @@ -91,15 +96,23 @@ let package = Package( path: "Sources/Foundation"), // _FoundationCShims (Internal) - .target(name: "_FoundationCShims", - cSettings: [.define("_CRT_SECURE_NO_WARNINGS", - .when(platforms: [.windows]))]), + .target( + name: "_FoundationCShims", + cSettings: [ + .define("_CRT_SECURE_NO_WARNINGS", .when(platforms: [.windows])) + ] + wasiLibcCSettings + ), // TestSupport (Internal) - .target(name: "TestSupport", dependencies: [ - "FoundationEssentials", - "FoundationInternationalization", - ], swiftSettings: availabilityMacros + concurrencyChecking), + .target( + name: "TestSupport", + dependencies: [ + "FoundationEssentials", + "FoundationInternationalization", + ], + cSettings: wasiLibcCSettings, + swiftSettings: availabilityMacros + concurrencyChecking + ), // FoundationEssentials .target( @@ -130,11 +143,14 @@ let package = Package( ], cSettings: [ .define("_GNU_SOURCE", .when(platforms: [.linux])) - ], + ] + wasiLibcCSettings, swiftSettings: [ .enableExperimentalFeature("VariadicGenerics"), .enableExperimentalFeature("AccessLevelOnImport") - ] + availabilityMacros + concurrencyChecking + ] + availabilityMacros + concurrencyChecking, + linkerSettings: [ + .linkedLibrary("wasi-emulated-getpid", .when(platforms: [.wasi])), + ] ), .testTarget( name: "FoundationEssentialsTests", @@ -166,6 +182,7 @@ let package = Package( "CMakeLists.txt", "Predicate/CMakeLists.txt" ], + cSettings: wasiLibcCSettings, swiftSettings: [ .enableExperimentalFeature("AccessLevelOnImport") ] + availabilityMacros + concurrencyChecking diff --git a/Sources/FoundationEssentials/CMakeLists.txt b/Sources/FoundationEssentials/CMakeLists.txt index d9ebc2ebb..7cddff682 100644 --- a/Sources/FoundationEssentials/CMakeLists.txt +++ b/Sources/FoundationEssentials/CMakeLists.txt @@ -66,6 +66,7 @@ target_compile_options(FoundationEssentials PRIVATE "SHELL:$<$:-Xfrontend -enable-experimental-feature -Xfrontend StrictConcurrency>" "SHELL:$<$:-Xfrontend -enable-upcoming-feature -Xfrontend InferSendableFromCaptures>") target_compile_options(FoundationEssentials PRIVATE ${_SwiftFoundation_availability_macros}) +target_compile_options(FoundationEssentials PRIVATE ${_SwiftFoundation_wasi_libc_flags}) target_compile_options(FoundationEssentials PRIVATE -package-name "SwiftFoundation") target_link_libraries(FoundationEssentials PUBLIC diff --git a/Sources/FoundationInternationalization/CMakeLists.txt b/Sources/FoundationInternationalization/CMakeLists.txt index 5a89ceb88..857db9cac 100644 --- a/Sources/FoundationInternationalization/CMakeLists.txt +++ b/Sources/FoundationInternationalization/CMakeLists.txt @@ -33,6 +33,7 @@ target_compile_options(FoundationInternationalization PRIVATE "SHELL:$<$:-Xfrontend -enable-experimental-feature -Xfrontend StrictConcurrency>" "SHELL:$<$:-Xfrontend -enable-upcoming-feature -Xfrontend InferSendableFromCaptures>") target_compile_options(FoundationInternationalization PRIVATE ${_SwiftFoundation_availability_macros}) +target_compile_options(FoundationInternationalization PRIVATE ${_SwiftFoundation_wasi_libc_flags}) target_compile_options(FoundationInternationalization PRIVATE -package-name "SwiftFoundation") target_link_libraries(FoundationInternationalization PUBLIC diff --git a/Sources/_FoundationCShims/include/_CStdlib.h b/Sources/_FoundationCShims/include/_CStdlib.h index 8967eb7f4..03373934b 100644 --- a/Sources/_FoundationCShims/include/_CStdlib.h +++ b/Sources/_FoundationCShims/include/_CStdlib.h @@ -60,7 +60,21 @@ #endif #if __has_include() -#include +/// Guard against including `signal.h` on WASI. The `signal.h` header file +/// itself is available in wasi-libc, but it's just a stub that doesn't actually +/// do anything. And also including it requires a special macro definition +/// (`_WASI_EMULATED_SIGNAL`) and it causes compilation errors without the macro. +# if !TARGET_OS_WASI || defined(_WASI_EMULATED_SIGNAL) +# include +# endif +#endif + +#if __has_include() +/// Similar to `signal.h`, guard against including `sys/mman.h` on WASI unless +/// `_WASI_EMULATED_MMAN` is enabled. +# if !TARGET_OS_WASI || defined(_WASI_EMULATED_MMAN) +# include +# endif #endif #if __has_include()