Skip to content

Commit 96ba771

Browse files
committed
fix(wasip3): Target-conditional deps
Now when wasm32-wasip3 lands, users can compile with just `--target wasm32-wasip3` The cfg alias activates the p3 path AND the wasip3 deps are auto-pulled.
1 parent 2712b93 commit 96ba771

28 files changed

Lines changed: 163 additions & 147 deletions

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
run: cargo test -p wstd -p wstd-axum --target wasm32-wasip2 -- --nocapture
5656

5757
- name: wstd tests (wasip3)
58-
run: cargo test -p wstd -p wstd-axum --target wasm32-wasip2 --no-default-features --features wasip3 -- --nocapture
58+
run: cargo test -p wstd -p wstd-axum --target wasm32-wasip2 --no-default-features --features wasip3,json -- --nocapture
5959

6060
- name: test-programs tests
6161
run: cargo test -p test-programs -- --nocapture

Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ json = ["dep:serde", "dep:serde_json"]
1818
wasip2 = ["dep:wasip2"]
1919
wasip3 = ["dep:wasip3", "dep:wit-bindgen"]
2020

21+
[build-dependencies]
22+
cfg_aliases = "0.2"
23+
2124
[dependencies]
2225
anyhow.workspace = true
2326
async-task.workspace = true
@@ -38,6 +41,12 @@ wstd-macro.workspace = true
3841
serde = { workspace = true, optional = true }
3942
serde_json = { workspace = true, optional = true }
4043

44+
# Auto-pull wasip3 deps when targeting wasm32-wasip3 so users don't need
45+
# to also pass --features wasip3.
46+
[target.'cfg(all(target_os = "wasi", target_env = "p3"))'.dependencies]
47+
wasip3 = { workspace = true }
48+
wit-bindgen = { workspace = true }
49+
4150
[dev-dependencies]
4251
anyhow.workspace = true
4352
clap.workspace = true

build.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use cfg_aliases::cfg_aliases;
2+
3+
fn main() {
4+
cfg_aliases! {
5+
// True when targeting a wasip3 component, either via the explicit
6+
// `wasip3` feature or the `wasm32-wasip3` target (`target_env = "p3"`).
7+
wstd_p3: { any(feature = "wasip3", target_env = "p3") },
8+
// True when targeting a wasip2 component, either via the `wasip2`
9+
// feature (default) or the `wasm32-wasip2` target. wasip3 takes
10+
// precedence when both apply.
11+
wstd_p2: { all(any(feature = "wasip2", target_env = "p2"), not(wstd_p3)) },
12+
}
13+
}

src/http/body.rs

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,25 @@ use http::header::CONTENT_LENGTH;
88
use http_body_util::{BodyExt, combinators::UnsyncBoxBody};
99
use std::fmt;
1010

11-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
11+
#[cfg(wstd_p2)]
1212
use crate::http::fields::{header_map_from_wasi, header_map_to_wasi};
13-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
13+
#[cfg(wstd_p2)]
1414
use crate::io::AsyncOutputStream;
15-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
15+
#[cfg(wstd_p2)]
1616
use std::future::{Future, poll_fn};
17-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
17+
#[cfg(wstd_p2)]
1818
use std::pin::{Pin, pin};
19-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
19+
#[cfg(wstd_p2)]
2020
use std::task::{Context, Poll};
2121

22-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
22+
#[cfg(wstd_p2)]
2323
use crate::runtime::{AsyncPollable, Reactor, WaitFor};
2424

25-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
25+
#[cfg(wstd_p2)]
2626
use wasip2::http::types::{
2727
FutureTrailers, IncomingBody as WasiIncomingBody, OutgoingBody as WasiOutgoingBody,
2828
};
29-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
29+
#[cfg(wstd_p2)]
3030
use wasip2::io::streams::{InputStream as WasiInputStream, StreamError};
3131

3232
pub mod util {
@@ -70,10 +70,10 @@ enum BodyInner {
7070
// a boxed http_body::Body impl
7171
Boxed(UnsyncBoxBody<Bytes, Error>),
7272
// a body created from a wasi-http incoming-body (p2)
73-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
73+
#[cfg(wstd_p2)]
7474
Incoming(Incoming),
7575
// a body created from a p3 StreamReader
76-
#[cfg(feature = "wasip3")]
76+
#[cfg(wstd_p3)]
7777
P3Stream(P3StreamBody),
7878
// a body in memory
7979
Complete {
@@ -82,7 +82,7 @@ enum BodyInner {
8282
},
8383
}
8484

85-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
85+
#[cfg(wstd_p2)]
8686
impl Body {
8787
pub(crate) async fn send(self, outgoing_body: WasiOutgoingBody) -> Result<(), Error> {
8888
match self.0 {
@@ -141,7 +141,7 @@ impl Body {
141141
}
142142
}
143143

144-
#[cfg(feature = "wasip3")]
144+
#[cfg(wstd_p3)]
145145
impl Body {
146146
pub(crate) fn from_p3_stream(
147147
reader: wit_bindgen::rt::async_support::StreamReader<u8>,
@@ -161,9 +161,9 @@ impl Body {
161161
unreachable!()
162162
}
163163
match self.0 {
164-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
164+
#[cfg(wstd_p2)]
165165
BodyInner::Incoming(i) => i.into_http_body().boxed_unsync(),
166-
#[cfg(feature = "wasip3")]
166+
#[cfg(wstd_p3)]
167167
BodyInner::P3Stream(p3) => {
168168
// Convert p3 stream body to a boxed body
169169
let stream = AsyncInputStream::new(p3.reader.unwrap());
@@ -198,7 +198,7 @@ impl Body {
198198
// For p3 streams, read directly using the async read method
199199
// instead of going through poll_next (which doesn't properly
200200
// persist the read future across polls, causing hangs).
201-
#[cfg(feature = "wasip3")]
201+
#[cfg(wstd_p3)]
202202
if let BodyInner::P3Stream(p3) = prev {
203203
let mut stream = AsyncInputStream::new(p3.reader.unwrap());
204204
let mut all_data = Vec::new();
@@ -221,11 +221,11 @@ impl Body {
221221
}
222222

223223
let boxed_body = match prev {
224-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
224+
#[cfg(wstd_p2)]
225225
BodyInner::Incoming(i) => i.into_http_body().boxed_unsync(),
226226
BodyInner::Boxed(b) => b,
227227
BodyInner::Complete { .. } => unreachable!(),
228-
#[cfg(feature = "wasip3")]
228+
#[cfg(wstd_p3)]
229229
BodyInner::P3Stream(_) => unreachable!(),
230230
};
231231
let collected = boxed_body.collect().await?;
@@ -251,9 +251,9 @@ impl Body {
251251
match &self.0 {
252252
BodyInner::Boxed(b) => b.size_hint().exact(),
253253
BodyInner::Complete { data, .. } => Some(data.len() as u64),
254-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
254+
#[cfg(wstd_p2)]
255255
BodyInner::Incoming(i) => i.size_hint.content_length(),
256-
#[cfg(feature = "wasip3")]
256+
#[cfg(wstd_p3)]
257257
BodyInner::P3Stream(p3) => p3.size_hint.content_length(),
258258
}
259259
}
@@ -426,28 +426,27 @@ impl fmt::Display for InvalidContentLength {
426426
}
427427
impl std::error::Error for InvalidContentLength {}
428428

429-
#[cfg(feature = "wasip3")]
429+
#[cfg(wstd_p3)]
430430
struct P3StreamBody {
431431
reader: Option<wit_bindgen::rt::async_support::StreamReader<u8>>,
432432
size_hint: BodyHint,
433433
}
434434

435-
#[cfg(feature = "wasip3")]
435+
#[cfg(wstd_p3)]
436436
impl fmt::Debug for P3StreamBody {
437437
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
438438
f.debug_struct("P3StreamBody").finish()
439439
}
440440
}
441441

442-
443-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
442+
#[cfg(wstd_p2)]
444443
#[derive(Debug)]
445444
struct Incoming {
446445
body: WasiIncomingBody,
447446
size_hint: BodyHint,
448447
}
449448

450-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
449+
#[cfg(wstd_p2)]
451450
impl Incoming {
452451
fn into_http_body(self) -> IncomingBody {
453452
IncomingBody::new(self.body, self.size_hint)
@@ -482,14 +481,14 @@ impl Incoming {
482481
}
483482
}
484483

485-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
484+
#[cfg(wstd_p2)]
486485
#[derive(Debug)]
487486
pub struct IncomingBody {
488487
state: Option<Pin<Box<IncomingBodyState>>>,
489488
size_hint: BodyHint,
490489
}
491490

492-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
491+
#[cfg(wstd_p2)]
493492
impl IncomingBody {
494493
fn new(body: WasiIncomingBody, size_hint: BodyHint) -> Self {
495494
Self {
@@ -508,7 +507,7 @@ impl IncomingBody {
508507
}
509508
}
510509

511-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
510+
#[cfg(wstd_p2)]
512511
impl HttpBody for IncomingBody {
513512
type Data = Bytes;
514513
type Error = Error;
@@ -564,7 +563,7 @@ impl HttpBody for IncomingBody {
564563
}
565564
}
566565

567-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
566+
#[cfg(wstd_p2)]
568567
pin_project_lite::pin_project! {
569568
#[project = IBSProj]
570569
#[derive(Debug)]
@@ -583,18 +582,18 @@ pin_project_lite::pin_project! {
583582
}
584583
}
585584

586-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
585+
#[cfg(wstd_p2)]
587586
#[derive(Debug)]
588587
struct BodyState {
589588
wait: Option<Pin<Box<WaitFor>>>,
590589
subscription: Option<AsyncPollable>,
591590
stream: WasiInputStream,
592591
}
593592

594-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
593+
#[cfg(wstd_p2)]
595594
const MAX_FRAME_SIZE: u64 = 64 * 1024;
596595

597-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
596+
#[cfg(wstd_p2)]
598597
impl BodyState {
599598
fn poll_frame(
600599
mut self: Pin<&mut Self>,
@@ -639,15 +638,15 @@ impl BodyState {
639638
}
640639
}
641640

642-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
641+
#[cfg(wstd_p2)]
643642
#[derive(Debug)]
644643
struct TrailersState {
645644
wait: Option<Pin<Box<WaitFor>>>,
646645
subscription: Option<AsyncPollable>,
647646
future_trailers: FutureTrailers,
648647
}
649648

650-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
649+
#[cfg(wstd_p2)]
651650
impl TrailersState {
652651
fn new(future_trailers: FutureTrailers) -> Self {
653652
Self {

src/http/client.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ impl Client {
2020
}
2121

2222
/// Send an HTTP request.
23-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
23+
#[cfg(wstd_p2)]
2424
pub async fn send<B: Into<Body>>(&self, req: Request<B>) -> Result<Response<Body>, Error> {
2525
use crate::http::request::try_into_outgoing;
2626
use crate::http::response::try_from_incoming;
@@ -42,7 +42,7 @@ impl Client {
4242
}
4343

4444
/// Send an HTTP request.
45-
#[cfg(feature = "wasip3")]
45+
#[cfg(wstd_p3)]
4646
pub async fn send<B: Into<Body>>(&self, req: Request<B>) -> Result<Response<Body>, Error> {
4747
use crate::http::request::try_into_wasi_request;
4848
use crate::http::response::try_from_wasi_response;
@@ -97,7 +97,7 @@ impl Client {
9797
}
9898
}
9999

100-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
100+
#[cfg(wstd_p2)]
101101
fn wasi_options_p2(
102102
&self,
103103
) -> Result<Option<wasip2::http::types::RequestOptions>, crate::http::Error> {
@@ -108,7 +108,7 @@ impl Client {
108108
}
109109
}
110110

111-
#[cfg(feature = "wasip3")]
111+
#[cfg(wstd_p3)]
112112
pub(crate) type P3RequestOptions = RequestOptions;
113113

114114
#[derive(Default, Debug, Clone)]
@@ -119,7 +119,7 @@ pub(crate) struct RequestOptions {
119119
}
120120

121121
impl RequestOptions {
122-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
122+
#[cfg(wstd_p2)]
123123
fn to_wasi_p2(&self) -> Result<wasip2::http::types::RequestOptions, crate::http::Error> {
124124
let wasi = wasip2::http::types::RequestOptions::new();
125125
if let Some(timeout) = self.connect_timeout {

src/http/error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ pub use anyhow::Context;
77
pub use http::header::{InvalidHeaderName, InvalidHeaderValue};
88
pub use http::method::InvalidMethod;
99

10-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
10+
#[cfg(wstd_p2)]
1111
pub use wasip2::http::types::{ErrorCode, HeaderError};
1212

13-
#[cfg(feature = "wasip3")]
13+
#[cfg(wstd_p3)]
1414
pub use wasip3::http::types::{ErrorCode, HeaderError};
1515

1616
pub type Error = anyhow::Error;

src/http/fields.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ pub use http::header::{HeaderMap, HeaderName, HeaderValue};
22

33
use super::{Error, error::Context};
44

5-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
5+
#[cfg(wstd_p2)]
66
use wasip2::http::types::Fields;
77

8-
#[cfg(feature = "wasip3")]
8+
#[cfg(wstd_p3)]
99
use wasip3::http::types::Fields;
1010

11-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
11+
#[cfg(wstd_p2)]
1212
pub(crate) fn header_map_from_wasi(wasi_fields: Fields) -> Result<HeaderMap, Error> {
1313
let mut output = HeaderMap::new();
1414
for (key, value) in wasi_fields.entries() {
@@ -21,7 +21,7 @@ pub(crate) fn header_map_from_wasi(wasi_fields: Fields) -> Result<HeaderMap, Err
2121
Ok(output)
2222
}
2323

24-
#[cfg(feature = "wasip3")]
24+
#[cfg(wstd_p3)]
2525
pub(crate) fn header_map_from_wasi(wasi_fields: Fields) -> Result<HeaderMap, Error> {
2626
let mut output = HeaderMap::new();
2727
for (key, value) in wasi_fields.copy_all() {

src/http/method.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
1+
#[cfg(wstd_p2)]
22
use wasip2::http::types::Method as WasiMethod;
33

4-
#[cfg(feature = "wasip3")]
4+
#[cfg(wstd_p3)]
55
use wasip3::http::types::Method as WasiMethod;
66

77
pub use http::Method;

src/http/mod.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ pub mod server;
2727
// Conditionally-compiled declarative macro for HTTP server export
2828
//
2929
// The `#[wstd::http_server]` proc macro delegates to this declarative macro.
30-
// Because `#[macro_export]` macros are compiled in wstd's context, the `cfg`
31-
// checks here use wstd's own feature flags so consumers don't need to define
32-
// `wasip2`/`wasip3` features themselves.
30+
// Because `#[macro_export]` macros are compiled in wstd's context, the
31+
// `wstd_p2` / `wstd_p3` cfg aliases (defined in build.rs) are evaluated against
32+
// wstd's own features and target environment. Consumers don't need to define
33+
// any features themselves.
3334

34-
#[cfg(all(feature = "wasip2", not(feature = "wasip3")))]
35+
#[cfg(wstd_p2)]
3536
#[macro_export]
3637
#[doc(hidden)]
3738
macro_rules! __http_server_export {
@@ -91,7 +92,7 @@ macro_rules! __http_server_export {
9192
};
9293
}
9394

94-
#[cfg(feature = "wasip3")]
95+
#[cfg(wstd_p3)]
9596
#[macro_export]
9697
#[doc(hidden)]
9798
macro_rules! __http_server_export {

0 commit comments

Comments
 (0)