Wasm on FreeBSD

Continuing the discussion from State of Wasm for Rust:

I wonder, what's the minimal way to get WebAssembly running on FreeBSD. I still get stuck here:

% wasm-pack build
[INFO]: 🎯  Checking for the Wasm target...
[INFO]: 🌀  Compiling to Wasm...
   Compiling proc-macro2 v1.0.56
   Compiling unicode-ident v1.0.8
   Compiling quote v1.0.26
   Compiling wasm-bindgen-shared v0.2.84
   Compiling log v0.4.17
   Compiling syn v1.0.109
   Compiling cfg-if v1.0.0
   Compiling bumpalo v3.12.1
   Compiling once_cell v1.17.1
   Compiling wasm-bindgen v0.2.84
   Compiling wasm-bindgen-backend v0.2.84
   Compiling wasm-bindgen-macro-support v0.2.84
   Compiling wasm-bindgen-macro v0.2.84
   Compiling console_error_panic_hook v0.1.7
   Compiling hello-wasm v0.1.0 (/usr/home/jbe/rust-experiment/webasm/hello-wasm)
warning: function `set_panic_hook` is never used
 --> src/utils.rs:1:8
  |
1 | pub fn set_panic_hook() {
  |        ^^^^^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: `hello-wasm` (lib) generated 1 warning
    Finished release [optimized] target(s) in 5.17s
[INFO]: ⬇️  Installing wasm-bindgen...
Error: no prebuilt wasm-opt binaries are available for this platform: Unrecognized target!
To disable `wasm-opt`, add `wasm-opt = false` to your package metadata in your `Cargo.toml`.
Caused by: no prebuilt wasm-opt binaries are available for this platform: Unrecognized target!
To disable `wasm-opt`, add `wasm-opt = false` to your package metadata in your `Cargo.toml`.

Do I really have to install more, as recommended here:

?

I just want to try a very very minimal example to see and/or understand the underlying principles and workflow. Ideally, I don't want to use a template but try to understand things from the bottom-up instead of installing a lot of tools.

Can't I just compile a super simple function to wasm and execute it in a web browser? Why are things so complicated? Or do I just miss to find proper documentation?

1 Like

I'm not quite sure I understand what you are trying to accomplish. Do you want to execute WASM on your BSD machine (with a runtime like wasmer or wasmtime) or do you just want to cross-compile on your BSD machine to browser-compatible WASM? The first quote suggests the former while the second quote suggests the latter.

I'm not an expert on either one, but the wasm32-unknown-unknown target works quite well out of the box on my Linux machine. I have it installed, because I played around with yew, not because I do any serious development with WASM, so could be that wasm32-unknown-unknown works well for my toy problems but not for more serious programs, I don't know. This suggests that it is probably not very useful beyond web-development, as it states that most of std is unavailable, probably including set_panic_hook.

I meant cross-compiling, sorry for not being more specific.

So I guess I can install it with rustup? Will try that later. Not sure how to setup my Cargo.toml to cross-compile, but will try to figure that out.

Well, I don'r expect I/O to work, but an allocator / Vec would be nice. I'll see later if I can get it work.

Yes, rustup target add wasm32-unknown-unknown

No. Strictly speaking, to compile to Wasm you don't need anything more than the toolchain. However, to write useful programs to run in browsers, you will need at least wasm-bindgen to generate glue code between the JS and Wasm worlds, unless you only want to pass integers to and from the Rust code. wasm-pack invokes wasm-bindgen for you and does a bunch of other stuff which you may or may not want.

If you want to use wasm-pack you can configure it to not expect wasm-opt, as the help message notes, or perhaps do a dev build, wasm-pack build --dev, that will skip that as well as having cargo use the dev profile instead of the release profile. (Without an option, wasm-pack defaults to release, which is the opposite of cargo's behavior.)

You may also want to pass --target web to get output suitable for direct use in a browser.

The standard library for wasm32-unknown-unknown supports:

  • Everything in the core library
  • an allocator and everything in the alloc library (Box, Vec, BTreeMap, etc.)
  • HashMap
  • all the thread-safe types (Arc, Mutex, etc.)

It does not support:

  • actually spawning threads
  • filesystem
  • network access
  • time access (SystemTime and Instant)
  • unwinding (all panics are fatal)

It is quite usable by itself for “performing computation”; for interacting with the outside world you need to talk to JS APIs usually using wasm-bindgen bindings (such as web-sys) or other libraries that do that for you (e.g. the reqwest HTTP client supports using the browser to send requests).

2 Likes

These libraries and tools are akin to ld, objcopy, otool, ar, nm, etc. You could ask "why do I need ld and ar, why can't I just compile a simple function with gcc?", and the answer would be the same — the compilation doesn't just have one phase, and different tools are needed for different stages.

In which Cargo.toml file does this need to go? Do I need to patch wasm-bindgen for it? I tried to put it in my hello-wasm/Cargo.toml, but there it didn't work.

wasm-pack build --dev works on FreeBSD for now. Thanks for that hint.

Using wasm-pack new hello-wasm and compiling that example, I now got the following files in my pkg/ directory:

% ls pkg/
README.md
hello_wasm.d.ts
hello_wasm.js
hello_wasm_bg.wasm
hello_wasm_bg.wasm.d.ts
package.json

If I naively add a test.html file with…

<html>
    <head>
        <script src="hello_wasm.js"></script>
    </head>
    <body>
    </body>
</html>

…then this will fail in my browser:

Uncaught SyntaxError: export declarations may only appear at top level of a module
hello_wasm.js:37

In line 37 that file says:

export function greet() {
    wasm.greet();
}

Please apologize my lack of knowledge in regard to modern JavaScript. I haven't worked with it for a long while. What am I doing wrong?

I also looked into the tutorial. The tutorials propose I should publish an NPM package. But I don't want to publish an NPM package (or publish anything in that matter). I just want to test things on my local machine.

Which browser and browser version are you using?

% firefox --version
Mozilla Firefox 112.0.2
% uname -mrsv
FreeBSD 13.1-RELEASE-p6 FreeBSD 13.1-RELEASE-p6 GENERIC amd64

You don't need to publish a NPM package to use it. But if you don't find any value in NPM at all (in the end, I did not either, but I would have if I wanted to use existing JS libraries for UI), use --target web instead.

Either way you will need to load the script as a module; add type="module" to your <script> tag. That is what the syntax error meant.

2 Likes

What you proposed fixed the error, but I don't see how I can access the greet function yet.

I see there is a guide by Mozilla, but I'm still confused. I guess I need to initialize the module somehow? How do I get the greet function to be available in my DOM tree in the webbrowser?

The hello_wasm.js file looks as follows:

let wasm;

const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });

cachedTextDecoder.decode();

let cachedUint8Memory0 = null;

/* … */

export function greet() {
    wasm.greet();
}

async function load(module, imports) {
        /* … */
}

/* … */

function initSync(module) {
    const imports = getImports();

    initMemory(imports);

    if (!(module instanceof WebAssembly.Module)) {
        module = new WebAssembly.Module(module);
    }

    const instance = new WebAssembly.Instance(module, imports);

    return finalizeInit(instance, module);
}

async function init(input) {
    if (typeof input === 'undefined') {
        input = new URL('hello_wasm_bg.wasm', import.meta.url);
    }
    const imports = getImports();

    if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
        input = fetch(input);
    }

    initMemory(imports);

    const { instance, module } = await load(await input, imports);

    return finalizeInit(instance, module);
}

export { initSync }
export default init;

How would my HTML file need to look like to access the greet function? Or am I doing something fundamentally wrong?

(I already started a webserver instead of using file://, because apparently loading a module doesn't work due to cross-origin policies.)

You did need to do something like

<html>
    <head>
        <script type="module">
            import {default as init, greet} from "./hello_wasm.js";
            await init();
            greet();
        </script>
    </head>
    <body>
    </body>
</html>

I think. You have to import hello_wasm.js as javascript module from the javascript module in which you want to use it. Unlike non-module javascript files you aren't expected to put any exports of a module in the global scope, so if you want to use an export of a javascript module, you have to import it using the import statement.

1 Like

I looked at this and figured out how to call a wasm function from a web page manually.

The mdn docs leave out a detail You need to put an "env" block in the javascripts "importObject"

var importObject = {
            env: {

Or at least that was the stumbling block for me.

1 Like

Of course you are aware of wat? If you compile a minimal rust library into wasm and turn that into wat, you can see the "env" inside.

This worked finally. I'll try to go from there to get a better understanding how everything works. Thanks a lot to everyone.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.