Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/test-with-openzeppelin-stellar-contracts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
fail-fast: false
matrix:
working-directory: ${{ fromJSON(needs.collect-crates.outputs.dirs) }}
experimental_spec_shaking_v2: [true, false]
defaults:
run:
working-directory: stellar-contracts/${{ matrix.working-directory }}
Expand Down Expand Up @@ -76,6 +77,15 @@ jobs:
sed -i 's|'"$crate"' = { \(.*\)version = "[^"]*"\(.*\)|'"$crate"' = { \1path = "'"$rel_path"'" \2|g' Cargo.toml
done

- name: Enable experimental_spec_shaking_v2 feature
if: matrix.experimental_spec_shaking_v2
working-directory: stellar-contracts
run: |
# Add feature to path-patched entries with existing features
sed -i '/soroban-sdk = {.*path = /s|features = \[|features = ["experimental_spec_shaking_v2", |' Cargo.toml
# Add features field to path-patched entries without features
sed -i '/soroban-sdk = {.*path = /{/features/!s| }|, features = ["experimental_spec_shaking_v2"] }|}' Cargo.toml

- name: Diff
run: (! git diff --exit-code) || (echo 'A diff is expected'; exit 1)

Expand All @@ -89,7 +99,7 @@ jobs:
- name: Set artifact name
if: steps.check-if-contract.outputs.is-a-contract == 'true'
id: artifact-name
run: echo "name=wasm-$(echo ${{ matrix.working-directory }} | sed 's/\//-/g')" | tee -a $GITHUB_OUTPUT
run: echo "name=wasm-$(echo ${{ matrix.working-directory }} | sed 's/\//-/g')${{ matrix.experimental_spec_shaking_v2 && '-spec-shaking-v2' || '' }}" | tee -a $GITHUB_OUTPUT

- name: Upload WASM artifacts
if: steps.check-if-contract.outputs.is-a-contract == 'true'
Expand Down
14 changes: 13 additions & 1 deletion .github/workflows/test-with-soroban-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ jobs:
fail-fast: false
matrix:
working-directory: ${{ fromJSON(needs.collect-examples.outputs.dirs) }}
experimental_spec_shaking_v2: [true, false]
# Exclude examples that depend on crates.io packages that transitively
# depend on soroban-sdk (e.g. soroban-poseidon). Cargo does not provide
# a way to override the version of soroban-sdk required as a transitive
Expand Down Expand Up @@ -102,6 +103,17 @@ jobs:
done
done

- name: Enable experimental_spec_shaking_v2 feature
if: matrix.experimental_spec_shaking_v2
working-directory: soroban-examples
run: |
find . -name Cargo.toml | while read -r file; do
# Add feature to path-patched entries with existing features
sed -i '/soroban-sdk = {.*path = /s|features = \[|features = ["experimental_spec_shaking_v2", |' "$file"
# Add features field to path-patched entries without features
sed -i '/soroban-sdk = {.*path = /{/features/!s| }|, features = ["experimental_spec_shaking_v2"] }|}' "$file"
done

- name: Diff
run: (! git diff --exit-code) || (echo 'A diff is expected'; exit 1)

Expand All @@ -113,7 +125,7 @@ jobs:

- name: Set artifact name
id: artifact-name
run: echo "name=wasm-$(echo ${{ matrix.working-directory }} | sed 's/\//-/g')" | tee -a $GITHUB_OUTPUT
run: echo "name=wasm-$(echo ${{ matrix.working-directory }} | sed 's/\//-/g')${{ matrix.experimental_spec_shaking_v2 && '-spec-shaking-v2' || '' }}" | tee -a $GITHUB_OUTPUT

- name: Upload WASM artifacts
uses: actions/upload-artifact@v4
Expand Down
16 changes: 2 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,9 @@ build: build-libs build-test-wasms
build-libs: fmt
cargo hack build --release $(foreach c,$(LIB_CRATES),--package $(c))

# First, build crate used as WASM deps to other test crates.
# Then, build `test_spec_shaking_v2` without the spec shaking v2 env var to verify
# that it falls back to spec_shaking_v1 behaviour.
# Then, build the test wasms with MSRV by default, with some meta disabled for
# binary stability for tests.
build-test-wasms: fmt
SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2=1 \
RUSTUP_TOOLCHAIN=$(TEST_CRATES_RUSTUP_TOOLCHAIN) \
RUSTFLAGS='--cfg soroban_sdk_internal_no_rssdkver_meta' \
cargo build --release --target wasm32v1-none --package test_spec_import
RUSTUP_TOOLCHAIN=$(TEST_CRATES_RUSTUP_TOOLCHAIN) \
RUSTFLAGS='--cfg soroban_sdk_internal_no_rssdkver_meta' \
cargo build --release --target wasm32v1-none --package test_spec_shaking_v2
cp target/wasm32v1-none/release/test_spec_shaking_v2.wasm \
target/wasm32v1-none/release/test_spec_shaking_v2_no_env.wasm
# Build the test wasms with MSRV by default, with some meta disabled for
# binary stability for tests.
SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2=1 \
RUSTUP_TOOLCHAIN=$(TEST_CRATES_RUSTUP_TOOLCHAIN) \
RUSTFLAGS='--cfg soroban_sdk_internal_no_rssdkver_meta' \
Expand Down
11 changes: 4 additions & 7 deletions soroban-sdk-macros/src/derive_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ use stellar_xdr::{
ScSpecUdtUnionCaseVoidV0, ScSpecUdtUnionV0, StringM, VecM, WriteXdr, SCSYMBOL_LIMIT,
};

use crate::{
doc::docs_from_attrs, map_type::map_type, shaking, spec_shaking_v2_enabled,
DEFAULT_XDR_RW_LIMITS,
};
use crate::{doc::docs_from_attrs, map_type::map_type, shaking, DEFAULT_XDR_RW_LIMITS};

pub fn derive_type_enum(
path: &Path,
Expand Down Expand Up @@ -184,9 +181,9 @@ pub fn derive_type_enum(
None
};

// SpecShakingMarker impl - only generated when spec is true and
// spec shaking v2 is enabled.
let spec_shaking_impl = if spec_shaking_v2_enabled() {
// SpecShakingMarker impl - only generated when spec is true and the
// experimental_spec_shaking_v2 feature is enabled.
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
spec_xdr.as_ref().map(|spec_xdr| {
// Flatten all variant field types for shaking calls, deduplicating
// to avoid redundant calls for types that appear in multiple variants.
Expand Down
8 changes: 4 additions & 4 deletions soroban-sdk-macros/src/derive_enum_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use syn::{

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

use crate::{doc::docs_from_attrs, shaking, spec_shaking_v2_enabled, DEFAULT_XDR_RW_LIMITS};
use crate::{doc::docs_from_attrs, shaking, DEFAULT_XDR_RW_LIMITS};

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

Expand Down Expand Up @@ -103,9 +103,9 @@ pub fn derive_type_enum_int(
None
};

// SpecShakingMarker impl - only generated when spec is true and
// spec shaking v2 is enabled.
let spec_shaking_impl = if spec_shaking_v2_enabled() {
// SpecShakingMarker impl - only generated when spec is true and the
// experimental_spec_shaking_v2 feature is enabled.
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
spec_xdr.as_ref().map(|spec_xdr| {
shaking::generate_marker_impl(
path,
Expand Down
8 changes: 4 additions & 4 deletions soroban-sdk-macros/src/derive_error_enum_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use syn::{
ext::IdentExt as _, spanned::Spanned, Attribute, DataEnum, Error, ExprLit, Ident, Lit, Path,
};

use crate::{doc::docs_from_attrs, shaking, spec_shaking_v2_enabled, DEFAULT_XDR_RW_LIMITS};
use crate::{doc::docs_from_attrs, shaking, DEFAULT_XDR_RW_LIMITS};

pub fn derive_type_error_enum_int(
path: &Path,
Expand Down Expand Up @@ -100,9 +100,9 @@ pub fn derive_type_error_enum_int(
None
};

// SpecShakingMarker impl - only generated when spec is true and
// spec shaking v2 is enabled.
let spec_shaking_impl = if spec_shaking_v2_enabled() {
// SpecShakingMarker impl - only generated when spec is true and the
// experimental_spec_shaking_v2 feature is enabled.
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
spec_xdr.as_ref().map(|spec_xdr| {
shaking::generate_marker_impl(
path,
Expand Down
10 changes: 5 additions & 5 deletions soroban-sdk-macros/src/derive_event.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
attribute::remove_attributes_from_item, default_crate_path, doc::docs_from_attrs,
map_type::map_type, shaking, spec_shaking_v2_enabled, symbol, DEFAULT_XDR_RW_LIMITS,
map_type::map_type, shaking, symbol, DEFAULT_XDR_RW_LIMITS,
};
use darling::{ast::NestedMeta, Error, FromMeta};
use heck::ToSnakeCase;
Expand Down Expand Up @@ -202,7 +202,7 @@ fn derive_impls(args: &ContractEventArgs, input: &DeriveInput) -> Result<TokenSt
"__SPEC_XDR_EVENT_{}",
input.ident.unraw().to_string().to_uppercase()
);
let spec_shaking_call = if export && spec_shaking_v2_enabled() {
let spec_shaking_call = if export && cfg!(feature = "experimental_spec_shaking_v2") {
Some(quote! { <Self as #path::SpecShakingMarker>::spec_shaking_marker(); })
} else {
None
Expand All @@ -220,9 +220,9 @@ fn derive_impls(args: &ContractEventArgs, input: &DeriveInput) -> Result<TokenSt
}
};

// SpecShakingMarker impl - only generated when export is true and
// spec shaking v2 is enabled.
let spec_shaking_impl = if export && spec_shaking_v2_enabled() {
// SpecShakingMarker impl - only generated when export is true and the
// experimental_spec_shaking_v2 feature is enabled.
let spec_shaking_impl = if export && cfg!(feature = "experimental_spec_shaking_v2") {
Some(shaking::generate_marker_impl(
path,
quote!(#ident),
Expand Down
11 changes: 4 additions & 7 deletions soroban-sdk-macros/src/derive_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ use stellar_xdr::{
ScSpecEntry, ScSpecTypeDef, ScSpecUdtStructFieldV0, ScSpecUdtStructV0, StringM, WriteXdr,
};

use crate::{
doc::docs_from_attrs, map_type::map_type, shaking, spec_shaking_v2_enabled,
DEFAULT_XDR_RW_LIMITS,
};
use crate::{doc::docs_from_attrs, map_type::map_type, shaking, DEFAULT_XDR_RW_LIMITS};

// TODO: Add field attribute for including/excluding fields in types.
// TODO: Better handling of partial types and types without all their fields and
Expand Down Expand Up @@ -113,9 +110,9 @@ pub fn derive_type_struct(
None
};

// SpecShakingMarker impl - only generated when spec is true and
// spec shaking v2 is enabled.
let spec_shaking_impl = if spec_shaking_v2_enabled() {
// SpecShakingMarker impl - only generated when spec is true and the
// experimental_spec_shaking_v2 feature is enabled.
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
spec_xdr.as_ref().map(|spec_xdr| {
shaking::generate_marker_impl(
path,
Expand Down
11 changes: 4 additions & 7 deletions soroban-sdk-macros/src/derive_struct_tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ use stellar_xdr::{
ScSpecEntry, ScSpecTypeDef, ScSpecUdtStructFieldV0, ScSpecUdtStructV0, StringM, WriteXdr,
};

use crate::{
doc::docs_from_attrs, map_type::map_type, shaking, spec_shaking_v2_enabled,
DEFAULT_XDR_RW_LIMITS,
};
use crate::{doc::docs_from_attrs, map_type::map_type, shaking, DEFAULT_XDR_RW_LIMITS};

pub fn derive_type_struct_tuple(
path: &Path,
Expand Down Expand Up @@ -102,9 +99,9 @@ pub fn derive_type_struct_tuple(
None
};

// SpecShakingMarker impl - only generated when spec is true and
// spec shaking v2 is enabled.
let spec_shaking_impl = if spec_shaking_v2_enabled() {
// SpecShakingMarker impl - only generated when spec is true and the
// experimental_spec_shaking_v2 feature is enabled.
let spec_shaking_impl = if cfg!(feature = "experimental_spec_shaking_v2") {
spec_xdr.as_ref().map(|spec_xdr| {
shaking::generate_marker_impl(
path,
Expand Down
22 changes: 7 additions & 15 deletions soroban-sdk-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,6 @@ pub(crate) fn default_crate_path() -> Path {
parse_str("soroban_sdk").unwrap()
}

/// Returns true if spec shaking v2 should be used. Requires both the
/// `experimental_spec_shaking_v2` feature to be enabled on the macro crate AND
/// the `SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2` env var to be set
/// at the time the macro expands (i.e. when the consumer crate is compiled).
fn spec_shaking_v2_enabled() -> bool {
cfg!(feature = "experimental_spec_shaking_v2")
&& option_env!("SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2").is_some()
}

#[derive(Debug, FromMeta)]
struct ContractSpecArgs {
name: Type,
Expand Down Expand Up @@ -437,10 +428,10 @@ pub fn contracttype(metadata: TokenStream, input: TokenStream) -> TokenStream {
}
// If the export argument has a value, do as it instructs regarding
// exporting. If it does not have a value, export if the type is pub,
// or always export when spec shaking v2 is enabled.
// or always export when spec shaking is enabled.
let gen_spec = if let Some(export) = args.export {
export
} else if spec_shaking_v2_enabled() {
} else if cfg!(feature = "experimental_spec_shaking_v2") {
true
} else {
matches!(input.vis, Visibility::Public(_))
Expand Down Expand Up @@ -511,10 +502,10 @@ pub fn contracterror(metadata: TokenStream, input: TokenStream) -> TokenStream {
let attrs = &input.attrs;
// If the export argument has a value, do as it instructs regarding
// exporting. If it does not have a value, export if the type is pub,
// or always export when spec shaking v2 is enabled.
// or always export when spec shaking is enabled.
let gen_spec = if let Some(export) = args.export {
export
} else if spec_shaking_v2_enabled() {
} else if cfg!(feature = "experimental_spec_shaking_v2") {
true
} else {
matches!(input.vis, Visibility::Public(_))
Expand Down Expand Up @@ -700,9 +691,10 @@ pub fn contractimport(metadata: TokenStream) -> TokenStream {
}
};

// Generate with options based on whether spec shaking v2 is enabled
// Generate with options based on whether the experimental_spec_shaking_v2
// feature is enabled.
let opts = GenerateOptions {
export: spec_shaking_v2_enabled(),
export: cfg!(feature = "experimental_spec_shaking_v2"),
};
match generate_from_wasm_with_options(&wasm, &args.file, args.sha256.as_deref(), &opts) {
Ok(code) => quote! { #code },
Expand Down
1 change: 0 additions & 1 deletion soroban-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ ark-bls12-381 = { version = "0.5", default-features = false, features = ["curve"
ark-ff = { version = "0.5", default-features = false }

[features]
default = ["experimental_spec_shaking_v2"]
alloc = []
testutils = ["soroban-sdk-macros/testutils", "soroban-env-host/testutils", "soroban-ledger-snapshot/testutils", "dep:ed25519-dalek", "dep:arbitrary", "dep:derive_arbitrary", "dep:ctor", "dep:soroban-ledger-snapshot"]
experimental_spec_shaking_v2 = ["soroban-sdk-macros/experimental_spec_shaking_v2"]
Expand Down
30 changes: 16 additions & 14 deletions soroban-sdk/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,25 @@ pub fn main() {
}

// When the experimental_spec_shaking_v2 feature is enabled, check for an env var from the
// build system (like Stellar CLI) that indicates it supports spec optimization using markers.
// If the env var is set, enable spec_shaking_v2 cfg for the crate. If not, fall back to
// spec shaking v1 behavior and emit a warning on wasm targets.
println!("cargo::rustc-check-cfg=cfg(spec_shaking_v2)");
// build system (Stellar CLI) that indicates it supports spec optimization using markers.
if std::env::var("CARGO_FEATURE_EXPERIMENTAL_SPEC_SHAKING_V2").is_ok() {
let env_name = "SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2";
println!("cargo::rerun-if-env-changed={env_name}");
if std::env::var(env_name).is_ok() {
println!("cargo::rustc-cfg=spec_shaking_v2");
} else if std::env::var("CARGO_CFG_TARGET_FAMILY").unwrap_or_default() == "wasm" {
println!(
"cargo::warning=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."
if std::env::var(env_name).is_err()
&& std::env::var("CARGO_CFG_TARGET_FAMILY").unwrap_or_default() == "wasm"
{
panic!(
"\
\n\nerror: soroban-sdk feature 'experimental_spec_shaking_v2' requires stellar-cli v25.2.0+\
\n\
\nThe soroban-sdk 'experimental_spec_shaking_v2' feature requires building\
\nwith `stellar contract build` from stellar-cli v25.2.0 or newer.\
\n\
\nTo fix, either:\
\n - Build with `stellar contract build` using stellar-cli v25.2.0+\
\n - Disable the feature by removing 'experimental_spec_shaking_v2' from\
\n the soroban-sdk import features list in Cargo.toml.\
\n"
);
Comment thread
mootz12 marked this conversation as resolved.
Outdated
}
}
Expand Down
30 changes: 9 additions & 21 deletions soroban-sdk/src/_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,12 @@
//! [`Address::from_payload`][crate::Address::from_payload]) that are easy to
//! misuse. Use with care.
//!
//! ## `experimental_spec_shaking_v2` (default)
//! ## `experimental_spec_shaking_v2`
//!
//! Enables v2 spec shaking, an improved mechanism for controlling which type,
//! event, and function definitions appear in a contract's spec. This feature is
//! enabled by default.
//! event, and function definitions appear in a contract's spec.
//!
//! This feature is no longer experimental. It was previously introduced as an
//! experimental opt-in feature and is now being rolled out as the default. The
//! original feature name `experimental_spec_shaking_v2` is preserved for
//! backwards compatibility. The feature is expected to be removed in v27, at
//! which point spec shaking v2 will be always on.
//!
//! ### Spec Shaking v1 (disabled by default, use `default-features = false`)
//! ### Spec Shaking v1 (default, no feature flag)
//!
//! - Lib imports (via `contractimport!`): exported
//! - Wasm imports (via `contractimport!`): not exported
Expand All @@ -47,7 +40,7 @@
//! - All events: exported
//! - All functions: exported
//!
//! ### Spec Shaking v2 (default)
//! ### Spec Shaking v2 (this feature)
//!
//! - Everything exported (types, events, functions, imports)
//! - Unused entries shaken out using dead code / spec elimination
Expand Down Expand Up @@ -135,16 +128,11 @@
//!
//! ### Build Requirements
//!
//! Spec shaking v2 requires the
//! `SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2` environment variable to
//! be set at build time. This is automatically set by `stellar contract build`
//! from `stellar-cli` v25.2.0 or newer.
//!
//! When the `experimental_spec_shaking_v2` feature is enabled but the env var
//! is not set, the SDK falls back to spec shaking v1 behavior and emits a
//! build warning on wasm targets. This allows contracts to build with plain
//! `cargo build` without errors, while still benefiting from v2 when built
//! with compatible tooling.
//! This feature requires building with `stellar contract build` from
//! `stellar-cli` v25.2.0 or newer. Building directly with `cargo build` will
//! produce a build error unless the
Comment thread
mootz12 marked this conversation as resolved.
Outdated
//! `SOROBAN_SDK_BUILD_SYSTEM_SUPPORTS_SPEC_SHAKING_V2` environment variable is
//! set.
//!
//! [`contracttype`]: crate::contracttype
//! [`contracterror`]: crate::contracterror
Expand Down
Loading
Loading