如何使用(或不使用)Oasis 创建多级模块层次结构

How to make a multi-level module hierarchy with (or without) Oasis

假设我有一组模块,每个模块 "bushy" 都有子模块。

M1.X        M2.X        M3.X
M1.Y        M2.Y        M3.Y
M1.Z        M2.Z        M3.Z
M1.W        M2.W        M3.W
M1.Q        M2.Q        M3.Q
M1.P        M2.P        M3.P

此外,我希望这些灌木丛中的每一个都位于一个主模块下。

Home.M1
Home.M2
Home.M3

现在,使用 Oasis 的 Pack: 选项可以很容易地为每个 M1M2M3 构建项目目录。特别是,我喜欢并试图解决的问题是 (a) 能够以标准 .ml/.mli 格式布置我的文件,以及 (b) 正确生成 ocamldoc链接文档。

但是因为我想在一个公共模块层次结构下的同一个库中分发 M1M2M3,所以我不能使用 Pack:而是被迫将整个该死的东西扔进一个文件中,以便 (a) 不会意外地破坏全局命名空间,(b) 不会意外地在整个 Home. 命名空间中扩散不打算使用的模块直接,并且 (c) 不会破坏 ocamldoc 链接。

所以我的问题是,根据上面建议的目标,我如何使用 Oasis 创建具有这种形式的分层模块的包?

一些额外的限制包括:

我的偏好能够得出一个解决方案,该解决方案还允许合理的文件布局,例如

src -+
     +-- m1 -+
     |       +-- x.ml
     |       +-- x.mli
     |       +-- y.ml
     |       +-- y.mli
     |       +-- z.ml
     |       +-- z.mli
     |       |
     |       ...
     |
     |
     +-- m2 -+
     |       +-- x.ml
     |       +-- x.mli
     |       +-- y.ml
     |       +-- y.mli
     |       +-- z.ml
     |       +-- z.mli
     |       |
     |       ...
     |
     |
     +-- m3 -+
             +-- x.ml
             +-- x.mli
             +-- y.ml
             +-- y.mli
             +-- z.ml
             +-- z.mli
             |
             ...

Oasis 也可能无法做到这一点。在这种情况下,关于如何使用其他构建工具的建议是可以接受和欢迎的。假设我对其他工具知之甚少……因为这可能是真的!

有绿洲

oasis 中的 pack 有点损坏,因为它通过将所有路径包含到搜索路径中来破坏命名空间。有一个长期存在的已接受的错误请求,但事情仍然存在。但是,如果它工作正常,它可以帮助你,因为它会创建一个库命名空间,允许你在不同的文件夹中拥有相同名称的模块。但是,它不起作用,所以我们应该忘记它。

我们在 BAP 项目中采用的方法是用丑陋的 bap_subproject_ 前缀破坏所有模块名称,这可以保护我们免受与我们自己的模块的冲突,以及来自外部的冲突图书馆。对于每个 bap_subproject,我们都有一个文件夹,其中包含所有名称混乱的实现模块和一个统辖它们的伞形模块。该模块存储在文件 bap_subproject.ml 中,它为所有要导出的模块和类型定义了别名。通常它包含如下条目:

module X = Bap_subproject_x

还有一个很大的 Bap 项目,它将所有子库统一在一个命名空间 Bap.Std 下,并重新导出它需要的一切,这样在一个 open Bap.Std 之后你就可以访问Insnbap_disasm/bap_disasm_insn.ml[i]

中定义的模块

没有绿洲

免责声明:如果您使用插件扩展此解决方案,它可能仍适用于 oasis。

我们在一个项目中使用了这种方法,不幸的是它是闭源的,所以我无法提供 link。每个子项目都在自己的子文件夹中,在自己的命名空间中(我们在每个子文件夹中都有 parser 模块,没有任何破坏)。对于每个子项目,在 top 文件夹中都有一个 mlpack 文件:

 root +
      |
      +-- expr.mlpack
      +-- expr -+
                |
                +- lexer.*
                +- parser.*
                +- ast.*
                +- ...
      +-- cameo.mlpack
      +-- cameo-+
                |
                +- lexer.*
                +- parser.*
                +- ast.*
                +- ...

expr.mlpack的内容是:

   expr/Lexer
   expr/Parser
   expr/Ast
   ...

ocamlbuild 将此 expr.mlpack 视为一个独立模块,其中定义了 LexerParser 等子模块(并且可以访问,例如 Expr.Lexer).并且与也有 Lexer 的兄弟项目没有冲突,也没有与 ocaml-libs 或任何其他外部 Lexer 的冲突,因为 expr 文件夹实际上永远不会包含在搜索路径中。

如果您需要为您的系统添加另一层,例如一个包的包,那么您有三个选择:

  1. 递归使用相同的方法,即创建另一个超级文件夹,其中有 mlpackmllib 个文件指向子文件夹。
  2. 只需添加 big.mlpack 即可引用所有顶层 mlpack 模块
  3. 创建 big.ml 重新引入所有模块。

当然,最后两种方法要求顶级打包模块具有不同的名称。但通常这是足够公平的。