Skip to content
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

Multi directory libraries and executables #1034

Merged
20 commits merged into from Aug 1, 2018
Merged

Multi directory libraries and executables #1034

20 commits merged into from Aug 1, 2018

Conversation

ghost
Copy link

@ghost ghost commented Jul 19, 2018

This PR adds the necessary support for multi-directory libraries and executables.

Description

When writing (include_subdirs true) or simple (include_subdirs) in a dune file, dune will recurse in sub-directories to look for modules.

The recursion stops when encountering a sub-directory that:

  • is part of another project
  • contains (include_subdirs true) or (include_subdirs)
  • contains at least one module consumer stanza: library, executable(s), test(s)

Implementation

Dir_contents was adapted to recognize directories that are part of a group of directories. When Dir_contents.get is called on a directory that is part of a group, the whole group is scanned at once.

The rule producer in gen_rules.ml does exactly the same thing.

TODO (before this can be merged)

  • when looking for .c/.h files, include all directories that are part of the group

Future work

Add a mode (include_subdirs qualified)``so that sub-directories are qualifed. i.e. file a/b/x.mlwill be seen asA.B.xat the root, and asB.xina/`

@ghost ghost requested a review from rgrinberg July 19, 2018 14:39
@rgrinberg
Copy link
Member

rgrinberg commented Jul 24, 2018

Add a mode (include_subdirs qualified)``so that sub-directories are qualifed. i.e. filea/b/x.mlwill be seen asA.B.xat the root, and asB.xina/`

Shouldn't this be the default? IMO we shouldn't encourage people to create arbitrary grouping of the modules that isn't reflected in the module structure.

Btw, I cherry picked the refactoring commit and rebased the rest so that it applies cleanly on master. It should make things easier to review.

| Standalone of File_tree.Dir.t
* Super_context.Dir_with_jbuild.t
| Group_root of File_tree.Dir.t
* Super_context.Dir_with_jbuild.t
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a brief doc for every constructor here would be hlpeful

Option.bind (Path.drop_build_context dir)
~f:(File_tree.find_dir (Super_context.file_tree sctx))
with
| None -> Empty_standalone None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it intentional that this case doesn't populate the cache?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, line 520


let cache = Hashtbl.create 32

let rec get sctx ~dir =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use Hashtbl.find_or_add for this function (and others like it)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we could use it here. It's just unfortunate that we end up systematically creating a closure while it's only useful in the not found case.

For other functions, we modify the cache in the not found case through a recursive call. It's not clear what the behavior of Hashtbl.find_or_add is in this case should be, so it seems better to avoid it.

@ghost
Copy link
Author

ghost commented Jul 30, 2018

Shouldn't this be the default? IMO we shouldn't encourage people to create arbitrary grouping of the modules that isn't reflected in the module structure.

True, although allowing unqualified subdirs is not much work and I believe will help porting older projects to dune. What about:

(include_subdirs <false|true|unqualified>)

?

@ghost
Copy link
Author

ghost commented Jul 30, 2018

Btw, I cherry picked the refactoring commit and rebased the rest so that it applies cleanly on master. It should make things easier to review.

Thanks

@rgrinberg
Copy link
Member

(include_subdirs <false|true|unqualified>)

What would (include_subdirs false) be useful for?

Copy link
Member

@rgrinberg rgrinberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm approving because the implementation looks good. @diml I'll let you pick what you think the default on qualified vs. unqualified should be.

@ghost
Copy link
Author

ghost commented Jul 31, 2018

Changing the default to (include_subdirs true) is a breaking change. For instance it breaks projects currently relying on copy_files to have multi-directory libraries. /cc @emillon as well in case he wants to chime in regarding the design (and/or code).

BTW, we still need to sort out the situation regarding .c/.h files before merging this. In the past there was a least one request to put .c files in a sub-directory. In the current state of this PR, .c/.h files in sub-directories are ignored, so we couldn't consider them in the future without introducing a breaking change. We can either make sure they are considered or simply forbid .c/.h files in sub-directories for now. I might do the latter for now since it's easier, in particular it's easier to setup the -I flags.

@rgrinberg
Copy link
Member

Changing the default to (include_subdirs true) is a breaking change. For instance it breaks projects currently relying on copy_files to have multi-directory libraries.

Oh, I simply suggested that (include_subdirs) without an argument be equivalent to (include_subdirs qualified) rather than (include_subdirs unqualified). Btw, now I'm thinking if it's best that we shouldn't use booleans at all as the argument here. It will be confusing as we are introducing a new feature that users aren't really used to.

@ghost
Copy link
Author

ghost commented Jul 31, 2018

That's a good point. I'll change the syntax to the following then:

(include_subdirs <qualified|unqualified>)

@ghost
Copy link
Author

ghost commented Jul 31, 2018

And maybe we can leave out the short (include_subdirs) syntax for now, just to make things explicit.

@ghost
Copy link
Author

ghost commented Jul 31, 2018

I changed the syntax and also implemented the support for C files in sub-directories, since it wasn't that hard. @rgrinberg I let you review the latest changes.

@ghost ghost requested a review from emillon July 31, 2018 17:01
@@ -57,6 +57,11 @@ allowed to write an explicit ``Foo`` module, in which case this will
be the interface of the library and you are free to expose only the
modules you want.

Note that by default libraries and other things that consume
OCaml/Reason modules only consume modules from the directory where the
stanza appear. In order to delcare a multi-directory library, you need
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: declare

stop when encountering a sub-directory that:

- is part of a different project (for instance when vendoring projects)
- contains ``(include_subdirs true)`` or ``(include_subdirs)``
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should be (include_subdirs unqualified)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

& the short syntax has been removed if I understand correctly.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Given that it's a new thing, it seems better to make things explicit

| Standalone -> [t]
| Group_root (lazy l) -> t :: l
| Group_part t ->
match t.kind with
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compiler seems to be doing fine, but I think adding begin/end around the nested match will make this easier to read. An alternative is to use deep pattern matching instead of the nested match, and move the assert false to the outer match.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used a deep pattern matching

src/jbuild.ml Outdated

let t =
enum
[ "unqualified", Unqualified
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that seems a bit weird is that there is no syntax for the default case. Would it be useful to have (include_subdirs no)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose. It especially makes sense if we plan to one day change the default to (include_subdirs qualified). I added no and reworked the documentation

Copy link
Member

@rgrinberg rgrinberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Just some comments that need to be updated first.


| Is_component_of_a_group_but_not_the_root of
Super_context.Dir_with_jbuild.t option
(* Sub-directory of a directory with [(include_subdirs true)] *)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commend needs to be updated as you've changed the argument for this stanza.


| Group_root of File_tree.Dir.t
* Super_context.Dir_with_jbuild.t
(* Directory with [(include_subdirs true)] *)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment as well

src/gen_rules.ml Outdated
Path.relative (Dir_contents.dir dc) fn :: acc
else
acc))
|> List.rev
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this reverse? I just see that we provide these as command line args, does the order matter there?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, it doesn't matter. Note that it's not even command line flags, it's just to specify dependencies

src/gen_rules.ml Outdated
| Some _ ->
match Dir_contents.kind (Dir_contents.get sctx ~dir) with
| Group_part root ->
SC.load_dir sctx ~dir:(Dir_contents.dir root)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come we only do this in the case when there are no stanzas in dir? Seems like we should let the paren that is part of multi dir lib regardless

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was done in the other gen_rules function. However, it's true that this code wasn't very clear and was starting to become spaghetti code, so I refactored it

jeremiedimino and others added 13 commits August 1, 2018 11:43
Signed-off-by: Jeremie Dimino <[email protected]>
Signed-off-by: Jeremie Dimino <[email protected]>
Signed-off-by: Jeremie Dimino <[email protected]>
Signed-off-by: Jeremie Dimino <[email protected]>
Signed-off-by: Jeremie Dimino <[email protected]>
_
Signed-off-by: Jeremie Dimino <[email protected]>
Signed-off-by: Jeremie Dimino <[email protected]>
Signed-off-by: Jeremie Dimino <[email protected]>
Signed-off-by: Jeremie Dimino <[email protected]>
Signed-off-by: Jeremie Dimino <[email protected]>
Signed-off-by: Jeremie Dimino <[email protected]>
Signed-off-by: Jeremie Dimino <[email protected]>
Signed-off-by: Jeremie Dimino <[email protected]>
Signed-off-by: Jeremie Dimino <[email protected]>
Copy link
Member

@rgrinberg rgrinberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Just need a CHANGES update

Signed-off-by: Jeremie Dimino <[email protected]>
@ghost ghost merged commit 282c0b3 into ocaml:master Aug 1, 2018
@ghost
Copy link
Author

ghost commented Aug 1, 2018

@bobot in the end we did add first class support for multi directory libraries

@emillon
Copy link
Collaborator

emillon commented Aug 1, 2018

Looks good, thanks!

rgrinberg added a commit to rgrinberg/opam-repository that referenced this pull request Aug 6, 2018
CHANGES:

- Fix lookup of command line specified files when `--root` is given. Previously,
  passing in `--root` in conjunction with `--workspace` or `--config` would not
  work correctly (ocaml/dune#997, @rgrinberg)

- Add support for customizing env nodes in workspace files. The `env` stanza is
  now allowed in toplevel position in the workspace file, or for individual
  contexts. This feature requires `(dune lang 1.1)` (ocaml/dune#1038, @rgrinberg)

- Add `enabled_if` field for aliases and tests. This field controls whether the
  test will be ran using a boolean expression language. (ocaml/dune#819, @rgrinberg)

- Make `name`, `names` fields optional when a `public_name`, `public_names`
  field is provided. (ocaml/dune#1041, fix ocaml/dune#1000, @rgrinberg)

- Interpret `X` in `--libdir X` as relative to `PREFIX` when `X` is relative
  (ocaml/dune#1072, fix ocaml/dune#1070, @diml)

- Add support for multi directory libraries by writing
  `(include_subdirs unqualified)` (ocaml/dune#1034, @diml)

- Add `(staged_pps ...)` to support staged ppx rewriters such as ones
  using the OCaml typer like `ppx_import` (ocaml/dune#1080, fix ocaml/dune#193, @diml)

- Use `-opaque` in the `dev` profile. This option trades off binary quality for
  compilation speed when compiling .cmx files. (ocaml/dune#1079, fix ocaml/dune#1058, @rgrinberg)

- Fix placeholders in `dune subst` documentation (ocaml/dune#1090, @emillon, thanks
  @trefis for the bug report)

- Add locations to errors when a missing binary in PATH comes from a dune file
  (ocaml/dune#1096, fixes ocaml/dune#1095, @rgrinberg)
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants