问题
I have an OCaml project that is currently built using OCamlMake. I am not happy with the current build system since it leaves all build artefacts in the same directory as source files, plus it also requires to manually specify order of dependencies between modules. I would like to switch to a build system that does not suffer from these issues. I decided to give Oasis a try but have run into issues.
The problems arise from the fact that the project is built in a very specific way. It supports several different database backends (PostgreSQL, MySQL, SQLite). Currently, to compile a database backend user must install extra libraries required by that backend and enable it by setting an environment variable. This is how it looks in the Makefile:
ifdef MYSQL_LIBDIR
DB_CODE += mysql_database.ml
DB_AUXLIBS += $(MYSQL_LIBDIR)
DB_LIBS += mysql
endif
Notice that this also adds extra module to the list of compiled modules. The important bit is that there is no dependency (in a sense of module import) between any module reachable from the applications entry point and database backend module. What happens rather is that each database backend module contains top-level code that runs when a module is initiated and registers itself, using side-effects, with the main application.
I cannot get this setup to work with Oasis. I declared each of the database backend modules as a separate library that can be enabled for compilation with a flag:
Library mysql-backend
Path : .
Build $: flag(mysql)
Install : false
BuildTools : ocamlbuild
BuildDepends : main, mysql
Modules : Mysql_backend
However, I cannot figure out a way to tell Oasis to link the optional modules into the executable. I tried to figure out a way of doing this by modifying myocamlbuild.ml
file but failed. Can I achieve this with the rule
function described here?
If what I describe cannot be achieved with ocamlbuild
, is there any ither tool that would do the job and avoid problems of OCamlMake?
回答1:
Well, I guess that answers it: https://github.com/links-lang/links/pull/77 :)
回答2:
I saw the question and started working on it before I noticed Drup's answer above. Below is a self-contained ocamlbuild solution that is essentially the same as Drup's.
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
Using the tag link_with_plugin
on an ocamlbuild target will make it depend on any module whose path (without extension) is listed in the file plugin.config
. For example if you have plugins pluga.ml
, plugb.ml
and a file main.ml
, then writing pluga plugb
in plugin.config
and having <main.{cmo,cmx}>: link_with_plugin
will link both plugin modules in the main executable.
回答3:
Unfortunately, this is beyond oasis capabilities. This limitation has nothing to do with ocamlbuild
, it just because oasis
author tried to keep it simple, and didn't provide optional dependencies as a feature.
As always, an extra level of indirection may solve your problem. What you need, is a configuration script (configure
) that will generate _oasis
file for you, depending on parameters, provided by a user.
For example, in our project we have a similar setup, i.e., multiple different backends, that might be chosen by a user during the configuration phase, with --{enable,disable}-<feature>
. We achieved this by writing our own ./configure
script that generate _oasis
file, depending on configuration. The configuration script just concatenates the resulting _oasis
files from pieces, described in oasis
folder.
An alternative solution would be to use m4
or just cpp
, and have an _oasis.in
file, that is preprocessed.
来源:https://stackoverflow.com/questions/39796986/optional-dependencies-with-ocamlbuild