-
Notifications
You must be signed in to change notification settings - Fork 470
Variants - Simplified #1859
Description
Variants - Simplified
(This proposal is aimed at @TheLortex who has expressed some interest in implementing this feature. However, some details need to be hashed between the maintainers first)
Now that we've merged virtual libraries, we can consider implementing the full
variants proposal. It's heavily simplified from the original proposal so I'm
rewriting the original ticket.
TL;DR:
Variants are now tags that are attached to implementations. Executables may now
select groups of implementations simultaneously by choosing such tags.
Implementation Steps
-
Modify the library stanza to allow marking library implementations with an
optional(variant ..)field. -
Modify the executables stanza to allow for a
(variants ..)field. It should
allow for a list of tags to be selected. -
Modify the
dune-pacakgeformat accordingly to maintain variant information
for implementations. -
When loading the library database, we must maintain a map from individual
variantsto a map of virtual libraries to implementations. The next step
will make it obvious why we need such a data structure. -
When calculating the closure for linking (executables), we now need to select
implementations based on the set of tags. I'll describe this in a bit more
detail
Roughly, the closure pipeline for executables would look like:
deps
(* (1) calculate the closure *)
|> closure
... (* (2) *)
(* (3) verify that there's an implementation present for every vlib
and make sure the linking order is correct *)
|> Virtual_libs.associateIn ..., we'd like to insert a step that would insert implementations based on
the selected set of variants.
So essentially:
val add_impls_for_variants : Lib.t list -> variants:Variant.t list -> Lib.t listNow there's a bit of a problem here: we must calculate the closure again after
this step, and recursively do (1; 2) until 2. doesn't change the closure.
This is because an implementation itself may depend on a virtual library.
Consider:
(library
(name bar)
(virtual_modules bar))
(library
(name baz_js)
(implements bar)
(variant js))
(library
(name foo)
(virtual_modules foo))
(library
(name foo_js)
(libraries bar)
(variant js)
(implements foo))
(executable
(name x)
(libraries foo)
(variants js))
Now, if we calculate the closure of x we'll discover foo and that we need
the foo_js library. But now we'll also need to select the correct
implementation for bar.
Taking the closure + finding implementations in a fixpoint doesn't seem very
attractive, so unless @diml has a better idea, I suggest we interleave the
variant-based implementation selection with the closure itself. In this new
closure, we'll check if the current library is virtual, and we have an
implementation for it based on the set of variants present. In this case, we'll
just add the correct implementation to the closure directly.
Possible Issues & Future Work
-
As usual, making decent error messages always increases the difficulty. Let's
worry about this once we have a prototype ready. We can threw some test cases
and improve errors from there. -
Since variant tags is going to be quite small (compared to the set of
libraries at least), We might need a conflict resolution mechanism when two
libraries provide an implementation for the same library with the same variant
name. Yes, in this case the user can always just drop variants altogether and
go back to concrete implementations, but that's a bit dissapointing.Perhaps one could resolve this ambiguity by providing a concrete
implementation in thelibrariesfield for the executable. So when doing
lookups from4, we'll keep in mind the list of libraries the user actually
listed.