使用不同的库版本:修复接口上不一致的假设

Using different library versions: fixing inconsistent assumptions over interface

问题

在创建依赖于 sqlite3.cm*a 的库 my_lib.cm*a 后,尝试使用 my_lib.cm*a 构建新项目,但使用旧版本的 sqlite3.cm*a 我得到了编译时间错误 "inconsistent assumptions over interface Sqlite3"。尝试将 my_lib.cma 与顶层中较旧的 sqlite3.cma 一起使用时会发生类似的错误。这两个不同的版本实际上在不同的系统上,my_lib.cm*a被复制到旧版本。

issue的测试与探索

我 运行 比较了两个 sqlite3.mli 文件,发现问题似乎是一行。在较新版本的 sqlite3 中是:

external enable_load_extension :
  db -> bool -> bool = "caml_sqlite3_enable_load_extension"

但在旧系统上有 "noalloc" 选项:

  external enable_load_extension :
  db -> bool -> bool = "caml_sqlite3_enable_load_extension" "noalloc"

所以我所做的是将较新版本的 sqlite3.mli 复制到具有旧 sqlite3 的系统(在临时目录中),将其编译成 sqlite3.cmi,复制旧的 sqlite3.cma 和 sqlite3.cmxa 到临时目录。现在,如果在顶层我做

#load "sqratch/dir/sqlite3.cma"
#load "my_lib.cma"
my_lib.do_stuff

它突然起作用了——没有错误报告。我还可以编译一个程序 prog.ml,它使用 my_lib.cmaocamlc sratch/dir/sqlite.cma my_lib.cma prog.ml -o prog,它编译没有错误并且运行得很好。

虽然我不太明白编译器是如何将接口文件与字节码文件一起使用的,但在我看来,字节码库使用.cmi文件来定义接口而不是它本身不包含任何接口信息,因此到目前为止我所描述的行为似乎是有道理的。

我感到困惑的地方是当我尝试使用本机编译器时。如果我尝试 ocamlopt sratch/dir/sqlite.cmxa my_lib.cmxa prog.ml -o prog 然后编译器再次抱怨 my_lib.cmxasratch/dir/sqlite3.cmxa 在接口 Sqlite3 上做出不一致的假设。由此我推断,本机编译单元(这是正确的术语吗?)或至少本机档案包含其中的接口信息。不过,这对我来说似乎很奇怪,因为 manual 没有说明以任何方式包含接口的 cmxa 文件(尽管它确实讨论了包含的其他文件类型)。

所以现在我的问题...

最后一点,我知道这不是 'right' 处理这种情况的方法;我想通常要做的是使用 opam 切换到用于制作 my_lib.cm*a 的同一编译器,然后使用 opam 安装相同版本的 sqlite3,但这不是我想要的for(主要是因为我想更好地理解编译过程,但当我尝试在旧系统上安装它时,opam 似乎 work/it 也没有吐出错误)。基本上,我会说我不是在寻找归结为 "use opam on the older system".

的答案

编辑

Are my deductions correct?

或多或少。

Is my hack for the toplevel/byte code compiler (i.e. editing the mli to the expected one and then using that) something that will generally/often work or have I stumbled across a rare case where it does.

您遇到了一个罕见的案例。实际上你只是提供了更多信息,允许编译器更有效地调用这个外部函数。一般来说,它当然不应该工作,因为你破坏了实现与其接口之间的一致性。

Is there a similar hack to get native compilation to work?

您可以像重新编译 cmi 文件一样重新编译 cmxa(库)文件。但这已经不是黑客了。

Any good suggestions for references about all this sort of compiler business?

编译器代码本身。有一个 OCaml Compiler Hacking wiki,其中包含一些有用的信息,但不包括链接。

Is there a standard way to make libraries more system independent (that does not depend on opam), like somehow including sqlite3.cmxa in my_lib.cmxa?

没有标准方法,但您可以将所有文件复制到一个文件夹中。 (顺便说一句,cmxa 不包含二进制代码,它在 .a 文件中。cmxacmx 只包含有关一个或多个编译单元的额外信息).

I would think using -for-pack/-pack...

for-packpack是为了解决命名空间问题而设计的,在引擎盖下,包仍然是同一套cmxacmxao 个文件。

but I need the actual sqlite3.ml files for this don't I?

从技术上讲是的,除非您打算使用编译器工具来破解它。

Is this behaviour in some way specific to the external function (I really don't know anything about interfacing ocaml with C)?.

没有。不一致性检查只是比较编译接口和实现的 md5 总和。

... but if someone knows off the top of their head; what does the "noalloc" actually do?

noalloc 向编译器指定此外部 C 函数不分配任何 OCaml 值。这意味着编译器在调用该函数时不需要为 GC 框架 table 插入特殊的序言和结尾代码。这实际上使调用非常快,只是一个汇编 call 指令。此限定符应记录在下一版本的 OCaml (4.03) 中。

From this I deduce that native compilation units (is that the right term?) or at least native archives contain the interface information in them.

是的,这是一个正确的术语。是的,它们包含一些关于接口的信息:导入接口的名称和 md5sum。您可以使用 ocamlobjinfo 程序来转储此信息。

Though I don't really understand exactly how the compiler makes use of the interface files with the byte code files, from this it seems to me that a byte code library uses a .cmi file to define to interface and doesn't include any interface information itself, so the behaviour I've described so far seems to make sense.

库代码至少包含接口的 md5 和。您刚刚绕过了在链接阶段进行的一致性检查,并打破了编译器的假设,即如果检查一个单元与一些 cmi,那么以后没有人会替换这个 cmi。所以 cma 文件仍然认为它使用旧的 cmi.