Skip to content

Commit 17548fe

Browse files
ricochetnickrum
andcommitted
Add wasm32-wasip2 target support
Rework of #532 by @nickrum, addressing review feedback from @Darksonn and @Thomasdezeeuw: - Reuses sys/unix.rs instead of creating sys/wasi.rs (per @Thomasdezeeuw's code review feedback) - MaybeUninitSlice stays unconditional since libc::iovec is available on WASI, eliminating 11+ scattered cfg gates (per @Darksonn's concern) - Internal cfg changes concentrated in the sys module Opt-in cfg patterns use `all(target_os = "wasi", not(target_env = "p1"))` to match how the libc crate gates WASI p2 socket support. Opt-out patterns use bare `target_os = "wasi"` for features unavailable on any WASI version. WASI tests run in CI with wasmtime (27/27 pass). Tests for unsupported socket options are gated out with references to the relevant WASI spec issues (SO_BROADCAST, SO_LINGER, IPV6_V6ONLY). ```bash export CARGO_TARGET_WASM32_WASIP2_RUNNER="wasmtime --wasi inherit-network" # Doc-tests are excluded because the SockRef example spawns threads, which # WASI does not yet support. cargo test --target wasm32-wasip2 --lib --tests ``` Closes #268 Based-on: #532 Co-authored-by: Nicola Krumschmidt <git@nkcom.de>
1 parent 43ba0ed commit 17548fe

8 files changed

Lines changed: 244 additions & 113 deletions

File tree

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ jobs:
105105
- x86_64-unknown-netbsd
106106
- x86_64-unknown-openbsd
107107
- x86_64-unknown-redox
108+
- wasm32-wasip2
108109
steps:
109110
- uses: actions/checkout@v4
110111
- uses: dtolnay/rust-toolchain@nightly

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ targets = [
4949
[package.metadata.playground]
5050
features = ["all"]
5151

52-
[target."cfg(unix)".dependencies]
52+
[target.'cfg(any(unix, target_os = "wasi"))'.dependencies]
5353
libc = "0.2.172"
5454

5555
[target.'cfg(windows)'.dependencies.windows-sys]

src/lib.rs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@
6161
#![doc(test(attr(deny(warnings))))]
6262

6363
use std::fmt;
64-
#[cfg(not(target_os = "redox"))]
64+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
6565
use std::io::IoSlice;
66-
#[cfg(not(target_os = "redox"))]
66+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
6767
use std::marker::PhantomData;
68-
#[cfg(not(target_os = "redox"))]
68+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
6969
use std::mem;
7070
use std::mem::MaybeUninit;
7171
use std::net::SocketAddr;
@@ -109,7 +109,7 @@ macro_rules! from {
109109
($from: ty, $for: ty) => {
110110
impl From<$from> for $for {
111111
fn from(socket: $from) -> $for {
112-
#[cfg(unix)]
112+
#[cfg(any(unix, all(target_os = "wasi", not(target_env = "p1"))))]
113113
unsafe {
114114
<$for>::from_raw_fd(socket.into_raw_fd())
115115
}
@@ -176,11 +176,11 @@ mod sockaddr;
176176
mod socket;
177177
mod sockref;
178178

179-
#[cfg_attr(unix, path = "sys/unix.rs")]
179+
#[cfg_attr(any(unix, all(target_os = "wasi", not(target_env = "p1"))), path = "sys/unix.rs")]
180180
#[cfg_attr(windows, path = "sys/windows.rs")]
181181
mod sys;
182182

183-
#[cfg(not(any(windows, unix)))]
183+
#[cfg(not(any(windows, unix, all(target_os = "wasi", not(target_env = "p1")))))]
184184
compile_error!("Socket2 doesn't support the compile target");
185185

186186
use sys::c_int;
@@ -192,6 +192,7 @@ pub use sockaddr::{sa_family_t, socklen_t, SockAddr, SockAddrStorage};
192192
target_os = "netbsd",
193193
target_os = "redox",
194194
target_os = "solaris",
195+
target_os = "wasi",
195196
)))]
196197
pub use socket::InterfaceIndexOrAddress;
197198
pub use socket::Socket;
@@ -221,6 +222,7 @@ impl Domain {
221222
pub const IPV6: Domain = Domain(sys::AF_INET6);
222223

223224
/// Domain for Unix socket communication, corresponding to `AF_UNIX`.
225+
#[cfg(not(target_os = "wasi"))]
224226
pub const UNIX: Domain = Domain(sys::AF_UNIX);
225227

226228
/// Returns the correct domain for `address`.
@@ -274,11 +276,14 @@ impl Type {
274276
pub const DCCP: Type = Type(sys::SOCK_DCCP);
275277

276278
/// Type corresponding to `SOCK_SEQPACKET`.
277-
#[cfg(all(feature = "all", not(target_os = "espidf")))]
279+
#[cfg(all(feature = "all", not(any(target_os = "espidf", target_os = "wasi"))))]
278280
pub const SEQPACKET: Type = Type(sys::SOCK_SEQPACKET);
279281

280282
/// Type corresponding to `SOCK_RAW`.
281-
#[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))]
283+
#[cfg(all(
284+
feature = "all",
285+
not(any(target_os = "redox", target_os = "espidf", target_os = "wasi"))
286+
))]
282287
pub const RAW: Type = Type(sys::SOCK_RAW);
283288
}
284289

@@ -306,9 +311,11 @@ pub struct Protocol(c_int);
306311

307312
impl Protocol {
308313
/// Protocol corresponding to `ICMPv4`.
314+
#[cfg(not(target_os = "wasi"))]
309315
pub const ICMPV4: Protocol = Protocol(sys::IPPROTO_ICMP);
310316

311317
/// Protocol corresponding to `ICMPv6`.
318+
#[cfg(not(target_os = "wasi"))]
312319
pub const ICMPV6: Protocol = Protocol(sys::IPPROTO_ICMPV6);
313320

314321
/// Protocol corresponding to `TCP`.
@@ -361,11 +368,11 @@ impl From<Protocol> for c_int {
361368
/// Flags for incoming messages.
362369
///
363370
/// Flags provide additional information about incoming messages.
364-
#[cfg(not(target_os = "redox"))]
371+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
365372
#[derive(Copy, Clone, Eq, PartialEq)]
366373
pub struct RecvFlags(c_int);
367374

368-
#[cfg(not(target_os = "redox"))]
375+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
369376
impl RecvFlags {
370377
/// Check if the message contains a truncated datagram.
371378
///
@@ -518,6 +525,7 @@ impl TcpKeepalive {
518525
target_os = "watchos",
519526
target_os = "windows",
520527
target_os = "cygwin",
528+
all(target_os = "wasi", not(target_env = "p1")),
521529
))]
522530
pub const fn with_interval(self, interval: Duration) -> Self {
523531
Self {
@@ -547,6 +555,7 @@ impl TcpKeepalive {
547555
target_os = "watchos",
548556
target_os = "cygwin",
549557
target_os = "windows",
558+
all(target_os = "wasi", not(target_env = "p1")),
550559
)
551560
))]
552561
pub const fn with_retries(self, retries: u32) -> Self {
@@ -561,15 +570,15 @@ impl TcpKeepalive {
561570
///
562571
/// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdrMut`]
563572
/// for the variant used by `recvmsg(2)`.
564-
#[cfg(not(target_os = "redox"))]
573+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
565574
#[repr(transparent)]
566575
pub struct MsgHdr<'addr, 'bufs, 'control> {
567576
inner: sys::msghdr,
568577
#[allow(clippy::type_complexity)]
569578
_lifetimes: PhantomData<(&'addr SockAddr, &'bufs IoSlice<'bufs>, &'control [u8])>,
570579
}
571580

572-
#[cfg(not(target_os = "redox"))]
581+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
573582
impl<'addr, 'bufs, 'control> MsgHdr<'addr, 'bufs, 'control> {
574583
/// Create a new `MsgHdr` with all empty/zero fields.
575584
#[allow(clippy::new_without_default)]
@@ -619,7 +628,7 @@ impl<'addr, 'bufs, 'control> MsgHdr<'addr, 'bufs, 'control> {
619628
}
620629
}
621630

622-
#[cfg(not(target_os = "redox"))]
631+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
623632
impl<'name, 'bufs, 'control> fmt::Debug for MsgHdr<'name, 'bufs, 'control> {
624633
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
625634
"MsgHdr".fmt(fmt)
@@ -630,7 +639,7 @@ impl<'name, 'bufs, 'control> fmt::Debug for MsgHdr<'name, 'bufs, 'control> {
630639
///
631640
/// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdr`] for
632641
/// the variant used by `sendmsg(2)`.
633-
#[cfg(not(target_os = "redox"))]
642+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
634643
#[repr(transparent)]
635644
pub struct MsgHdrMut<'addr, 'bufs, 'control> {
636645
inner: sys::msghdr,
@@ -642,7 +651,7 @@ pub struct MsgHdrMut<'addr, 'bufs, 'control> {
642651
)>,
643652
}
644653

645-
#[cfg(not(target_os = "redox"))]
654+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
646655
impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> {
647656
/// Create a new `MsgHdrMut` with all empty/zero fields.
648657
#[allow(clippy::new_without_default)]
@@ -697,7 +706,7 @@ impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> {
697706
}
698707
}
699708

700-
#[cfg(not(target_os = "redox"))]
709+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
701710
impl<'name, 'bufs, 'control> fmt::Debug for MsgHdrMut<'name, 'bufs, 'control> {
702711
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
703712
"MsgHdrMut".fmt(fmt)

src/sockaddr.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
use std::hash::Hash;
22
use std::mem::{self, size_of};
33
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
4+
#[cfg(not(target_os = "wasi"))]
45
use std::path::Path;
56
use std::{fmt, io, ptr};
67

78
#[cfg(windows)]
89
use windows_sys::Win32::Networking::WinSock::SOCKADDR_IN6_0;
910

10-
use crate::sys::{c_int, sockaddr_in, sockaddr_in6, sockaddr_storage, AF_INET, AF_INET6, AF_UNIX};
11+
#[cfg(not(target_os = "wasi"))]
12+
use crate::sys::AF_UNIX;
13+
use crate::sys::{c_int, sockaddr_in, sockaddr_in6, sockaddr_storage, AF_INET, AF_INET6};
1114
use crate::Domain;
1215

1316
/// The integer type used with `getsockname` on this platform.
@@ -212,6 +215,7 @@ impl SockAddr {
212215
/// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path.
213216
///
214217
/// Returns an error if the path is longer than `SUN_LEN`.
218+
#[cfg(not(target_os = "wasi"))]
215219
pub fn unix<P>(path: P) -> io::Result<SockAddr>
216220
where
217221
P: AsRef<Path>,
@@ -269,6 +273,7 @@ impl SockAddr {
269273

270274
/// Returns true if this address is of a unix socket (for local interprocess communication),
271275
/// i.e. it is from the `AF_UNIX` family, false otherwise.
276+
#[cfg(not(target_os = "wasi"))]
272277
pub fn is_unix(&self) -> bool {
273278
self.storage.ss_family == AF_UNIX as sa_family_t
274279
}
@@ -293,7 +298,7 @@ impl SockAddr {
293298
ip,
294299
port,
295300
addr.sin6_flowinfo,
296-
#[cfg(unix)]
301+
#[cfg(any(unix, all(target_os = "wasi", not(target_env = "p1"))))]
297302
addr.sin6_scope_id,
298303
#[cfg(windows)]
299304
unsafe {
@@ -350,7 +355,10 @@ impl From<SocketAddrV4> for SockAddr {
350355
storage.sin_family = AF_INET as sa_family_t;
351356
storage.sin_port = addr.port().to_be();
352357
storage.sin_addr = crate::sys::to_in_addr(addr.ip());
353-
storage.sin_zero = Default::default();
358+
#[cfg(not(target_os = "wasi"))]
359+
{
360+
storage.sin_zero = Default::default();
361+
}
354362
mem::size_of::<sockaddr_in>() as socklen_t
355363
};
356364
#[cfg(any(
@@ -385,7 +393,7 @@ impl From<SocketAddrV6> for SockAddr {
385393
storage.sin6_port = addr.port().to_be();
386394
storage.sin6_addr = crate::sys::to_in6_addr(addr.ip());
387395
storage.sin6_flowinfo = addr.flowinfo();
388-
#[cfg(unix)]
396+
#[cfg(any(unix, all(target_os = "wasi", not(target_env = "p1"))))]
389397
{
390398
storage.sin6_scope_id = addr.scope_id();
391399
}
@@ -469,6 +477,7 @@ mod tests {
469477
let addr = SockAddr::from(std);
470478
assert!(addr.is_ipv4());
471479
assert!(!addr.is_ipv6());
480+
#[cfg(not(target_os = "wasi"))]
472481
assert!(!addr.is_unix());
473482
assert_eq!(addr.family(), AF_INET as sa_family_t);
474483
assert_eq!(addr.domain(), Domain::IPV4);
@@ -483,7 +492,7 @@ mod tests {
483492
assert_eq!(addr.as_socket(), Some(SocketAddr::V4(std)));
484493
assert_eq!(addr.as_socket_ipv4(), Some(std));
485494
assert!(addr.as_socket_ipv6().is_none());
486-
#[cfg(unix)]
495+
#[cfg(all(unix, not(target_os = "wasi")))]
487496
{
488497
assert!(addr.as_pathname().is_none());
489498
assert!(addr.as_abstract_namespace().is_none());
@@ -497,6 +506,7 @@ mod tests {
497506
let addr = SockAddr::from(std);
498507
assert!(addr.is_ipv6());
499508
assert!(!addr.is_ipv4());
509+
#[cfg(not(target_os = "wasi"))]
500510
assert!(!addr.is_unix());
501511
assert_eq!(addr.family(), AF_INET6 as sa_family_t);
502512
assert_eq!(addr.domain(), Domain::IPV6);
@@ -511,7 +521,7 @@ mod tests {
511521
assert_eq!(addr.as_socket(), Some(SocketAddr::V6(std)));
512522
assert!(addr.as_socket_ipv4().is_none());
513523
assert_eq!(addr.as_socket_ipv6(), Some(std));
514-
#[cfg(unix)]
524+
#[cfg(all(unix, not(target_os = "wasi")))]
515525
{
516526
assert!(addr.as_pathname().is_none());
517527
assert!(addr.as_abstract_namespace().is_none());

0 commit comments

Comments
 (0)