Jbuilder does not generate .bc.js


I am trying to build JavaScript sources from my ML module collab_vis.ml which I plan to use from JavaScript. Unfortunately, I can’t get jbuilder (1.0+beta16) to launch js_of_ocaml (3.0.1)

The module looks like this:

let foo () =

The jbuild looks like this:

 ((name collab_vis)
  (modules (collab_vis))
  ;(public_name collab-vis)
  (libraries (js_of_ocaml js_of_ocaml.weak))
  (js_of_ocaml ((flags :standard)))
  (modes (byte))
  (wrapped false)
  (preprocess (pps (js_of_ocaml-ppx)))))

It just won’t build anything. The only thing it generates is collab_vis.requires.sexp. I tried converting it into an executable which generates both the .exe as well as .bc, but I haven’t managed to get .bc.js out of it.

There is no error or something, it just doesn’t create these files. As if it didn’t know it should.

Did you try requesting the target explicitly? E.g.

$ jbuilder build collab_vis.bc.js

I had the same problem, after spending a few hours with absolutely no recognition from jbuilder (no error messages, no .js output) I ended up reverting to ocamlbuild.

This yields Don't know how to build collab_vis.bc.js or if I call it with the full source path src/collab/collab_vis.bc.js then Don't know how to build src/collab/collab_vis.bc.js.

I looked at some other projects which use js_of_ocaml and jbuilder and found JsOfCairo, which when I build it with jbuilder build does not generate any .bc nor .bc.js files at all.

(I am using OCaml 4.04.2, so I doubt this is a problem with an unsupported OCaml version, and js_of_ocaml 3.0.1 fortunately supports up to 4.06 anyway)

Using JsOfCairo as an example, we can discover some js targets like so:

$ jbuilder rules -m | grep -i '\.bc\.js:'
_build/default/src/drawing_tests_in_javascript.bc.js: \
_build/default/demo/.utop/utop.bc.js: \
_build/default/demo/draw_in_browser.bc.js: \
_build/default/demo/draw_on_command_line.bc.js: \
_build/default/src/.utop/utop.bc.js: \
_build/default/src/drawing_tests_in_command_line.bc.js: \

From there, something like:

$ jbuilder build demo/draw_in_browser.bc.js
      ocamlc demo/drawings.{cmi,cmo,cmt}
      ocamlc demo/Drawings.cma
      ocamlc demo/draw_in_browser.{cmi,cmo,cmt}
      ocamlc demo/draw_in_browser.bc
 js_of_ocaml demo/draw_in_browser.bc.js

Works as expected. I’d have to look at your particular project to see if there’s something that doesn’t work there.

1 Like

In case it’s useful, in https://gitlab.com/smondet/genspio-web-demo, I just bypassed the (js_of_ocaml ...) thing:

;; ...
 ((targets (demo.js))
  (deps (demo.bc metadata.cmo))
  (action (progn
           (run js_of_ocaml -I .  +weak.js demo.bc -o demo.js)))))
 ((name demo)
  (preprocess (pps (js_of_ocaml-ppx)))
  (libraries (nonstd sosa rresult js_of_ocaml-tyxml
              js_of_ocaml-toplevel js_of_ocaml-lwt))))

Also have things like (run jsoo_mktop ....) or (with-stdout-to index.html (progn (echo "\n<!DOCTYPE html>\n<html ....")))

Ah, I ran the rules trick (it is a bit annoying it depends on odoc but that’s easy to install) and saw a lot of executable from my project having a .bc.js rule. So I transformed the target into

 ((name collab_vis)
  (modules (collab_vis))
  (public_name readmore-provider.collab-vis)
  (libraries (js_of_ocaml))
  (preprocess (pps (js_of_ocaml-ppx)))))

And invoking jbuilder build src/collab/collab_vis.bc.js did in fact generate a .bc.js! Thanks!

Now my follow up question: how to make jbuilder build build this target by default? I’d rather avoid to call this very specific incantation, it would be preferable to have that expressed somehow in the jbuild. Something like (modes (:standard js_of_ocaml)).

This is a bug that will be fixed in the next version of jbuilder.

To answer your other question, it helps to recall what it means to build “by default” in jbuilder. When you call $ jbuilder build, you really are doing $ jbuilder build @install i.e. requesting to build the install alias. So to make something build “by default”, you could simply add your target as a dependency of this alias. But IMO this is a bad idea because @install already has a semantic meaning - the set of installable targets. However, you can of course create your own aliases like this:

  ((name js)
   (deps (collab_vs.bc.js))))

and then simply do $ jbuilder build @install @js to build both installable and js targets.

There has been some discussion on improving the set of aliases to make development a little more convenient but we don’t really have anything conclusive yet. If you have some better ideas, you can add something here https://github.com/janestreet/jbuilder/issues/204


Excellent @rgrinberg, thank you! I’ll think about this some more but now I am in a position where I more or less understand how it works.

Maybe it helped @cfcs as well :slight_smile: