使用 Dune 编译和调试 OCaml 代码
下载 Dune
opam install dune
创建项目
dune init project <project-name>
如果创建成功,有
Success: initialized project component named <project-name>
得到如下的一个文件结构
project_name/
├── dune-project
├── test
│ ├── dune
│ └── test_project_name.ml
├── lib
│ └── dune
├── bin
│ ├── dune
│ └── main.ml
└── project_name.opam
/lib
lib
里存放编写的库 library(可以看作是一组模块 module 的集合),比如我有如下的一个 module MyModule
:
(* MyModule.ml *)
type t = int list
let init n = List.init n (fun i -> i)
let print l = List.iter print_int l
把它放在 lib
下后修改 /lib/dune
文件如下:
(library
(name my_project)
(modules MyModule))
在 /bin/main.ml
下可以这么调用:
let lst = My_project.MyModule.init 10
let () = My_project.MyModule.print lst
library 描述文件 stanza 的格式如下:
(library
(name <library-name>)
<optional-fields>)
<library-name>
指库 library 的名字,在上面的例子中,<library-name>
是 my_project
,那么在 /bin/main.ml
中就是用 My_project
去调用它内部的模块。
<optional-fields>
有很多可选项,包括:
-
(modules <modules>)
规定哪些模块 module 被包括在这个库 library 之中,使用 Ordered Set Language 来描述 -
(libraries <libraries-dependencies>)
决定了库 library 的依赖
/bin
/bin
存放可运行的 .ml
代码文件,关于 module
的调用方式见上文,使用 dune exec <project_name>
在终端中执行代码,上面的例子的运行结果如下:
dune exec my_project ─╯
Hello, World!
0123456789
如果想要引入外部库,可以修改 /bin/dune
,以使用 lwt 库为例:
(executable
(public_name myproject)
(name main)
(libraries myproject lwt.unix))
(* main.ml *)
let lst = Myproject.MyModule.init 10
let () = Myproject.MyModule.print lst;
Lwt_main.run (Lwt_io.printf "Hello, world!\n")
再次调用 dune exec
可以发现其正常工作
dune-project
dune-project 是描述工程 project 的元数据文件,以我刚刚建立的工程为例:
(lang dune 3.6) ; dune 的版本
(name my_project) ; 工程名
(generate_opam_files true)
(source
(github username/reponame))
(authors "Author Name") ; 作者
(maintainers "Maintainer Name") ; 拥有者
(license LICENSE)
(documentation https://url/to/documentation)
(package
(name my_project)
(synopsis "A short synopsis")
(description "A longer description")
(depends ocaml dune)
(tags
(topics "to describe" your project)))
; See the complete stanza docs at https://dune.readthedocs.io/en/stable/dune-files.html#dune-project
构建测试
根据 github 更新时间来看,推荐使用 QCheck 或者 ppx_inline_test
使用 QCheck 构建随机单元测试
Cornell cs3110 的课程中使用的是 QCheck,在我的 docker 容器上不是很能正确安装
一直报错 Curl failed
原因是 seq
这个依赖对应的地址没能被正确 dns 解析,把 dns 服务器地址改为 8.8.8.8 之后问题被修复( 有点难绷
使用 QCheck 的例子如下:
#require "qcheck"
(* [rev] is a function that reverses a list. For example, [rev [1;2;3]] is [3;2;1]. *)
let rev lst = List.rev lst
(* QCheck *)
let test =
QCheck.Test.make ~count:1000 ~name:"test_rev"
QCheck.(list small_nat)
(fun l -> rev l = l);;
(* we can check right now the property... *)
QCheck_runner.run_tests [test];;
#require "qcheck"
let leap_year year =
if year mod 400 = 0 then true
else if year mod 100 = 0 then false
else year mod 4 = 0
let test =
QCheck.Test.make ~count:1000 ~name:"test_leap_year_and_mod_4"
QCheck.(small_nat)
(fun year -> leap_year year = (year mod 4 = 0));;
QCheck_runner.run_tests [test]
集成到 Dune 中:
(executable
(public_name myproject)
(name main)
(libraries myproject lwt.unix qcheck))
使用 OUnit2 测试套件
(施工中)