使用 OCaml 编译器工具链

本教程解释了如何将 OCaml 程序编译成可执行形式。它依次介绍了

  1. OCaml 提供的编译命令 ocamlcocamlopt。学习这些命令有助于理解 OCaml 的编译模型。

  2. 编译器的 ocamlfind 前端,它可以让你不用担心库安装在你特定系统上的位置。

  3. OCaml 的自动构建系统,例如 dune,它可以让我们免于关注编译命令的细节,因此我们永远不会接触 ocamlcocamlopt 甚至 ocamlfind

"你的第一个 OCaml 程序" 中,我们直接跳到了使用自动化构建系统 dune。现在我们将深入了解其内部原理。

编译基础

在本节中,我们将首先了解如何仅使用 ocamlcocamlopt 编译一个简单的程序。然后我们将了解如何使用库以及如何利用 findlib 系统,该系统提供了 ocamlfind 命令。

ocamlcocamlopt 编译器

OCaml 带有两个编译器:ocamlc 是字节码编译器,ocamlopt 是原生代码编译器。如果你不知道使用哪个,请使用 ocamlopt,因为它提供的可执行文件比字节码更快。

但是,如果你想尝试 ocamlc,可以继续尝试以下操作。

创建一个名为 hello 的新目录,并导航到该目录

$ mkdir hello
$ cd hello

接下来,创建一个名为 hello.ml 的文件,并使用你喜欢的文本编辑器添加以下代码

let () = print_endline "Hello OCaml!"

现在,我们准备运行代码了。保存文件并返回到命令行。让我们编译代码

$ ocamlc -o hello hello.ml

-o hello 选项告诉编译器将输出可执行文件命名为 hello。可执行文件 hello 包含编译后的 OCaml 字节码。此外,还会生成另外两个文件,hello.cmihello.cmo

hello.cmi 包含 OCaml 模块的编译接口信息。接口文件包含类型信息和模块签名,但不包含实际代码。

hello.cmo 包含 OCaml 模块的编译字节码。字节码是代码的中间表示形式,由 OCaml 解释器或运行时系统执行。

注意: cmi 代表 Compiled Module Interface(编译模块接口),cmo 代表 Compiled Module Object(编译模块对象)。

现在让我们运行可执行文件,看看会发生什么

$ ./hello
Hello OCaml!

瞧!它显示了 Hello OCaml!

我们可以更改字符串或添加更多内容,保存文件,重新编译并重新运行。

接下来,我们将了解如何使用 ocamlopt。假设我们的程序 program 有两个源文件,module1.mlmodule2.ml。我们将使用 ocamlopt 将它们编译成原生代码。目前,我们还假设它们除了标准库之外不使用任何其他库,标准库会自动加载。你可以一步编译程序

ocamlopt -o program module1.ml module2.ml

编译器生成一个名为 programprogram.exe 的可执行文件。源文件的顺序很重要,因此 module1.ml 不能依赖于 module2.ml 中定义的内容。另请注意,应避免创建与正在使用的库公开的模块冲突的文件。例如,如果你创建了一个名为 graphics.ml 的文件并使用了 graphics 库,则 graphics 库公开的 Graphics 模块将被你新定义的模块隐藏,因此其中定义的所有函数都将无法访问。

OCaml 发行版附带标准库以及其他几个库。还有大量第三方库,可用于各种应用程序,从网络到图形。你应该了解以下内容

  1. OCaml 编译器知道标准库在哪里,并系统地使用它(尝试:ocamlc -where)。你无需过多担心它。

  2. 与 OCaml 发行版一起提供的其他库(str、unix 等)安装在与标准库相同的目录中。

  3. 第三方库可以安装在各种位置,甚至同一个库也可以在不同的系统上安装在不同的位置。

例如,如果你的程序除了标准库之外还使用了 unix 库,则命令行将是

ocamlopt -o program unix.cmxa module1.ml module2.ml

请注意,.cmxa 是原生代码库的扩展名,而 .cma 是字节码库的扩展名。文件 unix.cmxa 能够找到,因为它始终安装在与标准库相同的位置,并且此目录位于库搜索路径中。

如果你的程序依赖于第三方库,则必须在命令行中传递它们。你还必须指示这些库依赖的库。你还必须为 ocamlopt 的每个可能找到库的目录传递 -I 选项。这变得很复杂,并且此信息取决于安装。因此,我们将改用 ocamlfind,它可以为我们完成这些工作。

使用 ocamlfind 前端

ocamlfind 前端通常用于编译使用第三方 OCaml 库的程序。库作者本身也使他们的库可以使用 ocamlfind 进行安装。你可以使用 opam 包管理器安装 ocamlfind,方法是键入 opam install ocamlfind

假设你想要使用的所有库都已使用 ocamlfind 正确安装。你可以通过键入以下命令查看系统中有哪些库可用

ocamlfind list

这将显示包名称及其版本的列表。请注意,大多数 opam 包都使用 ocamlfind 安装软件,因此你的 ocamlfind 库列表将与通过 opam list 获取的已安装 opam 包列表有些类似。

使用包 pkg 编译程序的命令将是

ocamlfind ocamlopt -o program -linkpkg -package pkg module1.ml module2.ml

可以使用逗号指定多个包,例如 pkg1,pkg2。Ocamlfind 知道如何从包中找到 ocamlopt 可能需要的任何文件,例如 .cmxa 实现文件或 .cmi 接口文件,因为它们已打包在一起并由 ocamlfind 安装到已知位置。我们只需要使用名称 pkg 来引用它们——ocamlfind 会完成其余工作。

请注意,您可以分别编译这些文件。如果您只想重新编译程序的某些部分,这将非常有用。以下是一些等效的命令,可以分别编译源文件并在最后一步将它们链接在一起。

ocamlfind ocamlopt -c -package pkg module1.ml
ocamlfind ocamlopt -c -package pkg module2.ml
ocamlfind ocamlopt -o program -linkpkg -package pkg module1.cmx module2.cmx

单独编译(一个命令用于module1.ml,另一个用于module2.ml,还有一个用于链接最终输出)通常不会手动执行,而只在使用自动化构建系统时执行,该系统将负责仅重新编译必要的部分。

插曲:创建自定义顶层环境

OCaml 提供了另一个工具ocamlmktop,用于创建可访问库的交互式顶层环境。例如:

ocamlmktop -o toplevel unix.cma module1.ml module2.ml

我们运行toplevel,并获得一个 OCaml 顶层环境,其中包含UnixModule1Module2模块,所有这些模块都可用,允许我们以交互方式实验我们的程序。

OCamlfind 也支持ocamlmktop

ocamlfind ocamlmktop -o toplevel unix.cma -package pkg module1.ml module2.ml

Dune:一个自动化构建系统

构建 OCaml 项目最流行的现代系统是 dune,可以使用opam install dune进行安装。它允许人们根据其元素的简单描述来构建 OCaml 项目。例如,我们项目的 dune 文件可能如下所示:

;; our example project
(executable
  (name program)
  (libraries unix pkg))

dune 的 快速入门指南 演示了如何为更复杂的情况编写此类描述文件,以及如何构建、构建和运行 dune 项目。

使用 Dune 构建字节码

Dune 是一个用于 OCaml 项目的构建系统,它允许您配置构建可执行文件的不同模式。我们将在dune文件中使用(modes byte exe)节,这将生成 OCaml 程序的字节码(解释)和原生可执行文件版本。

让我们创建一个名为myproject的示例项目。

$ mkdir myproject
$ cd myproject

创建一个dune-project文件,添加以下内容。

(lang dune 3.0)
(name myproject)

这里,3.0 是已安装的Dune版本。您可以通过在终端中键入dune --version来检查它。而name是项目名称,即myproject

创建一个dune文件,添加以下内容。

(executable
 (name main)
 (libraries base)
 (modes byte exe))

如前所述,(modes byte exe)节会生成 OCaml 程序的字节码(解释)和原生可执行文件版本。

接下来,创建一个main.ml文件,添加以下内容。

let () = print_endline "Hello Dune!"

最后,我们编译并执行它。

$ dune build main.bc

.bc代表通用字节码文件,它可以是可执行文件或库文件。

$ dune exec ./main.bc
Hello Dune!

我们也可以使用.exe来完成此操作。

$ dune build main.exe
$ dune exec ./main.exe
Hello Dune!

其他构建系统

  • OMake 另一个 OCaml 构建系统。
  • GNU make GNU make 可以构建任何东西,包括 OCaml。可以与 OCamlmakefile 结合使用。
  • Oasis 根据规范生成配置、构建和安装系统。

仍然需要帮助?

帮助改进我们的文档

所有 OCaml 文档都是开源的。发现错误或不清楚的地方?提交一个拉取请求。

OCaml

创新。社区。安全。