Skip to content
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

Wasm of ocaml support #10695

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,94 @@ jobs:
# We disable the Dune cache when running the tests
DUNE_CACHE: disabled

wasm:
name: Wasm_of_ocaml
runs-on: ubuntu-latest
steps:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: latest

- name: Restore Cached Binaryen
id: cache-binaryen
uses: actions/cache/restore@v4
with:
path: binaryen
key: ${{ runner.os }}-binaryen-version_119

- name: Checkout Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
uses: actions/checkout@v4
with:
repository: WebAssembly/binaryen
path: binaryen
submodules: true
ref: version_119

- name: Install Ninja
if: steps.cache-binaryen.outputs.cache-hit != 'true'
run: sudo apt-get install ninja-build

- name: Build Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
working-directory: ./binaryen
run: |
cmake -G Ninja .
ninja

- name: Cache Binaryen
if: steps.cache-binaryen.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: binaryen
key: ${{ runner.os }}-binaryen-version_119

- name: Set Binaryen's Path
run: |
echo "$GITHUB_WORKSPACE/binaryen/bin" >> $GITHUB_PATH

- name: Checkout Code
uses: actions/checkout@v4
with:
path: dune

- name: Use OCaml 4.14.x
uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: 4.14.x
opam-pin: false
opam-depext: false
dune-cache: true

- name: Update Dune
working-directory: ./dune
run: opam pin add -n dune . --with-version 3.17.0

- name: Checkout Wasm_of_ocaml
uses: actions/checkout@v4
with:
repository: ocaml-wasm/wasm_of_ocaml
ref: target-dir
path: wasm_of_ocaml

- name: Install Wasm_of_ocaml
working-directory: ./wasm_of_ocaml
run: |
opam pin add -n . --with-version `< VERSION`
opam install wasm_of_ocaml-compiler

- name: Set Git User
run: |
git config --global user.name github-actions[bot]
git config --global user.email github-actions[bot]@users.noreply.github.com
- name: Run Tests
working-directory: ./dune
run: opam exec -- make test-wasm
env:
# We disable the Dune cache when running the tests
DUNE_CACHE: disabled

monorepo_benchmark_test:
name: Build monorepo benchmark docker image
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ test-windows: $(BIN)
test-js: $(BIN)
$(BIN) build @runtest-js

test-wasm: $(BIN)
DUNE_WASM_TEST=enable $(BIN) build @runtest-wasm

test-coq: $(BIN)
DUNE_COQ_TEST=enable $(BIN) build @runtest-coq

Expand Down
1 change: 1 addition & 0 deletions doc/changes/10695.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Wasm_of_ocaml support (#10695, @vouillon)
1 change: 1 addition & 0 deletions doc/howto/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ These guides will help you use Dune's features in your project.
../sites
../instrumentation
../jsoo
../wasmoo
../melange
../virtual-libraries
../tests
Expand Down
4 changes: 4 additions & 0 deletions doc/reference/dune/env.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ Fields supported in ``<settings>`` are:
- ``(js_of_ocaml (runtest_alias <alias-name>))`` specifies the alias under which
:ref:`inline_tests` and tests (:ref:`tests-stanza`) run for the `js` mode.

- ``(js_of_ocaml (submodes <submodes>))`` controls whether to generate
JavaScript, Wasm code, or both. Each submode is either ``js`` or ``wasm``.
The default is to generate JavaScript code.
Comment on lines +54 to +56
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of having this, which controls JS vs WASM generation globally, couldn't we add a wasm constructor to the (modes) field in executable stanzas (similar to how there is a js constructor)?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I've been unhappy about submodes but never had enough time to think about it.
I wonder if we could do something with modes of the form (<compilation-mode> <binary-kind>).
We could have (jsoo js) and (jsoo wasm), or something.

IIRC, you want to be able to compile both js and wasm without touching all dune files. We could imagine a new env field that define the semantic of the 'js' mode.

Copy link
Member Author

Choose a reason for hiding this comment

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

This directive can also be included in executable stanzas.

I'm not completely happy with the design either. It might be better to have a separate wasm_of_ocaml field, for instance, to be able to pass different flags to the compilers.

Still, I think we need a global way to specify whether we want to generate JavaScript and/or Wasm code.

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe we could have these in env stanzas:

(js_of_ocaml (by_default_if <blang expression>))
(wasm_of_ocaml (by_default_if <blang expression>))

and a (jsoo default) executable mode.

And the mode (jsoo js) and (jsoo wasm) could be used to generate explicitly some Wasm or JavaScript code.

Copy link
Member

Choose a reason for hiding this comment

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

Agreed that this submodes stuff doesn't seem satisfactory. Making it more similar to regular modes would be preferable.


- ``(binaries <binaries>)``, where ``<binaries>`` is a list of entries of the
form ``(<filepath> as <name>)``. ``(<filepath> as <name>)`` makes the binary
``<filepath>`` available in the command search as just ``<name>``. For
Expand Down
25 changes: 20 additions & 5 deletions doc/reference/dune/executable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ executable stanzas is as follows:
There can be additional modules in the current directory; you only need to
specify the entry point. Given an ``executable`` stanza with ``(name <name>)``,
Dune will know how to build ``<name>.exe``. If requested, it will also know how
to build ``<name>.bc`` and ``<name>.bc.js`` (Dune 2.0 and up also need specific
configuration (see the ``modes`` optional field below)).
to build ``<name>.bc``, ``<name>.bc.js`` and ``<name>.bc.wasm.js`` (Dune 2.0
and up also need specific configuration (see the ``modes`` optional field
below)).
vouillon marked this conversation as resolved.
Show resolved Hide resolved

``<name>.exe`` is a native code executable, ``<name>.bc`` is a bytecode
executable which requires ``ocamlrun`` to run, and ``<name>.bc.js`` is a
JavaScript generated using ``js_of_ocaml``.
executable which requires ``ocamlrun`` to run, ``<name>.bc.js`` is a
JavaScript generated using ``js_of_ocaml``, and ``<name>.bc.wasm.js`` is a
Wasm loader generated using ``wasm_of_ocaml`` (the Wasm modules are included in
directory ``<name>.bc.wasm.assets``).

Please note: in case native compilation is not available, ``<name>.exe`` will be
a custom bytecode executable, in the sense of ``ocamlc -custom``. This means
Expand Down Expand Up @@ -215,7 +218,8 @@ The extensions for the various linking modes are chosen as follows:
.. (byte shared_object) .bc%{ext_dll}
.. (native/best shared_object) %{ext_dll}
.. c .bc.c
.. js .bc.js
.. js .bc.js (JavaScript)
.. js .bc.wasm.js (Wasm)
.. (best plugin) %{ext_plugin}
.. (byte plugin) .cma
.. (native plugin) .cmxs
Expand All @@ -233,6 +237,10 @@ linking mode that's the same as ``byte_complete``, but it uses the extension
currently tracked by Dune, so they don't run ``.bc`` files during the build. Run
the ``.bc.exe`` or ``.exe`` ones instead, as these are self-contained.

When compiling to Wasm but not to JavaScript, a ``.bc.js`` file can
also be produced for compatibility. It is just a copy of the
``bc.wasm.js`` file.

Lastly, note that ``.bc`` executables cannot contain C stubs. If your executable
contains C stubs you may want to use ``(modes exe)``.

Expand All @@ -258,12 +266,19 @@ options using ``(js_of_ocaml (<js_of_ocaml-options>))``.
- ``(javascript_files (<files-list>))`` to specify ``js_of_ocaml`` JavaScript
runtime files.

- ``(wasm_files (<files-list>))`` to specify ``wasm_of_ocaml``
JavaScript and Wasm runtime files.

- ``(compilation_mode <mode>)`` where ``<mode>>`` is either ``whole_program`` or ``separate``.
This is only available inside ``executable`` stanzas.

- ``(sourcemap <config>)`` where ``<config>>`` is one of ``no``, ``file`` or ``inline``.
This is only available inside ``executable`` stanzas.

- ``(submodes <submodes>)`` controls whether to generate
JavaScript or Wasm code. Each submode is either ``js`` or ``wasm``.
The default is taken from the environment.

``<flags>`` is specified in the :doc:`/reference/ordered-set-language`.

The default values for ``flags``, ``compilation_mode`` and ``sourcemap`` depend on the selected build profile. The
Expand Down
46 changes: 46 additions & 0 deletions doc/wasmoo.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.. _wasmoo:

***************************************
Wasm Compilation With Wasm_of_ocaml
***************************************

.. TODO(diataxis)

This is an how-to guide.

Wasm_of_ocaml_ is a compiler from OCaml to WebAssembly (Wasm for
short). The compiler works by translating OCaml bytecode to Wasm code.

Compiling to Wasm is very similar to compiling to JavaScript. See
:doc:`jsoo` for more information.


Compiling to Wasm
=================

Dune has full support for building wasm_of_ocaml libraries and executables transparently.
There's no need to customise or enable anything to compile OCaml
libraries/executables to Wasm.

To build a Wasm executable, just define an executable as you would normally.
Consider this example:

.. code:: console

$ echo 'print_endline "hello from wasm"' > foo.ml

With the following ``dune`` file:

.. code:: dune

(executable (name foo) (modes js) (js_of_ocaml (submodes wasm)))

And then request the ``.wasm.js`` target:

.. code:: console

$ dune build ./foo.bc.wasm.js
$ node _build/default/foo.bc.wasm.js
hello from wasm

.. _wasm_of_ocaml: https://github.com/ocaml-wasm/wasm_of_ocaml
9 changes: 7 additions & 2 deletions src/dune_findlib/meta.ml
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,13 @@ let pp_print_string s =

let pp_quoted_value var =
match var with
| "archive" | "plugin" | "requires" | "ppx_runtime_deps" | "linkopts" | "jsoo_runtime"
-> pp_print_text
| "archive"
| "plugin"
| "requires"
| "ppx_runtime_deps"
| "linkopts"
| "jsoo_runtime"
| "wasmoo_runtime" -> pp_print_text
| _ -> pp_print_string
;;

Expand Down
1 change: 1 addition & 0 deletions src/dune_findlib/package0.ml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ let make_archives t var preds =
let version t = Vars.get t.vars "version" Ps.empty
let description t = Vars.get t.vars "description" Ps.empty
let jsoo_runtime t = get_paths t "jsoo_runtime" Ps.empty
let wasmoo_runtime t = get_paths t "wasmoo_runtime" Ps.empty

let requires t =
Vars.get_words t.vars "requires" preds
Expand Down
1 change: 1 addition & 0 deletions src/dune_findlib/package0.mli
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ val meta_fn : Filename.t
val version : t -> string option
val description : t -> string option
val jsoo_runtime : t -> Path.t list
val wasmoo_runtime : t -> Path.t list
val requires : t -> Lib_name.t list
val exports : t -> Lib_name.t list
val ppx_runtime_deps : t -> Lib_name.t list
Expand Down
Loading
Loading