Skip to content

Commit b0abbb4

Browse files
MaxDesiatovkateinoigakukun
andauthoredAug 29, 2024
6.0: Fix WASI support (#825)
* Add explicit include of `wasi/libc-environ.h` (#786) This is necessary to get the `__wasilibc_get_environ` function declaration. (cherry picked from commit 243066f) * 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 8f34f38) * Exclude EREMOTE definition for WASI platform (#778) WASI does not define the EREMOTE error code. (cherry picked from commit 6bb5ff7) * 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 fb11420) * 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 a8f1225) * 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 fab7195) * 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 0b3974d) * 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 e90b6c3) * 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 2a6afeb) * Port `LockedState` and `_ThreadLocal` to WASI without any locking (#780) (cherry picked from commit aa68eeb) * 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 c82d167) * 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 c86692f) --------- Co-authored-by: Yuta Saito <[email protected]>
1 parent 720a093 commit b0abbb4

31 files changed

+189
-38
lines changed
 

‎CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,14 @@ foreach(version ${_SwiftFoundation_versions})
116116
endforeach()
117117
endforeach()
118118

119+
# wasi-libc emulation feature flags
120+
set(_SwiftFoundation_wasi_libc_flags)
121+
if(CMAKE_SYSTEM_NAME STREQUAL "WASI")
122+
list(APPEND _SwiftFoundation_wasi_libc_flags
123+
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xcc -D_WASI_EMULATED_SIGNAL>"
124+
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xcc -D_WASI_EMULATED_MMAN>")
125+
endif()
126+
119127
include(GNUInstallDirs)
120128
include(SwiftFoundationSwiftSupport)
121129

‎Package.swift

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ var dependencies: [Package.Dependency] {
7070
}
7171
}
7272

73+
let wasiLibcCSettings: [CSetting] = [
74+
.define("_WASI_EMULATED_SIGNAL", .when(platforms: [.wasi])),
75+
.define("_WASI_EMULATED_MMAN", .when(platforms: [.wasi])),
76+
]
77+
7378
let package = Package(
7479
name: "FoundationPreview",
7580
platforms: [.macOS("13.3"), .iOS("16.4"), .tvOS("16.4"), .watchOS("9.4")],
@@ -91,15 +96,23 @@ let package = Package(
9196
path: "Sources/Foundation"),
9297

9398
// _FoundationCShims (Internal)
94-
.target(name: "_FoundationCShims",
95-
cSettings: [.define("_CRT_SECURE_NO_WARNINGS",
96-
.when(platforms: [.windows]))]),
99+
.target(
100+
name: "_FoundationCShims",
101+
cSettings: [
102+
.define("_CRT_SECURE_NO_WARNINGS", .when(platforms: [.windows]))
103+
] + wasiLibcCSettings
104+
),
97105

98106
// TestSupport (Internal)
99-
.target(name: "TestSupport", dependencies: [
100-
"FoundationEssentials",
101-
"FoundationInternationalization",
102-
], swiftSettings: availabilityMacros + concurrencyChecking),
107+
.target(
108+
name: "TestSupport",
109+
dependencies: [
110+
"FoundationEssentials",
111+
"FoundationInternationalization",
112+
],
113+
cSettings: wasiLibcCSettings,
114+
swiftSettings: availabilityMacros + concurrencyChecking
115+
),
103116

104117
// FoundationEssentials
105118
.target(
@@ -130,11 +143,14 @@ let package = Package(
130143
],
131144
cSettings: [
132145
.define("_GNU_SOURCE", .when(platforms: [.linux]))
133-
],
146+
] + wasiLibcCSettings,
134147
swiftSettings: [
135148
.enableExperimentalFeature("VariadicGenerics"),
136149
.enableExperimentalFeature("AccessLevelOnImport")
137-
] + availabilityMacros + concurrencyChecking
150+
] + availabilityMacros + concurrencyChecking,
151+
linkerSettings: [
152+
.linkedLibrary("wasi-emulated-getpid", .when(platforms: [.wasi])),
153+
]
138154
),
139155
.testTarget(
140156
name: "FoundationEssentialsTests",
@@ -166,6 +182,7 @@ let package = Package(
166182
"CMakeLists.txt",
167183
"Predicate/CMakeLists.txt"
168184
],
185+
cSettings: wasiLibcCSettings,
169186
swiftSettings: [
170187
.enableExperimentalFeature("AccessLevelOnImport")
171188
] + availabilityMacros + concurrencyChecking

‎Sources/FoundationEssentials/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ target_compile_options(FoundationEssentials PRIVATE
6565
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xfrontend -enable-experimental-feature -Xfrontend StrictConcurrency>"
6666
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xfrontend -enable-upcoming-feature -Xfrontend InferSendableFromCaptures>")
6767
target_compile_options(FoundationEssentials PRIVATE ${_SwiftFoundation_availability_macros})
68+
target_compile_options(FoundationEssentials PRIVATE ${_SwiftFoundation_wasi_libc_flags})
6869
target_compile_options(FoundationEssentials PRIVATE -package-name "SwiftFoundation")
6970

7071
target_link_libraries(FoundationEssentials PUBLIC

‎Sources/FoundationEssentials/Calendar/Calendar.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import Glibc
2020
import Musl
2121
#elseif canImport(CRT)
2222
import CRT
23+
#elseif os(WASI)
24+
import WASILibc
2325
#endif
2426

2527
#if FOUNDATION_FRAMEWORK

‎Sources/FoundationEssentials/Calendar/Calendar_Gregorian.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import Glibc
2020
import Musl
2121
#elseif canImport(CRT)
2222
import CRT
23+
#elseif os(WASI)
24+
import WASILibc
2325
#endif
2426

2527

‎Sources/FoundationEssentials/Data/Data+Reading.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import Musl
2727
#elseif os(Windows)
2828
import CRT
2929
import WinSDK
30+
#elseif os(WASI)
31+
import WASILibc
3032
#endif
3133

3234
func _fgetxattr(_ fd: Int32, _ name: UnsafePointer<CChar>!, _ value: UnsafeMutableRawPointer!, _ size: Int, _ position: UInt32, _ options: Int32) -> Int {

‎Sources/FoundationEssentials/Data/Data+Writing.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import Musl
2929
#elseif os(Windows)
3030
import CRT
3131
import WinSDK
32+
#elseif os(WASI)
33+
import WASILibc
3234
#endif
3335

3436
#if !NO_FILESYSTEM
@@ -129,6 +131,10 @@ private func cleanupTemporaryDirectory(at inPath: String?) {
129131

130132
/// Caller is responsible for calling `close` on the `Int32` file descriptor.
131133
private func createTemporaryFile(at destinationPath: String, inPath: PathOrURL, prefix: String, options: Data.WritingOptions) throws -> (Int32, String) {
134+
#if os(WASI)
135+
// WASI does not have temp directories
136+
throw CocoaError(.featureUnsupported)
137+
#else
132138
var directoryPath = destinationPath
133139
if !directoryPath.isEmpty && directoryPath.last! != "/" {
134140
directoryPath.append("/")
@@ -183,6 +189,7 @@ private func createTemporaryFile(at destinationPath: String, inPath: PathOrURL,
183189
}
184190
}
185191
} while true
192+
#endif // os(WASI)
186193
}
187194

188195
/// Returns `(file descriptor, temporary file path, temporary directory path)`
@@ -516,6 +523,7 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
516523

517524
cleanupTemporaryDirectory(at: temporaryDirectoryPath)
518525

526+
#if !os(WASI) // WASI does not support fchmod for now
519527
if let mode {
520528
// Try to change the mode if the path has not changed. Do our best, but don't report an error.
521529
#if FOUNDATION_FRAMEWORK
@@ -539,6 +547,7 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
539547
fchmod(fd, mode)
540548
#endif
541549
}
550+
#endif // os(WASI)
542551
}
543552
}
544553
}

‎Sources/FoundationEssentials/Data/Data.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ import Glibc
7676
import Musl
7777
#elseif canImport(ucrt)
7878
import ucrt
79+
#elseif canImport(WASILibc)
80+
import WASILibc
7981
#endif
8082

8183
#if os(Windows)
@@ -580,11 +582,11 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect
580582
@usableFromInline
581583
@frozen
582584
internal struct InlineData : Sendable {
583-
#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le)
585+
#if _pointerBitWidth(_64)
584586
@usableFromInline typealias Buffer = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
585587
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) //len //enum
586588
@usableFromInline var bytes: Buffer
587-
#elseif arch(i386) || arch(arm) || arch(arm64_32)
589+
#elseif _pointerBitWidth(_32)
588590
@usableFromInline typealias Buffer = (UInt8, UInt8, UInt8, UInt8,
589591
UInt8, UInt8) //len //enum
590592
@usableFromInline var bytes: Buffer
@@ -615,9 +617,9 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect
615617
@inlinable // This is @inlinable as a trivial initializer.
616618
init(count: Int = 0) {
617619
assert(count <= MemoryLayout<Buffer>.size)
618-
#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le)
620+
#if _pointerBitWidth(_64)
619621
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))
620-
#elseif arch(i386) || arch(arm) || arch(arm64_32)
622+
#elseif _pointerBitWidth(_32)
621623
bytes = (UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0))
622624
#else
623625
#error ("Unsupported architecture: initialization for Buffer is required for this architecture")
@@ -802,9 +804,9 @@ public struct Data : Equatable, Hashable, RandomAccessCollection, MutableCollect
802804
}
803805
}
804806

805-
#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le)
807+
#if _pointerBitWidth(_64)
806808
@usableFromInline internal typealias HalfInt = Int32
807-
#elseif arch(i386) || arch(arm) || arch(arm64_32)
809+
#elseif _pointerBitWidth(_32)
808810
@usableFromInline internal typealias HalfInt = Int16
809811
#else
810812
#error ("Unsupported architecture: a definition of half of the pointer sized Int needs to be defined for this architecture")

‎Sources/FoundationEssentials/Date.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import Glibc
2020
import Musl
2121
#elseif canImport(WinSDK)
2222
import WinSDK
23+
#elseif os(WASI)
24+
import WASILibc
2325
#endif
2426

2527
#if !FOUNDATION_FRAMEWORK

‎Sources/FoundationEssentials/Decimal/Decimal+Math.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import Glibc
2020
import Musl
2121
#elseif canImport(CRT)
2222
import CRT
23+
#elseif os(WASI)
24+
import WASILibc
2325
#endif
2426

2527
private let powerOfTen: [Decimal.VariableLengthInteger] = [

‎Sources/FoundationEssentials/Error/CocoaError+FilePath.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import Musl
2424
#elseif os(Windows)
2525
import CRT
2626
import WinSDK
27+
#elseif os(WASI)
28+
import WASILibc
2729
#endif
2830

2931
extension CocoaError.Code {

‎Sources/FoundationEssentials/Error/ErrorCodes+POSIX.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#elseif os(Windows)
2222
import CRT
2323
import WinSDK
24+
#elseif os(WASI)
25+
import WASILibc
2426
#endif
2527

2628
#if FOUNDATION_FRAMEWORK
@@ -467,11 +469,13 @@ extension POSIXError {
467469
return .ESTALE
468470
}
469471

472+
#if !os(WASI)
470473
/// Too many levels of remote in path.
471474
public static var EREMOTE: POSIXErrorCode {
472475
return .EREMOTE
473476
}
474477
#endif
478+
#endif
475479

476480
#if canImport(Darwin)
477481
/// RPC struct is bad.

‎Sources/FoundationEssentials/FileManager/FileManager+Basics.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import Musl
2121
#elseif os(Windows)
2222
import CRT
2323
import WinSDK
24+
#elseif os(WASI)
25+
import WASILibc
2426
#endif
2527

2628
#if os(Windows)

‎Sources/FoundationEssentials/FileManager/FileManager+Directories.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import Musl
2828
#elseif os(Windows)
2929
import CRT
3030
import WinSDK
31+
#elseif os(WASI)
32+
import WASILibc
3133
#endif
3234

3335
internal import _FoundationCShims

‎Sources/FoundationEssentials/FileManager/FileManager+Files.swift

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ internal import _FoundationCShims
2929
#elseif os(Windows)
3030
import CRT
3131
import WinSDK
32+
#elseif os(WASI)
33+
internal import _FoundationCShims
34+
import WASILibc
3235
#endif
3336

3437
extension Date {
@@ -471,7 +474,7 @@ extension _FileManagerImpl {
471474
parent = fileManager.currentDirectoryPath
472475
}
473476

474-
#if os(Windows)
477+
#if os(Windows) || os(WASI)
475478
return fileManager.isWritableFile(atPath: parent) && fileManager.isWritableFile(atPath: path)
476479
#else
477480
guard fileManager.isWritableFile(atPath: parent),
@@ -494,7 +497,7 @@ extension _FileManagerImpl {
494497
#endif
495498
}
496499

497-
#if !os(Windows)
500+
#if !os(Windows) && !os(WASI)
498501
private func _extendedAttribute(_ key: UnsafePointer<CChar>, at path: UnsafePointer<CChar>, followSymlinks: Bool) throws -> Data? {
499502
#if canImport(Darwin)
500503
var size = getxattr(path, key, nil, 0, 0, followSymlinks ? 0 : XATTR_NOFOLLOW)
@@ -648,10 +651,11 @@ extension _FileManagerImpl {
648651

649652
var attributes = statAtPath.fileAttributes
650653
try? Self._catInfo(for: URL(filePath: path, directoryHint: .isDirectory), statInfo: statAtPath, into: &attributes)
651-
654+
#if !os(WASI) // WASI does not support extended attributes
652655
if let extendedAttrs = try? _extendedAttributes(at: fsRep, followSymlinks: false) {
653656
attributes[._extendedAttributes] = extendedAttrs
654657
}
658+
#endif
655659

656660
#if !targetEnvironment(simulator) && FOUNDATION_FRAMEWORK
657661
if statAtPath.isRegular || statAtPath.isDirectory {
@@ -713,6 +717,9 @@ extension _FileManagerImpl {
713717
]
714718
}
715719
}
720+
#elseif os(WASI)
721+
// WASI does not support file system attributes
722+
return [:]
716723
#else
717724
try fileManager.withFileSystemRepresentation(for: path) { rep in
718725
guard let rep else {
@@ -928,19 +935,29 @@ extension _FileManagerImpl {
928935
let groupID = _readFileAttributePrimitive(attributes[.groupOwnerAccountID], as: UInt.self)
929936

930937
if user != nil || userID != nil || group != nil || groupID != nil {
938+
#if os(WASI)
939+
// WASI does not have the concept of users or groups
940+
throw CocoaError.errorWithFilePath(.featureUnsupported, path)
941+
#else
931942
// Bias toward userID & groupID - try to prevent round trips to getpwnam if possible.
932943
var leaveUnchanged: UInt32 { UInt32(bitPattern: -1) }
933944
let rawUserID = userID.flatMap(uid_t.init) ?? user.flatMap(Self._userAccountNameToNumber) ?? leaveUnchanged
934945
let rawGroupID = groupID.flatMap(gid_t.init) ?? group.flatMap(Self._groupAccountNameToNumber) ?? leaveUnchanged
935946
if chown(fileSystemRepresentation, rawUserID, rawGroupID) != 0 {
936947
throw CocoaError.errorWithFilePath(path, errno: errno, reading: false)
937948
}
949+
#endif
938950
}
939951

940952
try Self._setCatInfoAttributes(attributes, path: path)
941953

942954
if let extendedAttrs = attributes[.init("NSFileExtendedAttributes")] as? [String : Data] {
955+
#if os(WASI)
956+
// WASI does not support extended attributes
957+
throw CocoaError.errorWithFilePath(.featureUnsupported, path)
958+
#else
943959
try Self._setAttributes(extendedAttrs, at: fileSystemRepresentation, followSymLinks: false)
960+
#endif
944961
}
945962

946963
if let date = attributes[.modificationDate] as? Date {

‎Sources/FoundationEssentials/FileManager/FileManager+SymbolicLinks.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import Musl
2323
import CRT
2424
import WinSDK
2525
internal import _FoundationCShims
26+
#elseif os(WASI)
27+
import WASILibc
2628
#endif
2729

2830
extension _FileManagerImpl {

‎Sources/FoundationEssentials/FileManager/FileManager+Utilities.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ internal import _FoundationCShims
3434
#elseif os(Windows)
3535
import CRT
3636
import WinSDK
37+
#elseif os(WASI)
38+
import WASILibc
3739
#endif
3840

3941
#if os(Windows)
@@ -176,7 +178,7 @@ extension _FileManagerImpl {
176178
#endif
177179
}
178180

179-
#if !os(Windows)
181+
#if !os(Windows) && !os(WASI)
180182
static func _setAttribute(_ key: UnsafePointer<CChar>, value: Data, at path: UnsafePointer<CChar>, followSymLinks: Bool) throws {
181183
try value.withUnsafeBytes { buffer in
182184
#if canImport(Darwin)
@@ -274,7 +276,7 @@ extension _FileManagerImpl {
274276
}
275277
#endif
276278

277-
#if !os(Windows)
279+
#if !os(Windows) && !os(WASI)
278280
static func _userAccountNameToNumber(_ name: String) -> uid_t? {
279281
name.withCString { ptr in
280282
getpwnam(ptr)?.pointee.pw_uid

‎Sources/FoundationEssentials/FileManager/FileOperations.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import Musl
2121
#elseif os(Windows)
2222
import CRT
2323
import WinSDK
24+
#elseif os(WASI)
25+
import WASILibc
2426
#endif
2527

2628
#if FOUNDATION_FRAMEWORK
@@ -866,12 +868,14 @@ enum _FileOperations {
866868
}
867869
defer { close(dstfd) }
868870

871+
#if !os(WASI) // WASI doesn't have fchmod for now
869872
// Set the file permissions using fchmod() instead of when open()ing to avoid umask() issues
870873
let permissions = fileInfo.st_mode & ~S_IFMT
871874
guard fchmod(dstfd, permissions) == 0 else {
872875
try delegate.throwIfNecessary(errno, String(cString: srcPtr), String(cString: dstPtr))
873876
return
874877
}
878+
#endif
875879

876880
if fileInfo.st_size == 0 {
877881
// no copying required
@@ -882,12 +886,31 @@ enum _FileOperations {
882886
let chunkSize: Int = Int(fileInfo.st_blksize)
883887
var current: off_t = 0
884888

889+
#if os(WASI)
890+
// WASI doesn't have sendfile, so we need to do it in user space with read/write
891+
try withUnsafeTemporaryAllocation(of: UInt8.self, capacity: chunkSize) { buffer in
892+
while current < total {
893+
let readSize = Swift.min(total - Int(current), chunkSize)
894+
let bytesRead = read(srcfd, buffer.baseAddress, readSize)
895+
guard bytesRead >= 0 else {
896+
try delegate.throwIfNecessary(errno, String(cString: srcPtr), String(cString: dstPtr))
897+
return
898+
}
899+
guard write(dstfd, buffer.baseAddress, bytesRead) == bytesRead else {
900+
try delegate.throwIfNecessary(errno, String(cString: srcPtr), String(cString: dstPtr))
901+
return
902+
}
903+
current += off_t(bytesRead)
904+
}
905+
}
906+
#else
885907
while current < total {
886908
guard sendfile(dstfd, srcfd, &current, Swift.min(total - Int(current), chunkSize)) != -1 else {
887909
try delegate.throwIfNecessary(errno, String(cString: srcPtr), String(cString: dstPtr))
888910
return
889911
}
890912
}
913+
#endif
891914
}
892915
#endif
893916

‎Sources/FoundationEssentials/Formatting/BinaryInteger+NumericStringRepresentation.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import Glibc
2020
import Musl
2121
#elseif os(Windows)
2222
import CRT
23+
#elseif os(WASI)
24+
import WASILibc
2325
#endif
2426

2527
// MARK: - BinaryInteger + Numeric string representation

‎Sources/FoundationEssentials/LockedState.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ package struct LockedState<State> {
3535
typealias Primitive = pthread_mutex_t
3636
#elseif canImport(WinSDK)
3737
typealias Primitive = SRWLOCK
38+
#elseif os(WASI)
39+
// WASI is single-threaded, so we don't need a lock.
40+
typealias Primitive = Void
3841
#endif
3942

4043
typealias PlatformLock = UnsafeMutablePointer<Primitive>
@@ -47,6 +50,8 @@ package struct LockedState<State> {
4750
pthread_mutex_init(platformLock, nil)
4851
#elseif canImport(WinSDK)
4952
InitializeSRWLock(platformLock)
53+
#elseif os(WASI)
54+
// no-op
5055
#endif
5156
}
5257

@@ -64,6 +69,8 @@ package struct LockedState<State> {
6469
pthread_mutex_lock(platformLock)
6570
#elseif canImport(WinSDK)
6671
AcquireSRWLockExclusive(platformLock)
72+
#elseif os(WASI)
73+
// no-op
6774
#endif
6875
}
6976

@@ -74,6 +81,8 @@ package struct LockedState<State> {
7481
pthread_mutex_unlock(platformLock)
7582
#elseif canImport(WinSDK)
7683
ReleaseSRWLockExclusive(platformLock)
84+
#elseif os(WASI)
85+
// no-op
7786
#endif
7887
}
7988
}

‎Sources/FoundationEssentials/Platform.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ private let _cachedUGIDs: (uid_t, gid_t) = {
114114
}()
115115
#endif
116116

117-
#if !os(Windows)
117+
#if !os(Windows) && !os(WASI)
118118
extension Platform {
119119
private static var ROOT_USER: UInt32 { 0 }
120120
static func getUGIDs(allowEffectiveRootUID: Bool = true) -> (uid: UInt32, gid: UInt32) {
@@ -175,7 +175,7 @@ extension Platform {
175175
// FIXME: bionic implements this as `return 0;` and does not expose the
176176
// function via headers. We should be able to shim this and use the call
177177
// if it is available.
178-
#if !os(Android)
178+
#if !os(Android) && !os(WASI)
179179
guard issetugid() == 0 else { return nil }
180180
#endif
181181
if let value = getenv(name) {

‎Sources/FoundationEssentials/ProcessInfo/ProcessInfo.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import Glibc
2323
import Musl
2424
#elseif os(Windows)
2525
import WinSDK
26+
#elseif os(WASI)
27+
import WASILibc
2628
#endif
2729

2830
#if !NO_PROCESS
@@ -391,7 +393,7 @@ extension _ProcessInfo {
391393
patch: Int(osVersionInfo.dwBuildNumber)
392394
)
393395
#else
394-
return OperatingSystemVersion(majorVersion: -1, minorVersion: 0, patchVersion: 0)
396+
return (major: -1, minor: 0, patch: 0)
395397
#endif
396398
}
397399

‎Sources/FoundationEssentials/PropertyList/OpenStepPlist.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import Darwin
1616
import Bionic
1717
#elseif canImport(Glibc)
1818
import Glibc
19+
#elseif os(WASI)
20+
import WASILibc
1921
#elseif canImport(Musl)
2022
import Musl
2123
#endif

‎Sources/FoundationEssentials/String/String+Path.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import Glibc
2020
import Musl
2121
#elseif os(Windows)
2222
import WinSDK
23+
#elseif os(WASI)
24+
import WASILibc
2325
#endif
2426

2527
internal import _FoundationCShims
@@ -452,6 +454,7 @@ extension String {
452454
return envVar.standardizingPath
453455
}
454456

457+
#if !os(WASI) // WASI does not have user concept
455458
// Next, attempt to find the home directory via getpwnam/getpwuid
456459
var pass: UnsafeMutablePointer<passwd>?
457460
if let user {
@@ -465,6 +468,7 @@ extension String {
465468
if let dir = pass?.pointee.pw_dir {
466469
return String(cString: dir).standardizingPath
467470
}
471+
#endif
468472

469473
// Fallback to HOME for the current user if possible
470474
if user == nil, let home = getenv("HOME") {

‎Sources/FoundationEssentials/_ThreadLocal.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ struct _ThreadLocal {
3232
fileprivate typealias PlatformKey = tss_t
3333
#elseif canImport(WinSDK)
3434
fileprivate typealias PlatformKey = DWORD
35+
#elseif os(WASI)
36+
fileprivate typealias PlatformKey = UnsafeMutablePointer<UnsafeMutableRawPointer?>
3537
#endif
3638

3739
struct Key<Value> {
@@ -48,6 +50,8 @@ struct _ThreadLocal {
4850
self.key = key
4951
#elseif canImport(WinSDK)
5052
key = FlsAlloc(nil)
53+
#elseif os(WASI)
54+
key = UnsafeMutablePointer<UnsafeMutableRawPointer?>.allocate(capacity: 1)
5155
#endif
5256
}
5357
}
@@ -60,6 +64,8 @@ struct _ThreadLocal {
6064
tss_get(key)
6165
#elseif canImport(WinSDK)
6266
FlsGetValue(key)
67+
#elseif os(WASI)
68+
key.pointee
6369
#endif
6470
}
6571

@@ -70,6 +76,8 @@ struct _ThreadLocal {
7076
tss_set(key, newValue)
7177
#elseif canImport(WinSDK)
7278
FlsSetValue(key, newValue)
79+
#elseif os(WASI)
80+
key.pointee = newValue
7381
#endif
7482
}
7583
}

‎Sources/FoundationInternationalization/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ target_compile_options(FoundationInternationalization PRIVATE
3333
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xfrontend -enable-experimental-feature -Xfrontend StrictConcurrency>"
3434
"SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xfrontend -enable-upcoming-feature -Xfrontend InferSendableFromCaptures>")
3535
target_compile_options(FoundationInternationalization PRIVATE ${_SwiftFoundation_availability_macros})
36+
target_compile_options(FoundationInternationalization PRIVATE ${_SwiftFoundation_wasi_libc_flags})
3637
target_compile_options(FoundationInternationalization PRIVATE -package-name "SwiftFoundation")
3738

3839
target_link_libraries(FoundationInternationalization PUBLIC

‎Sources/FoundationInternationalization/Calendar/Calendar_ICU.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import Musl
2424
import CRT
2525
#elseif canImport(Darwin)
2626
import Darwin
27+
#elseif os(WASI)
28+
import WASILibc
2729
#endif
2830

2931
internal import _FoundationICU

‎Sources/FoundationInternationalization/Formatting/Duration+Formatting.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import Glibc
2424
import Musl
2525
#elseif os(Windows)
2626
import CRT
27+
#elseif os(WASI)
28+
import WASILibc
2729
#endif
2830

2931
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)

‎Sources/_FoundationCShims/include/_CStdlib.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,21 @@
6060
#endif
6161

6262
#if __has_include(<signal.h>)
63-
#include <signal.h>
63+
/// Guard against including `signal.h` on WASI. The `signal.h` header file
64+
/// itself is available in wasi-libc, but it's just a stub that doesn't actually
65+
/// do anything. And also including it requires a special macro definition
66+
/// (`_WASI_EMULATED_SIGNAL`) and it causes compilation errors without the macro.
67+
# if !TARGET_OS_WASI || defined(_WASI_EMULATED_SIGNAL)
68+
# include <signal.h>
69+
# endif
70+
#endif
71+
72+
#if __has_include(<sys/mman.h>)
73+
/// Similar to `signal.h`, guard against including `sys/mman.h` on WASI unless
74+
/// `_WASI_EMULATED_MMAN` is enabled.
75+
# if !TARGET_OS_WASI || defined(_WASI_EMULATED_MMAN)
76+
# include <sys/mman.h>
77+
# endif
6478
#endif
6579

6680
#if __has_include(<stdalign.h>)

‎Sources/_FoundationCShims/include/platform_shims.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,19 @@
3131
#include <security.h>
3232
#endif
3333

34-
INTERNAL char * _Nullable * _Nullable _platform_shims_get_environ();
34+
INTERNAL char * _Nullable * _Nullable _platform_shims_get_environ(void);
3535

36-
INTERNAL void _platform_shims_lock_environ();
37-
INTERNAL void _platform_shims_unlock_environ();
36+
INTERNAL void _platform_shims_lock_environ(void);
37+
INTERNAL void _platform_shims_unlock_environ(void);
3838

3939
#if __has_include(<mach/vm_page_size.h>)
4040
#include <mach/vm_page_size.h>
41-
INTERNAL vm_size_t _platform_shims_vm_size();
41+
INTERNAL vm_size_t _platform_shims_vm_size(void);
4242
#endif
4343

4444
#if __has_include(<mach/mach.h>)
4545
#include <mach/mach.h>
46-
INTERNAL mach_port_t _platform_mach_task_self();
46+
INTERNAL mach_port_t _platform_mach_task_self(void);
4747
#endif
4848

4949
#if __has_include(<libkern/OSThermalNotification.h>)
@@ -65,7 +65,7 @@ typedef enum {
6565
} _platform_shims_OSThermalPressureLevel;
6666

6767

68-
INTERNAL const char * _Nonnull _platform_shims_kOSThermalNotificationPressureLevelName();
68+
INTERNAL const char * _Nonnull _platform_shims_kOSThermalNotificationPressureLevelName(void);
6969
#endif
7070

7171
#endif /* CSHIMS_PLATFORM_SHIMS */

‎Sources/_FoundationCShims/platform_shims.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,25 @@
2121
extern char **environ;
2222
#endif
2323

24+
#if __wasi__
25+
#include <wasi/libc-environ.h> // for __wasilibc_get_environ
26+
#endif
27+
2428
#if __has_include(<libc_private.h>)
2529
#import <libc_private.h>
26-
void _platform_shims_lock_environ() {
30+
void _platform_shims_lock_environ(void) {
2731
environ_lock_np();
2832
}
2933

30-
void _platform_shims_unlock_environ() {
34+
void _platform_shims_unlock_environ(void) {
3135
environ_unlock_np();
3236
}
3337
#else
34-
void _platform_shims_lock_environ() { /* noop */ }
35-
void _platform_shims_unlock_environ() { /* noop */ }
38+
void _platform_shims_lock_environ(void) { /* noop */ }
39+
void _platform_shims_unlock_environ(void) { /* noop */ }
3640
#endif
3741

38-
char ** _platform_shims_get_environ() {
42+
char ** _platform_shims_get_environ(void) {
3943
#if __has_include(<crt_externs.h>)
4044
return *_NSGetEnviron();
4145
#elif defined(_WIN32)
@@ -48,20 +52,20 @@ char ** _platform_shims_get_environ() {
4852
}
4953

5054
#if __has_include(<libkern/OSThermalNotification.h>)
51-
const char * _platform_shims_kOSThermalNotificationPressureLevelName() {
55+
const char * _platform_shims_kOSThermalNotificationPressureLevelName(void) {
5256
return kOSThermalNotificationPressureLevelName;
5357
}
5458
#endif
5559

5660
#if __has_include(<mach/vm_page_size.h>)
57-
vm_size_t _platform_shims_vm_size() {
61+
vm_size_t _platform_shims_vm_size(void) {
5862
// This shim exists because vm_page_size is not marked const, and therefore looks like global mutable state to Swift.
5963
return vm_page_size;
6064
}
6165
#endif
6266

6367
#if __has_include(<mach/mach.h>)
64-
mach_port_t _platform_mach_task_self() {
68+
mach_port_t _platform_mach_task_self(void) {
6569
// This shim exists because mach_task_self_ is not marked const, and therefore looks like global mutable state to Swift.
6670
return mach_task_self();
6771
}

0 commit comments

Comments
 (0)
Please sign in to comment.