obazl / rules_ocaml

A Bazel Language Support Package for OCaml

Home Page:https://obazl.github.io/docs_obazl

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ocaml_ns version 2

mobileink opened this issue · comments

Namespace support inescapably involves a circular dependency. On the one hand, submodules must depend on the ns module for two things:

  • submodule compilation must -open the namespace module
  • in the current implementation, at least, submodules get their namespace prefix from the ns module

On the other hand, the ns module depends on the submodules, since it needs to know which modules to alias.

Version 1 resolves this by having the submodules depend on the ns module via the ns attribute of ocaml_module, and by having the ns module depend on the submodule source files rather than the submodule build targets. This works, but it's inelegant and error prone, since the build developer must ensure the two sets of deps match up.

The goal is to have the ns module depend on the submodule targets, period. That way clients can just depend on the ns, which would function as an aggregate, like a library or archive. Currently the ns module and submodules need to be bundled into a library or archive.

The proposal is to use a transition function together with a label-valued build setting to support this. Sketch:

  • Submodule names can be anything; there is no requirement that the filenames include an ns prefix. All that matters is that the alias point to the right filename. That is, if the ns is Foo, and the submodule is Bar, the convention is to rename bar.ml to Foo__Bar.ml, and alias it with module Bar = Foo__Bar. But this is not a requirement. We can give the submodule file any name we please, so long as the alias statement points to it. In particular, we can use the package name to construct a prefix for the submodule filename. So if foo.ml and bar.ml are in package //a/b/c, we can rename bar.ml to A__B__C__Bar.ml and alias it thusly: module Bar = A__B__C__Bar. In other words, there is no necessary relation between submodule name paths (e.g. Foo.Bar) and filesystem names (Foo.Bar maps to A__B__C__Bar).
  • We can use a transition function on the submodules attribute of rule ocaml_ns to notify submodules that they are being namespaced. Transition functions set build settings, which are in effect global variables. When a transition function on an attribute sets a build setting, it converts it to a kind of local variable; the setting becomes visible only to the transitive dependency graph rooted at the attribute. So the ocaml_ns instance will assign a value to build setting @ocaml//ns, and the submodules (ocaml_module instances) will read it to determine whether or not they are being used as submodules. If so, they will construct their filenames using their package names as a prefixes, as noted above.
  • We still need to compile the ns module so that the submodule can -open it. The proposal - and I'm not yet sure this will work - is to have the transition function compile the ns module and pass it as the value of @ocaml//ns.

I think this will work, because the dependency of the ns module on its submodules is actually a pseudo-dependency. The ns module does not depend on the compiled submodules, it only depends on their names. So it should be possible for the transition function to obtain those names from the submodules attribute of ocaml_ns, write a source file containing the necessary aliases, compile it, and pass it along as the value of @ocaml//ns.

Another possibility: always use the package name as the namespace prefix. That way both the ocaml_ns and the submodules know what the prefix is, so long as they are in the same package. Actually even if they are in different packages it would work, since the ocaml_ns would be able to extract the package name for all its deps.

The catch is that the submodules need to know that they should use the ns prefix. Simplest way to do this is to make the ns attribute a boolean. The drawback with this is just the need to add this attrib to each submodule. The alternative is to use a transition function to set the ns value.

The main unresolved problem is how to make the compiled ns module available to the submodules, who need it for compilation. We may be able to use the transition function for this as described above. Not sure how efficient that is. Another possibility is to have each submodule have a hidden dependency on a hidden package-global ns module, whose only purpose is to support submodule compilation. It would contain an alias statement for every module in the package. But I'm not sure "hidden ns module" is even possible.

Maybe a hidden attribute whose default value is 'glob(["*.ml"])' ? If ns is True, compile the helper ns module. No, then each submod would compile it. We need them all to depend on one target. An ocaml_ns_helper rule?

Best solution would be to put the alias statments on the compiler command line.