使用不同的库版本:在接口

时间:2016-01-23 07:05:04

标签: compilation ocaml

问题

创建依赖于my_lib.cm*a的库sqlite3.cm*a之后,尝试使用my_lib.cm*a构建新项目但是sqlite3.cm*a的旧版本我得到编译时错误& #34;对接口Sqlite3"的不一致假设。尝试在顶层中使用较早的my_lib.cma sqlite3.cma时会发生类似的错误。这两个版本实际上位于不同的系统上,my_lib.cm*a被复制到较旧的版本。

测试和探索问题

我运行了两个sqlite3.mli文件的差异,并确定问题似乎是一行。在较新版本的sqlite3中,它是:

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

但是有#34; 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.mlmy_lib.cma的程序ocamlc sratch/dir/sqlite.cma my_lib.cma prog.ml -o prog,它编译时没有错误,运行得很好。

虽然我并不完全理解编译器如何使用带有字节代码文件的接口文件,但在我看来,字节代码库使用.cmi文件来定义接口并且不会#39; t包括任何接口信息本身,所以我到目前为止所描述的行为似乎有意义。

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

所以现在我的问题......

  • 我的演绎是否正确?
  • 是我的顶级/字节代码编译器(即编辑mli到预期的那个然后使用它)的东西通常/经常工作的东西,或者我偶然发现了一个罕见的情况。
  • 是否有类似的黑客可以让本机编译工作?
  • 有关所有这类编译器业务的参考资料的任何好建议? (我一直在努力找到所有编译/链接工作的良好参考)我发现的所有内容似乎都没有真正解释的东西(甚至manual我希望它是一个巨大的参考文档这对我来说是完全不可理解的。)也许我还没有找到我正在寻找的类型的参考,我必须学习C编译器如何工作? (我之前只编写了#34; hello world"以前的C级程序,并且使用了我用来生成本机可执行文件的第一种语言)。
  • 是否有一种标准方法可以使库更加独立于系统(不依赖于opam),就像在sqlite3.cmxa中包含my_lib.cmxa一样? (我会认为使用-for-pack / -pack但我需要实际的sqlite3.ml文件,因为这不是吗?)
  • 这种行为在某种程度上是针对external函数的(我真的不知道关于ocaml与C的接口)吗?。
  • 这是我在这一点上超级懒惰(因为我没有精力去查看sqlite3的文档,而不是sqlite3.mli并且它不是特别相关),但如果有人知道他们的头顶;什么是" noalloc"实际上呢?我实际上并不知道external关键字是什么/我是否假设" noalloc"是C库中外部函数的参数,但我不知道使用它的优点/缺点是什么。

作为最后一点,我知道这不是正确的'处理这种情况的方法;我想通常要做的是使用opam切换到用于制作my_lib.cm*a的相同编译器,然后使用opam安装相同版本的sqlite3,但这不是我&#t; t 39;我正在寻找(主要是因为我希望更好地理解编译过程,但是当我尝试在旧系统上安装时,opam似乎无法正常工作/它会发出错误) 。基本上,我说我没有找到可以归结为在老系统上使用opam的答案"。

编辑

  • 几分钟后,更多的工作产生了一个不错的解决方案(我猜这基本上是正常的' /显而易见的方式来解决这个问题),我认为这个问题通常应该有效(除非是外部库的接口实际上发生了变化)。在旧系统上从源代码构建my_lib.cm*a会使一切正常。我想这应该是我编译/分发软件的新鲜事,尽管这并没有回答一些概念"的问题。

1 个答案:

答案 0 :(得分:1)

  

我的扣除是否正确?

或多或少。

  

我的顶级/字节代码编译器的攻击(即将mli编辑为预期的那个然后使用它)通常/经常工作的东西,或者偶然发现它遇到的罕见情况。

你偶然发现了一个罕见的案例。实际上,您只是提供了更多信息,允许编译器更有效地调用此外部函数。一般来说,当你打破实现与其界面之间的一致性时,它当然不应该工作。

  

是否有类似的hack才能使本机编译工作?

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

  

有关所有这类编译器业务的参考资料的任何好建议吗?

编译器代码本身。有一个OCaml编译器黑客wiki,其中包含一些有用的信息,但它们不包含链接。

  

是否有一种标准的方法可以使库更加独立于系统(不依赖于opam),就像某种方式在my_lib.cmxa中包含sqlite3.cmxa一样?

没有标准方法,但您可以将所有文件复制到文件夹中。 (顺便说一下,cmxa不包含二进制代码,它位于.a文件中。cmxa以及cmx只包含有关编译单元的eXtra信息或单位)。

  

我认为使用-for-pack / -pack ...

for-packpack旨在解决命名空间问题,在背后,包仍然是同一组cmxacmxao个文件。

  

但我需要实际的sqlite3.ml文件,因为这不是我吗?

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

  

这种行为是否特定于外部功能(我真的不知道有关将ocaml与C接口的事情)?。

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

  

......但是,如果有人知道他们的头顶;什么是" noalloc"实际上呢?

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

  

由此我推断出本机编译单元(正确的术语?)或至少本机存档包含其中的接口信息。

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

  

虽然我并不完全理解编译器如何使用带有字节代码文件的接口文件,但在我看来,字节代码库使用.cmi文件来定义接口并且不会#39; t包括任何接口信息本身,所以我到目前为止所描述的行为似乎有意义。

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