Skip to content

Commit 0955c52

Browse files
ricochetnickrum
andauthored
Add wasm32-wasip2 target support (#639)
Rework of #532, addressing review feedback: - Reuses sys/unix.rs instead of creating sys/wasi.rs (per code review feedback) - MaybeUninitSlice stays unconditional since libc::iovec is available on WASI, eliminating 11+ scattered cfg gates - 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" cargo test --target wasm32-wasip2 --lib --tests ``` Closes #268 Based-on: #532 Co-authored-by: Nicola Krumschmidt <git@nkcom.de>
1 parent 1a24057 commit 0955c52

File tree

8 files changed

+257
-114
lines changed

8 files changed

+257
-114
lines changed

.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: 28 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,14 @@ mod sockaddr;
176176
mod socket;
177177
mod sockref;
178178

179-
#[cfg_attr(unix, path = "sys/unix.rs")]
179+
#[cfg_attr(
180+
any(unix, all(target_os = "wasi", not(target_env = "p1"))),
181+
path = "sys/unix.rs"
182+
)]
180183
#[cfg_attr(windows, path = "sys/windows.rs")]
181184
mod sys;
182185

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

186189
use sys::c_int;
@@ -192,6 +195,7 @@ pub use sockaddr::{sa_family_t, socklen_t, SockAddr, SockAddrStorage};
192195
target_os = "netbsd",
193196
target_os = "redox",
194197
target_os = "solaris",
198+
target_os = "wasi",
195199
)))]
196200
pub use socket::InterfaceIndexOrAddress;
197201
pub use socket::Socket;
@@ -221,6 +225,7 @@ impl Domain {
221225
pub const IPV6: Domain = Domain(sys::AF_INET6);
222226

223227
/// Domain for Unix socket communication, corresponding to `AF_UNIX`.
228+
#[cfg(not(target_os = "wasi"))]
224229
pub const UNIX: Domain = Domain(sys::AF_UNIX);
225230

226231
/// Returns the correct domain for `address`.
@@ -274,11 +279,14 @@ impl Type {
274279
pub const DCCP: Type = Type(sys::SOCK_DCCP);
275280

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

280285
/// Type corresponding to `SOCK_RAW`.
281-
#[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))]
286+
#[cfg(all(
287+
feature = "all",
288+
not(any(target_os = "redox", target_os = "espidf", target_os = "wasi"))
289+
))]
282290
pub const RAW: Type = Type(sys::SOCK_RAW);
283291
}
284292

@@ -306,9 +314,11 @@ pub struct Protocol(c_int);
306314

307315
impl Protocol {
308316
/// Protocol corresponding to `ICMPv4`.
317+
#[cfg(not(target_os = "wasi"))]
309318
pub const ICMPV4: Protocol = Protocol(sys::IPPROTO_ICMP);
310319

311320
/// Protocol corresponding to `ICMPv6`.
321+
#[cfg(not(target_os = "wasi"))]
312322
pub const ICMPV6: Protocol = Protocol(sys::IPPROTO_ICMPV6);
313323

314324
/// Protocol corresponding to `TCP`.
@@ -361,11 +371,11 @@ impl From<Protocol> for c_int {
361371
/// Flags for incoming messages.
362372
///
363373
/// Flags provide additional information about incoming messages.
364-
#[cfg(not(target_os = "redox"))]
374+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
365375
#[derive(Copy, Clone, Eq, PartialEq)]
366376
pub struct RecvFlags(c_int);
367377

368-
#[cfg(not(target_os = "redox"))]
378+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
369379
impl RecvFlags {
370380
/// Check if the message contains a truncated datagram.
371381
///
@@ -518,6 +528,7 @@ impl TcpKeepalive {
518528
target_os = "watchos",
519529
target_os = "windows",
520530
target_os = "cygwin",
531+
all(target_os = "wasi", not(target_env = "p1")),
521532
))]
522533
pub const fn with_interval(self, interval: Duration) -> Self {
523534
Self {
@@ -547,6 +558,7 @@ impl TcpKeepalive {
547558
target_os = "watchos",
548559
target_os = "cygwin",
549560
target_os = "windows",
561+
all(target_os = "wasi", not(target_env = "p1")),
550562
)
551563
))]
552564
pub const fn with_retries(self, retries: u32) -> Self {
@@ -561,15 +573,15 @@ impl TcpKeepalive {
561573
///
562574
/// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdrMut`]
563575
/// for the variant used by `recvmsg(2)`.
564-
#[cfg(not(target_os = "redox"))]
576+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
565577
#[repr(transparent)]
566578
pub struct MsgHdr<'addr, 'bufs, 'control> {
567579
inner: sys::msghdr,
568580
#[allow(clippy::type_complexity)]
569581
_lifetimes: PhantomData<(&'addr SockAddr, &'bufs IoSlice<'bufs>, &'control [u8])>,
570582
}
571583

572-
#[cfg(not(target_os = "redox"))]
584+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
573585
impl<'addr, 'bufs, 'control> MsgHdr<'addr, 'bufs, 'control> {
574586
/// Create a new `MsgHdr` with all empty/zero fields.
575587
#[allow(clippy::new_without_default)]
@@ -619,7 +631,7 @@ impl<'addr, 'bufs, 'control> MsgHdr<'addr, 'bufs, 'control> {
619631
}
620632
}
621633

622-
#[cfg(not(target_os = "redox"))]
634+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
623635
impl<'name, 'bufs, 'control> fmt::Debug for MsgHdr<'name, 'bufs, 'control> {
624636
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
625637
"MsgHdr".fmt(fmt)
@@ -630,7 +642,7 @@ impl<'name, 'bufs, 'control> fmt::Debug for MsgHdr<'name, 'bufs, 'control> {
630642
///
631643
/// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdr`] for
632644
/// the variant used by `sendmsg(2)`.
633-
#[cfg(not(target_os = "redox"))]
645+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
634646
#[repr(transparent)]
635647
pub struct MsgHdrMut<'addr, 'bufs, 'control> {
636648
inner: sys::msghdr,
@@ -642,7 +654,7 @@ pub struct MsgHdrMut<'addr, 'bufs, 'control> {
642654
)>,
643655
}
644656

645-
#[cfg(not(target_os = "redox"))]
657+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
646658
impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> {
647659
/// Create a new `MsgHdrMut` with all empty/zero fields.
648660
#[allow(clippy::new_without_default)]
@@ -697,7 +709,7 @@ impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> {
697709
}
698710
}
699711

700-
#[cfg(not(target_os = "redox"))]
712+
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
701713
impl<'name, 'bufs, 'control> fmt::Debug for MsgHdrMut<'name, 'bufs, 'control> {
702714
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
703715
"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)