使用 OCaml 編譯器工具鏈

本教學說明如何將您的 OCaml 程式編譯成可執行檔。它將依序說明

  1. OCaml 提供的編譯指令 ocamlcocamlopt。學習這些指令對於理解 OCaml 的編譯模型很有用。

  2. 編譯器的 ocamlfind 前端,它可以讓您不必擔心函式庫安裝在您特定系統上的位置。

  3. OCaml 的自動建置系統,例如 dune,它可以將我們從編譯器指令呼叫的細節中解放出來,因此我們永遠不會接觸到 ocamlcocamlopt 甚至是 ocamlfind

「您的第一個 OCaml 程式」 中,我們直接跳到使用自動建置系統 dune。現在我們將深入了解其內部運作原理。

編譯基礎知識

在本節中,我們將首先了解如何僅使用 ocamlcocamlopt 編譯一個簡單的程式。然後,我們將了解如何使用函式庫,以及如何利用提供 ocamlfind 指令的 findlib 系統。

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 文件都是開源的。看到錯誤或不清楚的地方嗎?提交一個 Pull Request。

OCaml

創新。社群。安全。