-
Notifications
You must be signed in to change notification settings - Fork 414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Library Variants #136
Comments
This seems fine to me. As for everything in jbuilder, the user shouldn't have to think about the low-level machinery to implement this. Here are some possible ways to present it: Proposal 1The user writes this: (library
((name plop)
(public_name plop)
(implementations
(((name foo)
(libraries (...)) ;; deps specific to the foo backend
)
((name bar)
(libraries (...))
)))
)) Then for the module that have several implementations, instead of writing the implementation in Proposal 2The user writes this: ;; src/jbuild
(library
((name plop)
(public_name plop)
(virtual_modules (blah))
)) And in this library blah.ml must not exist. Then: ;; src/foo/jbuild
(library
((implements (plop))
(public_name plop.foo))) And in src/foo, we must only have blah.ml. I think I quite like the second proposal. In the end this should be quite easy to implement. We should make sure to properly report errors, for instance if a user tries to link an executable with virtual libraries without providing the corresponding implementations, jbuilder should report a proper error instead of letting the compiler dies with an error that is impossible to understand. |
I think I quite like the second proposal better, as I usually you want to distribute the implementations in different packages (for instance |
I'd also really prefer the second if only to avoid (re)naming sources name tricks (but then I know jbuilder is not shy of doing that). A few more comments about the general idea. Since I don't see this as a trick but a legitimate software engineering practice I nowadays avoid calling this a trick but "link time selection of libraries" or "library variants". I think it would be worthwhile to get @lpw25's input on how he plans to solve this in namespaces as it could inform the design. One of the thing I was always annoyed about the current state of affairs is that since you only get to use one of the library in a compilation it feels illegitimate to me for them to exist in the namespace of library (or ocamlfind package) names. Suppose I have an app that uses Somehow this is what I was trying to do with module selectors when I was getting into the territory of providing compilation helpers in |
The namespaces proposal supports multiple library implementations via the following mechanism:
Essentially the compiler will treat a directory called So at compile time you specify Mostly this is orthogonal to what jbuilder has to do: the namespaces proposal only cares about how things are laid out in the install directories, whereas the main thing for jbuilder is how you want to lay things out in the source directories.
This seems quite a lot harder to support. It requires different libraries to agree on the different variants that there are ( |
If the only problem is the agreement I don't see it as a huge problem. Somehow having to specify the library variant for all off the libraries seems to push back the problem on the end-user and the build-systems, where it could simply be a But indeed e.g. for |
I suppose |
Possibly relevant to the options being considered here - I'm not a betting man, but I think jbuilder is likely to have a story for cross-compilation using multiple opam switches before opam itself has an official story for cross-compilation. |
I agree that we shouldn't have to specify n times Looking at cases where one would want variants, or where we used to use findlib predicates it seems to me that the system could choose the implementation simply based on the set of libraries being linked. For instance:
Moreover there are libraries that work with only one of the alternatives. For instance Core forces the use of threads, and I'm sure there are libraries that are for javascript only. This means that we'd need some way to specify that the What would be the advantage of introducing a new concept of variants? |
Not sure exactly what you mean by this but it seems to me that:
Is strictly equivalent to introduce the concept of a variant but in a less direct way and thus potentially more obscure for build problem analysis. I don't mind if you can store |
Actually I was thinking that we might end up having to write But we shouldn't have too, typically the |
OK, so to sum up a bit this discussion, we have a concrete proposal that would allow to support choosing the implementation of a library at link time and would be compatible with the namespace proposal. Eventually we want to take advantage of namespaces and the notion of variants to make things simpler for users and especially not have to specify the implementation for every single library we link against. We can proceed as follow:
I think in addition to the proposal 2, we also need to add the limitation that all implementations must be defined in the same scope as the virtual library. i.e. they can be part of separate opam packages, but they must all be part of the same project. This will allow to know all the available implementations and infer the right one automatically when there is a single possible choice. We can lift this limitation later if variants work differently. Regarding how things are installed, there is just one thing that's not completely clear to me: if |
I think that is the easiest way to do it. I would suggest that opam's rules should allow the |
@lpw25 I think it would be better to keep variants of Why don't you simply use another convention for denoting variants ? In general I find a bit confusing if variants are distinguished and specified by |
No need for another convention: |
That's fine with me but I'd prefer if there was a single way of doing that (i.e. always in subs). |
Is someone trying to implement @diml's proposal 2? |
@diml How your proposal work when you use such library. Is the META giving specific information to know which package is the implementation of what? |
I suppose we can have a variable |
Why do you want add new variables to META files exactly ? Though it would be a noble goal that the implementation packages do not show up as ocamlfind packages themselves (see the |
Just to report better error messages. When the compiler knows about variants, it should be able to report proper errors. In the meantime, it will just report that the implementation of some unit is missing or present twice, which won't be helpful to debug the problem. |
Note sure exactly what your problem is. I have had libraries with variants (see e.g. |
I'm thinking that if someone lists
Which is more helpful. |
N.B. this is for But in fact this doesn't happen because I do not install the
The disavantage of this approach though is that you can't have another library coding against |
I see, what I had in mind with my proposal was this:
I think this is closer to the namespace proposal. Then, when you write a library, either you depend on |
Yes I think what you propose makes sense. I'm just always a bit wary when people want to add things to |
In the details, I'm not sure to understand if people agrees on what should be done for the META. To summarize my understanding of the problem:
The proposal 2.A (META implementation details of proposal 2):
|
TBH I'm not sure it's worth trying to do all the erroring stuff and make
|
In general my experience with |
If we do something clever with findlib predicates and it's not compatible with what ends up being merged in OCaml, it's going to be painful. Hopefully, namespaces are for a not too distant future, so for now I think we should implement the minimum to get people going, even if it means having to specify the same variant multiple times. Once variants are merged, then we can do something clever to emulate them with findlib predicates, so that we can start using them without having to drop compatibility with older versions of OCaml. |
@diml could you answer the second problem I see with @dbuenzli's proposition, namely that during linking you need to specify all the library variant in the right order and the tool must keep the relative order of independent dependencies?
Additional question, does jbuilder keep the relative order of independent modules when creating library? Of course jbuilder could implement its own logic and encode its own information in the META files in order to make the use of such libraries first class only for jbuilder user and not for ocamlfind, ocamlbuild, oasis, ... users. @diml Could you add in your second proposal how people describe the use and linking of such libraries in jbuild format? Just to fix if people must list all the implementation libraries, in the order or not, or just at least one to select the variant. Thank you. |
What I had in mind is that when you use such libraries, you simply list the concrete implementations: (executable
((name blah)
(libraries (mtime.clock.os lwt foo.os bar.os)))) I hadn't considered the ordering problem you describe, which is indeed a blocker. I guess we could ask users to specify ordering constraints when linking an executable, but TBH I'm not excited by this idea. It's not going to be very practical when using a lot of libraries and it's weird to have to re-specify at link time something you already wrote in jbuild files before. I suppose we could simply use @dbuenzli's layout. We couldn't write backend independent libraries without using functors, but at least we avoid the ordering problem. I agree that we could do something better right now, and encode it in ocamlfind predicates. At this point I would rather leave all the bike-shedding about variants to OCaml and the namespace proposal and simply follow what comes out of it. |
I think that the removing .cma files section of the namespaces proposal would be sufficient to solve the ordering problem. Unlike modules linked from |
You mean by the compiler ? |
Yeah |
I had another look at this and at the namespace proposal and talked with @lpw25 about variants etc... Updated proposalDefining a library with multiple implementationsThe user writes this:
And in this library:
Then to define an implementation:
And the only ml file The main difference with the original proposal is that implementations have no name and specify a variant instead. Using such a library and selecting an implementationExecutable stanzas now allow an additional field to specify a list of variants:
Implementation with namespaces or findlibThis scheme maps directly to the concept of variants in https://github.com/lpw25/namespaces, so we'll just need to install the cmx files at the same place. With findlib, the most natural encoding is to map every variant to a findlib predicate and select the archive and library dependencies depending on this predicate. We would generate META files that looks like this:
However, to match exactly the semantic of variants, we must ensure that it is an error to specify none of
As a side effect, this method should also fix #208 since it will effectively give a way to specify custom findlib predicates. |
The new proposals seems fine. Will it allow to define implementations outside of the package defining the virtual module? This is sometimes required because you want to test a new variant, or because you don't have ownership on the main repo, etc. |
Ah, no it doesn't. We could support having implementation in separate packages but only as long as all the packages are part of the same project, since we need to know all the variants in advance. Well at least with the findlib implementation, namespaces don't have this limitation since there is no In any case it means that you have to install files in other package installation directories, which is slightly annoying, but I don't see a way around it without explicitly naming implementations. |
The new proposal is neat, I like it. Use of |
I just want to ping this issue to know what is the state now after 2~3 months of the last reply. |
Will libraries with |
@dinosaure we haven't started working on it. It's quite a bit of work, but it's still definitely on the TODO list. @rgrinberg nope. There are no mli only modules here, at the time of linking the final executable, all implementations will be provided. A virtual library is just a library with holes that are filled when linking. |
fixed in #1430 and will be in dune 1.7.0+ |
A few library use the "linking trick" to be able to compile libraries against a single
cmi
and select the correct implementation at link time. This is similar to functors, where multiple implementation can have the same signature, but this is done out of the language using build system invocations, hence the name "linking trick".A good application for this is to select at link-time the best implementation for a given signature:
mtime
does this to select between the javascript native way of getting time when generating a javascript app vs. POSIX when generating a native app as described by @dbuenzli here. This could also be applied to SHA implementation, crypto, etc. We could even imagine using something similar in MirageOS, when we don't care having multiple implementation of the same signature living in the same app.The text was updated successfully, but these errors were encountered: