Jbuilder example for building to both native and js_of_ocaml

Could someone please show me a working (and preferably simple) example of a jbuilder config which builds both to native ocaml and jsoo?

Seems like whatever I do, I get an error that a target is already defined.

Maybe I didn’t fully understand your question, but it seems to me there’s nothing particular to do? Which is built of native or js depends on the target you are asking for (and you can ask for both with a single jbuilder invocation).

jbuild:

(jbuild_version 1)

(executable
  ((name main)))

main.ml:

let () = print_endline "hello"

Then:

$ jbuilder build main.exe main.bc.js
$ ./_build/default/main.exe
hello
$ node ./_build/default/main.bc.js
hello

What I’m trying to do is adapt this project to js_of_ocaml: https://github.com/loxs/ocaml-parsing

When I try doing what you suggest I get an error:

$ jbuilder build ocaml_parsing.bc.js
Don\'t know how to build ocaml_parsing.bc.js

Already tried various things - both compiling with the executable and library sntanza; tried adding (js_of_ocaml ()) to the file, nothing helps

If I do something like this I get both native and bytecode executables, but never managed to make it compile a .js file

(executables
 ((names (ocaml_parsing))
  (js_of_ocaml ())
  (modes (byte native))))

You can find a few jbuilder and jsoo examples here. https://github.com/ocsigen/js_of_ocaml/tree/master/examples.

Yeah, I already knew about these. The thing is when I try to replicate any of these configs (add them to my jbuilder file), it either fails if I try to provide the same executable name as the native or the bc version, or silently fails if I provide a completely separate name. No .js file is produced when I run jbuilder.

The only way I managed to make it work is if I call something like this manually: js_of_ocaml --pretty _build/default/src/ocaml_parsing.cma. This is not that bad and I could live with it (will include the command in my Makefile… but still, I’m kind of interested to find out how to do it via jbuilder.

I think you simply got the target name wrong. Note that your ocaml_parsing executable lives in the test directory. Hence that is where your bytecode (and hence js targets) live. So your build command should be something like:

$ jbuilder build test/ocaml_parsing.bc.js

If you’d like to see all the targets jbuilder generates, run something like:

$ jbuilder rules -m -r

Unfortunately the output is huge and messy (but greppable).

This is certainly something that jbuilder can address better btw - be a bit more helpful with incorrect target names.

Actually no. What you suggest does not work, there are targets under test/ but also under src/.
I started playing with various things and various targets do work, still can’t seem to manage anything js related:

MacBook-Pro:ocaml-parsing loxs$ jbuilder build src/ocaml_parsing.o
MacBook-Pro:ocaml-parsing loxs$ jbuilder build src/ocaml_parsing.cma
MacBook-Pro:ocaml-parsing loxs$ js_of_ocaml --pretty _build/default/src/ocaml_parsing.cma
MacBook-Pro:ocaml-parsing loxs$ jbuilder build src/ocaml_parsing.cma.js
Don't know how to build src/ocaml_parsing.cma.js
MacBook-Pro:ocaml-parsing loxs$ jbuilder build src/ocaml_parsing.bc.js
Don't know how to build src/ocaml_parsing.bc.js
MacBook-Pro:ocaml-parsing loxs$ jbuilder build src/ocaml_parsing.js
Don't know how to build src/ocaml_parsing.js
Hint: did you mean src/ocaml_parsing.o or src/ocaml_parsing.a?

All of this is with this jbuild file: https://github.com/loxs/ocaml-parsing/blob/master/src/jbuild

OK, it seems to work if I make it an executable with this config:

(executables
 ((names (ocaml_parsing))
  (modes (byte native))
  (js_of_ocaml ())
  (libraries
   (core_kernel
    sedlex
    compiler-libs.common
    menhirLib)
  )
  (preprocess (pps (ppx_deriving.std ppx_enumerate)))
))

And then compile it with jbuilder build src/ocaml_parsing.bc.js

I had to also create Ocaml_parsing.ml module and it builds with some warnings.
So it seems the whole problem is because jbuilder doesn’t know how to build JS in library mode?

I’ll continue research on if I can use the resulting .js file as a library in the browser. If yes, this is a reasonable tradeoff.

This is the correct way to do things. I should have clarified that these .js targets are only usable for executables. I also assumed your source was an executable and not a library.

A few more corrections:

  • There’s no need for js_of_ocaml field if you aren’t passing anything to it.
  • There’s no need to specify the modes for the executable.
4 Likes