Skip to content

Commit b0abbb4

Browse files
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

+8
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

+26-9
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

+1
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

+2
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

+2
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

+2
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

+9
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

+8-6
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

+2
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

+2
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

+2
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

+4
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

+2
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

+2
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

+20-3
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

+2
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

+4-2
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

0 commit comments

Comments
 (0)