Skip to content

WebAssembly

Source: examples/wasm/

Cross-compile Fortran to WebAssembly using Emscripten.

Prerequisites

Add emsdk to your MODULE.bazel:

starlark
bazel_dep(name = "emsdk", version = "4.0.7")

3.A. Hello World

wasm/
├── BUILD.bazel
└── hello.f90
fortran
program hello
    implicit none
    print *, "Hello from wasm32 with rules_fortran!"
end program hello
starlark
# cross compilation to webassembly

load("@emsdk//emscripten_toolchain:wasm_cc_binary.bzl", "wasm_cc_binary")
load("@rules_cc//cc:defs.bzl", "cc_binary")
load("@rules_fortran//fortran:defs.bzl", "fortran_library")

# Example-3.A hello world
fortran_library(
    name = "hello",
    srcs = ["hello.f90"],  # compiles the .f90 to an object file (.o)
)
#  _QQmain:                    # internal Fortran program entry (called by main)
#    (whole bunch of fortran asm...)
#  main:                       # the standard C entry point
#    call   5e <main+0xe>      # call 1: _FortranAProgramStart
#    call   63 <main+0x13>     # call 2: _QQmain
#    call   68 <main+0x18>     # call 3: _FortranAProgramEndStatement
#    xor    %eax,%eax
#    ret

# this `cc_binary` just wraps fortran intermediaries. main entry point from the
# original source is preserved. in this case, `main` and `_QQmain`.
# since there's no C main(), the linker picks up main from the Fortran library
cc_binary(
    name = "hello_cc",
    srcs = [],  # look ma no source!
    deps = [":hello"],
)

# produce final wasm output
wasm_cc_binary(
    name = "hello_wasm",
    cc_target = ":hello_cc",
    outputs = [
        "hello_cc.js",
        "hello_cc.wasm",
    ],
)

# Example-3.B full lapacke client, only single for faster build
cc_binary(
    name = "full",
    srcs = [
        "full.c",
    ],
    deps = [
        "@blas//:single",
        "@lapack//:single",
        "@lapacke//:single",
    ],
)

wasm_cc_binary(
    name = "full_wasm",
    cc_target = ":full",
    outputs = [
        "full.js",
        "full.wasm",
    ],
)
bash
bazel build //wasm:hello_wasm
node bazel-bin/wasm/hello_cc.js
Hello from wasm32 with rules_fortran!

The cc_binary target works as a native executable. Wrapping it with wasm_cc_binary produces WebAssembly output instead.

3.B. LAPACK

wasm/
├── BUILD.bazel
└── full.c
c
#include <lapacke.h>
#include <stdio.h>

int main() {
    float A[9] = {1, 2, 3, 4, 5, 6, 7, 8, 10};
    int ipiv[3];
    int info = LAPACKE_sgetrf(LAPACK_COL_MAJOR, 3, 3, A, 3, ipiv);

    printf(info ? "FAIL\n" : "OK\n");
    for (int i = 0; i < 9; i++)
        printf("%8.2f%c", A[i], (i+1)%3 ? ' ' : '\n');
    return info;
}
starlark
cc_binary(
    name = "full",
    srcs = ["full.c"],
    deps = [
        "@blas//:single",
        "@lapack//:single",
        "@lapacke//:single",
    ],
)

wasm_cc_binary(
    name = "full_wasm",
    cc_target = ":full",
    outputs = ["full.js", "full.wasm"],
)
bash
bazel build //wasm:full_wasm
node bazel-bin/wasm/full.js
OK
    3.00     0.33     0.67
    6.00     2.00     0.50
   10.00     3.67    -0.50