Skip to content

Commit c80fb1b

Browse files
authored
Fallback to spec shaking v1 if env var is not set (#1831)
### What Change spec shaking v2 feature to gracefully fall back to v1 when the `SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2` env var is not set, instead of panicking. If the build process falls back to v1, a build warning is emitted: ``` Compiling soroban-sdk v26.0.0-rc.1 (/Users/alex/dev/stellar/rs-soroban-sdk/soroban-sdk) warning: soroban-sdk@26.0.0-rc.1: soroban-sdk: feature 'experimental_spec_shaking_v2' was enabled but not used, because this build was not started by a tool that supports spec shaking v2. Falling back to spec shaking v1. To use v2, use a build tool that supports spec shaking v2 such as stellar-cli v25.2.0+ with `stellar contract build`. To manually use v2 without a supporting build tool, set the env var SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2 before building. ``` ### Why The `experimental_spec_shaking_v2` feature is enabled by default. Previously, if the env var was not set and the target was wasm, the build script panicked with an error. Now, spec shaking v2 activates only when both the feature and the env var are present. When the feature is enabled but the env var is missing, the SDK falls back to v1 behavior and emits a compiler warning on wasm targets. This allows `cargo build` to succeed without requiring `stellar-cli`, while still benefiting from v2 when built with compatible tooling. ### Known limitations None
1 parent 4b7e299 commit c80fb1b

29 files changed

Lines changed: 420 additions & 1328 deletions

.github/workflows/rust.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ concurrency:
1212

1313
env:
1414
ARTIFACT_RETENTION_DAYS_FOR_TEST_WASMS: 7
15-
SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2: 1
1615

1716
defaults:
1817
run:

Makefile

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,21 @@ build: build-libs build-test-wasms
3030
build-libs: fmt
3131
cargo hack build --release $(foreach c,$(LIB_CRATES),--package $(c))
3232

33+
# First, build crate used as WASM deps to other test crates.
34+
# Then, build `test_spec_shaking_v2` without the spec shaking v2 env var to verify
35+
# that it falls back to spec_shaking_v1 behaviour.
36+
# Then, build the test wasms with MSRV by default, with some meta disabled for
37+
# binary stability for tests.
3338
build-test-wasms: fmt
34-
# Build the test wasms with MSRV by default, with some meta disabled for
35-
# binary stability for tests.
39+
SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2=1 \
40+
RUSTUP_TOOLCHAIN=$(TEST_CRATES_RUSTUP_TOOLCHAIN) \
41+
RUSTFLAGS='--cfg soroban_sdk_internal_no_rssdkver_meta' \
42+
cargo build --release --target wasm32v1-none --package test_spec_import
43+
RUSTUP_TOOLCHAIN=$(TEST_CRATES_RUSTUP_TOOLCHAIN) \
44+
RUSTFLAGS='--cfg soroban_sdk_internal_no_rssdkver_meta' \
45+
cargo build --release --target wasm32v1-none --package test_spec_shaking_v2
46+
cp target/wasm32v1-none/release/test_spec_shaking_v2.wasm \
47+
target/wasm32v1-none/release/test_spec_shaking_v2_no_env.wasm
3648
SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2=1 \
3749
RUSTUP_TOOLCHAIN=$(TEST_CRATES_RUSTUP_TOOLCHAIN) \
3850
RUSTFLAGS='--cfg soroban_sdk_internal_no_rssdkver_meta' \

soroban-sdk-macros/src/derive_enum.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ use stellar_xdr::{
99
ScSpecUdtUnionCaseVoidV0, ScSpecUdtUnionV0, StringM, VecM, WriteXdr, SCSYMBOL_LIMIT,
1010
};
1111

12-
use crate::{doc::docs_from_attrs, map_type::map_type, shaking, DEFAULT_XDR_RW_LIMITS};
12+
use crate::{
13+
doc::docs_from_attrs, map_type::map_type, shaking, spec_shaking_v2_enabled,
14+
DEFAULT_XDR_RW_LIMITS,
15+
};
1316

1417
pub fn derive_type_enum(
1518
path: &Path,
@@ -175,9 +178,9 @@ pub fn derive_type_enum(
175178
None
176179
};
177180

178-
// SpecShakingMarker impl - only generated when spec is true and the
179-
// experimental_spec_shaking_v2 feature is enabled.
180-
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
181+
// SpecShakingMarker impl - only generated when spec is true and
182+
// spec shaking v2 is enabled.
183+
let spec_shaking_impl = if spec_shaking_v2_enabled() {
181184
spec_xdr.as_ref().map(|spec_xdr| {
182185
// Flatten all variant field types for shaking calls, deduplicating
183186
// to avoid redundant calls for types that appear in multiple variants.

soroban-sdk-macros/src/derive_enum_int.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use syn::{spanned::Spanned, Attribute, DataEnum, Error, ExprLit, Ident, Lit, Pat
77

88
use stellar_xdr::{ScSpecEntry, ScSpecUdtEnumCaseV0, WriteXdr};
99

10-
use crate::{doc::docs_from_attrs, shaking, DEFAULT_XDR_RW_LIMITS};
10+
use crate::{doc::docs_from_attrs, shaking, spec_shaking_v2_enabled, DEFAULT_XDR_RW_LIMITS};
1111

1212
// TODO: Add conversions to/from ScVal types.
1313

@@ -97,9 +97,9 @@ pub fn derive_type_enum_int(
9797
None
9898
};
9999

100-
// SpecShakingMarker impl - only generated when spec is true and the
101-
// experimental_spec_shaking_v2 feature is enabled.
102-
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
100+
// SpecShakingMarker impl - only generated when spec is true and
101+
// spec shaking v2 is enabled.
102+
let spec_shaking_impl = if spec_shaking_v2_enabled() {
103103
spec_xdr.as_ref().map(|spec_xdr| {
104104
shaking::generate_marker_impl(
105105
path,

soroban-sdk-macros/src/derive_error_enum_int.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use stellar_xdr::curr as stellar_xdr;
55
use stellar_xdr::{ScSpecEntry, ScSpecUdtErrorEnumCaseV0, ScSpecUdtErrorEnumV0, StringM, WriteXdr};
66
use syn::{spanned::Spanned, Attribute, DataEnum, Error, ExprLit, Ident, Lit, Path};
77

8-
use crate::{doc::docs_from_attrs, shaking, DEFAULT_XDR_RW_LIMITS};
8+
use crate::{doc::docs_from_attrs, shaking, spec_shaking_v2_enabled, DEFAULT_XDR_RW_LIMITS};
99

1010
pub fn derive_type_error_enum_int(
1111
path: &Path,
@@ -95,9 +95,9 @@ pub fn derive_type_error_enum_int(
9595
None
9696
};
9797

98-
// SpecShakingMarker impl - only generated when spec is true and the
99-
// experimental_spec_shaking_v2 feature is enabled.
100-
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
98+
// SpecShakingMarker impl - only generated when spec is true and
99+
// spec shaking v2 is enabled.
100+
let spec_shaking_impl = if spec_shaking_v2_enabled() {
101101
spec_xdr.as_ref().map(|spec_xdr| {
102102
shaking::generate_marker_impl(
103103
path,

soroban-sdk-macros/src/derive_event.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
attribute::remove_attributes_from_item, default_crate_path, doc::docs_from_attrs,
3-
map_type::map_type, shaking, symbol, DEFAULT_XDR_RW_LIMITS,
3+
map_type::map_type, shaking, spec_shaking_v2_enabled, symbol, DEFAULT_XDR_RW_LIMITS,
44
};
55
use darling::{ast::NestedMeta, Error, FromMeta};
66
use heck::ToSnakeCase;
@@ -197,7 +197,7 @@ fn derive_impls(args: &ContractEventArgs, input: &DeriveInput) -> Result<TokenSt
197197
"__SPEC_XDR_EVENT_{}",
198198
input.ident.to_string().to_uppercase()
199199
);
200-
let spec_shaking_call = if export && cfg!(feature = "experimental_spec_shaking_v2") {
200+
let spec_shaking_call = if export && spec_shaking_v2_enabled() {
201201
Some(quote! { <Self as #path::SpecShakingMarker>::spec_shaking_marker(); })
202202
} else {
203203
None
@@ -215,9 +215,9 @@ fn derive_impls(args: &ContractEventArgs, input: &DeriveInput) -> Result<TokenSt
215215
}
216216
};
217217

218-
// SpecShakingMarker impl - only generated when export is true and the
219-
// experimental_spec_shaking_v2 feature is enabled.
220-
let spec_shaking_impl = if export && cfg!(feature = "experimental_spec_shaking_v2") {
218+
// SpecShakingMarker impl - only generated when export is true and
219+
// spec shaking v2 is enabled.
220+
let spec_shaking_impl = if export && spec_shaking_v2_enabled() {
221221
Some(shaking::generate_marker_impl(
222222
path,
223223
quote!(#ident),

soroban-sdk-macros/src/derive_struct.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use stellar_xdr::{
88
ScSpecEntry, ScSpecTypeDef, ScSpecUdtStructFieldV0, ScSpecUdtStructV0, StringM, WriteXdr,
99
};
1010

11-
use crate::{doc::docs_from_attrs, map_type::map_type, shaking, DEFAULT_XDR_RW_LIMITS};
11+
use crate::{
12+
doc::docs_from_attrs, map_type::map_type, shaking, spec_shaking_v2_enabled,
13+
DEFAULT_XDR_RW_LIMITS,
14+
};
1215

1316
// TODO: Add field attribute for including/excluding fields in types.
1417
// TODO: Better handling of partial types and types without all their fields and
@@ -107,9 +110,9 @@ pub fn derive_type_struct(
107110
None
108111
};
109112

110-
// SpecShakingMarker impl - only generated when spec is true and the
111-
// experimental_spec_shaking_v2 feature is enabled.
112-
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
113+
// SpecShakingMarker impl - only generated when spec is true and
114+
// spec shaking v2 is enabled.
115+
let spec_shaking_impl = if spec_shaking_v2_enabled() {
113116
spec_xdr.as_ref().map(|spec_xdr| {
114117
shaking::generate_marker_impl(
115118
path,

soroban-sdk-macros/src/derive_struct_tuple.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use stellar_xdr::{
88
ScSpecEntry, ScSpecTypeDef, ScSpecUdtStructFieldV0, ScSpecUdtStructV0, StringM, WriteXdr,
99
};
1010

11-
use crate::{doc::docs_from_attrs, map_type::map_type, shaking, DEFAULT_XDR_RW_LIMITS};
11+
use crate::{
12+
doc::docs_from_attrs, map_type::map_type, shaking, spec_shaking_v2_enabled,
13+
DEFAULT_XDR_RW_LIMITS,
14+
};
1215

1316
pub fn derive_type_struct_tuple(
1417
path: &Path,
@@ -96,9 +99,9 @@ pub fn derive_type_struct_tuple(
9699
None
97100
};
98101

99-
// SpecShakingMarker impl - only generated when spec is true and the
100-
// experimental_spec_shaking_v2 feature is enabled.
101-
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
102+
// SpecShakingMarker impl - only generated when spec is true and
103+
// spec shaking v2 is enabled.
104+
let spec_shaking_impl = if spec_shaking_v2_enabled() {
102105
spec_xdr.as_ref().map(|spec_xdr| {
103106
shaking::generate_marker_impl(
104107
path,

soroban-sdk-macros/src/lib.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ pub(crate) fn default_crate_path() -> Path {
8080
parse_str("soroban_sdk").unwrap()
8181
}
8282

83+
/// Returns true if spec shaking v2 should be used. Requires both the
84+
/// `experimental_spec_shaking_v2` feature to be enabled on the macro crate AND
85+
/// the `SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2` env var to be set
86+
/// at the time the macro expands (i.e. when the consumer crate is compiled).
87+
fn spec_shaking_v2_enabled() -> bool {
88+
cfg!(feature = "experimental_spec_shaking_v2")
89+
&& option_env!("SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2").is_some()
90+
}
91+
8392
#[derive(Debug, FromMeta)]
8493
struct ContractSpecArgs {
8594
name: Type,
@@ -428,10 +437,10 @@ pub fn contracttype(metadata: TokenStream, input: TokenStream) -> TokenStream {
428437
}
429438
// If the export argument has a value, do as it instructs regarding
430439
// exporting. If it does not have a value, export if the type is pub,
431-
// or always export when spec shaking is enabled.
440+
// or always export when spec shaking v2 is enabled.
432441
let gen_spec = if let Some(export) = args.export {
433442
export
434-
} else if cfg!(feature = "experimental_spec_shaking_v2") {
443+
} else if spec_shaking_v2_enabled() {
435444
true
436445
} else {
437446
matches!(input.vis, Visibility::Public(_))
@@ -502,10 +511,10 @@ pub fn contracterror(metadata: TokenStream, input: TokenStream) -> TokenStream {
502511
let attrs = &input.attrs;
503512
// If the export argument has a value, do as it instructs regarding
504513
// exporting. If it does not have a value, export if the type is pub,
505-
// or always export when spec shaking is enabled.
514+
// or always export when spec shaking v2 is enabled.
506515
let gen_spec = if let Some(export) = args.export {
507516
export
508-
} else if cfg!(feature = "experimental_spec_shaking_v2") {
517+
} else if spec_shaking_v2_enabled() {
509518
true
510519
} else {
511520
matches!(input.vis, Visibility::Public(_))
@@ -691,10 +700,9 @@ pub fn contractimport(metadata: TokenStream) -> TokenStream {
691700
}
692701
};
693702

694-
// Generate with options based on whether the experimental_spec_shaking_v2
695-
// feature is enabled.
703+
// Generate with options based on whether spec shaking v2 is enabled
696704
let opts = GenerateOptions {
697-
export: cfg!(feature = "experimental_spec_shaking_v2"),
705+
export: spec_shaking_v2_enabled(),
698706
};
699707
match generate_from_wasm_with_options(&wasm, &args.file, args.sha256.as_deref(), &opts) {
700708
Ok(code) => quote! { #code },

soroban-sdk/build.rs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,23 @@ pub fn main() {
2121
}
2222

2323
// When the experimental_spec_shaking_v2 feature is enabled, check for an env var from the
24-
// build system (Stellar CLI) that indicates it supports spec optimization using markers.
24+
// build system (like Stellar CLI) that indicates it supports spec optimization using markers.
25+
// If the env var is set, enable spec_shaking_v2 cfg for the crate. If not, fall back to
26+
// spec shaking v1 behavior and emit a warning on wasm targets.
27+
println!("cargo::rustc-check-cfg=cfg(spec_shaking_v2)");
2528
if std::env::var("CARGO_FEATURE_EXPERIMENTAL_SPEC_SHAKING_V2").is_ok() {
2629
let env_name = "SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2";
2730
println!("cargo::rerun-if-env-changed={env_name}");
28-
if std::env::var(env_name).is_err()
29-
&& std::env::var("CARGO_CFG_TARGET_FAMILY").unwrap_or_default() == "wasm"
30-
{
31-
panic!(
32-
"\
33-
\n\nerror: soroban-sdk feature 'experimental_spec_shaking_v2' requires stellar-cli v25.2.0+\
34-
\n\
35-
\nThe soroban-sdk 'experimental_spec_shaking_v2' feature requires building\
36-
\nwith `stellar contract build` from stellar-cli v25.2.0 or newer.\
37-
\n\
38-
\nTo fix, either:\
39-
\n - Build with `stellar contract build` using stellar-cli v25.2.0+\
40-
\n - Disable the feature by removing 'experimental_spec_shaking_v2' from\
41-
\n the soroban-sdk import features list in Cargo.toml.\
42-
\n"
31+
if std::env::var(env_name).is_ok() {
32+
println!("cargo::rustc-cfg=spec_shaking_v2");
33+
} else if std::env::var("CARGO_CFG_TARGET_FAMILY").unwrap_or_default() == "wasm" {
34+
println!(
35+
"cargo::warning=soroban-sdk: feature 'experimental_spec_shaking_v2' was enabled but not used, \
36+
because this build was not started by a tool that supports spec shaking v2. \
37+
Falling back to spec shaking v1. To use v2, use a build tool that supports \
38+
spec shaking v2 such as stellar-cli v25.2.0+ with `stellar contract build`. \
39+
To manually use v2 without a supporting build tool, set the env var \
40+
SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2 before building."
4341
);
4442
}
4543
}

0 commit comments

Comments
 (0)