ocamlbuild 的可选依赖项

Optional dependencies with ocamlbuild

我有一个当前使用 OCamlMake 构建的 OCaml 项目。我对当前的构建系统不满意,因为它将所有构建工件都放在与源文件相同的目录中,而且它还需要手动指定模块之间的依赖关系顺序。我想切换到一个没有这些问题的构建系统。我决定尝试一下 Oasis,但遇到了 运行 问题。

问题的产生是因为项目是以一种非常具体的方式构建的。它支持多种不同的数据库后端(PostgreSQL、MySQL、SQLite)。目前,要编译数据库后端,用户必须安装该后端所需的额外库,并通过设置环境变量来启用它。这是它在 Makefile 中的样子:

ifdef MYSQL_LIBDIR
   DB_CODE    += mysql_database.ml
   DB_AUXLIBS += $(MYSQL_LIBDIR)
   DB_LIBS    += mysql
endif

请注意,这还会将额外的模块添加到已编译模块列表中。重要的一点是在应用程序入口点和数据库后端模块可访问的任何模块之间没有依赖性(在模块导入的意义上)。相反,每个数据库后端模块都包含顶级代码,当模块启动并使用副作用向主应用程序注册时 运行s。

我无法使此设置与 Oasis 一起使用。我将每个数据库后端模块声明为一个单独的库,可以使用标志启用编译:

Library mysql-backend
  Path          : .
  Build        $: flag(mysql)
  Install       : false
  BuildTools    : ocamlbuild
  BuildDepends  : main, mysql
  Modules       : Mysql_backend

但是,我想不出一种方法来告诉 Oasis 将可选模块 link 放入可执行文件中。我试图通过修改 myocamlbuild.ml 文件来找出一种方法,但失败了。我可以使用 here 中描述的 rule 函数实现吗?

如果我描述的内容无法用 ocamlbuild 实现,是否有任何其他工具可以完成这项工作并避免 OCamlMake 的问题?

不幸的是,这超出了绿洲的能力范围。此限制与 ocamlbuild 无关,只是因为 oasis 作者试图保持简单,并且没有提供可选的依赖项作为功能。

一如既往,额外的间接级别可能会解决您的问题。您需要的是一个配置脚本 (configure),它将根据用户提供的参数为您生成 _oasis 文件。

例如,在 our project 中,我们有一个类似的设置,即多个不同的后端,用户可以在配置阶段使用 --{enable,disable}-<feature> 选择这些后端。我们通过编写自己的 ./configure 脚本来实现这一点,该脚本根据配置生成 _oasis 文件。配置脚本只是将生成的 _oasis 文件连接起来,如 oasis 文件夹中所述。

另一种解决方案是使用 m4 或仅使用 cpp,并有一个 _oasis.in 文件,该文件经过预处理。

好吧,我想这就是答案:https://github.com/links-lang/links/pull/77 :)

在我注意到上面 Drup 的回答之前,我看到了这个问题并开始研究它。下面是一个独立的 ocamlbuild 解决方案,它与 Drup 的基本相同。

open Ocamlbuild_plugin

let enable_plugin () =
  let plugins = try string_list_of_file "plugin.config" with _ -> [] in
  dep ["ocaml"; "link_with_plugin"; "byte"]
    (List.map (fun p -> p^".cmo") plugins);
  dep ["ocaml"; "link_with_plugin"; "native"]
    (List.map (fun p -> p^".cmx") plugins);
  ()

let () = dispatch begin
    function
    | Before_rules -> enable_plugin ()
    | _ -> ()
end

在 ocamlbuild 目标上使用标签 link_with_plugin 将使其依赖于文件 plugin.config 中列出的路径(无扩展名)的任何模块。例如,如果您有插件 pluga.mlplugb.ml 和一个文件 main.ml,那么在 plugin.config 中写入 pluga plugb 并具有 <main.{cmo,cmx}>: link_with_plugin 将 link 主可执行文件中的两个插件模块。