使用 OCaml 编译器工具链
本教程解释了如何将 OCaml 程序编译成可执行形式。它依次介绍了
-
OCaml 提供的编译命令
ocamlc
和ocamlopt
。学习这些命令有助于理解 OCaml 的编译模型。 -
编译器的
ocamlfind
前端,它可以让你不用担心库安装在你特定系统上的位置。 -
OCaml 的自动构建系统,例如
dune
,它可以让我们免于关注编译命令的细节,因此我们永远不会接触ocamlc
、ocamlopt
甚至ocamlfind
。
在 "你的第一个 OCaml 程序" 中,我们直接跳到了使用自动化构建系统 dune
。现在我们将深入了解其内部原理。
编译基础
在本节中,我们将首先了解如何仅使用 ocamlc
或 ocamlopt
编译一个简单的程序。然后我们将了解如何使用库以及如何利用 findlib 系统,该系统提供了 ocamlfind
命令。
ocamlc
和 ocamlopt
编译器
OCaml 带有两个编译器:ocamlc
是字节码编译器,ocamlopt
是原生代码编译器。如果你不知道使用哪个,请使用 ocamlopt
,因为它提供的可执行文件比字节码更快。
但是,如果你想尝试 ocamlc
,可以继续尝试以下操作。
创建一个名为 hello
的新目录,并导航到该目录
$ mkdir hello
$ cd hello
接下来,创建一个名为 hello.ml
的文件,并使用你喜欢的文本编辑器添加以下代码
let()"Hello OCaml!"
现在,我们准备运行代码了。保存文件并返回到命令行。让我们编译代码
$ -
-o hello
选项告诉编译器将输出可执行文件命名为 hello
。可执行文件 hello
包含编译后的 OCaml 字节码。此外,还会生成另外两个文件,hello.cmi
和 hello.cmo
。
hello.cmi
包含 OCaml 模块的编译接口信息。接口文件包含类型信息和模块签名,但不包含实际代码。
hello.cmo
包含 OCaml 模块的编译字节码。字节码是代码的中间表示形式,由 OCaml 解释器或运行时系统执行。
注意:
cmi 代表 Compiled Module Interface(编译模块接口),cmo 代表 Compiled Module Object(编译模块对象)。
现在让我们运行可执行文件,看看会发生什么
$ ./hello
Hello OCaml!
瞧!它显示了 Hello OCaml!
。
我们可以更改字符串或添加更多内容,保存文件,重新编译并重新运行。
接下来,我们将了解如何使用 ocamlopt
。假设我们的程序 program
有两个源文件,module1.ml
和 module2.ml
。我们将使用 ocamlopt
将它们编译成原生代码。目前,我们还假设它们除了标准库之外不使用任何其他库,标准库会自动加载。你可以一步编译程序
ocamlopt -o program module1.ml module2.ml
编译器生成一个名为 program
或 program.exe
的可执行文件。源文件的顺序很重要,因此 module1.ml
不能依赖于 module2.ml
中定义的内容。另请注意,应避免创建与正在使用的库公开的模块冲突的文件。例如,如果你创建了一个名为 graphics.ml
的文件并使用了 graphics
库,则 graphics
库公开的 Graphics
模块将被你新定义的模块隐藏,因此其中定义的所有函数都将无法访问。
OCaml 发行版附带标准库以及其他几个库。还有大量第三方库,可用于各种应用程序,从网络到图形。你应该了解以下内容
-
OCaml 编译器知道标准库在哪里,并系统地使用它(尝试:
ocamlc -where
)。你无需过多担心它。 -
与 OCaml 发行版一起提供的其他库(str、unix 等)安装在与标准库相同的目录中。
-
第三方库可以安装在各种位置,甚至同一个库也可以在不同的系统上安装在不同的位置。
例如,如果你的程序除了标准库之外还使用了 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 顶层环境,其中包含Unix
、Module1
和Module2
模块,所有这些模块都可用,允许我们以交互方式实验我们的程序。
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
namelibraries
dune 的 快速入门指南 演示了如何为更复杂的情况编写此类描述文件,以及如何构建、构建和运行 dune 项目。
使用 Dune 构建字节码
Dune 是一个用于 OCaml 项目的构建系统,它允许您配置构建可执行文件的不同模式。我们将在dune
文件中使用(modes byte exe)
节,这将生成 OCaml 程序的字节码(解释)和原生可执行文件版本。
让我们创建一个名为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 根据规范生成配置、构建和安装系统。