diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 9f24ccc46b5..f81fb6d9ad8 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -190,6 +190,91 @@ 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 + + - 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: wasm-dune + 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 diff --git a/Makefile b/Makefile index 65d9b208827..728b2234370 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/bin/printenv.ml b/bin/printenv.ml index 2489149d649..709bf53c9b2 100644 --- a/bin/printenv.ml +++ b/bin/printenv.ml @@ -27,13 +27,25 @@ let dump sctx ~dir = |> Action_builder.of_memo >>= Dune_rules.Menhir_env.dump and+ coq_dump = Dune_rules.Coq.Coq_rules.coq_env ~dir >>| Dune_rules.Coq.Coq_flags.dump - and+ jsoo_dump = + and+ jsoo_js_dump = let module Js_of_ocaml = Dune_rules.Js_of_ocaml in - let* jsoo = Action_builder.of_memo (Dune_rules.Jsoo_rules.jsoo_env ~dir) in - Js_of_ocaml.Flags.dump jsoo.flags + let* jsoo = Action_builder.of_memo (Dune_rules.Jsoo_rules.jsoo_env ~dir ~mode:JS) in + Js_of_ocaml.Flags.dump ~mode:JS jsoo.flags + and+ jsoo_wasm_dump = + let module Js_of_ocaml = Dune_rules.Js_of_ocaml in + let* jsoo = Action_builder.of_memo (Dune_rules.Jsoo_rules.jsoo_env ~dir ~mode:Wasm) in + Js_of_ocaml.Flags.dump ~mode:Wasm jsoo.flags in let env = - List.concat [ o_dump; c_dump; link_flags_dump; menhir_dump; coq_dump; jsoo_dump ] + List.concat + [ o_dump + ; c_dump + ; link_flags_dump + ; menhir_dump + ; coq_dump + ; jsoo_js_dump + ; jsoo_wasm_dump + ] in Super_context.context sctx |> Context.name, env ;; diff --git a/doc/changes/11093.md b/doc/changes/11093.md new file mode 100644 index 00000000000..54000c8c106 --- /dev/null +++ b/doc/changes/11093.md @@ -0,0 +1 @@ +- Wasm_of_ocaml support (#11093, @vouillon) diff --git a/doc/howto/index.rst b/doc/howto/index.rst index 91362367a07..ca3fb49843f 100644 --- a/doc/howto/index.rst +++ b/doc/howto/index.rst @@ -15,6 +15,7 @@ These guides will help you use Dune's features in your project. ../sites ../instrumentation ../jsoo + ../wasmoo ../melange ../virtual-libraries ../tests diff --git a/doc/reference/dune/env.rst b/doc/reference/dune/env.rst index cb44d06e17d..b05ff751d82 100644 --- a/doc/reference/dune/env.rst +++ b/doc/reference/dune/env.rst @@ -49,7 +49,24 @@ Fields supported in ```` are: or not where ```` is either ``no``, ``file`` (to generate sourcemap in a ``.map`` file next the the generated javascript file) or ``inline`` (to inline the sourcemap at the end of the generated JavaScript file). - ``(js_of_ocaml (runtest_alias ))`` specifies the alias under which - :ref:`inline_tests` and tests (:ref:`tests-stanza`) run for the `js` mode. + :ref:`inline_tests` and tests (:ref:`tests-stanza`) run for the ``js`` mode. + +- ``(js_of_ocaml (enabled_if ))`` specifies whether the ``js`` mode is enabled. It is enabled by default. + +- ``(wasm_of_ocaml (flags )(build_runtime )(link_flags ))`` + specifies ``wasm_of_ocaml`` flags. See :ref:`wasmoo-field` for more details. + +- ``(wasm_of_ocaml (compilation_mode ))`` controls whether to use separate + compilation or not where ```` is either ``whole_program`` or + ``separate``. + +- ``(wasm_of_ocaml (sourcemap ))`` controls whether to generate sourcemap + or not where ```` is either ``no``, ``file`` (to generate sourcemap in a ``.map`` file next the the generated javascript file) or ``inline`` (to inline the sourcemap at the end of the generated JavaScript file). + +- ``(wasm_of_ocaml (runtest_alias ))`` specifies the alias under which + :ref:`inline_tests` and tests (:ref:`tests-stanza`) run for the ``wasm`` mode. + +- ``(wasm_of_ocaml (enabled_if ))`` specifies whether the ``wasm`` mode is enabled. It is enabled by default. - ``(binaries )``, where ```` is a list of entries of the form ``( as )``. ``( as )`` makes the binary diff --git a/doc/reference/dune/executable.rst b/doc/reference/dune/executable.rst index 6521f17a652..9fad1969f56 100644 --- a/doc/reference/dune/executable.rst +++ b/doc/reference/dune/executable.rst @@ -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 )``, Dune will know how to build ``.exe``. If requested, it will also know how -to build ``.bc`` and ``.bc.js`` (Dune 2.0 and up also need specific -configuration (see the ``modes`` optional field below)). +to build ``.bc``, ``.bc.js`` and ``.bc.wasm.js`` (Dune 2.0 +and up also needs specific configuration (see the ``modes`` optional field +below)). ``.exe`` is a native code executable, ``.bc`` is a bytecode -executable which requires ``ocamlrun`` to run, and ``.bc.js`` is a -JavaScript generated using ``js_of_ocaml``. +executable which requires ``ocamlrun`` to run, ``.bc.js`` is a +JavaScript generated using ``js_of_ocaml``, and ``.bc.wasm.js`` is a +Wasm loader script generated using ``wasm_of_ocaml`` (the Wasm modules are included in +directory ``.bc.wasm.assets``). Please note: in case native compilation is not available, ``.exe`` will be a custom bytecode executable, in the sense of ``ocamlc -custom``. This means @@ -91,6 +94,8 @@ files for executables. See - ``js_of_ocaml``: See the section about :ref:`jsoo-field` +- ``wasm_of_ocaml``: See the section about :ref:`wasmoo-field` + - ``flags``, ``ocamlc_flags``, and ``ocamlopt_flags``: See :doc:`/concepts/ocaml-flags`. @@ -165,6 +170,7 @@ available. non-OCaml application. - ``js`` for producing JavaScript from bytecode executables, see :doc:`/reference/dune-project/explicit_js_mode`. +- ``wasm`` for producing JavaScript from bytecode executables. - ``plugin`` for producing a plugin (``.cmxs`` if native or ``.cma`` if bytecode). @@ -186,6 +192,7 @@ Additionally, you can use the following shorthands: - ``byte`` for ``(byte exe)`` - ``native`` for ``(native exe)`` - ``js`` for ``(byte js)`` +- ``wasm`` for ``(byte wasm)`` - ``plugin`` for ``(best plugin)`` For instance, the following ``modes`` fields are all equivalent: @@ -216,6 +223,7 @@ The extensions for the various linking modes are chosen as follows: .. (native/best shared_object) %{ext_dll} .. c .bc.c .. js .bc.js +.. wasm .bc.wasm.js .. (best plugin) %{ext_plugin} .. (byte plugin) .cma .. (native plugin) .cmxs @@ -264,7 +272,11 @@ options using ``(js_of_ocaml ())``. - ``(sourcemap )`` where ``>`` is one of ``no``, ``file`` or ``inline``. This is only available inside ``executable`` stanzas. +- ``(enabled_if )`` to specify whether the ``js`` mode is enabled. It is enabled by default. + This is only available inside ``executable`` stanzas. + ```` is specified in the :doc:`/reference/ordered-set-language`. +```` is specified using the :doc:`/reference/boolean-language`, The default values for ``flags``, ``compilation_mode`` and ``sourcemap`` depend on the selected build profile. The build profile ``dev`` (the default) will enable inline sourcemap, separate compilation and pretty @@ -272,6 +284,26 @@ JavaScript output. See :ref:`jsoo` for more information. +.. _wasmoo-field: + +wasm_of_ocaml +~~~~~~~~~~~~~ + +In ``library`` and ``executable`` stanzas, you can specify ``wasm_of_ocaml`` +options using ``(wasm_of_ocaml ())``. + +```` are all optional. They are the same as the ```` above plus: + +- ``(wasm_files ())`` to specify ``wasm_of_ocaml`` + Wasm runtime files. + +For the ``(sourcemap )`` option, ```` must be one of ``no`` or ``inline``. Source maps are put within the ``.bc.wasm.assets`` directory. + +The default values for ``flags``, ``compilation_mode`` and ``sourcemap`` depend on the selected build profile. The +build profile ``dev`` (the default) will enable sourcemaps, separate compilation and pretty Wasm output. + +See :ref:`wasmoo` for more information. + executables ----------- diff --git a/doc/reference/dune/library.rst b/doc/reference/dune/library.rst index 8237e6d8f86..667ab5acaa4 100644 --- a/doc/reference/dune/library.rst +++ b/doc/reference/dune/library.rst @@ -213,6 +213,10 @@ order to declare a multi-directory library, you need to use the Sets options for JavaScript compilation, see :ref:`jsoo-field`. +.. describe:: (wasm_of_ocaml ...) + + Sets options for JavaScript compilation, see :ref:`wasmoo-field`. + .. describe:: (flags ...) See :doc:`/concepts/ocaml-flags`. diff --git a/doc/tests.rst b/doc/tests.rst index a26960658b1..9e9a9f9267f 100644 --- a/doc/tests.rst +++ b/doc/tests.rst @@ -258,6 +258,7 @@ field. Available modes are: - ``best`` for running tests in native mode with fallback to byte code, if native compilation is not available - ``js`` for running tests in JavaScript using Node.js +- ``wasm`` for running tests in Wasm using Node.js For instance: @@ -265,7 +266,7 @@ For instance: (library (name foo) - (inline_tests (modes byte best js)) + (inline_tests (modes byte best js wasm)) (preprocess (pps ppx_expect))) diff --git a/doc/wasmoo.rst b/doc/wasmoo.rst new file mode 100644 index 00000000000..31380d69276 --- /dev/null +++ b/doc/wasmoo.rst @@ -0,0 +1,104 @@ +.. _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. +The compiler can currently be installed from [its Github repository](https://github.com/ocaml-wasm/wasm_of_ocaml). + +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 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 + +If you're using the js_of_ocaml syntax extension, you must remember to add the +appropriate PPX in the ``preprocess`` field: + +.. code:: dune + + (executable + (name foo) + (modes wasm) + (preprocess (pps js_of_ocaml-ppx))) + +Selective Compilation +===================== + +The ``js`` and ``wasm`` modes can be selectively disabled using the ``(js_of_ocaml (enabled_if ...))`` and ``(wasm_of_ocaml (enabled_if ...))`` options. This allows for instance to generate one, or the other dependings on a profile: + +.. code:: dune + + (env + (js-only + (wasm_of_ocaml + (enabled_if false))) + (wasm-only + (js_of_ocaml + (enabled_if false)))) + +To be able to invoke the generated code using the same JavaScript script name in all cases, you can add a rule to copy the Wasm launcher script when the js_of_ocaml compilation is disabled. + +.. code:: dune + + (rule + (action + (copy foo.bc.wasm.js foo.bc.js)) + (enabled_if + (= %{profile} wasm-only))) + +Separate Compilation +==================== + +Dune supports two modes of compilation: + +- Direct compilation of a bytecode program to Wasm. This mode allows + wasm_of_ocaml to perform whole-program deadcode elimination and whole-program + inlining. + +- Separate compilation, where compilation units are compiled to Wasm + separately and then linked together. This mode is useful during development as + it builds more quickly. + +The separate compilation mode will be selected when the build profile +is ``dev``, which is the default. It can also be explicitly specified +in an ``env`` stanza (see :doc:`/reference/dune/env`) or per executable +inside ``(wasm_of_ocaml (compilation_mode ...))`` (see :doc:`/reference/dune/executable`) + +Sourcemap +========= + +Wasm_of_ocaml can generate sourcemaps for the generated Wasm code. +By default, they are generated when using the ``dev`` build profile and are not generated otherwise. +The behavior can explicitly be specified in an ``env`` stanza (see :doc:`/reference/dune/env`) +or per executable inside ``(wasm_of_ocaml (sourcemap ...))`` (see :doc:`/reference/dune/executable`) + +.. _wasm_of_ocaml: https://github.com/ocaml-wasm/wasm_of_ocaml diff --git a/src/dune_findlib/meta.ml b/src/dune_findlib/meta.ml index 34715f2c013..b05db277f81 100644 --- a/src/dune_findlib/meta.ml +++ b/src/dune_findlib/meta.ml @@ -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 ;; diff --git a/src/dune_findlib/package0.ml b/src/dune_findlib/package0.ml index 8d42b30791c..5e8152c1612 100644 --- a/src/dune_findlib/package0.ml +++ b/src/dune_findlib/package0.ml @@ -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 diff --git a/src/dune_findlib/package0.mli b/src/dune_findlib/package0.mli index 83e8ff5b809..e736926ac5e 100644 --- a/src/dune_findlib/package0.mli +++ b/src/dune_findlib/package0.mli @@ -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 diff --git a/src/dune_lang/binary_kind.ml b/src/dune_lang/binary_kind.ml index ea635dd8f61..781ee7b88ce 100644 --- a/src/dune_lang/binary_kind.ml +++ b/src/dune_lang/binary_kind.ml @@ -6,7 +6,6 @@ type t = | Object | Shared_object | Plugin - | Js let compare x y = match x, y with @@ -23,9 +22,6 @@ let compare x y = | Shared_object, _ -> Lt | _, Shared_object -> Gt | Plugin, Plugin -> Eq - | Plugin, _ -> Lt - | _, Plugin -> Gt - | Js, Js -> Eq ;; let decode = @@ -37,7 +33,6 @@ let decode = ; "object", return Object ; "shared_object", return Shared_object ; "plugin", Syntax.since Stanza.syntax (2, 4) >>> return Plugin - ; "js", Syntax.since Stanza.syntax (1, 11) >>> return Js ] ;; @@ -47,9 +42,8 @@ let to_string = function | Object -> "object" | Shared_object -> "shared_object" | Plugin -> "plugin" - | Js -> "js" ;; let to_dyn t = Dyn.variant (to_string t) [] let encode t = Dune_sexp.atom (to_string t) -let all = [ C; Exe; Object; Shared_object; Plugin; Js ] +let all = [ C; Exe; Object; Shared_object; Plugin ] diff --git a/src/dune_lang/binary_kind.mli b/src/dune_lang/binary_kind.mli index 8a06def5b42..019fd74f72e 100644 --- a/src/dune_lang/binary_kind.mli +++ b/src/dune_lang/binary_kind.mli @@ -6,7 +6,6 @@ type t = | Object | Shared_object | Plugin - | Js val compare : t -> t -> Ordering.t diff --git a/src/dune_rules/cinaps.ml b/src/dune_rules/cinaps.ml index 4029e480457..5d6ad7223ab 100644 --- a/src/dune_rules/cinaps.ml +++ b/src/dune_rules/cinaps.ml @@ -179,7 +179,7 @@ let gen_rules sctx t ~dir ~scope = ~requires_compile ~requires_link ~flags:(Ocaml_flags.of_list [ "-w"; "-24" ]) - ~js_of_ocaml:None + ~js_of_ocaml:(Js_of_ocaml.Mode.Pair.make None) ~melange_package_name:None ~package:None in diff --git a/src/dune_rules/compilation_context.ml b/src/dune_rules/compilation_context.ml index 9e9e9e25db9..4b3c64fde40 100644 --- a/src/dune_rules/compilation_context.ml +++ b/src/dune_rules/compilation_context.ml @@ -87,7 +87,7 @@ type t = ; preprocessing : Pp_spec.t ; opaque : bool ; stdlib : Ocaml_stdlib.t option - ; js_of_ocaml : Js_of_ocaml.In_context.t option + ; js_of_ocaml : Js_of_ocaml.In_context.t option Js_of_ocaml.Mode.Pair.t ; sandbox : Sandbox_config.t ; package : Package.t option ; vimpl : Vimpl.t option diff --git a/src/dune_rules/compilation_context.mli b/src/dune_rules/compilation_context.mli index 9924ea719a6..c6352430c82 100644 --- a/src/dune_rules/compilation_context.mli +++ b/src/dune_rules/compilation_context.mli @@ -30,7 +30,7 @@ val create -> ?preprocessing:Pp_spec.t -> opaque:opaque -> ?stdlib:Ocaml_stdlib.t - -> js_of_ocaml:Js_of_ocaml.In_context.t option + -> js_of_ocaml:Js_of_ocaml.In_context.t option Js_of_ocaml.Mode.Pair.t -> package:Package.t option -> melange_package_name:Lib_name.t option -> ?vimpl:Vimpl.t @@ -61,7 +61,7 @@ val includes : t -> Command.Args.without_targets Command.Args.t Lib_mode.Cm_kind val preprocessing : t -> Pp_spec.t val opaque : t -> bool val stdlib : t -> Ocaml_stdlib.t option -val js_of_ocaml : t -> Js_of_ocaml.In_context.t option +val js_of_ocaml : t -> Js_of_ocaml.In_context.t option Js_of_ocaml.Mode.Pair.t val sandbox : t -> Sandbox_config.t val set_sandbox : t -> Sandbox_config.t -> t val package : t -> Package.t option diff --git a/src/dune_rules/dir_status.ml b/src/dune_rules/dir_status.ml index 9047ae71529..de7f19f1303 100644 --- a/src/dune_rules/dir_status.ml +++ b/src/dune_rules/dir_status.ml @@ -85,6 +85,23 @@ let error_no_module_consumer ~loc (qualification : Include_subdirs.qualification ] ;; +let when_enabled ~dir ~enabled_if directory_targets = + if Path.Build.Map.is_empty directory_targets + then Memo.return directory_targets + else + (match enabled_if with + | Blang.Const const -> Memo.return const + | _ -> + (* Only evaluate the expander if the enabled_if field is + non-trivial to avoid memo cycles. If the enabled_if field is absent + from the "rule" stanza then its value will be [Const true]. *) + let* expander = Expander0.get ~dir in + Expander0.eval_blang expander enabled_if) + >>| function + | false -> Path.Build.Map.empty + | true -> directory_targets +;; + let directory_targets_of_rule ~dir { Rule_conf.targets; loc = rule_loc; enabled_if; _ } = match targets with | Infer -> @@ -114,26 +131,75 @@ let directory_targets_of_rule ~dir { Rule_conf.targets; loc = rule_loc; enabled_ completely, so just ignore this rule for now. *) acc)) in - if Path.Build.Map.is_empty directory_targets - then Memo.return directory_targets - else - (match enabled_if with - | Blang.Const const -> Memo.return const - | _ -> - (* Only evaluate the expander if the enabled_if field is - non-trivial to avoid memo cycles. If the enabled_if field is absent - from the "rule" stanza then its value will be [Const true]. *) - let* expander = Expander0.get ~dir in - Expander0.eval_blang expander enabled_if) + when_enabled ~dir ~enabled_if directory_targets +;; + +let jsoo_wasm_enabled ~jsoo_enabled ~dir ~(buildable : Buildable.t) = + let* expander = Expander0.get ~dir in + jsoo_enabled + ~eval:(Expander0.eval_blang expander) + ~dir + ~in_context:(Js_of_ocaml.In_context.make ~dir buildable.js_of_ocaml) + ~mode:Js_of_ocaml.Mode.Wasm +;; + +let directory_targets_of_executables + ~jsoo_enabled + ~dir + { Executables.names; modes; enabled_if; buildable; _ } + = + let* directory_targets = + match Executables.Link_mode.(Map.mem modes wasm) with + | false -> Memo.return Path.Build.Map.empty + | true -> + jsoo_wasm_enabled ~jsoo_enabled ~dir ~buildable >>| (function | false -> Path.Build.Map.empty - | true -> directory_targets) + | true -> + Nonempty_list.to_list names + |> List.fold_left ~init:Path.Build.Map.empty ~f:(fun acc (_, name) -> + let dir_target = Path.Build.relative dir (name ^ Js_of_ocaml.Ext.wasm_dir) in + Path.Build.Map.set acc dir_target buildable.loc)) + in + when_enabled ~dir ~enabled_if directory_targets +;; + +let directory_targets_of_library + ~jsoo_enabled + ~dir + { Library.sub_systems; name; enabled_if; buildable; _ } + = + let* directory_targets = + match Sub_system_name.Map.find sub_systems Inline_tests_info.Tests.name with + | Some (Inline_tests_info.Tests.T { modes; loc; enabled_if; _ }) + when Inline_tests_info.Mode_conf.Set.mem modes (Jsoo Wasm) -> + jsoo_wasm_enabled ~jsoo_enabled ~dir ~buildable + >>| (function + | false -> Path.Build.Map.empty + | true -> + let dir_target = + let lib_name = Lib_name.Local.to_string (snd name) in + let name = sprintf "inline_test_runner_%s" lib_name in + let inline_test_dir = + let inline_test_name = sprintf "%s.inline-tests" lib_name in + Path.Build.relative dir ("." ^ inline_test_name) + in + Path.Build.relative inline_test_dir (name ^ Js_of_ocaml.Ext.wasm_dir) + in + Path.Build.Map.singleton dir_target loc) + >>= when_enabled ~dir ~enabled_if + | _ -> Memo.return Path.Build.Map.empty + in + when_enabled ~dir ~enabled_if directory_targets ;; -let extract_directory_targets ~dir stanzas = +let extract_directory_targets ~jsoo_enabled ~dir stanzas = Memo.parallel_map stanzas ~f:(fun stanza -> match Stanza.repr stanza with | Rule_conf.T rule -> directory_targets_of_rule ~dir rule + | Executables.T exes | Tests.T { exes; _ } -> + directory_targets_of_executables ~jsoo_enabled ~dir exes + | Library.T lib -> directory_targets_of_library ~jsoo_enabled ~dir lib | Coq_stanza.Theory.T m -> (* It's unfortunate that we need to pull in the coq rules here. But we don't have a generic mechanism for this yet. *) @@ -280,15 +346,16 @@ end = struct ;; end -let directory_targets t ~dir = +let directory_targets t ~jsoo_enabled ~dir = match t with | Lock_dir | Generated | Source_only _ | Is_component_of_a_group_but_not_the_root _ -> Memo.return Path.Build.Map.empty | Standalone (_, dune_file) -> - Dune_file.stanzas dune_file >>= extract_directory_targets ~dir + Dune_file.stanzas dune_file >>= extract_directory_targets ~jsoo_enabled ~dir | Group_root { components; dune_file; _ } -> let f ~dir stanzas acc = - extract_directory_targets ~dir stanzas >>| Path.Build.Map.superpose acc + extract_directory_targets ~jsoo_enabled ~dir stanzas + >>| Path.Build.Map.superpose acc in let* init = let* stanzas = Dune_file.stanzas dune_file in diff --git a/src/dune_rules/dir_status.mli b/src/dune_rules/dir_status.mli index 0980d46911d..f1b70b2b2c8 100644 --- a/src/dune_rules/dir_status.mli +++ b/src/dune_rules/dir_status.mli @@ -42,4 +42,13 @@ module DB : sig val get : dir:Path.Build.t -> t Memo.t end -val directory_targets : t -> dir:Path.Build.t -> Loc.t Path.Build.Map.t Memo.t +val directory_targets + : t + -> jsoo_enabled: + (eval:(Blang.t -> bool Memo.t) + -> dir:Path.Build.t + -> in_context:Js_of_ocaml.In_context.t Js_of_ocaml.Mode.Pair.t + -> mode:Js_of_ocaml.Mode.t + -> bool Memo.t) + -> dir:Path.Build.t + -> Loc.t Path.Build.Map.t Memo.t diff --git a/src/dune_rules/dune_env.ml b/src/dune_rules/dune_env.ml index 8b3a018b08a..b0aeffb4808 100644 --- a/src/dune_rules/dune_env.ml +++ b/src/dune_rules/dune_env.ml @@ -80,6 +80,7 @@ type config = ; menhir : Ordered_set_lang.Unexpanded.t Menhir_env.t ; odoc : Odoc.t ; js_of_ocaml : Ordered_set_lang.Unexpanded.t Js_of_ocaml.Env.t + ; wasm_of_ocaml : Ordered_set_lang.Unexpanded.t Js_of_ocaml.Env.t ; coq : Coq_env.t ; format_config : Format_config.t option ; error_on_use : User_message.t option @@ -102,6 +103,7 @@ let equal_config ; menhir ; odoc ; js_of_ocaml + ; wasm_of_ocaml ; coq ; format_config ; error_on_use @@ -124,6 +126,7 @@ let equal_config && Coq_env.equal coq t.coq && Option.equal Format_config.equal format_config t.format_config && Js_of_ocaml.Env.equal js_of_ocaml t.js_of_ocaml + && Js_of_ocaml.Env.equal wasm_of_ocaml t.wasm_of_ocaml && Option.equal User_message.equal error_on_use t.error_on_use && Option.equal User_message.equal warn_on_load t.warn_on_load && Option.equal Bool.equal bin_annot t.bin_annot @@ -141,6 +144,7 @@ let empty_config = ; menhir = Menhir_env.empty ; odoc = Odoc.empty ; js_of_ocaml = Js_of_ocaml.Env.empty + ; wasm_of_ocaml = Js_of_ocaml.Env.empty ; coq = Coq_env.default ; format_config = None ; error_on_use = None @@ -221,7 +225,14 @@ let js_of_ocaml_field = field "js_of_ocaml" ~default:Js_of_ocaml.Env.empty - (Dune_lang.Syntax.since Stanza.syntax (3, 0) >>> Js_of_ocaml.Env.decode) + (Dune_lang.Syntax.since Stanza.syntax (3, 0) >>> Js_of_ocaml.Env.decode ~mode:JS) +;; + +let wasm_of_ocaml_field = + field + "wasm_of_ocaml" + ~default:Js_of_ocaml.Env.empty + (Dune_lang.Syntax.since Stanza.syntax (3, 17) >>> Js_of_ocaml.Env.decode ~mode:Wasm) ;; let bin_annot = field_o "bin_annot" (Dune_lang.Syntax.since Stanza.syntax (3, 8) >>> bool) @@ -241,6 +252,7 @@ let config = and+ menhir_flags = menhir_flags ~since:(2, 1) ~deleted_in:Menhir_stanza.explain_since and+ odoc = odoc_field and+ js_of_ocaml = js_of_ocaml_field + and+ wasm_of_ocaml = wasm_of_ocaml_field and+ coq = Coq_env.decode and+ format_config = Format_config.field ~since:(2, 8) and+ bin_annot = bin_annot in @@ -261,6 +273,7 @@ let config = ; menhir ; odoc ; js_of_ocaml + ; wasm_of_ocaml ; coq ; format_config ; error_on_use = None diff --git a/src/dune_rules/dune_env.mli b/src/dune_rules/dune_env.mli index a1d46d8c330..d904e237f7f 100644 --- a/src/dune_rules/dune_env.mli +++ b/src/dune_rules/dune_env.mli @@ -30,6 +30,7 @@ type config = ; menhir : Ordered_set_lang.Unexpanded.t Menhir_env.t ; odoc : Odoc.t ; js_of_ocaml : Ordered_set_lang.Unexpanded.t Js_of_ocaml.Env.t + ; wasm_of_ocaml : Ordered_set_lang.Unexpanded.t Js_of_ocaml.Env.t ; coq : Coq_env.t ; format_config : Format_config.t option ; error_on_use : User_message.t option diff --git a/src/dune_rules/dune_package.ml b/src/dune_rules/dune_package.ml index d14728342ba..8aa155f9b65 100644 --- a/src/dune_rules/dune_package.ml +++ b/src/dune_rules/dune_package.ml @@ -104,6 +104,7 @@ module Lib = struct in let melange_runtime_deps = additional_paths (Lib_info.melange_runtime_deps info) in let jsoo_runtime = Lib_info.jsoo_runtime info in + let wasmoo_runtime = Lib_info.wasmoo_runtime info in let virtual_ = Option.is_some (Lib_info.virtual_ info) in let instrumentation_backend = Lib_info.instrumentation_backend info in let native_archives = @@ -137,6 +138,7 @@ module Lib = struct ; paths "foreign_dll_files" foreign_dll_files ; paths "native_archives" native_archives ; paths "jsoo_runtime" jsoo_runtime + ; paths "wasmoo_runtime" wasmoo_runtime ; Lib_dep.L.field_encode requires ~name:"requires" ; libs "ppx_runtime_deps" ppx_runtime_deps ; field_o "implements" (no_loc Lib_name.encode) implements @@ -212,6 +214,7 @@ module Lib = struct and+ foreign_dll_files = paths "foreign_dll_files" and+ native_archives = paths "native_archives" and+ jsoo_runtime = paths "jsoo_runtime" + and+ wasmoo_runtime = paths "wasmoo_runtime" and+ melange_runtime_deps = paths "melange_runtime_deps" and+ requires = field_l "requires" (Lib_dep.decode ~allow_re_export:true) and+ ppx_runtime_deps = libs "ppx_runtime_deps" @@ -281,6 +284,7 @@ module Lib = struct ~native_archives:(Files native_archives) ~foreign_dll_files ~jsoo_runtime + ~wasmoo_runtime ~preprocess ~enabled ~virtual_deps diff --git a/src/dune_rules/exe.ml b/src/dune_rules/exe.ml index 906ba06739a..3eaa16f1adf 100644 --- a/src/dune_rules/exe.ml +++ b/src/dune_rules/exe.ml @@ -10,28 +10,47 @@ module Program = struct end module Linkage = struct + type mode = + | Ocaml of Link_mode.t + | Jsoo of Js_of_ocaml.Mode.t + type t = - { mode : Link_mode.t + { mode : mode ; ext : string ; flags : string list } - let byte = { mode = Byte; ext = ".bc"; flags = [] } + let byte = { mode = Ocaml Byte; ext = ".bc"; flags = [] } let byte_for_jsoo = - { mode = Byte_for_jsoo + { mode = Ocaml Byte_for_jsoo ; ext = ".bc-for-jsoo" ; flags = [ "-no-check-prims"; "-noautolink" ] } ;; - let native = { mode = Native; ext = ".exe"; flags = [] } - let is_native x = x.mode = Native - let is_js x = x.mode = Byte && x.ext = Js_of_ocaml.Ext.exe - let is_byte x = x.mode = Byte && not (is_js x) + let native = { mode = Ocaml Native; ext = ".exe"; flags = [] } + + let is_native x = + match x.mode with + | Ocaml Native -> true + | _ -> false + ;; + + let is_jsoo ~mode x = + match x.mode with + | Jsoo m -> Js_of_ocaml.Mode.equal mode m + | _ -> false + ;; + + let is_byte x = + match x.mode with + | Ocaml Byte -> true + | _ -> false + ;; let custom_with_ext ~ext ocaml_version = - { mode = Byte_with_stubs_statically_linked_in + { mode = Ocaml Byte_with_stubs_statically_linked_in ; ext ; flags = [ Ocaml.Version.custom_or_output_complete_exe ocaml_version ] } @@ -45,7 +64,8 @@ module Linkage = struct | Ok _ -> native ;; - let js = { mode = Byte; ext = Js_of_ocaml.Ext.exe; flags = [] } + let js = { mode = Jsoo JS; ext = Js_of_ocaml.Ext.exe ~mode:JS; flags = [] } + let wasm = { mode = Jsoo Wasm; ext = Js_of_ocaml.Ext.exe ~mode:Wasm; flags = [] } let is_plugin t = List.mem (List.map ~f:Mode.plugin_ext Mode.all) t.ext ~equal:String.equal @@ -65,7 +85,8 @@ module Linkage = struct (m : Executables.Link_mode.t) = match m with - | Other { mode = Byte; kind = Js } -> js + | Jsoo JS -> js + | Jsoo Wasm -> wasm | _ -> let link_mode : Link_mode.t = match m with @@ -85,6 +106,7 @@ module Linkage = struct if Result.is_ok ocaml.ocamlopt then Native else Byte_with_stubs_statically_linked_in) + | Jsoo _ -> assert false (* Handled above *) in let ext = let lib_config = ocaml.lib_config in @@ -100,7 +122,6 @@ module Linkage = struct | Other { kind; _ } -> (match kind with | C -> c_flags - | Js -> [] | Exe -> (match link_mode with | Byte_with_stubs_statically_linked_in -> @@ -126,8 +147,9 @@ module Linkage = struct List.concat_map native_c_libraries ~f:(fun flag -> [ "-cclib"; flag ]) @ so_flags | Byte | Byte_for_jsoo | Byte_with_stubs_statically_linked_in -> so_flags)) + | Jsoo _ -> assert false (* Handled above *) in - { ext; mode = link_mode; flags } + { ext; mode = Ocaml link_mode; flags } ;; end @@ -139,6 +161,7 @@ let link_exe ~loc ~name ~(linkage : Linkage.t) + ~linkage_mode ~cm_files ~link_time_code_gen ~promote @@ -150,7 +173,7 @@ let link_exe let sctx = Compilation_context.super_context cctx in let ctx = Super_context.context sctx in let dir = Compilation_context.dir cctx in - let mode = Link_mode.mode linkage.mode in + let mode = Link_mode.mode linkage_mode in let exe = exe_path_from_name cctx ~name ~linkage in let top_sorted_cms = Cm_files.top_sorted_cms cm_files ~mode in let fdo_linker_script = Fdo.Linker_script.create cctx (Path.build exe) in @@ -199,7 +222,7 @@ let link_exe sctx to_link ~lib_config:ocaml.lib_config - ~mode:linkage.mode + ~mode:linkage_mode ]) ; Deps o_files ; Dyn (Action_builder.map top_sorted_cms ~f:(fun x -> Command.Args.Deps x)) @@ -227,10 +250,12 @@ let link_js ~link_args ~promote ~link_time_code_gen + ~jsoo_mode cctx = let in_context = Compilation_context.js_of_ocaml cctx + |> Js_of_ocaml.Mode.Pair.select ~mode:jsoo_mode |> Option.value ~default:Js_of_ocaml.In_context.default in let src = exe_path_from_name cctx ~name ~linkage:Linkage.byte_for_jsoo in @@ -252,6 +277,7 @@ let link_js ~promote ~link_time_code_gen ~linkall + ~jsoo_mode ;; type dep_graphs = { for_exes : Module.t list Action_builder.t list } @@ -302,8 +328,8 @@ let link_many in let+ () = Memo.parallel_iter linkages ~f:(fun linkage -> - if Linkage.is_js linkage - then ( + match linkage.Linkage.mode with + | Jsoo jsoo_mode -> let obj_dir = Compilation_context.obj_dir cctx in link_js ~loc @@ -313,8 +339,9 @@ let link_many ~promote ~link_args cctx - ~link_time_code_gen) - else + ~link_time_code_gen + ~jsoo_mode + | Ocaml linkage_mode -> let* link_time_code_gen = match Linkage.is_plugin linkage with | false -> Memo.return link_time_code_gen @@ -328,7 +355,7 @@ let link_many in let link_args, o_files = let select_o_files = Mode.Map.Multi.for_only ~and_all:true o_files in - match linkage.mode with + match linkage_mode with | Native -> link_args, select_o_files Mode.Native | Byte | Byte_for_jsoo | Byte_with_stubs_statically_linked_in -> link_args, select_o_files Mode.Byte @@ -338,6 +365,7 @@ let link_many ~loc ~name ~linkage + ~linkage_mode ~cm_files ~link_time_code_gen ~promote diff --git a/src/dune_rules/exe.mli b/src/dune_rules/exe.mli index 0fdc14db1ed..a1e2fd6338e 100644 --- a/src/dune_rules/exe.mli +++ b/src/dune_rules/exe.mli @@ -33,8 +33,11 @@ module Linkage : sig (** Javascript compilation, extension [.bc.js] *) val js : t + (** Wasm compilation, extension [.bc.wasm.js] *) + val wasm : t + val is_native : t -> bool - val is_js : t -> bool + val is_jsoo : mode:Js_of_ocaml.Mode.t -> t -> bool val is_byte : t -> bool val of_user_config diff --git a/src/dune_rules/exe_rules.ml b/src/dune_rules/exe_rules.ml index 528c2fb768b..379f69ce84d 100644 --- a/src/dune_rules/exe_rules.ml +++ b/src/dune_rules/exe_rules.ml @@ -8,13 +8,18 @@ let linkages (ocaml : Ocaml_toolchain.t) ~(exes : Executables.t) ~explicit_js_mode - ~(jsoo_compilation_mode : Js_of_ocaml.Compilation_mode.t) + ~jsoo_enabled_modes + ~jsoo_is_whole_program = let module L = Executables.Link_mode in let l = let has_native = Result.is_ok ocaml.ocamlopt in let modes = L.Map.to_list exes.modes + |> List.filter ~f:(fun (mode, _) -> + match (mode : Executables.Link_mode.t) with + | Jsoo mode -> Js_of_ocaml.Mode.Pair.select ~mode jsoo_enabled_modes + | Byte_complete | Other _ -> true) |> List.map ~f:(fun (mode, loc) -> Exe.Linkage.of_user_config ocaml ~dynamically_linked_foreign_archives ~loc mode) in @@ -24,20 +29,29 @@ let linkages else List.filter modes ~f:(fun x -> not (Exe.Linkage.is_native x)) in let modes = - if L.Map.mem exes.modes L.js + if L.Map.existsi ~f:(fun m _ -> L.is_jsoo m) exes.modes then ( - match jsoo_compilation_mode with - | Whole_program -> Exe.Linkage.byte_for_jsoo :: modes - | Separate_compilation -> modes) + let jsoo_bytecode_exe_needed = + Js_of_ocaml.Mode.Set.inter jsoo_enabled_modes jsoo_is_whole_program + in + let bytecode_exe_needed = + L.Map.existsi + ~f:(fun mode _ -> + match (mode : Executables.Link_mode.t) with + | Jsoo mode -> Js_of_ocaml.Mode.Pair.select ~mode jsoo_bytecode_exe_needed + | Byte_complete | Other _ -> false) + exes.modes + in + if bytecode_exe_needed then Exe.Linkage.byte_for_jsoo :: modes else modes) else if explicit_js_mode then modes else if L.Map.mem exes.modes L.byte then Exe.Linkage.js :: - (match jsoo_compilation_mode with - | Whole_program -> Exe.Linkage.byte_for_jsoo :: modes - | Separate_compilation -> modes) + (if Js_of_ocaml.Mode.Pair.select ~mode:JS jsoo_is_whole_program + then Exe.Linkage.byte_for_jsoo :: modes + else modes) else modes in modes @@ -143,9 +157,12 @@ let executables_rules let* ocaml = Context.ocaml ctx in let project = Scope.project scope in let explicit_js_mode = Dune_project.explicit_js_mode project in + let js_of_ocaml = Js_of_ocaml.In_context.make ~dir exes.buildable.js_of_ocaml in let* linkages = - let* jsoo_compilation_mode = Jsoo_rules.js_of_ocaml_compilation_mode sctx ~dir in - let+ dynamically_linked_foreign_archives = + let+ jsoo_enabled_modes = + Jsoo_rules.jsoo_enabled_modes ~expander ~dir ~in_context:js_of_ocaml + and+ jsoo_is_whole_program = Jsoo_rules.jsoo_is_whole_program sctx ~dir + and+ dynamically_linked_foreign_archives = Context.dynamically_linked_foreign_archives ctx in linkages @@ -153,7 +170,8 @@ let executables_rules ~dynamically_linked_foreign_archives ~exes ~explicit_js_mode - ~jsoo_compilation_mode + ~jsoo_enabled_modes + ~jsoo_is_whole_program in let* flags = Buildable_rules.ocaml_flags sctx ~dir exes.buildable.flags in let* modules, pp = @@ -173,10 +191,12 @@ let executables_rules let requires_compile = Lib.Compile.direct_requires compile_info in let requires_link = Lib.Compile.requires_link compile_info in let js_of_ocaml = - let js_of_ocaml = Js_of_ocaml.In_context.make ~dir exes.buildable.js_of_ocaml in - if explicit_js_mode - then Option.some_if (List.exists linkages ~f:Exe.Linkage.is_js) js_of_ocaml - else Some js_of_ocaml + Js_of_ocaml.Mode.Pair.mapi + ~f:(fun mode x -> + Option.some_if + ((not explicit_js_mode) || List.exists linkages ~f:(Exe.Linkage.is_jsoo ~mode)) + x) + js_of_ocaml in Compilation_context.create () diff --git a/src/dune_rules/findlib.ml b/src/dune_rules/findlib.ml index 4029c5d2b67..73228dde5e4 100644 --- a/src/dune_rules/findlib.ml +++ b/src/dune_rules/findlib.ml @@ -141,6 +141,7 @@ let to_dune_library (t : Findlib.Package.t) ~dir_contents ~ext_lib ~external_loc let public_headers = Lib_info.File_deps.External [] in let plugins = Findlib.Package.plugins t in let jsoo_runtime = Findlib.Package.jsoo_runtime t in + let wasmoo_runtime = Findlib.Package.wasmoo_runtime t in let melange_runtime_deps = Lib_info.File_deps.External [] in let preprocess = Preprocess.Per_module.no_preprocessing () in let virtual_ = None in @@ -237,6 +238,7 @@ let to_dune_library (t : Findlib.Package.t) ~dir_contents ~ext_lib ~external_loc ~native_archives:(Files native_archives) ~foreign_dll_files:[] ~jsoo_runtime + ~wasmoo_runtime ~preprocess ~enabled ~virtual_deps diff --git a/src/dune_rules/gen_meta.ml b/src/dune_rules/gen_meta.ml index b96a1f03b6c..d04fef2f695 100644 --- a/src/dune_rules/gen_meta.ml +++ b/src/dune_rules/gen_meta.ml @@ -158,6 +158,11 @@ let gen_lib pub_name lib ~version = | l -> let l = List.map l ~f:Path.basename in [ rule "jsoo_runtime" [] Set (String.concat l ~sep:" ") ]) + ; (match Lib_info.wasmoo_runtime info with + | [] -> [] + | l -> + let l = List.map l ~f:Path.basename in + [ rule "wasmoo_runtime" [] Set (String.concat l ~sep:" ") ]) ] ;; diff --git a/src/dune_rules/gen_rules.ml b/src/dune_rules/gen_rules.ml index c752c5d9566..0c98ce1287e 100644 --- a/src/dune_rules/gen_rules.ml +++ b/src/dune_rules/gen_rules.ml @@ -136,8 +136,9 @@ end = struct { (with_cctx_merlin ~loc:exes.buildable.loc cctx_merlin) with js = Some - (List.map (Nonempty_list.to_list exes.names) ~f:(fun (_, exe) -> - Path.Build.relative dir (exe ^ Js_of_ocaml.Ext.exe))) + (List.concat_map (Nonempty_list.to_list exes.names) ~f:(fun (_, exe) -> + List.map Js_of_ocaml.Mode.all ~f:(fun mode -> + Path.Build.relative dir (exe ^ Js_of_ocaml.Ext.exe ~mode)))) }) | Alias_conf.T alias -> let+ () = Simple_rules.alias sctx alias ~dir ~expander in @@ -520,7 +521,12 @@ let gen_rules_regular_directory sctx ~src_dir ~components ~dir = in let+ rules = let+ make_rules = - let+ directory_targets = Dir_status.directory_targets dir_status ~dir in + let+ directory_targets = + Dir_status.directory_targets + dir_status + ~jsoo_enabled:Jsoo_rules.jsoo_enabled + ~dir + in let allowed_subdirs = let automatic = Automatic_subdir.subdirs components in let toplevel = diff --git a/src/dune_rules/inline_tests.ml b/src/dune_rules/inline_tests.ml index 5d63bfddf7d..8e6e69b4a79 100644 --- a/src/dune_rules/inline_tests.ml +++ b/src/dune_rules/inline_tests.ml @@ -107,6 +107,12 @@ include Sub_system.Register_end_point (struct Lib.closure ~linking:true ((lib :: libs) @ more_libs) in (* Generate the runner file *) + let js_of_ocaml = + Js_of_ocaml.Mode.Pair.map + ~f:(fun (x : Js_of_ocaml.In_context.t) -> + { x with javascript_files = []; wasm_files = [] }) + (Js_of_ocaml.In_context.make ~dir lib.buildable.js_of_ocaml) + in let* () = Super_context.add_rule sctx @@ -150,11 +156,6 @@ include Sub_system.Register_end_point (struct Buildable_rules.ocaml_flags sctx ~dir info.executable_ocaml_flags in let flags = Ocaml_flags.append_common ocaml_flags [ "-w"; "-24"; "-g" ] in - let js_of_ocaml = - Js_of_ocaml.In_context.make - ~dir - { lib.buildable.js_of_ocaml with javascript_files = [] } - in Compilation_context.create () ~super_context:sctx @@ -165,23 +166,38 @@ include Sub_system.Register_end_point (struct ~requires_compile:runner_libs ~requires_link:(Memo.lazy_ (fun () -> runner_libs)) ~flags - ~js_of_ocaml:(Some js_of_ocaml) + ~js_of_ocaml:(Js_of_ocaml.Mode.Pair.map ~f:Option.some js_of_ocaml) ~melange_package_name:None ~package in + let* modes = + let+ jsoo_enabled_modes = + Jsoo_rules.jsoo_enabled_modes ~expander ~dir ~in_context:js_of_ocaml + in + Mode_conf.Set.to_list info.modes + |> List.filter ~f:(fun (mode : Mode_conf.t) -> + match mode with + | Native | Best | Byte -> true + | Jsoo mode -> Js_of_ocaml.Mode.Pair.select ~mode jsoo_enabled_modes) + in let* linkages = - let+ jsoo_compilation_mode = Jsoo_rules.js_of_ocaml_compilation_mode sctx ~dir in let ocaml = Compilation_context.ocaml cctx in - List.concat_map (Mode_conf.Set.to_list info.modes) ~f:(fun (mode : Mode_conf.t) -> - match mode with - | Native -> [ Exe.Linkage.native ] - | Best -> [ Exe.Linkage.native_or_custom ocaml ] - | Byte -> [ Exe.Linkage.custom_with_ext ~ext:".bc" ocaml.version ] - | Javascript -> - (match jsoo_compilation_mode with - | Js_of_ocaml.Compilation_mode.Whole_program -> - [ Exe.Linkage.js; Exe.Linkage.byte_for_jsoo ] - | Separate_compilation -> [ Exe.Linkage.js ])) + let l = + List.map modes ~f:(fun (mode : Mode_conf.t) -> + match mode with + | Native -> Exe.Linkage.native + | Best -> Exe.Linkage.native_or_custom ocaml + | Byte -> Exe.Linkage.custom_with_ext ~ext:".bc" ocaml.version + | Jsoo JS -> Exe.Linkage.js + | Jsoo Wasm -> Exe.Linkage.wasm) + in + let+ jsoo_is_whole_program = Jsoo_rules.jsoo_is_whole_program sctx ~dir in + if List.exists modes ~f:(fun mode -> + match (mode : Mode_conf.t) with + | Jsoo mode -> Js_of_ocaml.Mode.Pair.select ~mode jsoo_is_whole_program + | Native | Best | Byte -> false) + then Exe.Linkage.byte_for_jsoo :: l + else l in let* (_ : Exe.dep_graphs) = let link_args = @@ -245,13 +261,13 @@ include Sub_system.Register_end_point (struct let ext = match mode with | Native | Best -> ".exe" - | Javascript -> Js_of_ocaml.Ext.exe + | Jsoo mode -> Js_of_ocaml.Ext.exe ~mode | Byte -> ".bc" in let custom_runner = match mode with | Native | Best | Byte -> None - | Javascript -> Some Jsoo_rules.runner + | Jsoo _ -> Some Jsoo_rules.runner in let exe = Path.build (Path.Build.relative inline_test_dir (name ^ ext)) in let open Action_builder.O in @@ -308,58 +324,54 @@ include Sub_system.Register_end_point (struct List.concat l in let source_files = List.concat_map source_modules ~f:Module.sources in - Memo.parallel_iter_seq - (Mode_conf.Set.to_seq info.modes) - ~f:(fun (mode : Mode_conf.t) -> - let partition_file = - Path.Build.relative inline_test_dir ("partitions-" ^ Mode_conf.to_string mode) - in - let* () = - match partitions_flags with - | None -> Memo.return () - | Some partitions_flags -> - let open Action_builder.O in - action mode partitions_flags - >>| Action.Full.make ~sandbox - |> Action_builder.with_stdout_to partition_file - |> Super_context.add_rule sctx ~dir ~loc - in - let* runtest_alias = - match mode with - | Native | Best | Byte -> Memo.return Alias0.runtest - | Javascript -> Jsoo_rules.js_of_ocaml_runtest_alias ~dir - in - Super_context.add_alias_action - sctx - ~dir - ~loc:info.loc - (Alias.make ~dir runtest_alias) - (let open Action_builder.O in - let+ actions = - let* partitions_flags = - match partitions_flags with - | None -> Action_builder.return [ None ] - | Some _ -> - let+ partitions = - Action_builder.lines_of (Path.build partition_file) - in - List.map ~f:(fun x -> Some x) partitions - in - List.map partitions_flags ~f:(fun p -> action mode (flags p)) - |> Action_builder.all - and+ () = Action_builder.paths source_files in - match actions with - | [] -> Action.Full.empty - | _ :: _ -> - let run_tests = Action.concurrent actions in - let diffs = - List.map source_files ~f:(fun fn -> - Path.as_in_build_dir_exn fn - |> Path.Build.extend_basename ~suffix:".corrected" - |> Promote.Diff_action.diff ~optional:true fn) - |> Action.concurrent - in - Action.Full.make ~sandbox @@ Action.progn [ run_tests; diffs ])) + Memo.parallel_iter modes ~f:(fun (mode : Mode_conf.t) -> + let partition_file = + Path.Build.relative inline_test_dir ("partitions-" ^ Mode_conf.to_string mode) + in + let* () = + match partitions_flags with + | None -> Memo.return () + | Some partitions_flags -> + let open Action_builder.O in + action mode partitions_flags + >>| Action.Full.make ~sandbox + |> Action_builder.with_stdout_to partition_file + |> Super_context.add_rule sctx ~dir ~loc + in + let* runtest_alias = + match mode with + | Native | Best | Byte -> Memo.return Alias0.runtest + | Jsoo mode -> Jsoo_rules.js_of_ocaml_runtest_alias ~dir ~mode + in + Super_context.add_alias_action + sctx + ~dir + ~loc:info.loc + (Alias.make ~dir runtest_alias) + (let open Action_builder.O in + let+ actions = + let* partitions_flags = + match partitions_flags with + | None -> Action_builder.return [ None ] + | Some _ -> + let+ partitions = Action_builder.lines_of (Path.build partition_file) in + List.map ~f:(fun x -> Some x) partitions + in + List.map partitions_flags ~f:(fun p -> action mode (flags p)) + |> Action_builder.all + and+ () = Action_builder.paths source_files in + match actions with + | [] -> Action.Full.empty + | _ :: _ -> + let run_tests = Action.concurrent actions in + let diffs = + List.map source_files ~f:(fun fn -> + Path.as_in_build_dir_exn fn + |> Path.Build.extend_basename ~suffix:".corrected" + |> Promote.Diff_action.diff ~optional:true fn) + |> Action.concurrent + in + Action.Full.make ~sandbox @@ Action.progn [ run_tests; diffs ])) ;; let gen_rules c ~(info : Info.t) ~backends = diff --git a/src/dune_rules/inline_tests_info.ml b/src/dune_rules/inline_tests_info.ml index 44772b2977f..47e247aa7ba 100644 --- a/src/dune_rules/inline_tests_info.ml +++ b/src/dune_rules/inline_tests_info.ml @@ -68,7 +68,7 @@ module Mode_conf = struct module T = struct type t = | Byte - | Javascript + | Jsoo of Js_of_ocaml.Mode.t | Native | Best @@ -77,9 +77,9 @@ module Mode_conf = struct | Byte, Byte -> Eq | Byte, _ -> Lt | _, Byte -> Gt - | Javascript, Javascript -> Eq - | Javascript, _ -> Lt - | _, Javascript -> Gt + | Jsoo m, Jsoo m' -> Js_of_ocaml.Mode.compare m m' + | Jsoo _, _ -> Lt + | _, Jsoo _ -> Gt | Native, Native -> Eq | Native, _ -> Lt | _, Native -> Gt @@ -94,12 +94,16 @@ module Mode_conf = struct let to_string = function | Byte -> "byte" - | Javascript -> "js" + | Jsoo JS -> "js" + | Jsoo Wasm -> "wasm" | Native -> "native" | Best -> "best" ;; - let decode = enum [ "byte", Byte; "js", Javascript; "native", Native; "best", Best ] + let decode = + enum [ "byte", Byte; "js", Jsoo JS; "native", Native; "best", Best ] + <|> sum [ "wasm", Syntax.since Stanza.syntax (3, 17) >>> return (Jsoo Wasm) ] + ;; module O = Comparable.Make (T) module Map = O.Map diff --git a/src/dune_rules/inline_tests_info.mli b/src/dune_rules/inline_tests_info.mli index af486d63786..2135df4aa88 100644 --- a/src/dune_rules/inline_tests_info.mli +++ b/src/dune_rules/inline_tests_info.mli @@ -16,7 +16,7 @@ end module Mode_conf : sig type t = | Byte - | Javascript + | Jsoo of Js_of_ocaml.Mode.t | Native | Best diff --git a/src/dune_rules/install_rules.ml b/src/dune_rules/install_rules.ml index 28b6f36cb6a..d53e6b32b07 100644 --- a/src/dune_rules/install_rules.ml +++ b/src/dune_rules/install_rules.ml @@ -110,11 +110,17 @@ end = struct (List.rev_concat_map ~f:(List.rev_map ~f:(fun f -> Section.Lib, f)) (let { Mode.Dict.byte; native } = Lib_info.archives lib in + let jsoo_files = + (* A same runtime file can be used both for jsoo and wasmoo *) + Lib_info.jsoo_runtime lib + |> List.rev_append (Lib_info.wasmoo_runtime lib) + |> List.sort_uniq ~compare:Path.Build.compare + in [ byte ; native ; foreign_archives ; Lib_info.eval_native_archives_exn lib ~modules - ; Lib_info.jsoo_runtime lib + ; jsoo_files ])) (List.rev_map ~f:(fun f -> Section.Libexec, f) (Lib_info.plugins lib).native) ;; diff --git a/src/dune_rules/jsoo/js_of_ocaml.ml b/src/dune_rules/jsoo/js_of_ocaml.ml index b8876d01eec..8ce2b5946f8 100644 --- a/src/dune_rules/jsoo/js_of_ocaml.ml +++ b/src/dune_rules/jsoo/js_of_ocaml.ml @@ -1,16 +1,62 @@ open Import open Dune_lang.Decoder -module Ext = struct - type t = string +(* We use the same set of options when producing Wasm code with + wasm_of_ocaml, since the compilation process is similar and + generates a JavaScript file with basically the same behavior. *) - let exe = ".bc.js" - let cmo = ".cmo.js" - let cma = ".cma.js" - let runtime = ".bc.runtime.js" -end +module Mode = struct + type t = + | JS + | Wasm -let field_oslu name = Ordered_set_lang.Unexpanded.field name + let equal (a : t) b = Poly.equal a b + + let select ~mode ~js ~wasm = + match mode with + | JS -> js + | Wasm -> wasm + ;; + + let compare m m' = + match m, m' with + | JS, JS -> Eq + | JS, _ -> Lt + | _, JS -> Gt + | Wasm, Wasm -> Eq + ;; + + let decode = + let open Dune_sexp.Decoder in + sum [ "js", return JS; "wasm", return Wasm ] + ;; + + let to_string mode = select ~mode ~js:"js" ~wasm:"wasm" + let to_dyn t = Dyn.variant (to_string t) [] + let all = [ JS; Wasm ] + + module Pair = struct + type 'a t = + { js : 'a + ; wasm : 'a + } + + let select ~mode { js; wasm } = select ~mode ~js ~wasm + let make v = { js = v; wasm = v } + let map ~f { js; wasm } = { js = f js; wasm = f wasm } + let mapi ~f { js; wasm } = { js = f JS js; wasm = f Wasm wasm } + + let map2 ~f { js; wasm } { js = js'; wasm = wasm' } = + { js = f js js'; wasm = f wasm wasm' } + ;; + end + + module Set = struct + type t = bool Pair.t + + let inter = Pair.map2 ~f:( && ) + end +end module Sourcemap = struct type t = @@ -18,7 +64,11 @@ module Sourcemap = struct | Inline | File - let decode = enum [ "no", No; "inline", Inline; "file", File ] + let decode ~mode = + match (mode : Mode.t) with + | JS -> enum [ "no", No; "inline", Inline; "file", File ] + | Wasm -> enum [ "no", No; "inline", Inline ] + ;; let equal x y = match x, y with @@ -43,6 +93,7 @@ module Flags = struct let build_runtime t = t.build_runtime let compile t = t.compile let link t = t.link + let field_oslu name = Ordered_set_lang.Unexpanded.field name let decode = let+ build_runtime = field_oslu "build_runtime_flags" @@ -86,16 +137,17 @@ module Flags = struct { build_runtime; compile; link } ;; - let dump t = + let dump ~mode t = let open Action_builder.O in let+ build_runtime = t.build_runtime and+ compile = t.compile and+ link = t.link in + let prefix = Mode.to_string mode in List.map ~f:Dune_lang.Encoder.(pair string (list string)) - [ "js_of_ocaml_flags", compile - ; "js_of_ocaml_build_runtime_flags", build_runtime - ; "js_of_ocaml_link_flags", link + [ prefix ^ "_of_ocaml_flags", compile + ; prefix ^ "_of_ocaml_build_runtime_flags", build_runtime + ; prefix ^ "_of_ocaml_link_flags", link ] ;; end @@ -119,12 +171,14 @@ end module In_buildable = struct type t = { flags : Ordered_set_lang.Unexpanded.t Flags.t + ; enabled_if : Blang.t option ; javascript_files : string list + ; wasm_files : string list ; compilation_mode : Compilation_mode.t option ; sourcemap : Sourcemap.t option } - let decode ~executable = + let decode ~in_library ~mode = let* syntax_version = Dune_lang.Syntax.get_exn Stanza.syntax in if syntax_version < (3, 0) then @@ -136,35 +190,49 @@ module In_buildable = struct ; compile = flags ; link = flags (* we set link as well to preserve the old semantic *) } + ; enabled_if = Some Blang.true_ ; javascript_files + ; wasm_files = [] ; compilation_mode = None ; sourcemap = None }) - else + else ( + let only_in_executable decode = if in_library then return None else decode in fields (let+ flags = Flags.decode + and+ enabled_if = + only_in_executable + (field_o + "enabled_if" + (Dune_lang.Syntax.since Stanza.syntax (3, 17) >>> Blang.decode)) and+ javascript_files = field "javascript_files" (repeat string) ~default:[] + and+ wasm_files = + match (mode : Mode.t) with + | JS -> return [] + | Wasm -> + field + "wasm_files" + (Dune_lang.Syntax.since Stanza.syntax (3, 17) >>> repeat string) + ~default:[] and+ compilation_mode = - if executable - then - field_o - "compilation_mode" - (Dune_lang.Syntax.since Stanza.syntax (3, 17) >>> Compilation_mode.decode) - else return None + only_in_executable + (field_o + "compilation_mode" + (Dune_lang.Syntax.since Stanza.syntax (3, 17) >>> Compilation_mode.decode)) and+ sourcemap = - if executable - then - field_o - "sourcemap" - (Dune_lang.Syntax.since Stanza.syntax (3, 17) >>> Sourcemap.decode) - else return None + only_in_executable + (field_o + "sourcemap" + (Dune_lang.Syntax.since Stanza.syntax (3, 17) >>> Sourcemap.decode ~mode)) in - { flags; javascript_files; compilation_mode; sourcemap }) + { flags; enabled_if; javascript_files; wasm_files; compilation_mode; sourcemap })) ;; let default = { flags = Flags.standard + ; enabled_if = None ; javascript_files = [] + ; wasm_files = [] ; compilation_mode = None ; sourcemap = None } @@ -174,59 +242,84 @@ end module In_context = struct type t = { flags : Ordered_set_lang.Unexpanded.t Flags.t + ; enabled_if : Blang.t option ; javascript_files : Path.Build.t list + ; wasm_files : Path.Build.t list ; compilation_mode : Compilation_mode.t option ; sourcemap : Sourcemap.t option } - let make ~(dir : Path.Build.t) (x : In_buildable.t) = + let make_one ~(dir : Path.Build.t) (x : In_buildable.t) = { flags = x.flags + ; enabled_if = x.enabled_if ; javascript_files = List.map ~f:(fun name -> Path.Build.relative dir name) x.javascript_files + ; wasm_files = List.map ~f:(fun name -> Path.Build.relative dir name) x.wasm_files ; compilation_mode = x.compilation_mode ; sourcemap = x.sourcemap } ;; + let make ~dir x = Mode.Pair.map ~f:(fun x -> make_one ~dir x) x + let default = { flags = Flags.standard + ; enabled_if = None ; javascript_files = [] + ; wasm_files = [] ; compilation_mode = None ; sourcemap = None } ;; end +module Ext = struct + type t = string + + let exe ~mode = Mode.select ~mode ~js:".bc.js" ~wasm:".bc.wasm.js" + let cmo ~mode = Mode.select ~mode ~js:".cmo.js" ~wasm:".wasmo" + let cma ~mode = Mode.select ~mode ~js:".cma.js" ~wasm:".wasma" + let runtime ~mode = Mode.select ~mode ~js:".bc.runtime.js" ~wasm:".bc.runtime.wasma" + let wasm_dir = ".bc.wasm.assets" +end + module Env = struct type 'a t = { compilation_mode : Compilation_mode.t option ; sourcemap : Sourcemap.t option ; runtest_alias : Alias.Name.t option ; flags : 'a Flags.t + ; enabled_if : Blang.t option } - let decode = + let decode ~mode = fields @@ let+ compilation_mode = field_o "compilation_mode" Compilation_mode.decode and+ sourcemap = field_o "sourcemap" - (Dune_lang.Syntax.since Stanza.syntax (3, 17) >>> Sourcemap.decode) + (Dune_lang.Syntax.since Stanza.syntax (3, 17) >>> Sourcemap.decode ~mode) and+ runtest_alias = field_o "runtest_alias" Dune_lang.Alias.decode - and+ flags = Flags.decode in + and+ flags = Flags.decode + and+ enabled_if = + field_o + "enabled_if" + (Dune_lang.Syntax.since Stanza.syntax (3, 17) >>> Blang.decode) + in Option.iter ~f:Alias0.register_as_standard runtest_alias; - { compilation_mode; sourcemap; runtest_alias; flags } + { compilation_mode; sourcemap; runtest_alias; flags; enabled_if } ;; - let equal { compilation_mode; sourcemap; runtest_alias; flags } t = + let equal { compilation_mode; sourcemap; runtest_alias; flags; enabled_if } t = Option.equal Compilation_mode.equal compilation_mode t.compilation_mode && Option.equal Sourcemap.equal sourcemap t.sourcemap && Option.equal Alias.Name.equal runtest_alias t.runtest_alias && Flags.equal Ordered_set_lang.Unexpanded.equal flags t.flags + && Option.equal Blang.equal enabled_if t.enabled_if ;; - let map ~f { compilation_mode; sourcemap; runtest_alias; flags } = - { compilation_mode; sourcemap; runtest_alias; flags = Flags.map ~f flags } + let map ~f { compilation_mode; sourcemap; runtest_alias; flags; enabled_if } = + { compilation_mode; sourcemap; runtest_alias; flags = Flags.map ~f flags; enabled_if } ;; let empty = @@ -234,6 +327,7 @@ module Env = struct ; sourcemap = None ; runtest_alias = None ; flags = Flags.standard + ; enabled_if = None } ;; @@ -242,6 +336,7 @@ module Env = struct ; sourcemap = None ; runtest_alias = None ; flags = Flags.default ~profile + ; enabled_if = None } ;; end diff --git a/src/dune_rules/jsoo/js_of_ocaml.mli b/src/dune_rules/jsoo/js_of_ocaml.mli index 0716f35ebde..4b0a5e77a9c 100644 --- a/src/dune_rules/jsoo/js_of_ocaml.mli +++ b/src/dune_rules/jsoo/js_of_ocaml.mli @@ -1,12 +1,36 @@ open Import -module Ext : sig - type t = string +module Mode : sig + type t = + | JS + | Wasm + + type mode := t + + val select : mode:t -> js:'a -> wasm:'a -> 'a + val equal : t -> t -> bool + val compare : t -> t -> Ordering.t + val decode : t Dune_lang.Decoder.t + val to_dyn : t -> Dyn.t + val all : t list + + module Pair : sig + type 'a t = + { js : 'a + ; wasm : 'a + } + + val select : mode:mode -> 'a t -> 'a + val make : 'a -> 'a t + val map : f:('a -> 'b) -> 'a t -> 'b t + val mapi : f:(mode -> 'a -> 'b) -> 'a t -> 'b t + end - val exe : t - val cmo : t - val cma : t - val runtime : t + module Set : sig + type t = bool Pair.t + + val inter : t -> t -> t + end end module Flags : sig @@ -35,7 +59,10 @@ module Flags : sig -> string list Action_builder.t) -> string list Action_builder.t t - val dump : string list Action_builder.t t -> Dune_lang.t list Action_builder.t + val dump + : mode:Mode.t + -> string list Action_builder.t t + -> Dune_lang.t list Action_builder.t end module Sourcemap : sig @@ -54,38 +81,53 @@ end module In_buildable : sig type t = { flags : Flags.Spec.t + ; enabled_if : Blang.t option ; javascript_files : string list + ; wasm_files : string list ; compilation_mode : Compilation_mode.t option ; sourcemap : Sourcemap.t option } - val decode : executable:bool -> t Dune_lang.Decoder.t + val decode : in_library:bool -> mode:Mode.t -> t Dune_lang.Decoder.t val default : t end module In_context : sig type t = { flags : Flags.Spec.t + ; enabled_if : Blang.t option ; javascript_files : Path.Build.t list + ; wasm_files : Path.Build.t list ; compilation_mode : Compilation_mode.t option ; sourcemap : Sourcemap.t option } - val make : dir:Path.Build.t -> In_buildable.t -> t + val make : dir:Path.Build.t -> In_buildable.t Mode.Pair.t -> t Mode.Pair.t val default : t end +module Ext : sig + type t = string + + val exe : mode:Mode.t -> t + val cmo : mode:Mode.t -> t + val cma : mode:Mode.t -> t + val runtime : mode:Mode.t -> t + val wasm_dir : t +end + module Env : sig type 'a t = { compilation_mode : Compilation_mode.t option ; sourcemap : Sourcemap.t option ; runtest_alias : Alias.Name.t option ; flags : 'a Flags.t + ; enabled_if : Blang.t option } val map : f:('a -> 'b) -> 'a t -> 'b t val equal : Ordered_set_lang.Unexpanded.t t -> Ordered_set_lang.Unexpanded.t t -> bool - val decode : Ordered_set_lang.Unexpanded.t t Dune_lang.Decoder.t + val decode : mode:Mode.t -> Ordered_set_lang.Unexpanded.t t Dune_lang.Decoder.t val default : profile:Profile.t -> string list t val empty : Ordered_set_lang.Unexpanded.t t end diff --git a/src/dune_rules/jsoo/jsoo_rules.ml b/src/dune_rules/jsoo/jsoo_rules.ml index 1825efeb7b0..7ad41a6dddb 100644 --- a/src/dune_rules/jsoo/jsoo_rules.ml +++ b/src/dune_rules/jsoo/jsoo_rules.ml @@ -1,7 +1,7 @@ open Import open Memo.O -let jsoo_env = +let compute_env ~mode = let f = Env_stanza_db_flags.flags ~name:"jsoo-env" @@ -9,11 +9,21 @@ let jsoo_env = let+ profile = Per_context.profile ctx in Js_of_ocaml.Env.(map ~f:Action_builder.return (default ~profile))) ~f:(fun ~parent expander (local : Dune_env.config) -> - let local = local.js_of_ocaml in - let+ parent = parent in + let local = + Js_of_ocaml.Mode.select ~mode ~js:local.js_of_ocaml ~wasm:local.wasm_of_ocaml + in + let* parent = parent in + let+ enabled_if = + match Option.first_some local.enabled_if parent.enabled_if with + | None -> Memo.return None + | Some enabled_if -> + let+ v = Expander.eval_blang expander enabled_if in + Some (Blang.Const v) + in { Js_of_ocaml.Env.compilation_mode = Option.first_some local.compilation_mode parent.compilation_mode ; sourcemap = Option.first_some local.sourcemap parent.sourcemap + ; enabled_if ; runtest_alias = Option.first_some local.runtest_alias parent.runtest_alias ; flags = Js_of_ocaml.Flags.make @@ -27,6 +37,10 @@ let jsoo_env = (Staged.unstage f) dir ;; +let js_env = compute_env ~mode:JS +let wasm_env = compute_env ~mode:Wasm +let jsoo_env ~dir ~mode = (Js_of_ocaml.Mode.select ~mode ~js:js_env ~wasm:wasm_env) ~dir + module Config : sig type t @@ -194,16 +208,21 @@ let jsoo ~dir sctx = "js_of_ocaml" ;; +let wasmoo ~dir sctx = + (* TODO add a hint when wasm_of_ocaml released on opam *) + Super_context.resolve_program sctx ~dir ~loc:None "wasm_of_ocaml" +;; + type sub_command = | Compile | Link | Build_runtime -let js_of_ocaml_flags t ~dir (spec : Js_of_ocaml.Flags.Spec.t) = +let js_of_ocaml_flags t ~dir ~mode (spec : Js_of_ocaml.Flags.Spec.t) = Action_builder.of_memo @@ let+ expander = Super_context.expander t ~dir - and+ js_of_ocaml = jsoo_env ~dir in + and+ js_of_ocaml = jsoo_env ~dir ~mode in Js_of_ocaml.Flags.make ~spec ~default:js_of_ocaml.flags @@ -212,6 +231,7 @@ let js_of_ocaml_flags t ~dir (spec : Js_of_ocaml.Flags.Spec.t) = let js_of_ocaml_rule sctx + ~(mode : Js_of_ocaml.Mode.t) ~sub_command ~dir ~(flags : _ Js_of_ocaml.Flags.t) @@ -219,11 +239,16 @@ let js_of_ocaml_rule ~spec ~target ~sourcemap + ~directory_targets = let open Action_builder.O in - let jsoo = jsoo ~dir sctx in + let jsoo = + match mode with + | JS -> jsoo ~dir sctx + | Wasm -> wasmoo ~dir sctx + in let flags = - let* flags = js_of_ocaml_flags sctx ~dir flags in + let* flags = js_of_ocaml_flags sctx ~dir ~mode flags in match sub_command with | Compile -> flags.compile | Link -> flags.link @@ -240,6 +265,7 @@ let js_of_ocaml_rule | No -> A "--no-source-map" | Inline -> A "--source-map-inline" | File -> + assert (Js_of_ocaml.Mode.select ~mode ~js:true ~wasm:false); S [ A "--source-map" ; Hidden_targets [ Path.Build.set_extension target ~ext:".map" ] @@ -256,15 +282,22 @@ let js_of_ocaml_rule ; Target target ; spec ] + |> Action_builder.With_targets.add_directories ~directory_targets ;; -let jsoo_runtime_files = List.concat_map ~f:(fun t -> Lib_info.jsoo_runtime (Lib.info t)) +let jsoo_runtime_files ~(mode : Js_of_ocaml.Mode.t) libs = + List.concat_map libs ~f:(fun t -> + (match mode with + | JS -> Lib_info.jsoo_runtime + | Wasm -> Lib_info.wasmoo_runtime) + (Lib.info t)) +;; -let standalone_runtime_rule cc ~javascript_files ~target ~flags = +let standalone_runtime_rule ~mode cc ~runtime_files ~target ~flags = let dir = Compilation_context.dir cc in let sctx = Compilation_context.super_context cc in let config = - js_of_ocaml_flags sctx ~dir flags + js_of_ocaml_flags sctx ~dir ~mode flags |> Action_builder.bind ~f:(fun (x : _ Js_of_ocaml.Flags.t) -> x.compile) |> Action_builder.map ~f:Config.of_flags in @@ -274,22 +307,24 @@ let standalone_runtime_rule cc ~javascript_files ~target ~flags = [ Resolve.Memo.args (let open Resolve.Memo.O in let+ libs = libs in - Command.Args.Deps (jsoo_runtime_files libs)) - ; Deps (List.map ~f:Path.build javascript_files) + Command.Args.Deps (jsoo_runtime_files ~mode libs)) + ; Deps (List.map ~f:Path.build runtime_files) ] in let dir = Compilation_context.dir cc in js_of_ocaml_rule (Compilation_context.super_context cc) + ~mode ~sub_command:Build_runtime ~dir ~flags ~target + ~directory_targets:[] ~spec ~config:(Some config) ;; -let exe_rule cc ~linkall ~javascript_files ~src ~target ~flags = +let exe_rule ~mode cc ~linkall ~runtime_files ~src ~target ~directory_targets ~flags = let dir = Compilation_context.dir cc in let sctx = Compilation_context.super_context cc in let libs = Compilation_context.requires_link cc in @@ -313,50 +348,77 @@ let exe_rule cc ~linkall ~javascript_files ~src ~target ~flags = [ Resolve.Memo.args (let open Resolve.Memo.O in let+ libs = libs in - Command.Args.Deps (jsoo_runtime_files libs)) - ; Deps (List.map ~f:Path.build javascript_files) + Command.Args.Deps (jsoo_runtime_files ~mode libs)) + ; Deps (List.map ~f:Path.build runtime_files) ; Dep (Path.build src) ; Dyn linkall ] in - js_of_ocaml_rule sctx ~sub_command:Compile ~dir ~spec ~target ~flags ~config:None + js_of_ocaml_rule + sctx + ~mode + ~sub_command:Compile + ~dir + ~spec + ~target + ~directory_targets + ~flags + ~config:None ;; -let with_js_ext s = +let with_js_ext ~mode s = match Filename.split_extension s with - | name, ".cma" -> name ^ Js_of_ocaml.Ext.cma - | name, ".cmo" -> name ^ Js_of_ocaml.Ext.cmo + | name, ".cma" -> name ^ Js_of_ocaml.Ext.cma ~mode + | name, ".cmo" -> name ^ Js_of_ocaml.Ext.cmo ~mode | _ -> assert false ;; -let jsoo_archives ctx config lib = +let jsoo_archives ~mode ctx config lib = let info = Lib.info lib in let archives = Lib_info.archives info in match Lib.is_local lib with | true -> let obj_dir = Lib_info.obj_dir info in List.map archives.byte ~f:(fun archive -> - in_obj_dir' ~obj_dir ~config:(Some config) [ with_js_ext (Path.basename archive) ]) + in_obj_dir' + ~obj_dir + ~config:(Some config) + [ with_js_ext ~mode (Path.basename archive) ]) | false -> List.map archives.byte ~f:(fun archive -> Path.build (in_build_dir ctx ~config - [ Lib_name.to_string (Lib.name lib); with_js_ext (Path.basename archive) ])) + [ Lib_name.to_string (Lib.name lib) + ; with_js_ext ~mode (Path.basename archive) + ])) ;; -let link_rule cc ~runtime ~target ~obj_dir cm ~flags ~linkall ~link_time_code_gen = +let link_rule + ~mode + cc + ~runtime + ~target + ~directory_targets + ~obj_dir + cm + ~flags + ~linkall + ~link_time_code_gen + = let sctx = Compilation_context.super_context cc in let dir = Compilation_context.dir cc in let mod_name m = - Module_name.Unique.artifact_filename (Module.obj_name m) ~ext:Js_of_ocaml.Ext.cmo + Module_name.Unique.artifact_filename + (Module.obj_name m) + ~ext:(Js_of_ocaml.Ext.cmo ~mode) in let ctx = Super_context.context sctx |> Context.build_context in let get_all = let open Action_builder.O in let+ config = - js_of_ocaml_flags sctx ~dir flags + js_of_ocaml_flags sctx ~dir ~mode flags |> Action_builder.bind ~f:(fun (x : _ Js_of_ocaml.Flags.t) -> x.compile) |> Action_builder.map ~f:Config.of_flags and+ cm = cm @@ -371,20 +433,22 @@ let link_rule cc ~runtime ~target ~obj_dir cm ~flags ~linkall ~link_time_code_ge (* Special case for the stdlib because it is not referenced in the META *) let stdlib = - Path.build (in_build_dir ctx ~config [ "stdlib"; "stdlib" ^ Js_of_ocaml.Ext.cma ]) + Path.build + (in_build_dir ctx ~config [ "stdlib"; "stdlib" ^ Js_of_ocaml.Ext.cma ~mode ]) in let special_units = List.concat_map to_link ~f:(function | Lib_flags.Lib_and_module.Lib _lib -> [] | Module (obj_dir, m) -> [ in_obj_dir' ~obj_dir ~config:None [ mod_name m ] ]) in - let all_libs = List.concat_map libs ~f:(jsoo_archives ctx config) in + let all_libs = List.concat_map libs ~f:(jsoo_archives ~mode ctx config) in let all_other_modules = List.map cm ~f:(fun m -> Path.build (in_obj_dir ~obj_dir ~config:None [ mod_name m ])) in let std_exit = - Path.build (in_build_dir ctx ~config [ "stdlib"; "std_exit" ^ Js_of_ocaml.Ext.cmo ]) + Path.build + (in_build_dir ctx ~config [ "stdlib"; "std_exit" ^ Js_of_ocaml.Ext.cmo ~mode ]) in let linkall = force_linkall || linkall in Command.Args.S @@ -401,22 +465,42 @@ let link_rule cc ~runtime ~target ~obj_dir cm ~flags ~linkall ~link_time_code_ge ] in let spec = Command.Args.S [ Dep (Path.build runtime); Dyn get_all ] in - js_of_ocaml_rule sctx ~sub_command:Link ~dir ~spec ~target ~flags ~config:None + js_of_ocaml_rule + sctx + ~mode + ~sub_command:Link + ~dir + ~spec + ~target + ~directory_targets + ~flags + ~config:None ;; -let build_cm' sctx ~dir ~in_context ~src ~target ~config ~sourcemap = +let build_cm' sctx ~dir ~in_context ~mode ~src ~target ~config ~sourcemap = let spec = Command.Args.Dep src in let flags = in_context.Js_of_ocaml.In_context.flags in - js_of_ocaml_rule sctx ~sub_command:Compile ~dir ~flags ~spec ~target ~config ~sourcemap + js_of_ocaml_rule + sctx + ~mode + ~sub_command:Compile + ~dir + ~flags + ~spec + ~target + ~directory_targets:[] + ~config + ~sourcemap ;; -let build_cm sctx ~dir ~in_context ~src ~obj_dir ~config = - let name = with_js_ext (Path.basename src) in +let build_cm sctx ~dir ~in_context ~mode ~src ~obj_dir ~config = + let name = with_js_ext ~mode (Path.basename src) in let target = in_obj_dir ~obj_dir ~config [ name ] in build_cm' sctx ~dir ~in_context + ~mode ~src ~target ~config:(Option.map config ~f:Action_builder.return) @@ -450,35 +534,32 @@ let setup_separate_compilation_rules sctx components = archive "stdlib.cma" :: archive "std_exit.cmo" :: archives | _ -> archives in - Memo.parallel_iter archives ~f:(fun fn -> - let build_context = Context.build_context ctx in - let name = Path.basename fn in - let dir = in_build_dir build_context ~config [ lib_name ] in - let in_context = - { Js_of_ocaml.In_context.flags = Js_of_ocaml.Flags.standard - ; javascript_files = [] - ; compilation_mode = None - ; sourcemap = None - } - in - let src = - let src_dir = Lib_info.src_dir info in - Path.relative src_dir name - in - let target = in_build_dir build_context ~config [ lib_name; with_js_ext name ] in - build_cm' - sctx - ~dir - ~in_context - ~src - ~target - ~config:(Some (Action_builder.return config)) - ~sourcemap:Js_of_ocaml.Sourcemap.Inline - |> Super_context.add_rule sctx ~dir)) + Memo.parallel_iter Js_of_ocaml.Mode.all ~f:(fun mode -> + Memo.parallel_iter archives ~f:(fun fn -> + let build_context = Context.build_context ctx in + let name = Path.basename fn in + let dir = in_build_dir build_context ~config [ lib_name ] in + let src = + let src_dir = Lib_info.src_dir info in + Path.relative src_dir name + in + let target = + in_build_dir build_context ~config [ lib_name; with_js_ext ~mode name ] + in + build_cm' + sctx + ~dir + ~in_context:Js_of_ocaml.In_context.default + ~mode + ~src + ~target + ~config:(Some (Action_builder.return config)) + ~sourcemap:Js_of_ocaml.Sourcemap.Inline + |> Super_context.add_rule sctx ~dir))) ;; -let js_of_ocaml_compilation_mode t ~dir = - let+ js_of_ocaml = jsoo_env ~dir in +let js_of_ocaml_compilation_mode t ~dir ~mode = + let+ js_of_ocaml = jsoo_env ~dir ~mode in match js_of_ocaml.compilation_mode with | Some m -> m | None -> @@ -487,8 +568,8 @@ let js_of_ocaml_compilation_mode t ~dir = else Whole_program ;; -let js_of_ocaml_sourcemap t ~dir = - let+ js_of_ocaml = jsoo_env ~dir in +let js_of_ocaml_sourcemap t ~dir ~mode = + let+ js_of_ocaml = jsoo_env ~dir ~mode in match js_of_ocaml.sourcemap with | Some sm -> sm | None -> @@ -497,6 +578,39 @@ let js_of_ocaml_sourcemap t ~dir = else No ;; +let jsoo_enabled + ~eval + ~dir + ~(in_context : Js_of_ocaml.In_context.t Js_of_ocaml.Mode.Pair.t) + ~mode + = + match (Js_of_ocaml.Mode.Pair.select ~mode in_context).enabled_if with + | Some enabled_if -> eval enabled_if + | None -> + let* js_of_ocaml = jsoo_env ~dir ~mode in + (match js_of_ocaml.enabled_if with + | Some enabled_if -> eval enabled_if + | None -> Memo.return true) +;; + +let jsoo_enabled_modes ~expander ~dir ~in_context = + let eval = Expander.eval_blang expander in + let+ js = jsoo_enabled ~eval ~dir ~in_context ~mode:JS + and+ wasm = jsoo_enabled ~eval ~dir ~in_context ~mode:Wasm in + { Js_of_ocaml.Mode.Pair.js; wasm } +;; + +let jsoo_is_whole_program t ~dir = + let is_whole_program (mode : Js_of_ocaml.Compilation_mode.t) = + match mode with + | Whole_program -> true + | Separate_compilation -> false + in + let+ js = js_of_ocaml_compilation_mode t ~dir ~mode:JS + and+ wasm = js_of_ocaml_compilation_mode t ~dir ~mode:Wasm in + { Js_of_ocaml.Mode.Pair.js = is_whole_program js; wasm = is_whole_program wasm } +;; + let build_exe cc ~loc @@ -507,66 +621,90 @@ let build_exe ~promote ~linkall ~link_time_code_gen + ~jsoo_mode:mode = let sctx = Compilation_context.super_context cc in let dir = Compilation_context.dir cc in - let { Js_of_ocaml.In_context.javascript_files; flags; compilation_mode; sourcemap } = + let { javascript_files; wasm_files; flags; compilation_mode; sourcemap; _ } + : Js_of_ocaml.In_context.t + = in_context in - let target = Path.Build.set_extension src ~ext:Js_of_ocaml.Ext.exe in + let target = Path.Build.set_extension src ~ext:(Js_of_ocaml.Ext.exe ~mode) in let standalone_runtime = in_obj_dir ~obj_dir ~config:None - [ Path.Build.basename (Path.Build.set_extension src ~ext:Js_of_ocaml.Ext.runtime) ] + [ Path.Build.basename + (Path.Build.set_extension src ~ext:(Js_of_ocaml.Ext.runtime ~mode)) + ] in - let mode : Rule.Mode.t = + let rule_mode : Rule.Mode.t = match promote with | None -> Standard | Some p -> Promote p in let* cmode = match compilation_mode with - | None -> js_of_ocaml_compilation_mode sctx ~dir + | None -> js_of_ocaml_compilation_mode sctx ~dir ~mode | Some x -> Memo.return x and* sourcemap = match sourcemap with - | None -> js_of_ocaml_sourcemap sctx ~dir + | None -> js_of_ocaml_sourcemap sctx ~dir ~mode | Some x -> Memo.return x in + assert (Js_of_ocaml.Mode.select ~mode ~js:(wasm_files = []) ~wasm:true); + let runtime_files = javascript_files @ wasm_files in + let directory_targets = + match mode with + | JS -> [] + | Wasm -> [ Path.Build.set_extension src ~ext:Js_of_ocaml.Ext.wasm_dir ] + in match (cmode : Js_of_ocaml.Compilation_mode.t) with | Separate_compilation -> let+ () = standalone_runtime_rule + ~mode cc - ~javascript_files + ~runtime_files ~target:standalone_runtime ~flags ~sourcemap:Js_of_ocaml.Sourcemap.Inline |> Super_context.add_rule ~loc sctx ~dir and+ () = link_rule + ~mode cc ~runtime:standalone_runtime ~target + ~directory_targets ~obj_dir top_sorted_modules ~flags ~linkall ~link_time_code_gen ~sourcemap - |> Super_context.add_rule sctx ~loc ~dir ~mode + |> Super_context.add_rule sctx ~loc ~dir ~mode:rule_mode in () | Whole_program -> - exe_rule cc ~linkall ~javascript_files ~src ~target ~flags ~sourcemap - |> Super_context.add_rule sctx ~loc ~dir ~mode + exe_rule + ~mode + cc + ~linkall + ~runtime_files + ~src + ~target + ~directory_targets + ~flags + ~sourcemap + |> Super_context.add_rule sctx ~loc ~dir ~mode:rule_mode ;; let runner = "node" -let js_of_ocaml_runtest_alias ~dir = - let+ js_of_ocaml = jsoo_env ~dir in +let js_of_ocaml_runtest_alias ~dir ~mode = + let+ js_of_ocaml = jsoo_env ~dir ~mode in match js_of_ocaml.runtest_alias with | Some a -> a | None -> Alias0.runtest diff --git a/src/dune_rules/jsoo/jsoo_rules.mli b/src/dune_rules/jsoo/jsoo_rules.mli index e5643e1b4c7..df7e768e62f 100644 --- a/src/dune_rules/jsoo/jsoo_rules.mli +++ b/src/dune_rules/jsoo/jsoo_rules.mli @@ -19,6 +19,7 @@ val build_cm : Super_context.t -> dir:Path.Build.t -> in_context:Js_of_ocaml.In_context.t + -> mode:Js_of_ocaml.Mode.t -> src:Path.t -> obj_dir:Path.Build.t Obj_dir.t -> config:Config.t option @@ -34,14 +35,42 @@ val build_exe -> promote:Rule.Promote.t option -> linkall:bool Action_builder.t -> link_time_code_gen:Link_time_code_gen_type.t Resolve.t + -> jsoo_mode:Js_of_ocaml.Mode.t -> unit Memo.t val setup_separate_compilation_rules : Super_context.t -> string list -> unit Memo.t val runner : string -val js_of_ocaml_runtest_alias : dir:Path.Build.t -> Alias.Name.t Memo.t -val jsoo_env : dir:Path.Build.t -> string list Action_builder.t Js_of_ocaml.Env.t Memo.t + +val js_of_ocaml_runtest_alias + : dir:Path.Build.t + -> mode:Js_of_ocaml.Mode.t + -> Alias.Name.t Memo.t + +val jsoo_env + : dir:Path.Build.t + -> mode:Js_of_ocaml.Mode.t + -> string list Action_builder.t Js_of_ocaml.Env.t Memo.t + +val jsoo_enabled + : eval:(Blang.t -> bool Memo.t) + -> dir:Path.Build.t + -> in_context:Js_of_ocaml.In_context.t Js_of_ocaml.Mode.Pair.t + -> mode:Js_of_ocaml.Mode.t + -> bool Memo.t + +val jsoo_enabled_modes + : expander:Expander.t + -> dir:Path.Build.t + -> in_context:Js_of_ocaml.In_context.t Js_of_ocaml.Mode.Pair.t + -> Js_of_ocaml.Mode.Set.t Memo.t + +val jsoo_is_whole_program + : Super_context.t + -> dir:Path.Build.t + -> Js_of_ocaml.Mode.Set.t Memo.t val js_of_ocaml_compilation_mode : Super_context.t -> dir:Path.Build.t + -> mode:Js_of_ocaml.Mode.t -> Js_of_ocaml.Compilation_mode.t Memo.t diff --git a/src/dune_rules/lib_info.ml b/src/dune_rules/lib_info.ml index 32a89a883f8..ca5d45f6536 100644 --- a/src/dune_rules/lib_info.ml +++ b/src/dune_rules/lib_info.ml @@ -316,6 +316,7 @@ type 'path t = ; native_archives : 'path native_archives ; foreign_dll_files : 'path list ; jsoo_runtime : 'path list + ; wasmoo_runtime : 'path list ; requires : Lib_dep.t list ; ppx_runtime_deps : (Loc.t * Lib_name.t) list ; preprocess : Preprocess.With_instrumentation.t Preprocess.Per_module.t @@ -371,6 +372,7 @@ let synopsis t = t.synopsis let wrapped t = t.wrapped let special_builtin_support t = t.special_builtin_support let jsoo_runtime t = t.jsoo_runtime +let wasmoo_runtime t = t.wasmoo_runtime let main_module_name t = t.main_module_name let orig_src_dir t = t.orig_src_dir let best_src_dir t = Option.value ~default:t.src_dir t.orig_src_dir @@ -414,6 +416,7 @@ let create ~native_archives ~foreign_dll_files ~jsoo_runtime + ~wasmoo_runtime ~preprocess ~enabled ~virtual_deps @@ -451,6 +454,7 @@ let create ; native_archives ; foreign_dll_files ; jsoo_runtime + ; wasmoo_runtime ; preprocess ; enabled ; virtual_deps @@ -495,6 +499,7 @@ let map t ~path_kind ~f_path ~f_obj_dir ~f_public_deps = ; foreign_dll_files = List.map ~f t.foreign_dll_files ; native_archives ; jsoo_runtime = List.map ~f t.jsoo_runtime + ; wasmoo_runtime = List.map ~f t.wasmoo_runtime ; melange_runtime_deps = File_deps.map ~f:f_public_deps t.melange_runtime_deps ; path_kind } @@ -544,6 +549,7 @@ let to_dyn ; native_archives ; foreign_dll_files ; jsoo_runtime + ; wasmoo_runtime ; preprocess = _ ; enabled = _ ; virtual_deps @@ -583,6 +589,7 @@ let to_dyn ; "native_archives", dyn_of_native_archives path native_archives ; "foreign_dll_files", list path foreign_dll_files ; "jsoo_runtime", list path jsoo_runtime + ; "wasmoo_runtime", list path wasmoo_runtime ; "requires", list Lib_dep.to_dyn requires ; "ppx_runtime_deps", list (snd Lib_name.to_dyn) ppx_runtime_deps ; "virtual_deps", list (snd Lib_name.to_dyn) virtual_deps diff --git a/src/dune_rules/lib_info.mli b/src/dune_rules/lib_info.mli index 41ea767962a..0fe1fa5868c 100644 --- a/src/dune_rules/lib_info.mli +++ b/src/dune_rules/lib_info.mli @@ -136,6 +136,7 @@ val default_implementation : _ t -> (Loc.t * Lib_name.t) option val kind : _ t -> Lib_kind.t val synopsis : _ t -> string option val jsoo_runtime : 'path t -> 'path list +val wasmoo_runtime : 'path t -> 'path list val melange_runtime_deps : 'path t -> 'path File_deps.t val obj_dir : 'path t -> 'path Obj_dir.t val virtual_ : _ t -> Modules.t Source.t option @@ -212,6 +213,7 @@ val create -> native_archives:'a native_archives -> foreign_dll_files:'a list -> jsoo_runtime:'a list + -> wasmoo_runtime:'a list -> preprocess:Preprocess.With_instrumentation.t Preprocess.Per_module.t -> enabled:Enabled_status.t Memo.t -> virtual_deps:(Loc.t * Lib_name.t) list diff --git a/src/dune_rules/lib_rules.ml b/src/dune_rules/lib_rules.ml index 8fbb9b2d2de..5919ad64359 100644 --- a/src/dune_rules/lib_rules.ml +++ b/src/dune_rules/lib_rules.ml @@ -471,21 +471,23 @@ let setup_build_archives (lib : Library.t) ~top_sorted_modules ~cctx ~expander ~ iter_modes_concurrently modes.ocaml ~f:(fun mode -> build_lib lib ~native_archives ~dir ~sctx ~expander ~flags ~mode ~cm_files) and* () = - (* Build *.cma.js *) + (* Build *.cma.js / *.wasma *) Memo.when_ modes.ocaml.byte (fun () -> let src = Library.archive lib ~dir ~ext:(Mode.compiled_lib_ext Mode.Byte) in - let action_with_targets = - List.map Jsoo_rules.Config.all ~f:(fun config -> - Jsoo_rules.build_cm - sctx - ~dir - ~in_context:js_of_ocaml - ~config:(Some config) - ~src:(Path.build src) - ~obj_dir) - in - Memo.parallel_iter action_with_targets ~f:(fun rule -> - Super_context.add_rule sctx ~dir ~loc:lib.buildable.loc rule)) + Memo.parallel_iter Js_of_ocaml.Mode.all ~f:(fun mode -> + let action_with_targets = + List.map Jsoo_rules.Config.all ~f:(fun config -> + Jsoo_rules.build_cm + sctx + ~dir + ~in_context:(Js_of_ocaml.Mode.Pair.select ~mode js_of_ocaml) + ~mode + ~config:(Some config) + ~src:(Path.build src) + ~obj_dir) + in + Memo.parallel_iter action_with_targets ~f:(fun rule -> + Super_context.add_rule sctx ~dir ~loc:lib.buildable.loc rule))) in Memo.when_ (Dynlink_supported.By_the_os.get natdynlink_supported && modes.ocaml.native) @@ -540,7 +542,7 @@ let cctx (lib : Library.t) ~sctx ~source_modules ~dir ~expander ~scope ~compile_ ~requires_link ~preprocessing:pp ~opaque:Inherit_from_settings - ~js_of_ocaml:(Some js_of_ocaml) + ~js_of_ocaml:(Js_of_ocaml.Mode.Pair.map ~f:Option.some js_of_ocaml) ?stdlib:lib.stdlib ~package ?vimpl diff --git a/src/dune_rules/mdx.ml b/src/dune_rules/mdx.ml index 891a6df0bfa..03df5c205aa 100644 --- a/src/dune_rules/mdx.ml +++ b/src/dune_rules/mdx.ml @@ -476,7 +476,7 @@ let mdx_prog_gen t ~sctx ~dir ~scope ~mdx_prog = ~requires_compile ~requires_link ~opaque:(Explicit false) - ~js_of_ocaml:None + ~js_of_ocaml:(Js_of_ocaml.Mode.Pair.make None) ~melange_package_name:None ~package:None () diff --git a/src/dune_rules/melange/melange_rules.ml b/src/dune_rules/melange/melange_rules.ml index 146cd46aef8..6bf099324c1 100644 --- a/src/dune_rules/melange/melange_rules.ml +++ b/src/dune_rules/melange/melange_rules.ml @@ -316,7 +316,7 @@ let setup_emit_cmj_rules ~requires_link ~requires_compile:direct_requires ~preprocessing:pp - ~js_of_ocaml:None + ~js_of_ocaml:(Js_of_ocaml.Mode.Pair.make None) ~opaque:Inherit_from_settings ~melange_package_name:None ~package:mel.package diff --git a/src/dune_rules/module_compilation.ml b/src/dune_rules/module_compilation.ml index 57e3bd62aaf..3b469b24ae5 100644 --- a/src/dune_rules/module_compilation.ml +++ b/src/dune_rules/module_compilation.ml @@ -311,21 +311,24 @@ let build_module ?(force_write_cmi = false) ?(precompiled_cmi = false) cctx m = match Obj_dir.Module.cm_file obj_dir m ~kind:(Ocaml Cmo) with | None -> Memo.return () | Some src -> - Compilation_context.js_of_ocaml cctx - |> Memo.Option.iter ~f:(fun in_context -> - (* Build *.cmo.js *) - let sctx = Compilation_context.super_context cctx in - let dir = Compilation_context.dir cctx in - let action_with_targets = - Jsoo_rules.build_cm - sctx - ~dir - ~in_context - ~src:(Path.build src) - ~obj_dir - ~config:None - in - Super_context.add_rule sctx ~dir action_with_targets)) + Memo.parallel_iter Js_of_ocaml.Mode.all ~f:(fun mode -> + Compilation_context.js_of_ocaml cctx + |> Js_of_ocaml.Mode.Pair.select ~mode + |> Memo.Option.iter ~f:(fun in_context -> + (* Build *.cmo.js / *.wasmo *) + let sctx = Compilation_context.super_context cctx in + let dir = Compilation_context.dir cctx in + let action_with_targets = + Jsoo_rules.build_cm + sctx + ~dir + ~in_context + ~mode + ~src:(Path.build src) + ~obj_dir + ~config:None + in + Super_context.add_rule sctx ~dir action_with_targets))) in Memo.when_ melange (fun () -> let* () = build_cm ~cm_kind:(Melange Cmj) ~phase:None in diff --git a/src/dune_rules/ppx_driver.ml b/src/dune_rules/ppx_driver.ml index ab2e78b5b49..cb42f95a9d1 100644 --- a/src/dune_rules/ppx_driver.ml +++ b/src/dune_rules/ppx_driver.ml @@ -323,7 +323,7 @@ let build_ppx_driver sctx ~scope ~target ~pps ~pp_names = ~requires_compile:(Memo.return requires_compile) ~requires_link ~opaque - ~js_of_ocaml:None + ~js_of_ocaml:(Js_of_ocaml.Mode.Pair.make None) ~melange_package_name:None ~package:None ~bin_annot:false diff --git a/src/dune_rules/stanzas/buildable.ml b/src/dune_rules/stanzas/buildable.ml index d4768cc49a4..5b6a3ca86ff 100644 --- a/src/dune_rules/stanzas/buildable.ml +++ b/src/dune_rules/stanzas/buildable.ml @@ -17,7 +17,7 @@ type t = ; preprocessor_deps : Dep_conf.t list ; lint : Preprocess.Without_instrumentation.t Preprocess.Per_module.t ; flags : Ocaml_flags.Spec.t - ; js_of_ocaml : Js_of_ocaml.In_buildable.t + ; js_of_ocaml : Js_of_ocaml.In_buildable.t Js_of_ocaml.Mode.Pair.t ; allow_overlapping_dependencies : bool ; ctypes : Ctypes_field.t option } @@ -29,11 +29,12 @@ let decode (for_ : for_) = (2, 0) ~extra_info:"Use the (foreign_stubs ...) field instead." in - let only_in_library decode = + let in_library = match for_ with - | Executable -> return None - | Library _ -> decode + | Library _ -> true + | Executable -> false in + let only_in_library decode = if in_library then decode else return None in let add_stubs language ~loc ~names ~flags foreign_stubs = match names with | None -> foreign_stubs @@ -84,22 +85,18 @@ let decode (for_ : for_) = ~extra_info:"Use the (foreign_archives ...) field instead." >>> enter (maybe string)))) and+ libraries = - let allow_re_export = - match for_ with - | Library _ -> true - | Executable -> false - in - field "libraries" (Lib_dep.L.decode ~allow_re_export) ~default:[] + field "libraries" (Lib_dep.L.decode ~allow_re_export:in_library) ~default:[] and+ flags = Ocaml_flags.Spec.decode and+ js_of_ocaml = - let executable = - match for_ with - | Executable -> true - | Library _ -> false - in field "js_of_ocaml" - (Js_of_ocaml.In_buildable.decode ~executable) + (Js_of_ocaml.In_buildable.decode ~in_library ~mode:JS) + ~default:Js_of_ocaml.In_buildable.default + and+ wasm_of_ocaml = + field + "wasm_of_ocaml" + (Dune_lang.Syntax.since Stanza.syntax (3, 17) + >>> Js_of_ocaml.In_buildable.decode ~in_library ~mode:Wasm) ~default:Js_of_ocaml.In_buildable.default and+ allow_overlapping_dependencies = field_b "allow_overlapping_dependencies" and+ version = Dune_lang.Syntax.get_exn Stanza.syntax @@ -171,7 +168,7 @@ let decode (for_ : for_) = ; extra_objects ; libraries ; flags - ; js_of_ocaml + ; js_of_ocaml = { js = js_of_ocaml; wasm = wasm_of_ocaml } ; allow_overlapping_dependencies ; ctypes } diff --git a/src/dune_rules/stanzas/buildable.mli b/src/dune_rules/stanzas/buildable.mli index a4490318911..ec96635d088 100644 --- a/src/dune_rules/stanzas/buildable.mli +++ b/src/dune_rules/stanzas/buildable.mli @@ -16,7 +16,7 @@ type t = ; preprocessor_deps : Dep_conf.t list ; lint : Lint.t ; flags : Ocaml_flags.Spec.t - ; js_of_ocaml : Js_of_ocaml.In_buildable.t + ; js_of_ocaml : Js_of_ocaml.In_buildable.t Js_of_ocaml.Mode.Pair.t ; allow_overlapping_dependencies : bool ; ctypes : Ctypes_field.t option } diff --git a/src/dune_rules/stanzas/executables.ml b/src/dune_rules/stanzas/executables.ml index 875b8f61cdf..2fb025d2488 100644 --- a/src/dune_rules/stanzas/executables.ml +++ b/src/dune_rules/stanzas/executables.ml @@ -206,6 +206,7 @@ module Link_mode = struct module T = struct type t = | Byte_complete + | Jsoo of Js_of_ocaml.Mode.t | Other of { mode : Mode_conf.t ; kind : Binary_kind.t @@ -220,6 +221,9 @@ module Link_mode = struct let open Ordering.O in let= () = Mode_conf.compare mode t.mode in Binary_kind.compare kind t.kind + | Other _, _ -> Lt + | _, Other _ -> Gt + | Jsoo m, Jsoo m' -> Js_of_ocaml.Mode.compare m m' ;; let to_dyn = Dyn.opaque @@ -233,8 +237,15 @@ module Link_mode = struct let shared_object = make Best Shared_object let byte = make Byte Exe let native = make Native Exe - let js = make Byte Js let plugin = make Best Plugin + let js = Jsoo JS + let wasm = Jsoo Wasm + + let is_jsoo m = + match m with + | Jsoo _ -> true + | Byte_complete | Other _ -> false + ;; let simple_representations = [ "exe", exe @@ -248,7 +259,12 @@ module Link_mode = struct ] ;; - let simple = Dune_lang.Decoder.enum simple_representations + let simple_representations_including_wasm = ("wasm", wasm) :: simple_representations + + let simple = + Dune_lang.Decoder.enum simple_representations + <|> sum [ "wasm", Syntax.since Stanza.syntax (3, 17) >>> return wasm ] + ;; let decode = enter @@ -256,11 +272,17 @@ module Link_mode = struct and+ kind = Binary_kind.decode in make mode kind) <|> simple + <|> enter + (keyword "byte" + >>> sum + [ "js", Syntax.since Stanza.syntax (1, 11) >>> return js + ; "wasm", Syntax.since Stanza.syntax (3, 17) >>> return wasm + ]) ;; let simple_encode link_mode = let is_ok (_, candidate) = compare candidate link_mode = Eq in - List.find ~f:is_ok simple_representations + List.find ~f:is_ok simple_representations_including_wasm |> Option.map ~f:(fun (s, _) -> Dune_lang.atom s) ;; @@ -269,7 +291,7 @@ module Link_mode = struct | Some s -> s | None -> (match link_mode with - | Byte_complete -> assert false + | Byte_complete | Jsoo _ -> assert false | Other { mode; kind } -> Dune_lang.Encoder.pair Mode_conf.encode Binary_kind.encode (mode, kind)) ;; @@ -277,6 +299,7 @@ module Link_mode = struct let to_dyn t = match t with | Byte_complete -> Dyn.Variant ("Byte_complete", []) + | Jsoo mode -> Dyn.Variant ("Jsoo", [ Js_of_ocaml.Mode.to_dyn mode ]) | Other { mode; kind } -> let open Dyn in Variant @@ -287,6 +310,7 @@ module Link_mode = struct let extension t ~loc ~ext_obj ~ext_dll = match t with | Byte_complete -> ".bc.exe" + | Jsoo mode -> Js_of_ocaml.Ext.exe ~mode | Other { mode; kind } -> let same_as_mode : Mode.t = match mode with @@ -306,10 +330,7 @@ module Link_mode = struct | Native, Object -> ".exe" ^ ext_obj | Byte, Shared_object -> ".bc" ^ ext_dll | Native, Shared_object -> ext_dll - | mode, Plugin -> Mode.plugin_ext mode - | Byte, Js -> Js_of_ocaml.Ext.exe - | Native, Js -> - User_error.raise ~loc [ Pp.text "Javascript generation only supports bytecode!" ]) + | mode, Plugin -> Mode.plugin_ext mode) ;; module O = Comparable.Make (T) @@ -494,6 +515,7 @@ let common = let ext = match mode with | Byte_complete -> ".bc.exe" + | Jsoo mode -> Js_of_ocaml.Ext.exe ~mode | Other { mode = Byte; _ } -> ".bc" | Other { mode = Native | Best; _ } -> ".exe" in diff --git a/src/dune_rules/stanzas/executables.mli b/src/dune_rules/stanzas/executables.mli index 245112db886..8c5375cf37e 100644 --- a/src/dune_rules/stanzas/executables.mli +++ b/src/dune_rules/stanzas/executables.mli @@ -3,6 +3,7 @@ open Import module Link_mode : sig type t = | Byte_complete + | Jsoo of Js_of_ocaml.Mode.t | Other of { mode : Mode_conf.t ; kind : Binary_kind.t @@ -16,6 +17,8 @@ module Link_mode : sig val byte : t val native : t val js : t + val wasm : t + val is_jsoo : t -> bool val compare : t -> t -> Ordering.t val to_dyn : t -> Dyn.t diff --git a/src/dune_rules/stanzas/library.ml b/src/dune_rules/stanzas/library.ml index ae64703d300..c0a4f42c6d2 100644 --- a/src/dune_rules/stanzas/library.ml +++ b/src/dune_rules/stanzas/library.ml @@ -426,7 +426,13 @@ let to_lib_info Mode.Dict.of_func (fun ~mode -> archive_for_mode ~f_ext ~mode |> Option.to_list) in let jsoo_runtime = - List.map conf.buildable.js_of_ocaml.javascript_files ~f:(Path.Build.relative dir) + let in_buildable = conf.buildable.js_of_ocaml.js in + List.map in_buildable.javascript_files ~f:(Path.Build.relative dir) + in + let wasmoo_runtime = + let in_buildable = conf.buildable.js_of_ocaml.wasm in + List.map in_buildable.javascript_files ~f:(Path.Build.relative dir) + @ List.map in_buildable.wasm_files ~f:(Path.Build.relative dir) in let status = match conf.visibility with @@ -566,6 +572,7 @@ let to_lib_info ~native_archives ~foreign_dll_files ~jsoo_runtime + ~wasmoo_runtime ~preprocess ~enabled ~virtual_deps diff --git a/src/dune_rules/test_rules.ml b/src/dune_rules/test_rules.ml index 33a3dc4b393..d1a5e1ceac9 100644 --- a/src/dune_rules/test_rules.ml +++ b/src/dune_rules/test_rules.ml @@ -3,7 +3,7 @@ open Memo.O let alias mode ~dir = match mode with - | `js -> Jsoo_rules.js_of_ocaml_runtest_alias ~dir + | `js mode -> Jsoo_rules.js_of_ocaml_runtest_alias ~dir ~mode | `exe | `bc -> Memo.return Alias0.runtest ;; @@ -23,18 +23,18 @@ let test_kind dir_contents (loc, name, ext) = let ext_of_mode runtest_mode = match runtest_mode with - | `js -> Js_of_ocaml.Ext.exe + | `js mode -> Js_of_ocaml.Ext.exe ~mode | `bc -> ".bc" | `exe -> ".exe" ;; let custom_runner runtest_mode = match runtest_mode with - | `js -> Some Jsoo_rules.runner + | `js _ -> Some Jsoo_rules.runner | `bc | `exe -> None ;; -let runtest_modes modes project = +let runtest_modes modes jsoo_enabled_modes project = if Dune_project.dune_version project < (3, 0) then [ `exe ] else @@ -44,16 +44,25 @@ let runtest_modes modes project = | Byte_complete -> Some `exe | Other { kind = Exe; mode = Native | Best } -> Some `exe | Other { kind = Exe; mode = Byte } -> Some `bc - | Other { kind = Js; _ } -> Some `js | Other { kind = C | Object | Shared_object | Plugin; _ } -> (* We don't know how to run tests in these cases *) - None) + None + | Jsoo mode -> + Option.some_if (Js_of_ocaml.Mode.Pair.select ~mode jsoo_enabled_modes) (`js mode)) |> List.sort_uniq ~compare:Poly.compare ;; let rules (t : Tests.t) ~sctx ~dir ~scope ~expander ~dir_contents = let* () = - let runtest_modes = runtest_modes t.exes.modes (Scope.project scope) in + let* runtest_modes = + let+ jsoo_enabled_modes = + Jsoo_rules.jsoo_enabled_modes + ~expander + ~dir + ~in_context:(Js_of_ocaml.In_context.make ~dir t.exes.buildable.js_of_ocaml) + in + runtest_modes t.exes.modes jsoo_enabled_modes (Scope.project scope) + in Expander.eval_blang expander t.enabled_if >>= function | false -> diff --git a/src/dune_rules/toplevel.ml b/src/dune_rules/toplevel.ml index e80bad4b3b0..dd131b4e12c 100644 --- a/src/dune_rules/toplevel.ml +++ b/src/dune_rules/toplevel.ml @@ -233,7 +233,7 @@ module Stanza = struct ~requires_compile ~requires_link ~flags - ~js_of_ocaml:None + ~js_of_ocaml:(Js_of_ocaml.Mode.Pair.make None) ~melange_package_name:None ~package:None ~preprocessing diff --git a/src/dune_rules/utop.ml b/src/dune_rules/utop.ml index 42369be6f5a..85c53b2b17e 100644 --- a/src/dune_rules/utop.ml +++ b/src/dune_rules/utop.ml @@ -186,7 +186,7 @@ let setup sctx ~dir = ~requires_link ~requires_compile:requires ~flags - ~js_of_ocaml:None + ~js_of_ocaml:(Js_of_ocaml.Mode.Pair.make None) ~melange_package_name:None ~package:None ~preprocessing diff --git a/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/dune b/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/dune index f6f742a6fc9..3c4373c19b2 100644 --- a/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/dune +++ b/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/dune @@ -2,10 +2,11 @@ (name foo) (install_c_headers cfoo) (js_of_ocaml (javascript_files foo.js)) - (c_names c) - (cxx_names cpp) + (wasm_of_ocaml (javascript_files foo.js) (wasm_files foo.wat)) + (foreign_stubs (language c) (names c)) + (foreign_stubs (language cxx) (names cpp)) (public_name foo)) -(alias - (name default) - (action (echo "%{read:foo.install}"))) \ No newline at end of file +(rule + (alias default) + (action (echo "%{read:foo.install}"))) diff --git a/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/dune-project b/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/dune-project index de4fc209200..3e2a6150dda 100644 --- a/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/dune-project +++ b/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/dune-project @@ -1 +1 @@ -(lang dune 1.0) +(lang dune 3.17) diff --git a/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/foo.wat b/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/foo.wat new file mode 100644 index 00000000000..3af8f254547 --- /dev/null +++ b/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/foo.wat @@ -0,0 +1 @@ +(module) diff --git a/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/run.t b/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/run.t index 6a8b999201e..25fedb24d65 100644 --- a/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/run.t +++ b/test/blackbox-tests/test-cases/gen-opam-install-file/stubs.t/run.t @@ -13,6 +13,7 @@ stubs and js files installed "_build/install/default/lib/foo/foo.cmxa" "_build/install/default/lib/foo/foo.js" "_build/install/default/lib/foo/foo.ml" + "_build/install/default/lib/foo/foo.wat" "_build/install/default/lib/foo/libfoo_stubs$ext_lib" "_build/install/default/lib/foo/opam" ] diff --git a/test/blackbox-tests/test-cases/meta-gen.t/dune b/test/blackbox-tests/test-cases/meta-gen.t/dune index 91925536c33..6310c17a1d3 100644 --- a/test/blackbox-tests/test-cases/meta-gen.t/dune +++ b/test/blackbox-tests/test-cases/meta-gen.t/dune @@ -13,7 +13,11 @@ (library (name foobar_runtime_lib2) - (js_of_ocaml (javascript_files foobar_runtime.js foobar_runtime2.js)) + (js_of_ocaml + (javascript_files foobar_runtime.js foobar_runtime2.js)) + (wasm_of_ocaml + (javascript_files foobar_runtime.js foobar_runtime2.js) + (wasm_files foobar_runtime.wat foobar_runtime2.wat)) (public_name foobar.runtime-lib2) (synopsis "runtime library for foobar.rewriter2")) @@ -41,6 +45,5 @@ (libraries foobar) (preprocess (pps foobar_rewriter))) -(alias - (name runtest) +(rule (alias runtest) (action (echo "%{read:META.foobar}"))) diff --git a/test/blackbox-tests/test-cases/meta-gen.t/dune-project b/test/blackbox-tests/test-cases/meta-gen.t/dune-project index 6aae99ad17a..3e2a6150dda 100644 --- a/test/blackbox-tests/test-cases/meta-gen.t/dune-project +++ b/test/blackbox-tests/test-cases/meta-gen.t/dune-project @@ -1 +1 @@ -(lang dune 1.9) +(lang dune 3.17) diff --git a/test/blackbox-tests/test-cases/meta-gen.t/run.t b/test/blackbox-tests/test-cases/meta-gen.t/run.t index 8a939042406..5d3c4cb8a2a 100644 --- a/test/blackbox-tests/test-cases/meta-gen.t/run.t +++ b/test/blackbox-tests/test-cases/meta-gen.t/run.t @@ -61,6 +61,11 @@ plugin(byte) = "foobar_runtime_lib2.cma" plugin(native) = "foobar_runtime_lib2.cmxs" jsoo_runtime = "foobar_runtime.js foobar_runtime2.js" + wasmoo_runtime = + "foobar_runtime.js + foobar_runtime2.js + foobar_runtime.wat + foobar_runtime2.wat" ) package "sub" ( directory = "sub" diff --git a/test/blackbox-tests/test-cases/wasmoo/build-info.t/main.opam b/test/blackbox-tests/test-cases/wasmoo/build-info.t/main.opam new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/blackbox-tests/test-cases/wasmoo/build-info.t/run.t b/test/blackbox-tests/test-cases/wasmoo/build-info.t/run.t new file mode 100644 index 00000000000..429a5cca1be --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/build-info.t/run.t @@ -0,0 +1,125 @@ +Jsoo and build-info + + $ echo "(lang dune 3.17)" > dune-project + $ dune build + Warning: '--source-map' is enabled but the bytecode program was compiled with no debugging information. + Warning: Consider passing '-g' option to ocamlc. + $ node _build/default/src/main.bc.wasm.js + unknown + $ dune install --prefix _install --display short 2>&1 | sed 's/-[a-f0-9]*[.]wasm/.wasm/' + Installing _install/lib/main/META + Installing _install/lib/main/dune-package + Installing _install/lib/main/opam + Installing _install/bin/main + Installing _install/bin/main.bc.wasm.assets/build_info.wasm + Installing _install/bin/main.bc.wasm.assets/build_info.wasm.map + Installing _install/bin/main.bc.wasm.assets/build_info__Build_info_data.wasm + Installing _install/bin/main.bc.wasm.assets/build_info__Build_info_data.wasm.map + Installing _install/bin/main.bc.wasm.assets/dune__exe__Main.wasm + Installing _install/bin/main.bc.wasm.assets/dune__exe__Main.wasm.map + Installing _install/bin/main.bc.wasm.assets/prelude.wasm + Installing _install/bin/main.bc.wasm.assets/runtime.wasm + Installing _install/bin/main.bc.wasm.assets/start.wasm + Installing _install/bin/main.bc.wasm.assets/std_exit.wasm + Installing _install/bin/main.bc.wasm.assets/std_exit.wasm.map + Installing _install/bin/main.bc.wasm.assets/stdlib.wasm + Installing _install/bin/main.bc.wasm.assets/stdlib.wasm.map + Installing _install/bin/main.bc.wasm.js + $ node _install/bin/main.bc.wasm.js + unknown + $ git init -q + $ touch README + $ git add README + $ git commit -m "initial" -q + $ git tag v1 -am "V1" + $ git commit -m "empty2" --allow-empty -q + $ echo "HELLO" > README + $ dune build + Warning: '--source-map' is enabled but the bytecode program was compiled with no debugging information. + Warning: Consider passing '-g' option to ocamlc. + $ node _build/default/src/main.bc.wasm.js + unknown + $ dune install --prefix _install --display short 2>&1 | sed 's/-[a-f0-9]*[.]wasm/.wasm/' + Deleting _install/lib/main/META + Installing _install/lib/main/META + Deleting _install/lib/main/dune-package + Installing _install/lib/main/dune-package + Deleting _install/lib/main/opam + Installing _install/lib/main/opam + Deleting _install/bin/main + Installing _install/bin/main + Deleting _install/bin/main.bc.wasm.assets/build_info.wasm + Installing _install/bin/main.bc.wasm.assets/build_info.wasm + Deleting _install/bin/main.bc.wasm.assets/build_info.wasm.map + Installing _install/bin/main.bc.wasm.assets/build_info.wasm.map + Installing _install/bin/main.bc.wasm.assets/build_info__Build_info_data.wasm + Installing _install/bin/main.bc.wasm.assets/build_info__Build_info_data.wasm.map + Deleting _install/bin/main.bc.wasm.assets/dune__exe__Main.wasm + Installing _install/bin/main.bc.wasm.assets/dune__exe__Main.wasm + Deleting _install/bin/main.bc.wasm.assets/dune__exe__Main.wasm.map + Installing _install/bin/main.bc.wasm.assets/dune__exe__Main.wasm.map + Deleting _install/bin/main.bc.wasm.assets/prelude.wasm + Installing _install/bin/main.bc.wasm.assets/prelude.wasm + Deleting _install/bin/main.bc.wasm.assets/runtime.wasm + Installing _install/bin/main.bc.wasm.assets/runtime.wasm + Deleting _install/bin/main.bc.wasm.assets/start.wasm + Installing _install/bin/main.bc.wasm.assets/start.wasm + Deleting _install/bin/main.bc.wasm.assets/std_exit.wasm + Installing _install/bin/main.bc.wasm.assets/std_exit.wasm + Deleting _install/bin/main.bc.wasm.assets/std_exit.wasm.map + Installing _install/bin/main.bc.wasm.assets/std_exit.wasm.map + Deleting _install/bin/main.bc.wasm.assets/stdlib.wasm + Installing _install/bin/main.bc.wasm.assets/stdlib.wasm + Deleting _install/bin/main.bc.wasm.assets/stdlib.wasm.map + Installing _install/bin/main.bc.wasm.assets/stdlib.wasm.map + Deleting _install/bin/main.bc.wasm.js + Installing _install/bin/main.bc.wasm.js + Installing _install/doc/main/README + $ node _install/bin/main.bc.wasm.js + v1-1-xxxxx-dirty + $ echo "(name main)" >> dune-project + $ echo "(version 0.2.0)" >> dune-project + $ dune build + Warning: '--source-map' is enabled but the bytecode program was compiled with no debugging information. + Warning: Consider passing '-g' option to ocamlc. + $ node _build/default/src/main.bc.wasm.js + 0.2.0 + $ dune install --prefix _install --display short 2>&1 | sed 's/-[a-f0-9]*[.]wasm/.wasm/' + Deleting _install/lib/main/META + Installing _install/lib/main/META + Deleting _install/lib/main/dune-package + Installing _install/lib/main/dune-package + Deleting _install/lib/main/opam + Installing _install/lib/main/opam + Deleting _install/bin/main + Installing _install/bin/main + Deleting _install/bin/main.bc.wasm.assets/build_info.wasm + Installing _install/bin/main.bc.wasm.assets/build_info.wasm + Deleting _install/bin/main.bc.wasm.assets/build_info.wasm.map + Installing _install/bin/main.bc.wasm.assets/build_info.wasm.map + Installing _install/bin/main.bc.wasm.assets/build_info__Build_info_data.wasm + Installing _install/bin/main.bc.wasm.assets/build_info__Build_info_data.wasm.map + Deleting _install/bin/main.bc.wasm.assets/dune__exe__Main.wasm + Installing _install/bin/main.bc.wasm.assets/dune__exe__Main.wasm + Deleting _install/bin/main.bc.wasm.assets/dune__exe__Main.wasm.map + Installing _install/bin/main.bc.wasm.assets/dune__exe__Main.wasm.map + Deleting _install/bin/main.bc.wasm.assets/prelude.wasm + Installing _install/bin/main.bc.wasm.assets/prelude.wasm + Deleting _install/bin/main.bc.wasm.assets/runtime.wasm + Installing _install/bin/main.bc.wasm.assets/runtime.wasm + Deleting _install/bin/main.bc.wasm.assets/start.wasm + Installing _install/bin/main.bc.wasm.assets/start.wasm + Deleting _install/bin/main.bc.wasm.assets/std_exit.wasm + Installing _install/bin/main.bc.wasm.assets/std_exit.wasm + Deleting _install/bin/main.bc.wasm.assets/std_exit.wasm.map + Installing _install/bin/main.bc.wasm.assets/std_exit.wasm.map + Deleting _install/bin/main.bc.wasm.assets/stdlib.wasm + Installing _install/bin/main.bc.wasm.assets/stdlib.wasm + Deleting _install/bin/main.bc.wasm.assets/stdlib.wasm.map + Installing _install/bin/main.bc.wasm.assets/stdlib.wasm.map + Deleting _install/bin/main.bc.wasm.js + Installing _install/bin/main.bc.wasm.js + Deleting _install/doc/main/README + Installing _install/doc/main/README + $ node _build/default/src/main.bc.wasm.js + 0.2.0 diff --git a/test/blackbox-tests/test-cases/wasmoo/build-info.t/src/dune b/test/blackbox-tests/test-cases/wasmoo/build-info.t/src/dune new file mode 100644 index 00000000000..b3391eec43c --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/build-info.t/src/dune @@ -0,0 +1,12 @@ +(executable + (name main) + (public_name main) + (modes wasm byte) + (modules main) + (package main) + (libraries dune-build-info)) + +(install + (section bin) + (files main.bc.wasm.js) + (dirs main.bc.wasm.assets)) diff --git a/test/blackbox-tests/test-cases/wasmoo/build-info.t/src/main.ml b/test/blackbox-tests/test-cases/wasmoo/build-info.t/src/main.ml new file mode 100644 index 00000000000..c526da6abf4 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/build-info.t/src/main.ml @@ -0,0 +1,9 @@ +let version = + match Build_info.V1.version () with + | None -> "unknown" + | Some v -> Build_info.V1.Version.to_string v + +let () = match String.split_on_char '-' version with + | [tag; plus; _commit; dirty] -> Printf.printf "%s-%s-%s-%s" tag plus "xxxxx" dirty + | [ x ] -> print_endline x + | _ -> print_endline "unexpected" diff --git a/test/blackbox-tests/test-cases/wasmoo/dune b/test/blackbox-tests/test-cases/wasmoo/dune new file mode 100644 index 00000000000..c5e142618c0 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/dune @@ -0,0 +1,11 @@ +(cram + (applies_to :whole_subtree) + (deps %{bin:node} %{bin:js_of_ocaml} %{bin:wasm_of_ocaml}) + (alias runtest-wasm) + (enabled_if + (= %{env:DUNE_WASM_TEST=disable} enable))) + +(cram + (applies_to build-info) + (deps + (package dune-build-info))) diff --git a/test/blackbox-tests/test-cases/wasmoo/github3622.t b/test/blackbox-tests/test-cases/wasmoo/github3622.t new file mode 100644 index 00000000000..b4a79f8b5a5 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/github3622.t @@ -0,0 +1,27 @@ +This test demonstrates a bug in Dune's separation compilation of JSOO. +`std_exit.cmo` wasn't being linked in the end, so the `at_exit` +hooks didn't run and the channels weren't flushed. + +Setup fixtures: + + $ echo "(lang dune 3.17)" > dune-project + $ echo 'let () = print_string "bla"' > main.ml + $ cat >dune < (executable + > (name main) + > (modes wasm)) + > EOF + +Test without separate compilation: + + $ dune build --profile=release ./main.bc.wasm.js + $ node _build/default/main.bc.wasm.js + bla + +Test with separate compilation: + + $ dune build --profile=dev ./main.bc.wasm.js + $ node _build/default/main.bc.wasm.js + bla + +The result should be the same diff --git a/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/byte/byte.ml b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/byte/byte.ml new file mode 100644 index 00000000000..23b5d38322a --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/byte/byte.ml @@ -0,0 +1,2 @@ +let _ = assert (Sys.backend_type = Bytecode) +let _ = print_endline "inline tests (Byte)" diff --git a/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/byte/dune b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/byte/dune new file mode 100644 index 00000000000..aaf265b6a85 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/byte/dune @@ -0,0 +1,3 @@ +(library + (name inline_tests_byte) + (inline_tests (modes byte) (backend fake_backend))) diff --git a/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/dune b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/dune new file mode 100644 index 00000000000..418c95e453a --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/dune @@ -0,0 +1,8 @@ +(library + (name fake_backend) + (modules ()) + (inline_tests.backend + (generate_runner + (progn + (echo "[@@@warning \"-40\"]\n") + (cat %{impl-files}))))) diff --git a/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/native/dune b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/native/dune new file mode 100644 index 00000000000..44373b41fb1 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/native/dune @@ -0,0 +1,3 @@ +(library + (name inline_tests_native) + (inline_tests (modes native) (backend fake_backend))) diff --git a/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/native/native.ml b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/native/native.ml new file mode 100644 index 00000000000..f28bab40046 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/native/native.ml @@ -0,0 +1,2 @@ +let _ = assert (Sys.backend_type = Native) +let _ = print_endline "inline tests (Native)" diff --git a/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/run.t b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/run.t new file mode 100644 index 00000000000..ea39b6b928c --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/run.t @@ -0,0 +1,24 @@ +Run inline tests using Node.js + + $ cat >dune-project < (lang dune 3.17) + > EOF + + $ dune runtest + inline tests (Byte) + inline tests (Byte) + inline tests (Native) + inline tests (Native) + inline tests (Wasm) + inline tests (Wasm) + + $ dune runtest --profile release + inline tests (Native) + inline tests (Native) + inline tests (Wasm) + inline tests (Wasm) + + $ dune build wasm/.inline_tests_wasm.inline-tests/inline_test_runner_inline_tests_wasm.bc --display short + Error: Don't know how to build + wasm/.inline_tests_wasm.inline-tests/inline_test_runner_inline_tests_wasm.bc + [1] diff --git a/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/wasm/dune b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/wasm/dune new file mode 100644 index 00000000000..de925d0bb79 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/wasm/dune @@ -0,0 +1,6 @@ +(library + (name inline_tests_wasm) + (wasm_of_ocaml (wasm_files wasm.wat)) + (inline_tests + (modes wasm) + (backend fake_backend))) diff --git a/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/wasm/wasm.ml b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/wasm/wasm.ml new file mode 100644 index 00000000000..2db226905c3 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/wasm/wasm.ml @@ -0,0 +1,5 @@ +external prim : unit -> bool = "wasmoo_stubs" + +let _ = assert (Sys.int_size = 31) +let _ = assert (prim ()) +let _ = print_endline "inline tests (Wasm)" diff --git a/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/wasm/wasm.wat b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/wasm/wasm.wat new file mode 100644 index 00000000000..810e81aa67e --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/inline-tests.t/wasm/wasm.wat @@ -0,0 +1,4 @@ +(module + (func (export "wasmoo_stubs") (param (ref eq)) (result (ref eq)) + (ref.i31 (i32.const 1))) +) diff --git a/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/bin/bin1.ml b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/bin/bin1.ml new file mode 100644 index 00000000000..bf7f3d4d481 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/bin/bin1.ml @@ -0,0 +1,6 @@ +let name = "bin1" +let hello name = print_endline ("Hi " ^ name) + +let () = Library1.hello name + +let () = hello Library1.name diff --git a/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/bin/bin2.ml b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/bin/bin2.ml new file mode 100644 index 00000000000..87c52bec613 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/bin/bin2.ml @@ -0,0 +1,6 @@ +let name = "bin2" +let hello name = print_endline ("Hi " ^ name) + +let () = Library1.hello name + +let () = hello Library1.name diff --git a/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/bin/bin3.ml b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/bin/bin3.ml new file mode 100644 index 00000000000..d94b8d12e3a --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/bin/bin3.ml @@ -0,0 +1,6 @@ +let name = "bin3" +let hello name = print_endline ("Hi " ^ name) + +let () = Library1.hello name + +let () = hello Library1.name diff --git a/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/bin/dune b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/bin/dune new file mode 100644 index 00000000000..996bef7f4a0 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/bin/dune @@ -0,0 +1,20 @@ +(executable + (name bin1) + (modules bin1) + (modes wasm) + (libraries library1) +) + +(executable + (name bin2) + (modules bin2) + (modes wasm) + (libraries library1) +) + +(executable + (name bin3) + (modules bin3) + (modes wasm) + (libraries library1) +) diff --git a/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/dune b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/dune new file mode 100644 index 00000000000..4c6f3c67cf6 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/dune @@ -0,0 +1,6 @@ +(env + (_ + (wasm_of_ocaml + (flags (:standard --quiet)) + (compilation_mode separate) +))) diff --git a/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/dune-project b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/dune-project new file mode 100644 index 00000000000..3e2a6150dda --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/dune-project @@ -0,0 +1 @@ +(lang dune 3.17) diff --git a/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/lib/dune b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/lib/dune new file mode 100644 index 00000000000..22f5ea64365 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/lib/dune @@ -0,0 +1 @@ +(library (name library1)) \ No newline at end of file diff --git a/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/lib/library1.ml b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/lib/library1.ml new file mode 100644 index 00000000000..70ad25d7d71 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/lib/library1.ml @@ -0,0 +1,3 @@ +let name = "library1" + +let hello name = print_endline ("Hello " ^ name) diff --git a/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/run.t b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/run.t new file mode 100644 index 00000000000..656ea0da897 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/jsoo-config.t/run.t @@ -0,0 +1,12 @@ +tests js_of_ocaml configs + + $ dune build bin/bin1.bc.wasm.js bin/bin2.bc.wasm.js bin/bin3.bc.wasm.js + $ node _build/default/bin/bin1.bc.wasm.js + Hello bin1 + Hi library1 + $ node _build/default/bin/bin2.bc.wasm.js + Hello bin2 + Hi library1 + $ node _build/default/bin/bin3.bc.wasm.js + Hello bin3 + Hi library1 diff --git a/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/bin/dune b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/bin/dune new file mode 100644 index 00000000000..11fce31221b --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/bin/dune @@ -0,0 +1,6 @@ +(executables + (names technologic) + (libraries js_of_ocaml x) + (modes wasm) + (wasm_of_ocaml + (javascript_files runtime.js))) diff --git a/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/bin/runtime.js b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/bin/runtime.js new file mode 100644 index 00000000000..c8210c33522 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/bin/runtime.js @@ -0,0 +1,3 @@ +global.globalPrintFunction = function(x){ + console.log(x); +} diff --git a/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/bin/technologic.ml b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/bin/technologic.ml new file mode 100644 index 00000000000..06e79a164ec --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/bin/technologic.ml @@ -0,0 +1,13 @@ +module Js = Js_of_ocaml.Js + +let _ = + print_endline X.buy_it; + let obj = Js.Unsafe.obj [|"name", Js.Unsafe.inject (Js.string Z.use_it)|] in + Printf.printf "%s\n%!" (X.print obj); + X.external_print (Js.string "break it"); + (fun x -> + let global = Js.Unsafe.get Js.Unsafe.global "global" in + let globalPrintFunction : Js.js_string Js.t -> unit = + Js.Unsafe.get global "globalPrintFunction" in + Js.Unsafe.fun_call globalPrintFunction [|Js.Unsafe.inject x|] + ) (Js.string "fix it") diff --git a/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/bin/z.ml b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/bin/z.ml new file mode 100644 index 00000000000..30bb9cee296 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/bin/z.ml @@ -0,0 +1 @@ +let use_it = "use it" diff --git a/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/dune-project b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/dune-project new file mode 100644 index 00000000000..3e2a6150dda --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/dune-project @@ -0,0 +1 @@ +(lang dune 3.17) diff --git a/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/dune b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/dune new file mode 100644 index 00000000000..b75532802b2 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/dune @@ -0,0 +1,7 @@ +(library + (name x) + (libraries js_of_ocaml) + (public_name x) + (wasm_of_ocaml + (flags (--pretty)) (javascript_files runtime.js) (wasm_files runtime.wat)) + ) diff --git a/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/runtime.js b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/runtime.js new file mode 100644 index 00000000000..30ad61bc948 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/runtime.js @@ -0,0 +1,6 @@ + + +//Provides: jsPrint +function jsPrint(x){ + globalThis.console.log(x); +} diff --git a/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/runtime.wat b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/runtime.wat new file mode 100644 index 00000000000..d88b8003704 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/runtime.wat @@ -0,0 +1,7 @@ +(module + (import "env" "unwrap" (func $unwrap (param (ref eq)) (result anyref))) + (import "js" "jsPrint" (func $jsPrint (param anyref))) + (func (export "jsPrint") (param $x (ref eq)) (result (ref eq)) + (call $jsPrint (call $unwrap (local.get $x))) + (ref.i31 (i32.const 0))) +) diff --git a/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/x.ml b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/x.ml new file mode 100644 index 00000000000..37ff0eaa243 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/x.ml @@ -0,0 +1,5 @@ +module Js = Js_of_ocaml.Js + +let buy_it = "buy " ^ Y.it +let print x = Js.to_string (Js.Unsafe.get x "name") +external external_print : Js.js_string Js.t -> unit = "jsPrint" diff --git a/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/y.ml b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/y.ml new file mode 100644 index 00000000000..d11948ee573 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/lib/y.ml @@ -0,0 +1 @@ +let it = "it" diff --git a/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/run.t b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/run.t new file mode 100644 index 00000000000..79999cde7db --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/run.t @@ -0,0 +1,50 @@ +Compilation using WasmOO + + $ dune build --display short bin/technologic.bc.wasm.js @install 2>&1 | \ + > sed s,^\ *$(ocamlc -config-var c_compiler),\ \ C_COMPILER,g + wasm_of_ocaml bin/.technologic.eobjs/jsoo/technologic.bc.runtime.wasma + ocamldep bin/.technologic.eobjs/dune__exe__Technologic.impl.d + ocamldep lib/.x.objs/x.impl.d + ocamlc lib/.x.objs/byte/x__.{cmi,cmo,cmt} + ocamldep lib/.x.objs/x__Y.impl.d + ocamldep bin/.technologic.eobjs/dune__exe__Z.impl.d + ocamlopt lib/.x.objs/native/x__.{cmx,o} + ocamlc lib/.x.objs/byte/x__Y.{cmi,cmo,cmt} + wasm_of_ocaml .js/default/js_of_ocaml-compiler.runtime/jsoo_runtime.wasma + wasm_of_ocaml .js/default/js_of_ocaml/js_of_ocaml.wasma + wasm_of_ocaml .js/default/stdlib/std_exit.wasmo + wasm_of_ocaml .js/default/stdlib/stdlib.wasma + ocamlc bin/.technologic.eobjs/byte/dune__exe.{cmi,cmo,cmt} + ocamldep bin/.technologic.eobjs/dune__exe__Technologic.intf.d + ocamlopt lib/.x.objs/native/x__Y.{cmx,o} + ocamlc lib/.x.objs/byte/x.{cmi,cmo,cmt} + wasm_of_ocaml bin/.technologic.eobjs/jsoo/dune__exe.wasmo + ocamlopt lib/.x.objs/native/x.{cmx,o} + ocamlc bin/.technologic.eobjs/byte/dune__exe__Technologic.{cmi,cmti} + ocamlc lib/x.cma + ocamlc bin/.technologic.eobjs/byte/dune__exe__Z.{cmi,cmo,cmt} + ocamlopt lib/x.{a,cmxa} + wasm_of_ocaml lib/.x.objs/jsoo/default/x.wasma + ocamlc bin/.technologic.eobjs/byte/dune__exe__Technologic.{cmo,cmt} + wasm_of_ocaml bin/.technologic.eobjs/jsoo/dune__exe__Z.wasmo + ocamlopt lib/x.cmxs + wasm_of_ocaml bin/.technologic.eobjs/jsoo/dune__exe__Technologic.wasmo + wasm_of_ocaml bin/technologic.bc.wasm.{js,assets} + $ node ./_build/default/bin/technologic.bc.wasm.js + buy it + use it + break it + fix it + $ dune build bin/technologic.bc.wasm.js @install --profile release + $ node ./_build/default/bin/technologic.bc.wasm.js + buy it + use it + break it + fix it + $ cat >dune-workspace < (lang dune 3.17) + > (context + > (default (disable_dynamically_linked_foreign_archives true))) + > EOF + $ dune build bin/technologic.bc.wasm.js @install --profile dev + $ dune build bin/technologic.bc.wasm.js @install --profile release diff --git a/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/x.opam b/test/blackbox-tests/test-cases/wasmoo/no-check-prim.t/x.opam new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/blackbox-tests/test-cases/wasmoo/public-libs.t/a/a.ml b/test/blackbox-tests/test-cases/wasmoo/public-libs.t/a/a.ml new file mode 100644 index 00000000000..df5e542079b --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/public-libs.t/a/a.ml @@ -0,0 +1 @@ +let x = 12 diff --git a/test/blackbox-tests/test-cases/wasmoo/public-libs.t/a/dune b/test/blackbox-tests/test-cases/wasmoo/public-libs.t/a/dune new file mode 100644 index 00000000000..9222db1e318 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/public-libs.t/a/dune @@ -0,0 +1,3 @@ +(library + (public_name foo.a) + (name a)) diff --git a/test/blackbox-tests/test-cases/wasmoo/public-libs.t/b/dune b/test/blackbox-tests/test-cases/wasmoo/public-libs.t/b/dune new file mode 100644 index 00000000000..2d2e43adf78 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/public-libs.t/b/dune @@ -0,0 +1,4 @@ +(executable + (name main) + (libraries a) + (modes wasm)) diff --git a/test/blackbox-tests/test-cases/wasmoo/public-libs.t/b/main.ml b/test/blackbox-tests/test-cases/wasmoo/public-libs.t/b/main.ml new file mode 100644 index 00000000000..e9273e98aa0 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/public-libs.t/b/main.ml @@ -0,0 +1 @@ +let _ = A.x diff --git a/test/blackbox-tests/test-cases/wasmoo/public-libs.t/dune-project b/test/blackbox-tests/test-cases/wasmoo/public-libs.t/dune-project new file mode 100644 index 00000000000..a8110e485a8 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/public-libs.t/dune-project @@ -0,0 +1,3 @@ +(lang dune 3.17) + +(package (name foo)) diff --git a/test/blackbox-tests/test-cases/wasmoo/public-libs.t/run.t b/test/blackbox-tests/test-cases/wasmoo/public-libs.t/run.t new file mode 100644 index 00000000000..2a5b7fbfd7b --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/public-libs.t/run.t @@ -0,0 +1,3 @@ +Compilation of libraries with `public-names` + + $ dune build diff --git a/test/blackbox-tests/test-cases/wasmoo/simple.t/bin/dune b/test/blackbox-tests/test-cases/wasmoo/simple.t/bin/dune new file mode 100644 index 00000000000..08ca6a4ce9f --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/simple.t/bin/dune @@ -0,0 +1,6 @@ +(executables + (names technologic) + (libraries js_of_ocaml x) + (modes wasm) + (wasm_of_ocaml + (javascript_files runtime.js))) diff --git a/test/blackbox-tests/test-cases/wasmoo/simple.t/bin/runtime.js b/test/blackbox-tests/test-cases/wasmoo/simple.t/bin/runtime.js new file mode 100644 index 00000000000..c8210c33522 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/simple.t/bin/runtime.js @@ -0,0 +1,3 @@ +global.globalPrintFunction = function(x){ + console.log(x); +} diff --git a/test/blackbox-tests/test-cases/wasmoo/simple.t/bin/technologic.ml b/test/blackbox-tests/test-cases/wasmoo/simple.t/bin/technologic.ml new file mode 100644 index 00000000000..06e79a164ec --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/simple.t/bin/technologic.ml @@ -0,0 +1,13 @@ +module Js = Js_of_ocaml.Js + +let _ = + print_endline X.buy_it; + let obj = Js.Unsafe.obj [|"name", Js.Unsafe.inject (Js.string Z.use_it)|] in + Printf.printf "%s\n%!" (X.print obj); + X.external_print (Js.string "break it"); + (fun x -> + let global = Js.Unsafe.get Js.Unsafe.global "global" in + let globalPrintFunction : Js.js_string Js.t -> unit = + Js.Unsafe.get global "globalPrintFunction" in + Js.Unsafe.fun_call globalPrintFunction [|Js.Unsafe.inject x|] + ) (Js.string "fix it") diff --git a/test/blackbox-tests/test-cases/wasmoo/simple.t/bin/z.ml b/test/blackbox-tests/test-cases/wasmoo/simple.t/bin/z.ml new file mode 100644 index 00000000000..30bb9cee296 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/simple.t/bin/z.ml @@ -0,0 +1 @@ +let use_it = "use it" diff --git a/test/blackbox-tests/test-cases/wasmoo/simple.t/dune-project b/test/blackbox-tests/test-cases/wasmoo/simple.t/dune-project new file mode 100644 index 00000000000..3e2a6150dda --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/simple.t/dune-project @@ -0,0 +1 @@ +(lang dune 3.17) diff --git a/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/dune b/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/dune new file mode 100644 index 00000000000..6bd6eaa51b6 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/dune @@ -0,0 +1,7 @@ +(library + (name x) + (libraries js_of_ocaml) + (public_name x) + (wasm_of_ocaml + (flags (--pretty)) (wasm_files runtime.js runtime.wat)) + (foreign_stubs (language c) (names stubs))) diff --git a/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/runtime.js b/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/runtime.js new file mode 100644 index 00000000000..30ad61bc948 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/runtime.js @@ -0,0 +1,6 @@ + + +//Provides: jsPrint +function jsPrint(x){ + globalThis.console.log(x); +} diff --git a/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/runtime.wat b/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/runtime.wat new file mode 100644 index 00000000000..d88b8003704 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/runtime.wat @@ -0,0 +1,7 @@ +(module + (import "env" "unwrap" (func $unwrap (param (ref eq)) (result anyref))) + (import "js" "jsPrint" (func $jsPrint (param anyref))) + (func (export "jsPrint") (param $x (ref eq)) (result (ref eq)) + (call $jsPrint (call $unwrap (local.get $x))) + (ref.i31 (i32.const 0))) +) diff --git a/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/stubs.c b/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/stubs.c new file mode 100644 index 00000000000..9d98a815a89 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/stubs.c @@ -0,0 +1,3 @@ +#include +#include +void jsPrint () { fprintf(stderr, "Unimplemented Javascript primitive myPrint!\n"); exit(1); } diff --git a/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/x.ml b/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/x.ml new file mode 100644 index 00000000000..37ff0eaa243 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/x.ml @@ -0,0 +1,5 @@ +module Js = Js_of_ocaml.Js + +let buy_it = "buy " ^ Y.it +let print x = Js.to_string (Js.Unsafe.get x "name") +external external_print : Js.js_string Js.t -> unit = "jsPrint" diff --git a/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/y.ml b/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/y.ml new file mode 100644 index 00000000000..d11948ee573 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/simple.t/lib/y.ml @@ -0,0 +1 @@ +let it = "it" diff --git a/test/blackbox-tests/test-cases/wasmoo/simple.t/run.t b/test/blackbox-tests/test-cases/wasmoo/simple.t/run.t new file mode 100644 index 00000000000..f8194c95599 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/simple.t/run.t @@ -0,0 +1,32 @@ +Compilation using WasmOO + $ dune build bin/technologic.bc.wasm.js @install --profile dev + $ node ./_build/default/bin/technologic.bc.wasm.js + buy it + use it + break it + fix it + $ dune build @install --profile release + $ node ./_build/default/bin/technologic.bc.wasm.js + buy it + use it + break it + fix it + +Compilation using WasmOO with `disable_dynamically_linked_foreign_archives = true` + + $ cat >dune-workspace < (lang dune 3.17) + > (context + > (default (disable_dynamically_linked_foreign_archives true))) + > EOF + $ dune clean + $ dune build bin/technologic.bc.wasm.js @install --profile dev + +Wasm_of_ocaml whole program compilation works with +`disable_dynamically_linked_foreign_archives = true`: + + $ dune build bin/technologic.bc.wasm.js @install --profile release + +We expect a runtime error when running this `bc-for-jsoo` file. + + $ ! if dune exe bin/technologic.bc-for-jsoo ; then true ; else false ; fi 2> /dev/null diff --git a/test/blackbox-tests/test-cases/wasmoo/simple.t/x.opam b/test/blackbox-tests/test-cases/wasmoo/simple.t/x.opam new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/blackbox-tests/test-cases/wasmoo/submodes.t/dune b/test/blackbox-tests/test-cases/wasmoo/submodes.t/dune new file mode 100644 index 00000000000..def8e78b42f --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/submodes.t/dune @@ -0,0 +1,7 @@ +(executable + (name main) + (modes js wasm)) + +(env + (js (wasm_of_ocaml (enabled_if false))) + (wasm (js_of_ocaml (enabled_if false)))) diff --git a/test/blackbox-tests/test-cases/wasmoo/submodes.t/dune-project b/test/blackbox-tests/test-cases/wasmoo/submodes.t/dune-project new file mode 100644 index 00000000000..3e2a6150dda --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/submodes.t/dune-project @@ -0,0 +1 @@ +(lang dune 3.17) diff --git a/test/blackbox-tests/test-cases/wasmoo/submodes.t/main.ml b/test/blackbox-tests/test-cases/wasmoo/submodes.t/main.ml new file mode 100644 index 00000000000..caaffbbaa9d --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/submodes.t/main.ml @@ -0,0 +1,4 @@ +let () = + match Sys.backend_type with + Other typ -> print_endline typ + | _ -> assert false diff --git a/test/blackbox-tests/test-cases/wasmoo/submodes.t/run.t b/test/blackbox-tests/test-cases/wasmoo/submodes.t/run.t new file mode 100644 index 00000000000..0bf84390a67 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/submodes.t/run.t @@ -0,0 +1,27 @@ +Building with different combinations of submodes + +Compiling to JavaScript + + $ dune build --profile js + $ node _build/default/main.bc.js + js_of_ocaml + $ dune build --profile js main.bc.wasm.js + Error: Don't know how to build main.bc.wasm.js + [1] + +Compiling to Wasm. + + $ dune build --profile wasm + $ node _build/default/main.bc.wasm.js + wasm_of_ocaml + $ dune build --profile wasm main.bc.js + Error: Don't know how to build main.bc.js + [1] + +Compiling to both JS and Wasm. + + $ dune build --profile both + $ node _build/default/main.bc.js + js_of_ocaml + $ node _build/default/main.bc.wasm.js + wasm_of_ocaml diff --git a/test/blackbox-tests/test-cases/wasmoo/tests.t/a.ml b/test/blackbox-tests/test-cases/wasmoo/tests.t/a.ml new file mode 100644 index 00000000000..351293c912d --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/tests.t/a.ml @@ -0,0 +1 @@ +let () = print_endline "a: ok" diff --git a/test/blackbox-tests/test-cases/wasmoo/tests.t/b.expected b/test/blackbox-tests/test-cases/wasmoo/tests.t/b.expected new file mode 100644 index 00000000000..3aca7c21f74 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/tests.t/b.expected @@ -0,0 +1 @@ +b: ok diff --git a/test/blackbox-tests/test-cases/wasmoo/tests.t/b.ml b/test/blackbox-tests/test-cases/wasmoo/tests.t/b.ml new file mode 100644 index 00000000000..9b1f9b5f9f2 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/tests.t/b.ml @@ -0,0 +1 @@ +let () = print_endline "b: ok" diff --git a/test/blackbox-tests/test-cases/wasmoo/tests.t/dune b/test/blackbox-tests/test-cases/wasmoo/tests.t/dune new file mode 100644 index 00000000000..fe703fcd7dd --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/tests.t/dune @@ -0,0 +1,9 @@ +(tests + (names a b) + (modes wasm)) + +(env + (_ + (wasm_of_ocaml + (runtest_alias runtest-js) + (compilation_mode whole_program)))) diff --git a/test/blackbox-tests/test-cases/wasmoo/tests.t/dune-project b/test/blackbox-tests/test-cases/wasmoo/tests.t/dune-project new file mode 100644 index 00000000000..3e2a6150dda --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/tests.t/dune-project @@ -0,0 +1 @@ +(lang dune 3.17) diff --git a/test/blackbox-tests/test-cases/wasmoo/tests.t/run.t b/test/blackbox-tests/test-cases/wasmoo/tests.t/run.t new file mode 100644 index 00000000000..60d459816d0 --- /dev/null +++ b/test/blackbox-tests/test-cases/wasmoo/tests.t/run.t @@ -0,0 +1,4 @@ +tests stanza with wasmoo + + $ dune build @default @runtest-js + a: ok diff --git a/test/blackbox-tests/test-cases/workspaces/workspace-env.t/run.t b/test/blackbox-tests/test-cases/workspaces/workspace-env.t/run.t index bd7c0a3cf09..094d6d20d76 100644 --- a/test/blackbox-tests/test-cases/workspaces/workspace-env.t/run.t +++ b/test/blackbox-tests/test-cases/workspaces/workspace-env.t/run.t @@ -17,3 +17,6 @@ Workspaces also allow you to set the env for a context: (js_of_ocaml_flags ()) (js_of_ocaml_build_runtime_flags ()) (js_of_ocaml_link_flags ()) + (wasm_of_ocaml_flags ()) + (wasm_of_ocaml_build_runtime_flags ()) + (wasm_of_ocaml_link_flags ())