diff --git a/CMakeLists.txt b/CMakeLists.txt index a8dc410cf..3243e5398 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,6 +116,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 daaf6389b..67bce500a 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 6dc5929d4..5525efec6 100644 --- a/Sources/FoundationEssentials/CMakeLists.txt +++ b/Sources/FoundationEssentials/CMakeLists.txt @@ -65,6 +65,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/FoundationEssentials/Calendar/Calendar.swift b/Sources/FoundationEssentials/Calendar/Calendar.swift index 9de55b372..257b742ec 100644 --- a/Sources/FoundationEssentials/Calendar/Calendar.swift +++ b/Sources/FoundationEssentials/Calendar/Calendar.swift @@ -20,6 +20,8 @@ import Glibc import Musl #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 797a8e8b7..8c25c77f6 100644 --- a/Sources/FoundationEssentials/Calendar/Calendar_Gregorian.swift +++ b/Sources/FoundationEssentials/Calendar/Calendar_Gregorian.swift @@ -20,6 +20,8 @@ import Glibc import Musl #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 2540b14eb..48b95214d 100644 --- a/Sources/FoundationEssentials/Data/Data+Reading.swift +++ b/Sources/FoundationEssentials/Data/Data+Reading.swift @@ -27,6 +27,8 @@ import Musl #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 1e75b43cf..0256e51ef 100644 --- a/Sources/FoundationEssentials/Data/Data+Writing.swift +++ b/Sources/FoundationEssentials/Data/Data+Writing.swift @@ -29,6 +29,8 @@ import Musl #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +import WASILibc #endif #if !NO_FILESYSTEM @@ -129,6 +131,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("/") @@ -183,6 +189,7 @@ private func createTemporaryFile(at destinationPath: String, inPath: PathOrURL, } } } while true +#endif // os(WASI) } /// Returns `(file descriptor, temporary file path, temporary directory path)` @@ -516,6 +523,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 @@ -539,6 +547,7 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint fchmod(fd, mode) #endif } +#endif // os(WASI) } } } diff --git a/Sources/FoundationEssentials/Data/Data.swift b/Sources/FoundationEssentials/Data/Data.swift index 8bded856a..ad3ac42e9 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 b65066f14..37548e498 100644 --- a/Sources/FoundationEssentials/Date.swift +++ b/Sources/FoundationEssentials/Date.swift @@ -20,6 +20,8 @@ import Glibc import Musl #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 7b35f1189..eb344b214 100644 --- a/Sources/FoundationEssentials/Decimal/Decimal+Math.swift +++ b/Sources/FoundationEssentials/Decimal/Decimal+Math.swift @@ -20,6 +20,8 @@ import Glibc import Musl #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 d9b249761..586c781c3 100644 --- a/Sources/FoundationEssentials/Error/CocoaError+FilePath.swift +++ b/Sources/FoundationEssentials/Error/CocoaError+FilePath.swift @@ -24,6 +24,8 @@ import Musl #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 048cd29b2..e1bfffa3f 100644 --- a/Sources/FoundationEssentials/Error/ErrorCodes+POSIX.swift +++ b/Sources/FoundationEssentials/Error/ErrorCodes+POSIX.swift @@ -21,6 +21,8 @@ #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +import WASILibc #endif #if FOUNDATION_FRAMEWORK @@ -467,11 +469,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. diff --git a/Sources/FoundationEssentials/FileManager/FileManager+Basics.swift b/Sources/FoundationEssentials/FileManager/FileManager+Basics.swift index 991c5e817..9896b35a4 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Basics.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Basics.swift @@ -21,6 +21,8 @@ import Musl #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 0941e5186..f8375b382 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Directories.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Directories.swift @@ -28,6 +28,8 @@ import Musl #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 b8cd50a4c..9cd97525a 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Files.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Files.swift @@ -29,6 +29,9 @@ internal import _FoundationCShims #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +internal import _FoundationCShims +import WASILibc #endif extension Date { @@ -471,7 +474,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), @@ -494,7 +497,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) @@ -648,10 +651,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 { @@ -713,6 +717,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 { @@ -928,6 +935,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 @@ -935,12 +946,18 @@ extension _FileManagerImpl { if chown(fileSystemRepresentation, rawUserID, rawGroupID) != 0 { throw CocoaError.errorWithFilePath(path, errno: errno, reading: false) } + #endif } 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+SymbolicLinks.swift b/Sources/FoundationEssentials/FileManager/FileManager+SymbolicLinks.swift index a1355e78d..12d32e578 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+SymbolicLinks.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+SymbolicLinks.swift @@ -23,6 +23,8 @@ import Musl 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 9bac9676f..036f50cc5 100644 --- a/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift +++ b/Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift @@ -34,6 +34,8 @@ internal import _FoundationCShims #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +import WASILibc #endif #if os(Windows) @@ -176,7 +178,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) @@ -274,7 +276,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 03adcc6fa..14c6fd81d 100644 --- a/Sources/FoundationEssentials/FileManager/FileOperations.swift +++ b/Sources/FoundationEssentials/FileManager/FileOperations.swift @@ -21,6 +21,8 @@ import Musl #elseif os(Windows) import CRT import WinSDK +#elseif os(WASI) +import WASILibc #endif #if FOUNDATION_FRAMEWORK @@ -866,12 +868,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 @@ -882,12 +886,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 diff --git a/Sources/FoundationEssentials/Formatting/BinaryInteger+NumericStringRepresentation.swift b/Sources/FoundationEssentials/Formatting/BinaryInteger+NumericStringRepresentation.swift index 43e9fcdaa..663509deb 100644 --- a/Sources/FoundationEssentials/Formatting/BinaryInteger+NumericStringRepresentation.swift +++ b/Sources/FoundationEssentials/Formatting/BinaryInteger+NumericStringRepresentation.swift @@ -20,6 +20,8 @@ import Glibc import Musl #elseif os(Windows) import CRT +#elseif os(WASI) +import WASILibc #endif // MARK: - BinaryInteger + Numeric string representation diff --git a/Sources/FoundationEssentials/LockedState.swift b/Sources/FoundationEssentials/LockedState.swift index 6eb9ad840..4e6aefa8a 100644 --- a/Sources/FoundationEssentials/LockedState.swift +++ b/Sources/FoundationEssentials/LockedState.swift @@ -35,6 +35,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 @@ -47,6 +50,8 @@ package struct LockedState { pthread_mutex_init(platformLock, nil) #elseif canImport(WinSDK) InitializeSRWLock(platformLock) +#elseif os(WASI) + // no-op #endif } @@ -64,6 +69,8 @@ package struct LockedState { pthread_mutex_lock(platformLock) #elseif canImport(WinSDK) AcquireSRWLockExclusive(platformLock) +#elseif os(WASI) + // no-op #endif } @@ -74,6 +81,8 @@ package struct LockedState { pthread_mutex_unlock(platformLock) #elseif canImport(WinSDK) ReleaseSRWLockExclusive(platformLock) +#elseif os(WASI) + // no-op #endif } } diff --git a/Sources/FoundationEssentials/Platform.swift b/Sources/FoundationEssentials/Platform.swift index 9c3f2d7a3..4549a4524 100644 --- a/Sources/FoundationEssentials/Platform.swift +++ b/Sources/FoundationEssentials/Platform.swift @@ -114,7 +114,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) { @@ -175,7 +175,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/ProcessInfo/ProcessInfo.swift b/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift index 2e809fa70..eb55c273d 100644 --- a/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift +++ b/Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift @@ -23,6 +23,8 @@ import Glibc import Musl #elseif os(Windows) import WinSDK +#elseif os(WASI) +import WASILibc #endif #if !NO_PROCESS @@ -391,7 +393,7 @@ extension _ProcessInfo { patch: Int(osVersionInfo.dwBuildNumber) ) #else - return OperatingSystemVersion(majorVersion: -1, minorVersion: 0, patchVersion: 0) + return (major: -1, minor: 0, patch: 0) #endif } diff --git a/Sources/FoundationEssentials/PropertyList/OpenStepPlist.swift b/Sources/FoundationEssentials/PropertyList/OpenStepPlist.swift index c0428202d..a484557c2 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 #elseif canImport(Musl) import Musl #endif diff --git a/Sources/FoundationEssentials/String/String+Path.swift b/Sources/FoundationEssentials/String/String+Path.swift index 477d5d39b..7047b9be8 100644 --- a/Sources/FoundationEssentials/String/String+Path.swift +++ b/Sources/FoundationEssentials/String/String+Path.swift @@ -20,6 +20,8 @@ import Glibc import Musl #elseif os(Windows) import WinSDK +#elseif os(WASI) +import WASILibc #endif internal import _FoundationCShims @@ -452,6 +454,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 { @@ -465,6 +468,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") { diff --git a/Sources/FoundationEssentials/_ThreadLocal.swift b/Sources/FoundationEssentials/_ThreadLocal.swift index aea9c4116..ffe010c93 100644 --- a/Sources/FoundationEssentials/_ThreadLocal.swift +++ b/Sources/FoundationEssentials/_ThreadLocal.swift @@ -32,6 +32,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 { @@ -48,6 +50,8 @@ struct _ThreadLocal { self.key = key #elseif canImport(WinSDK) key = FlsAlloc(nil) +#elseif os(WASI) + key = UnsafeMutablePointer.allocate(capacity: 1) #endif } } @@ -60,6 +64,8 @@ struct _ThreadLocal { tss_get(key) #elseif canImport(WinSDK) FlsGetValue(key) +#elseif os(WASI) + key.pointee #endif } @@ -70,6 +76,8 @@ struct _ThreadLocal { tss_set(key, newValue) #elseif canImport(WinSDK) FlsSetValue(key, newValue) +#elseif os(WASI) + key.pointee = newValue #endif } } 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/FoundationInternationalization/Calendar/Calendar_ICU.swift b/Sources/FoundationInternationalization/Calendar/Calendar_ICU.swift index 0d3c3710a..01895b8b9 100644 --- a/Sources/FoundationInternationalization/Calendar/Calendar_ICU.swift +++ b/Sources/FoundationInternationalization/Calendar/Calendar_ICU.swift @@ -24,6 +24,8 @@ import Musl 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 a94f57161..dfe2fad10 100644 --- a/Sources/FoundationInternationalization/Formatting/Duration+Formatting.swift +++ b/Sources/FoundationInternationalization/Formatting/Duration+Formatting.swift @@ -24,6 +24,8 @@ import Glibc import Musl #elseif os(Windows) import CRT +#elseif os(WASI) +import WASILibc #endif @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) 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() 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 5a400a468..556bc9416 100644 --- a/Sources/_FoundationCShims/platform_shims.c +++ b/Sources/_FoundationCShims/platform_shims.c @@ -21,21 +21,25 @@ extern char **environ; #endif +#if __wasi__ +#include // for __wasilibc_get_environ +#endif + #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) @@ -48,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(); }