maturin icon indicating copy to clipboard operation
maturin copied to clipboard

UniFFI binding generation ignores code-based exports when UDL file exists

Open JoFrost opened this issue 7 months ago • 0 comments

Bug Description

When using UniFFI with Maturin for Rust-Python bindings, I discovered that if a UDL file exists in the source directory, Maturin exclusively uses that file and ignores the exported methods (with uniffi::export) inside the code itself, making the bindings incomplete:

2025-05-22T09:44:30.311582Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK/__init__.py
2025-05-22T09:44:30.311844Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK/sdk_ffi.py from /home/snow/rust-sdk/target/maturin/uniffi/RustSDK/sdk_ffi.py
2025-05-22T09:44:30.312358Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK/libuniffi_api-sdk.so from /home/snow/rust-sdk/target/maturin/librust-sdk.so
2025-05-22T09:44:33.513033Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK-0.6.3.dist-info/RECORD
📦 Built wheel to /home/snow/rust-sdk/target/wheels/RustSDK-0.6.3-py3-none-manylinux_2_34_x86_64.whl

This creates an unfortunate limitation where developers must choose between:

  • Using UDL files and losing all code based exports
  • Not using UDL files at all, which in some setups causes issues, since your code might rely on types declared in the UDL for proper imports.

I was able to find out why by looking in Maturin's code, for some reasons, if an UDL file is present, the library mode is totally ignored, only generating the code from the UDL. Forcibly activating the library mode by modyfing the code allowed me to have a fully working wheel, but this shouldn't be necessary in the first place:

2025-05-22T13:39:45.999524Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK/__init__.py
2025-05-22T13:39:45.999884Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK/sdk_ui.py from /home/snow/rust-sdk/target/maturin/uniffi/RustSDK/sdk_ui.py
2025-05-22T13:39:46.000324Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK/sdk.py from /home/snow/rust-sdk/target/maturin/uniffi/RustSDK/sdk.py
2025-05-22T13:39:46.001114Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK/sdk_ffi.py from /home/snow/rust-sdk/target/maturin/uniffi/RustSDK/sdk_ffi.py
2025-05-22T13:39:46.027227Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK/sdk_crypto.py from /home/snow/rust-sdk/target/maturin/uniffi/RustSDK/sdk_crypto.py
2025-05-22T13:39:46.028042Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK/sdk_base.py from /home/snow/rust-sdk/target/maturin/uniffi/RustSDK/sdk_base.py
2025-05-22T13:39:46.028611Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK/endpoints.py from /home/snow/rust-sdk/target/maturin/uniffi/RustSDK/endpoints.py
2025-05-22T13:39:46.029231Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK/sdk_common.py from /home/snow/rust-sdk/target/maturin/uniffi/RustSDK/sdk_common.py
2025-05-22T13:39:46.030556Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK/libsdk_ffi.so from /home/snow/rust-sdk/target/maturin/libsdk_ffi.so
2025-05-22T13:39:49.688572Z DEBUG build_wheels: maturin::module_writer: Adding RustSDK-0.6.3.dist-info/RECORD
📦 Built wheel to dist/RustSDK-0.6.3-py3-none-manylinux_2_34_x86_64.whl
2025-05-22T13:39:49.690911Z  INFO build_wheels: maturin::build_context: close time.busy=93.6s time.idle=2.87µs

This makes the binding fully working, with all methods properly exported.

Is it planned in the near future to be able to force the library mode, if it's needed? This would allow to support hybrid approachs. I would be happy to contribute and make this change myself if this change is not planned in a near future.

Thanks!

Your maturin version (maturin --version)

1.8.6

Your Python version (python -V)

3.10.12

Your pip version (pip -V)

22.0.2

What bindings you're using

uniffi

Does cargo build work?

  • [x] Yes, it works

If on windows, have you checked that you aren't accidentally using unix path (those with the forward slash /)?

  • [ ] Yes

Steps to Reproduce

  1. Have a project with an UDL, and call uniffi::generate_scaffolding("src/api.udl").unwrap(); from the build.rs
  2. Have methods that use uniffi::export in your code
  3. Run maturin build in your project. You will then realize that the uniffi bindings will only have what was declared in the UDL.

JoFrost avatar May 22 '25 14:05 JoFrost