-
Notifications
You must be signed in to change notification settings - Fork 413
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
Add the ability to choose the host context and create duplicates of the default context with different settings #2098
Conversation
e2f7e95
to
d5acec6
Compare
For context, this would be useful for building mirage unikernels with all C stubs systematically compiled using the right embedded cflags. mirage/mirage#969 |
Amusingly, this kind of a host/target context setup is what we had for our initial version of cross compilation. But against our better senses, we went for something that more closely mimics the way cross compilation is currently done with findlib and the opam-cross repos. I'm definitely in favor of bringing up the simplified, and in my opinion more natural setup. |
Well, it's also simplicity of use. It's nice to be able to just do |
src/context.ml
Outdated
|
||
let resolve_host_contexts contexts = | ||
let empty = String.Map.empty in | ||
let map = List.fold_left |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let map = List.fold_left | |
let map = Map.of_list_map_exn contexts ~f:(fun (_, (_, c)) -> c.name, c) |
This makes the intent more explicit and the code more strict
src/context.ml
Outdated
>>| List.concat | ||
>>| resolve_host_contexts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I read this code correctly, we might create several Context.t
values with the same name but not physically equal. Do you agree? Given that there are mutable things in Context.t
and the Context.t
record is quite big, we should make sure to share these values.
I suggest to topologically sort the Workspace.contexts
field in Workspace.t
, this will allow us to assume that when we resolve for_host
here, the Context.t
value has already been computed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, the only mutation that happens in resolve_host_contexts
that might lead in a duplication of a Context.t
is the for_host
field update. But I think the case where it happens should be forbidden: it's the case where a
sets b
as host, and b
sets c
as host, and the order of resolution is a
then b
then c
. In this case the context value of b
duplicated during the resolve_host_context
. I'm not sure this configuration is really useful: so instead of building a topological sort we can simply forbid this kind of host
chain.
What do you think ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forbidding configurations that are not good seems good. However, I feel like it would be easier for someone reading the code to convince themselves that Context.t
values are never duplicated if {context with ...}
is never explicitly written. Building a topological sort is quite easy with Top_closure
BTW
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given this:
let equal x y = String.equal x.name y.name
if there is any chance at all that different context values can end up with the same name, the equal
function should change to use physical equality.
I think it was a mistake to make it use anything other than physical equality in the first place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, so far we have made sure to never create different context values with the same name, though it's true that we never enforced it programmatically. Comparing the names seems fine to me given that physically they can be only a single context with a given name, since each context correspond to a sub-directory in _build
. To start with, we should make Context.t
private, so that at least we only need to enforce this invariant in context.ml
.
In this case, the |
d5acec6
to
1554d5a
Compare
1554d5a
to
9d74f55
Compare
@diml I've added the topological sort and the bad configuration check with some tests and little documentation. |
Thanks, however I'd feel more comfortable if we really didn't have |
This PR should make it to 1.10 as well. I will try to review it this week. |
22cfdac
to
d697943
Compare
@diml Okay so now the topological sort is done earlier in order to avoid |
Hmm, it seems reasonable to build the dependant contexts sequentially. However, independent one should be constructed in parallel.
Yep. We should fail right at parsing time. |
2e71431
to
9d4f3fa
Compare
Now the result of the topological sort is split into independent group of contexts and
Done! I also added two more tests for error cases. |
@TheLortex I've pushed some commits to clean things up. Please review. |
Looks good to me ! |
I moved the checks to |
Just to make sure I understand the interaction with the existing @TheLortex that's intended, right? |
@diml does it also make sense to have a |
src/context.ml
Outdated
match Workspace.Context.host_context elem, cur with | ||
| None, [] -> (acc, [elem]) | ||
| None, _ -> ((List.rev cur) :: acc, [elem]) | ||
| Some _, _ -> (acc, elem :: cur) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm finding it hard to understand the logic here. Another approach would be to add the following function to Fiber
:
module type Memory = sig
type key
type 'a t
val empty : 'a t
val add : 'a t -> key -> 'a -> 'a t
val find : 'a t -> key -> 'a option
end
(** [memoize memory f] returns a version of [f] that remembers the
result of previous call to [f] in order to not recompute them. *)
val memoize
: (module Memory with type key := 'key)
-> ('a -> 'b t)
-> ('a -> 'b t) Staged.t
and then memoize the instantiation function before passing it to parallel_map
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been thinking about this as well, but I'm wondering why do we need anything extra here. In the previous version of this feature, we managed without any topological sorting by doing something like this:
let rec contexts = lazy (
List.map workspace.contexts ~f:(fun context ->
let context =
let* host = String.Map.find_exn (Lazy.force contexts) context.host in
create_context ~context ~host
in
(context.name, context))
|> String.Map.of_list_exn
) in
let* contexts = Lazy.force contexts |> String.Map.values |> Fiber.all in
...
Actually, I'm pretty sure we used a hashtable before, but I don't see why a map can't be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, that even simpler. There is just one little detail: unlike lwt or async, Fiber.t
values are not shared implicitly and need to be shared explicitly, so this code would end up doing the work twice for some contexts. It's easy to fix by using Fiber.Once.t
though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More precisely, you should think of Fiber.t
values as one time use. Only Fiber.Ivar.t
, Fiber.Future.t
or Fiber.Once.t
values can be shared and used multiple times.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, makes sense. When I wrote this originally, we still had our original Future.t
abstraction.
There's just one little blemish here btw, since some contexts may have targets, they introduce more than one context per name. However, in the current code we cannot use targets as hosts for other contexts anyway, so it doesn't matter. But perhaps it would be cleaner if we could include the targets in the map anyways since those can be computed outside the Fiber.t
regardless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Including the targets in the map seems good
@rgrinberg yep, that makes sense. Although, if we want to be complete, that's a serious refactoring; think about |
This would require us to move As much as I would like to do this, I don't know of a good way to accomplish this. Adding |
3083c7e
to
f3fdc91
Compare
A dune specialised path module would be nice indeed. We should simply use a different name to make things simpler. Maybe |
And in particular, |
@diml see the latest commit where I process all the contexts in parallel. The topological sorting isn't really required now, but I think it's still nice to have. In particular, it's nice to know all the context names have been validated. |
If the commit is good, I will incorporate the target contexts into the map as well. |
The topological sort is still useful to get a good error message, otherwise we'd get |
The commit looks good 👍 |
I think i'm going to leave the code as is for now. The targets are indeed included in the map, it's just they cannot be looked up as strings, which doesn't seem so bad. In the end, I think we just need to refactor the constructors for contexts with targets and a context with a host. |
a31f62d
to
c692a71
Compare
Also allows to use (default) with different names Signed-off-by: Lucas Pluvinage <lucas.pluvinage@gmail.com>
Signed-off-by: Jeremie Dimino <jeremie@dimino.org>
It also means that the topological sorting isn't rquired. Signed-off-by: Rudi Grinberg <rudi.grinberg@gmail.com>
c692a71
to
5dc27f9
Compare
CHANGES: - Restricted the set of variables available for expansion in the destination filename of `install` stanza to simplify implementation and avoid dependency cycles. (ocaml/dune#2073, @aalekseyev, @diml) - [menhir] call menhir from context root build_dir (ocaml/dune#2067, @ejgallego, review by @diml, @rgrinberg) - [coq] Add `coq.pp` stanza to help with pre-processing of grammar files (ocaml/dune#2054, @ejgallego, review by @rgrinberg) - Add a new more generic form for the *promote* mode: `(promote (until-clean) (into <dir>))` (ocaml/dune#2068, @diml) - Allow to promote only a subset of the targets via `(promote (only <pred>))`. For instance: `(promote (only *.mli))` (ocaml/dune#2068, @diml) - Improve the behavior when a strict subset of the targets of a rule is already in the source tree for projects using the dune language < 1.10 (ocaml/dune#2068, fixes ocaml/dune#2061, @diml) - With lang dune >= 1.10, rules in standard mode are no longer allowed to produce targets that are present in the source tree. This has been a warning for long enough (ocaml/dune#2068, @diml) - Allow %{...} variables in pps flags (ocaml/dune#2076, @mlasson review by @diml and @aalekseyev). - Add a 'cookies' option to ppx_rewriter/deriver flags in library stanzas. This allow to specify cookie requests from variables expanded at each invocation of the preprocessor. (ocaml/dune#2106, @mlasson @diml) - Add more opam metadata and use it to generate `.opam` files. In particular, a `package` field has been added to specify package specific information. (ocaml/dune#2017, ocaml/dune#2091, @avsm, @jonludlam, @rgrinberg) - Clean up the special support for `findlib.dynload`. Before, Dune would simply match on the library name. Now, we only match on the findlib package name when the library doesn't come from Dune. Someone writing a library called `findlib.dynload` with Dune would have to add `(special_builtin_support findlib_dynload)` to trigger the special behavior. (ocaml/dune#2115, @diml) - Install the `future_syntax` preprocessor as `ocaml-syntax-shims.exe` (ocaml/dune#2125, @rgrinberg) - Hide full command on errors and warnings in development and show them in CI. (detected using the `CI` environment variable). Commands for which the invocation might be omitted must output an error prefixed with `File `. Add an `--always-show-command-line` option to disable this behavior and always show the full command. (ocaml/dune#2120, fixes ocaml/dune#1733, @rgrinberg) - In `dune-workspace` files, add the ability to choose the host context and to create duplicates of the default context with different settings. (ocaml/dune#2098, @TheLortex, review by @diml, @rgrinberg and @aalekseyev) - Add support for hg in `dune subst` (ocaml/dune#2135, @diml) - Don't build documentation for implementations of virtual libraries (ocaml/dune#2141, fixes ocaml/dune#2138, @jonludlam) - Fix generation of the `-pp` flag in .merlin (ocaml/dune#2142, @rgrinberg) - Make `dune subst` add a `(version ...)` field to the `dune-project` file (ocaml/dune#2148, @diml) - Add the `%{os_type}` variable, which is a short-hand for `%{ocaml-config:os_type}` (ocaml/dune#1764, @diml) - Allow `enabled_if` fields in `library` stanzas, restricted to the `%{os_type}`, `%{model}`, `%{architecture}`, `%{system}` variables (ocaml/dune#1764, ocaml/dune#2164 @diml, @rgrinberg) - Fix `chdir` on external and source paths. Dune will also fail gracefully if the external or source path does not exist (ocaml/dune#2165, fixes ocaml/dune#2158, @rgrinberg) - Support the `.cc` extension fro C++ sources (ocaml/dune#2195, fixes ocaml/dune#83, @rgrinberg) - Run `ocamlformat` relative to the context root. This improves the locations of errors. (ocaml/dune#2196, fixes ocaml/dune#1370, @rgrinberg) - Fix detection of `README`, `LICENSE`, `CHANGE`, and `HISTORY` files. These would be undetected whenever the project was nested in another workspace. (ocaml/dune#2194, @rgrinberg) - Fix generation of `.merlin` whenever there's more than one stanza with the same ppx preprocessing specification (ocaml/dune#2209 ,fixes ocaml/dune#2206, @rgrinberg) - Fix generation of `.meriln` in the presence of the `copy_files` stanza and preprocessing specifications of other stanazs. (ocaml/dune#2211, fixes ocaml/dune#2206, @rgrinberg)
CHANGES: - Restricted the set of variables available for expansion in the destination filename of `install` stanza to simplify implementation and avoid dependency cycles. (ocaml/dune#2073, @aalekseyev, @diml) - [menhir] call menhir from context root build_dir (ocaml/dune#2067, @ejgallego, review by @diml, @rgrinberg) - [coq] Add `coq.pp` stanza to help with pre-processing of grammar files (ocaml/dune#2054, @ejgallego, review by @rgrinberg) - Add a new more generic form for the *promote* mode: `(promote (until-clean) (into <dir>))` (ocaml/dune#2068, @diml) - Allow to promote only a subset of the targets via `(promote (only <pred>))`. For instance: `(promote (only *.mli))` (ocaml/dune#2068, @diml) - Improve the behavior when a strict subset of the targets of a rule is already in the source tree for projects using the dune language < 1.10 (ocaml/dune#2068, fixes ocaml/dune#2061, @diml) - With lang dune >= 1.10, rules in standard mode are no longer allowed to produce targets that are present in the source tree. This has been a warning for long enough (ocaml/dune#2068, @diml) - Allow %{...} variables in pps flags (ocaml/dune#2076, @mlasson review by @diml and @aalekseyev). - Add a 'cookies' option to ppx_rewriter/deriver flags in library stanzas. This allow to specify cookie requests from variables expanded at each invocation of the preprocessor. (ocaml/dune#2106, @mlasson @diml) - Add more opam metadata and use it to generate `.opam` files. In particular, a `package` field has been added to specify package specific information. (ocaml/dune#2017, ocaml/dune#2091, @avsm, @jonludlam, @rgrinberg) - Clean up the special support for `findlib.dynload`. Before, Dune would simply match on the library name. Now, we only match on the findlib package name when the library doesn't come from Dune. Someone writing a library called `findlib.dynload` with Dune would have to add `(special_builtin_support findlib_dynload)` to trigger the special behavior. (ocaml/dune#2115, @diml) - Install the `future_syntax` preprocessor as `ocaml-syntax-shims.exe` (ocaml/dune#2125, @rgrinberg) - Hide full command on errors and warnings in development and show them in CI. (detected using the `CI` environment variable). Commands for which the invocation might be omitted must output an error prefixed with `File `. Add an `--always-show-command-line` option to disable this behavior and always show the full command. (ocaml/dune#2120, fixes ocaml/dune#1733, @rgrinberg) - In `dune-workspace` files, add the ability to choose the host context and to create duplicates of the default context with different settings. (ocaml/dune#2098, @TheLortex, review by @diml, @rgrinberg and @aalekseyev) - Add support for hg in `dune subst` (ocaml/dune#2135, @diml) - Don't build documentation for implementations of virtual libraries (ocaml/dune#2141, fixes ocaml/dune#2138, @jonludlam) - Fix generation of the `-pp` flag in .merlin (ocaml/dune#2142, @rgrinberg) - Make `dune subst` add a `(version ...)` field to the `dune-project` file (ocaml/dune#2148, @diml) - Add the `%{os_type}` variable, which is a short-hand for `%{ocaml-config:os_type}` (ocaml/dune#1764, @diml) - Allow `enabled_if` fields in `library` stanzas, restricted to the `%{os_type}`, `%{model}`, `%{architecture}`, `%{system}` variables (ocaml/dune#1764, ocaml/dune#2164 @diml, @rgrinberg) - Fix `chdir` on external and source paths. Dune will also fail gracefully if the external or source path does not exist (ocaml/dune#2165, fixes ocaml/dune#2158, @rgrinberg) - Support the `.cc` extension fro C++ sources (ocaml/dune#2195, fixes ocaml/dune#83, @rgrinberg) - Run `ocamlformat` relative to the context root. This improves the locations of errors. (ocaml/dune#2196, fixes ocaml/dune#1370, @rgrinberg) - Fix detection of `README`, `LICENSE`, `CHANGE`, and `HISTORY` files. These would be undetected whenever the project was nested in another workspace. (ocaml/dune#2194, @rgrinberg) - Fix generation of `.merlin` whenever there's more than one stanza with the same ppx preprocessing specification (ocaml/dune#2209 ,fixes ocaml/dune#2206, @rgrinberg) - Fix generation of `.merlin` in the presence of the `copy_files` stanza and preprocessing specifications of other stanazs. (ocaml/dune#2211, fixes ocaml/dune#2206, @rgrinberg) - Run `refmt` from the context's root directory. This improves error messages in case of syntax errors. (ocaml/dune#2223, @rgrinberg) - In .merlin files, don't pass `-dump-ast` to the `future_syntax` preprocessor. Merlin doesn't seem to like it when binary AST is generated by a `-pp` preprocessor. (ocaml/dune#2236, @aalekseyev) - `dune install` will verify that all files mentioned in all .install files exist before trying to install anything. This prevents partial installation of packages (ocaml/dune#2230, @rgrinberg)
This PR adds two features in
dune-workspace
files:(host <context_name>)
allows to consider the given context as a cross-compiled context, and usecontext_name
as its host context.(name <context_name>)
can be used on the default context: this allows have a duplicate of the default context using different flags, for example.My use case is the following:
Which sets up a cross-compiled context based on the default compilation context.