Files
roto/TASKS.md
T
charles 2806772fd1 feat: gate alloc/std features for no_std/no_alloc support
Gate generated code behind feature flags so consumers can build in
no_std/no_alloc environments:

- Runtime: gate RotoMessage, RotoOwned, BufMutBuilder behind alloc
- Codegen: split DATA_IMPORTS, gate Owned* structs + service code
- Fix RevBuilder::with() to iterate forward without Vec allocation
- Wire up bytes/std via alloc feature (Bytes requires Arc)
- Update consumer Cargo.toml files with proper feature chains
- Add no_std_test example for embedded validation
2026-06-30 01:40:13 -04:00

4.4 KiB

Task: Verify Generated Code Works in no_std / no_alloc

Goal: Ensure all generated protobuf code compiles and runs in no_std + no_alloc environments. Features requiring alloc must be gated behind feature flags in the proto generator's output.

Status: COMPLETE


Findings

Already clean (no_std + no_alloc):

  • All reader structs (Message<'a>) — zero-copy accessors over &[u8]
  • All field accessors, enums, builders (MessageBuilder<'b>), typed iterators
  • Core runtime: ProtoAccessor, ProtoBuilder, RevBuilder, read_varint, write_varint, all iterators

Needs alloc feature gate:

  • RotoMessage trait — uses bytes::Bytes (has Arc internally)
  • RotoOwned trait — same issue
  • OwnedMessage structs generated by codegen
  • RevBuilder::with() — uses Vec<_> to collect raw fields
  • Bytes, BytesMut imports in DATA_IMPORTS

Needs std feature gate:

  • All service code (SERVICE_IMPORTS) — uses std::sync::Arc, std::pin::Pin, String, etc.
  • std::error::Error for RotoError (already gated)

Plan

Phase 1: Runtime (runtime/src/lib.rs, runtime/Cargo.toml)

  1. Gate RotoMessage and RotoOwned behind #[cfg(feature = "alloc")]
  2. Gate BufMutBuilder behind #[cfg(feature = "alloc")]
  3. Gate use bytes::BufMut — only import it conditionally
  4. Wire up bytes features in Cargo.toml:

Note: bytes::Bytes requires Arc internally, which is in std. Since bytes only has a std feature (no separate alloc), our alloc feature implies std:

[features]
default = ["std", "alloc"]
std = []
alloc = ["std", "bytes/std"]

Phase 2: Codegen — DATA_IMPORTS & Generated Data Code (codegen/src/generator/mod.rs)

  1. Split DATA_IMPORTS into unconditional + gated sections
  2. Gate OwnedMessage structs + RotoMessage impls with #[cfg(feature = "alloc")]
  3. Fix RevBuilder::with() — replaced Vec::collect() + .rev() with forward iteration

Phase 3: Codegen — Service Code (codegen/src/generator/mod.rs)

  1. Gate SERVICE_IMPORTS with #[cfg(feature = "std")]
  2. Gate all service traits, server impls, and *Server structs with #[cfg(feature = "std")]

Phase 4: Generated Consumer Setup

  1. Emit a Cargo.toml (or instruct consumers) with the right feature setup

Updated consumer Cargo.toml files:

  • examples/hello_world/Cargo.toml
  • roto-tonic/Cargo.toml
  • benches/Cargo.toml
[features]
default = ["std"]
std = ["roto-runtime/std", "alloc"]
alloc = ["roto-runtime/alloc"]

Phase 5: Validation

  1. Update examples/no_std_test to build with --no-default-features
  2. Run existing tests with default features
  3. Add a compile-test that verifies --no-default-features builds succeed

Dependency Graph

no_std + no_alloc  →  readers, enums, builders, iterators
         ↓ alloc   →  + OwnedMessage, RotoMessage trait (implies std via bytes::Bytes)
         ↓ std     →  + service code, std::error::Error

Files Modified

File Changes
runtime/src/lib.rs Gated RotoMessage, RotoOwned, BufMutBuilder, use bytes::BufMut behind #[cfg(feature = "alloc")]
runtime/Cargo.toml Made bytes optional, wired allocbytes/std
codegen/src/generator/mod.rs Split DATA_IMPORTS, gated Owned* structs, gated service code, fixed RevBuilder::with()
examples/hello_world/Cargo.toml Added std/alloc feature wiring
roto-tonic/Cargo.toml Added std/alloc feature wiring
benches/Cargo.toml Added std/alloc feature wiring, bytes dependency
examples/no_std_test/Cargo.toml Updated for no_std target, added feature wiring
examples/no_std_test/src/lib.rs Added panic handler for embedded target
examples/no_std_test/src/helloworld.rs Rewrote as minimal gated example

Validation

  • cargo build — all packages build with default features
  • cargo build --package roto-runtime --no-default-features — runtime builds without alloc
  • cargo test --all — 38 tests pass (runtime + codegen + integration)
  • examples/no_std_test builds with --no-default-features on thumbv7em-none-eabihf target
  • Generated code from integration test (test_helloworld_build) compiles successfully