Currently, a platform that is used to build a wasm file has to have a zig-main-function. Without it, you get an error like lib/std/start.zig:546:45: error: root source file has no member called 'main'
But the main-function is not needed. A "normal" platform needs the main function to start. But the wasm file gets called from a wasm-runtime that can call any exported function.
Brian Carroll said:
A good mental model here is that the wasm program is a dynamic library, not an executable. You don't want a
mainbecause themainis in the browser or node. That runs the event loop which enters JS, which calls Wasm. So Wasm is not really an entry point.
To remove the main function, zig has to be called with build-lib instead of build-exe and it also needs the argument -dynamic. Here is an example: https://github.com/roc-lang/roc/compare/main...ostcar:roc:wasm_without_main
I think for wasi we do need build-exe, so it seems like we should start using the currently unused _target: &Triple arg (see link_wasm32 fn) and add an if based on that.
Yeah that makes sense to me
I made #5585 for this
How do we specify that triple from the command line? As far as I know we only have --target=wasm32, and nothing to indicate WASI
Sounds like we should add a wasm32-wasi target flag
I don't think we should need it long-term (but maybe we do short-term)
because in the future I think we want to infer whether something is executable or not by looking at the precompiled host (and seeing if it's an executable binary or not)
Ok in Wasm I think the only way to do that is to parse the headers and check for an export called _start
(When the standard was defined, it was for the browser use case, and there was no concept of executables.)
It seems, the entry point for wasi will be renamed from _start to run in preview_2. I did not find any other source then this comment: https://github.com/WebAssembly/WASI/issues/19#issuecomment-1589803513
Another possibility would be to always use build-lib. If a platform author wants to support wasi, there just has to be an exported function _start() (or run() in preview 2). This example works without a main function: https://github.com/roc-lang/roc/compare/main...ostcar:wasm_without_main
Thanks for looking into this @Oskar Hahn! I'm no wasm expert but that seems like a good solution to me
Great idea!
However it does break with conventions a bit, and to a platform developer it might feel like a rough edge of Roc. I wonder, where can we document that this is needed?
What happens if we always use build-lib but the platform author defines main as per the usual convention? Does it "just work"? It would be great if it did.
(I'm on mobile right now, can't check)
I do remember though that the linker actually generates a _start to call main if it's not already present.
I actually had to implement this in the Wasm Dev backend to get it to work with WASI.
So I'm hoping that the platform author can write main, we can compile it with build-lib, and everything just works. Because lib vs exe is a pretty meaningless distinction in Wasm.
It seems, that with build-lib, that no _start function will be added. For the example to work, I had to add the start function manually: https://github.com/roc-lang/roc/compare/main...ostcar:wasm_without_main
Is this something roc can fix or is this something, zig has to do?
_start just contains a single call instruction to main.
Roc should be able generate that if we make it a special case for this target and just insert the appropriate IR.
But then if there's no main in the host, it would probably fail to link.
So we'd need to know this about the host somehow.
And if we know that, then why didn't we just call build-exe?
:thinking:
Last updated: Jun 16 2026 at 16:19 UTC