Skip to content

Link iOS example with rustc, and avoid C trampoline #14780

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 17, 2025

Conversation

madsmtm
Copy link
Contributor

@madsmtm madsmtm commented Aug 16, 2024

Objective

On iOS:

  • Allow std to do its runtime initialization.
  • Avoid requiring the user to specify linked libraries and framework in Xcode.
  • Reduce the amount of work that #[bevy_main] does
    • In the future we may also be able to eliminate the need for it on Android, cc @daxpedda.

Solution

We previously:

  • Exposed an extern "C" fn main_rs entry point.
  • Ran Cargo in a separate Xcode target as an external build system.
  • Imported that as a dependency of bevy_mobile_example.app.
  • Compiled a trampoline C file with Xcode that called main_rs.
  • Linked that via. Xcode.

All of this is unnecessary; rustc is well capable of creating iOS executables, the trick is just to place it at the correct location for Xcode to understand it, namely $TARGET_BUILD_DIR/$EXECUTABLE_PATH (places it in bevy_mobile_example.app/bevy_mobile_example).

Note: We might want to wait with the changes to #[bevy_main] until the problem is resolved on Android too, to make the migration easier.

Testing

Open the Xcode project, and build for an iOS target.


Migration Guide

If you have been building your application for iOS:

Previously, the #[bevy_main] attribute created a main_rs entry point that most Xcode templates were using to run your Rust code from C. This was found to be unnecessary, as you can simply let Rust build your application as a binary, and run that directly.

You have two options for dealing with this:

If you've added further C code and Xcode customizations, or it makes sense for your use-case to continue link with Xcode, you can revert to the old behaviour by adding #[no_mangle] extern "C" main_rs() { main() } to your main.rs. Note that the old approach of linking a static library prevents the Rust standard library from doing runtime initialization, so certain functionality provided by std might be unavailable (stack overflow handlers, stdout/stderr flushing and other such functionality provided by the initialization routines).

The other, preferred option is to remove your "compile" and "link" build phases, and instead replace it with a "run script" phase that invokes cargo build --bin ..., and moves the built binary to the Xcode path $TARGET_BUILD_DIR/$EXECUTABLE_PATH. An example of how to do this can be viewed at [INSERT LINK TO UPDATED EXAMPLE PROJECT].
To make the debugging experience in Xcode nicer after this, you might also want to consider either enabling panic = "abort" or to set a breakpoint on the rust_panic symbol.

We previously:
- Exposed an `extern "C" fn main_rs` entry point.
- Ran Cargo in a separate Xcode target as an external build system.
- Imported that as a dependency of `bevy_mobile_example.app`.
- Compiled a trampoline C file with Xcode that called `main_rs`.
- Linked that via. Xcode.

All of this is unnecessary; rustc is well capable of creating iOS
executables, the trick is just to place it at the correct location for
Xcode to understand it, namely `$TARGET_BUILD_DIR/$EXECUTABLE_PATH`.

This:
- Allows `std` to do its runtime initialization.
- Avoids requiring the user to specify linked libraries and framework in
  Xcode.
- Reduces the amount of work that `#[bevy_main]` does (in the future we
  may also be able to eliminate it on Android).
Copy link
Contributor

Welcome, new contributor!

Please make sure you've read our contributing guide and we look forward to reviewing your pull request shortly ✨

@alice-i-cecile alice-i-cecile added O-iOS Specific to the iOS mobile operating system D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Aug 16, 2024
@alice-i-cecile alice-i-cecile added the M-Needs-Release-Note Work that should be called out in the blog due to impact label Aug 16, 2024
@extrawurst
Copy link
Contributor

@madsmtm looks like a great simplification! I like it. I tested your branch on my mac and at least a simulator build worked exactly as expected, both using make run and xcode-itself 👍

Since its hard for me to make my whole project (and its dependencies) use this branch of bevy I cannot give it a spin in real-world projects though, so my only concern is if this affects any objc2 usage from withing rust or projects that put actual objc/swift code into their xcode project - I strongly assume this should not be affected by this change, right?

We might instead want to wait with the changes to #[bevy_main] before the problem is resolved on Android too, to make the migration easier.

Can this be cfgd to get it to work in both for now? or is there another workaround? I think we too often hold up PRs because perfect is the enemy of done

@madsmtm
Copy link
Contributor Author

madsmtm commented Aug 17, 2024

if this affects any objc2 usage from withing rust or projects that put actual objc/swift code into their xcode project

This PR is really mostly about changing the default recommendation to link using rustc instead of using the trampoline.

Users can still link Objective-C and/or Swift code into their project using the usual mechanisms, either by letting Rust drive it via. build.rs, or by going with the approach that Bevy did before, and exposing an extern "C" fn and linking with Xcode instead.

Can this be cfgd to get it to work in both for now?

We could just avoid making that change, and only changing this in the example - it doesn't matter if the main_rs function is unused.

@madsmtm
Copy link
Contributor Author

madsmtm commented Aug 18, 2024

I have now attempted to write a short migration guide (see above), but dealing with Xcode really is a rather hairy process for the uninitiated, I fear that a lot of users might not be able to make this work, though I'm not sure what to do about that. Feel free to edit the PR description to make the language clearer!

Copy link
Contributor

@BenjaminBrienen BenjaminBrienen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have an iOS device to test this, but it looks good.

@BenjaminBrienen BenjaminBrienen added S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged C-Examples An addition or correction to our examples and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Jan 22, 2025
@mockersf mockersf added this to the 0.16 milestone Feb 7, 2025
@ChristopherBiscardi
Copy link
Contributor

I have this branch working in simulator and am attempting to build it for a real device but the install step is taking a suspiciously long time before failing (rust build step finishes in 1s, installing can take 5min before failing). Might have to clear up some codesigning config.

Failed to verify code signature of /var/installd/Library/Caches/com.apple.mobile.installd.staging/temp.VZIDwx/extracted/bevy_mobile_example.app : 0xe800801c (No code signature found.)

screenshot-2025-02-25-at-18 32 11@2x

Copy link
Contributor

@ChristopherBiscardi ChristopherBiscardi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is working on-device for me. This screenshot is from an iphone 11 pro max on iOS 18.3.1.

IMG_0348


My previous issue was just not having set the "Code Signing Identity" field 😅 (in case it helps anyone searching this in the future)

screenshot-2025-02-25-at-20 14 56@2x

@mockersf
Copy link
Member

mockersf commented Mar 4, 2025

resolved conflicts with main and fixed CI (can't import internal bevy crates in the mobile example as its an external crate itself so it's safe to not check it)

@extrawurst
Copy link
Contributor

@mockersf is there anything left to do here? I dont think the Waiting-On-Author label is correct here, right?

@mockersf
Copy link
Member

I would have liked an extra review after my last commits, otherwise I would merge commits on main by me without a review

@madsmtm
Copy link
Contributor Author

madsmtm commented Mar 17, 2025

I just tested it myself, and can confirm that building still works (though GitHub won't let me mark the PR as approved/reviewed).

@mockersf mockersf added this pull request to the merge queue Mar 17, 2025
Merged via the queue into bevyengine:main with commit e6a6c9f Mar 17, 2025
34 checks passed
@alice-i-cecile
Copy link
Member

Thank you to everyone involved with the authoring or reviewing of this PR! This work is relatively important and needs release notes! Head over to bevyengine/bevy-website#2004 if you'd like to help out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-Examples An addition or correction to our examples D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes M-Needs-Release-Note Work that should be called out in the blog due to impact O-iOS Specific to the iOS mobile operating system S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants