Dune do not build bytecode

Hey all,

I am trying to compile a file with the intention of dynamically loading it as a plugin later. As I don’t intent to use the bytecode version I am only interested in its native version, however, I fail to ask dune/opam not to generate the bytecode version.
Here is my dune file for the plugin:

 (name MyPlugin_plugin) 
 (public_name MyPlugin.plugin)
 (modes native)
 (libraries my_lib))
 (flags (:standard -warn-error -A -w -a -opaque))))

the dune-project file:
(lang dune 1.10)
(name Fat20PolSim)

and the respective .opam file:
name: “MyPlugin-plugin”
build: [
“dune” “build” “-p” name “-j” jobs ]

Finally here is the .install file generated by running dune build; dune install

lib: [
  "_build/install/default/lib/MyPlugin/plugin/MyPlugin.ml" {"plugin/MyPlugin.ml"}
  "_build/install/default/lib/MyPlugin/plugin/MyPlugin_plugin.a" {"plugin/MyPlugin_plugin.a"}
  "_build/install/default/lib/MyPlugin/plugin/MyPlugin_plugin.cmxa" {"plugin/MyPlugin_plugin.cmxa"}
  "_build/install/default/lib/MyPlugin/plugin/MyPlugin_plugin.cmxs" {"plugin/MyPlugin_plugin.cmxs"}
  "_build/install/default/lib/MyPlugin/plugin/myPlugin_plugin.cmi" {"plugin/myPlugin_plugin.cmi"}
  "_build/install/default/lib/MyPlugin/plugin/myPlugin_plugin.cmt" {"plugin/myPlugin_plugin.cmt"}
  "_build/install/default/lib/MyPlugin/plugin/myPlugin_plugin.cmx" {"plugin/myPlugin_plugin.cmx"}
  "_build/install/default/lib/MyPlugin/plugin/myPlugin_plugin.ml" {"plugin/myPlugin_plugin.ml"}
  "_build/install/default/lib/MyPlugin/plugin/myPlugin_plugin__MyPlugin.cmi" {"plugin/myPlugin_plugin__MyPlugin.cmi"}
  "_build/install/default/lib/MyPlugin/plugin/myPlugin_plugin__MyPlugin.cmt" {"plugin/myPlugin_plugin__MyPlugin.cmt"}
  "_build/install/default/lib/MyPlugin/plugin/myPlugin_plugin__MyPlugin.cmx" {"plugin/myPlugin_plugin__MyPlugin.cmx"}

During the build process ocamlc is called to build .{cmi,cmt,cmo} files which takes a lot of time in my case and I would like to avoid it. Is it possible to ask opam or dune to only generate targets for native libraries?

It’s not possible to avoid building .cmi files. It’s possible to avoid building .cmt files but it means that your library will be opaque to users of tools like merlin. Is that really what you want? Producing cmo files is quite quick in my experience. Do you have some benchmarks to show that they’re slowing down your build? Nevertheless, if you really want to avoid .cmo files, then you should just write .mli files for all your modules.

1 Like

Thank you for your reply!
The files I am compiling are essentially consumed by another tool. It’s a bunch of syntax of a DSL that instead of interpreting I compile to OCaml with the goal of natively executing. So merlin or anything else is not really of interest here.
As the files are rather large, it’s often the case that compilation time dominates the execution time, which is arguably annoying.
As for benchmarks, I have the timings of the OCaml compiler, here is a mid-sized example:
ocamlc: 12.449s
00.231s parsing
00.230s parser
05.100s typing
06.274s transl
00.828s generate
00.016s other
00.020s other

ocamlopt 17.078s
**00.236s parsing**
**00.236s parser**
**04.871s typing**
**09.796s transl**
**02.160s generate**

The cmt and cmo files are the largest in size too: 19MB and 4MB respectively (the cmi is 3kbs).
I guess the other unavoidable bottleneck is that type inference has to be run twice, once by ocamlc and once by ocamlopt.

As I said, it’s already possible to avoid the cmo files may be omitted by writing mli files in conjunction with (modes native).

For .cmt files, it’s possible for us to add a mode that disables their generation, but it’s really quite a niche use case. Doesn’t your plugin have a public interface? How do you expect users to use it without merlin?

It does not need a public interface; it’s a synthesized program generated to perform a one-off computation; users are not supposed to directly use it.
The reason I am using a plugin is because plain dynlink from OCaml stdlib is very brittle.

Would it be possible to generate the interface as well? You would increase the speed of your incremental builds at least in development regardless.

I’m not sure I understand. When I said “plugin”, I meant a cmxs artifact. That is usable only via dynlink as far as I understand.

Let me resurrect this thread. I have a library that takes 19.36 seconds to build, out of which 3.26 seconds is spent creating cmo files. I would very much like to get away from that. I found it to be confusing that (modes native) doesn’t work as I expected.

I found it to be confusing that (modes native) doesn’t work as I expected.

Could you expand on this?

Best wishes,

@keleshev, is the behavior you see potentially another instance of https://github.com/ocaml/dune/issues/3467?

1 Like

@nojb I found confusing that (modes native) produces cmo files (in addition to the expected cmx).

Isn’t it expected for ml files without a corresponding mli?

I think so, but it happens even when there are mli files.

1 Like

That’s not quite what I get with ocamlopt if there no mli files. If I just compile a file test.ml with 'ocamlopt -o test test.ml' then this generates test.cmi, test.cmx, test.o and test. In particular, no test.cmo file is emitted.

This seems to be a dune thing.