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

(root_module ..) field for libraries & executables #3825

Merged
merged 4 commits into from
Nov 23, 2020

Conversation

rgrinberg
Copy link
Member

@rgrinberg rgrinberg commented Sep 27, 2020

There's an inconvenient situation that can occur when one is trying to define a
module that clashes with a dependency's top level name. The end result is that
the dependency becomes shadowed and unusable. For example:

(library
 (name foo)
 (modules re)
 (libraries re))

Now the library re is inaccessible

The current workaround is to rename the module used internally, and then modify the library
interface file to get back the desired public name:

(library
 (name foo)
 (modules re0 foo)
 (libraries re))
$ cat foo.ml
module Re = Re0

However, this is still problematic because:

  • One has to use the crappy Re0 name everywhere
  • Generated documentation will still refer to Re0.

I propose the following fix instead:

(library
 (name foo)
 (extra_aliases (re re_unshadow))
 (libraries re))

Now, we can us the re library via the re_unshadow name, and we don't need to
modify our own names.

This feature is pretty simple, so I prototyped it immediately. However, it's not
completely robust yet as it doesn't work for executables. If people like it, I
can polish this PR.

@rgrinberg rgrinberg requested review from a user, nojb and aalekseyev September 27, 2020 21:13
@aalekseyev
Copy link
Collaborator

I like the feature in principle. I thought the name extra_aliases is a bit opaque, thought. I wonder if something like this would be easier to understand:

(library
  (name foo)
  (libraries (re as re_unshadow))
)

This way the dependencies in scope are still controlled solely by the libraries field, so I feel the potential for confusion is smaller.

@bobot
Copy link
Collaborator

bobot commented Sep 28, 2020

I like the as, but libraries could define more than one toplevel library, with names different from the name of the library.

For the overall need of the PR, I'm not against just sad, If ocaml would have a root directory we would do ROOT.Re. I think pxx and other code generator have the same need. I don't remember what is the current state in the OCaml typer. But perhaps Dune can propose something similar, we would define a module alias ROOT with all the toplevel symbols we can find. It seems hackish...

@aalekseyev
Copy link
Collaborator

aalekseyev commented Sep 28, 2020

I think we at jane street think of that as an anti-pattern, so I wouldn't be too sad if the syntax in that case would be verbose.
Something like

(libraries
  (foo (renaming (
    (foo_a foo_a_unshadow)
    (foo_b foo_b_unshadow)
   )))
)

I don't know if dune actually has enough information to check that a top-level module foo_a is indeed exposed by the library foo.
If it does, then I'm not even joking, the above seems fine to me. If not, then this seems bad because it will let users write some nonsensical stuff without any ability to check.

@rgrinberg
Copy link
Member Author

I like the as, but libraries could define more than one toplevel library, with names different from the name of the library.

Yes, this is why a separate field is a little more flexible. However, I don't think it's very important to extend full support for unwrapped libraries. I don't know of many unwrapped libraries left in the wild and it's a poor practice to create new ones. So I think as is fine.

For the overall need of the PR, I'm not against just sad, If ocaml would have a root directory we would do ROOT.Re. I think pxx and other code generator have the same need. I don't remember what is the current state in the OCaml typer. But perhaps Dune can propose something similar, we would define a module alias ROOT with all the toplevel symbols we can find. It seems hackish

I think that's an excellent idea for a proposal. However, I wonder if we can use ROOT or any other valid name since it would break existing code. In any case, I'd be very interested to see such a proposal.

I don't know if dune actually has enough information to check that a top-level module foo_a is indeed exposed by the library foo.
If it does, then I'm not even joking, the above seems fine to me. If not, then this seems bad because it will let users write some nonsensical stuff without any ability to check.

It has enough information if foo is written in dune. Tbh, we could probably extend this checking to non dune libs with some ocamlobjinfo hacks, but it doesn't seem worth it. I don't think it's all that important to verify the names the user writes anyway, they will get a compilation if they mess it up regardless.

By the way, one practical issue with constructors is that it's a bit confusing to handle select with them. For example, how would you interpret:

(select bar.ml from
 (foo (renaming (
    (foo_a foo_a_unshadow)
    (foo_b foo_b_unshadow))) -> bar.a.ml)
 (!foo -> bar.b.ml))

I don't think we are going to handle conditional shadowing. It's a bit painful to implement. There's also going to be some awkward syntax with:

(re_export (foo (renaming (..)))

Which is regrettable.

@bobot
Copy link
Collaborator

bobot commented Sep 29, 2020

Tbh, we could probably extend this checking to non dune libs with some ocamlobjinfo hacks, but it doesn't seem worth it

I agree but just wondering if just listing the .cmi is sufficent since it is just about typing not linking.

@rgrinberg
Copy link
Member Author

I've discussed this feature with Jeremie and here are some quick suggestions:

  • Using as reads well, but isn't uniform with the syntax. We need the first token to be a constructor name for consistency. So let's pick something like (rename re -> re_unshadow). Where rename is the constructor name, and I've replaced as with an arrow as we already use -> in select and (rename .. as ..) sounds unnatural.

  • Supporting unwrapped modules is better done as follows: Consider an unwrapped library foo with modules bar and baz. Using this library with (rename foo -> foo_blah) will add the following aliases:

module Foo_blah = struct
  module Bar = Bar
  module Baz = Baz
end

We'll need to generate the list of modules for foo, but that's quite easy. In essence, this allows us to use unwrapped libraries as if they are wrapped. That seems quite good to me.

  • There's no need to support rename and export simultaneously.

@bobot, @aalekseyev are you ok with these tweaks?

@aalekseyev
Copy link
Collaborator

Yeah, that sounds good.
Additionally, I think we should shadow the original names to make them unusable when this construction is used.

@rgrinberg
Copy link
Member Author

I pushed a commit that implements the new proposal. For now I've left off the implementation for unwrapped libraries. It's a little tough to add, and I have no use it for it myself at the moment.

Copy link
Collaborator

@emillon emillon left a comment

Choose a reason for hiding this comment

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

I think it's a neat feature and the limitation to wrapped libraries seems completely fair.

@rgrinberg
Copy link
Member Author

Thanks. I'll give @bobot and @aalekseyev a chance to see if they are happy with the limitation and my implementation of this feature before merging.

@rgrinberg rgrinberg changed the title [RFC] Extra aliases to unshadow libraries [RFC] (rename .. -> ..) syntax for dependencies Oct 12, 2020
Copy link
Collaborator

@aalekseyev aalekseyev left a comment

Choose a reason for hiding this comment

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

I added some new tests and CR comments to rename-deps.t.
Please have a look.

In particular I think we should be clear from the very beginning what the shadowing behavior of renaming is (what if you rename a library and thereby shadow another library? should renaming that library still work?).

@@ -114,10 +115,31 @@ let bin_annot t = t.bin_annot

let context t = Super_context.context t.super_context

type rename =
{ new_name : Module_name.t
; toplevel_modules : Module_name.t list
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why have this as a list if it's required to be a singleton list?

Copy link
Member Author

Choose a reason for hiding this comment

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

When I wrote it, I expected to support unwrapped libraries. For such libraries, there are multiple toplevel modules. Since we don't support them yet, it doesn't need to be a list. Should we simplify the code for the use case that we support? Or keep the more general data type in anticipation?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't anticipate that anybody will need it any time soon, and simplifying the code to work with a simpler data structure seems worth it to me. It will probably make it more clear that the downstream code is reasonable.

src/dune_rules/lib_dep.ml Outdated Show resolved Hide resolved
src/dune_rules/modules.mli Outdated Show resolved Hide resolved
@aalekseyev
Copy link
Collaborator

Regarding the limitation to wrapped libraries only, that seems reasonable to me.

@rgrinberg rgrinberg force-pushed the extra-aliases branch 2 times, most recently from 58001a5 to d453d69 Compare October 14, 2020 18:20
@rgrinberg
Copy link
Member Author

@aalekseyev should be ready now

@rgrinberg
Copy link
Member Author

rgrinberg commented Oct 14, 2020

Something like a user written Root would work if there was a way to specify that it has no module dependencies and we properly sandboxed module compilation. We'd have:

(library
  (module_deps
   (root -> ()))

and then a user written root could be used to do whatever renames are necessary. We'd just need to make sure that -I isn't polluted by other modules in the library.

Copy link
Collaborator

@bobot bobot 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 ok with the limitation to wrapped thing.

doc/concepts.rst Outdated Show resolved Hide resolved
@rgrinberg
Copy link
Member Author

@bobot @aalekseyev should be good now. I've switched to the simpler root_module proposal, with full support for unwrapped and non dune libraries. Leaving them unsupported seemed wrong in the end.

Just need to update the docs.

@rgrinberg rgrinberg changed the title [RFC] (rename .. -> ..) syntax for dependencies (root_module ..) field for libraries & executables Nov 16, 2020
@rgrinberg
Copy link
Member Author

@bobot @aalekseyev does one of you have time to review this PR?

src/dune_rules/compilation_context.ml Show resolved Hide resolved
src/dune_rules/modules.mli Outdated Show resolved Hide resolved
doc/dune-files.rst Show resolved Hide resolved
@rgrinberg rgrinberg force-pushed the extra-aliases branch 2 times, most recently from 3452100 to 1d83158 Compare November 21, 2020 02:53
(rename lib -> new_nam) to rename dependencies when they are shadowed

Signed-off-by: Rudi Grinberg <[email protected]>
This feature is both simpler and more widely useful

Signed-off-by: Rudi Grinberg <[email protected]>
Signed-off-by: Rudi Grinberg <[email protected]>
* Hide modules with __ root_module
* Populate module lists manually for some built in libraries

Signed-off-by: Rudi Grinberg <[email protected]>
@rgrinberg rgrinberg merged commit 5cf80f1 into ocaml:master Nov 23, 2020
rgrinberg added a commit to rgrinberg/opam-repository that referenced this pull request Jan 13, 2021
…ne-action-plugin, dune-private-libs and dune-glob (2.8.0)

CHANGES:

- `dune rules` accepts aliases and other non-path rules (ocaml/dune#4063, @mrmr1993)

- Action `(diff reference test_result)` now accept `reference` to be absent and
  in that case consider that the reference is empty. Then running `dune promote`
  will create the reference file. (ocaml/dune#3795, @bobot)

- Ignore special files (BLK, CHR, FIFO, SOCKET), (ocaml/dune#3570, fixes ocaml/dune#3124, ocaml/dune#3546,
  @ejgallego)

- Experimental: Simplify loading of additional files (data or code) at runtime
  in programs by introducing specific installation sites. In particular it allow
  to define plugins to be installed in these sites. (ocaml/dune#3104, ocaml/dune#3794, fixes ocaml/dune#1185,
  @bobot)

- Move all temporary files created by dune to run actions to a single directory
  and make sure that actions executed by dune also use this directory by setting
  `TMPDIR` (or `TEMP` on Windows). (ocaml/dune#3691, fixes ocaml/dune#3422, @rgrinberg)

- Fix bootstrap script with custom configuration. (ocaml/dune#3757, fixes ocaml/dune#3774, @marsam)

- Add the `executable` field to `inline_tests` to customize the compilation
  flags of the test runner executable (ocaml/dune#3747, fixes ocaml/dune#3679, @lubegasimon)

- Add `(enabled_if ...)` to `(copy_files ...)` (ocaml/dune#3756, @nojb)

- Make sure Dune cleans up the status line before exiting (ocaml/dune#3767,
  fixes ocaml/dune#3737, @alan-j-hu)

- Add `{gitlab,bitbucket}` as options for defining project sources with `source`
  stanza `(source (<host> user/repo))` in the `dune-project` file.  (ocaml/dune#3813,
  @rgrinberg)

- Fix generation of `META` and `dune-package` files when some targets (byte,
  native, dynlink) are disabled. Previously, dune would generate all archives
  for regardless of settings. (ocaml/dune#3829, ocaml/dune#4041, @rgrinberg)

- Do not run ocamldep to for single module executables & libraries. The
  dependency graph for such artifacts is trivial (ocaml/dune#3847, @rgrinberg)

- Fix cram tests inside vendored directories not being interpreted correctly.
  (ocaml/dune#3860, fixes ocaml/dune#3843, @rgrinberg)

- Add `package` field to private libraries. This allows such libraries to be
  installed and to be usable by other public libraries in the same project
  (ocaml/dune#3655, fixes ocaml/dune#1017, @rgrinberg)

- Fix the `%{make}` variable on Windows by only checking for a `gmake` binary
  on UNIX-like systems as a unrelated `gmake` binary might exist on Windows.
  (ocaml/dune#3853, @kit-ty-kate)

- Fix `$ dune install` modifying the build directory. This made the build
  directory unusable when `$ sudo dune install` modified permissions. (fix
  ocaml/dune#3857, @rgrinberg)

- Fix handling of aliases given on the command line (using the `@` and `@@`
  syntax) so as to correctly handle relative paths. (ocaml/dune#3874, fixes ocaml/dune#3850, @nojb)

- Allow link time code generation to be used in preprocessing executable. This
  makes it possible to use the build info module inside the preprocessor.
  (ocaml/dune#3848, fix ocaml/dune#3848, @rgrinberg)

- Correctly call `git ls-tree` so unicode files are not quoted, this fixes
  problems with `dune subst` in the presence of unicode files. Fixes ocaml/dune#3219
  (ocaml/dune#3879, @ejgallego)

- `dune subst` now accepts common command-line arguments such as
  `--debug-backtraces` (ocaml/dune#3878, @ejgallego)

- `dune describe` now also includes information about executables in addition to
  that of libraries. (ocaml/dune#3892, ocaml/dune#3895, @nojb)

- instrumentation backends can now receive arguments via `(instrumentation
  (backend <name> <args>))`. (ocaml/dune#3906, ocaml/dune#3932, @nojb)

- Tweak auto-formatting of `dune` files to improve readability. (ocaml/dune#3928, @nojb)

- Add a switch argument to opam when context is not default. (ocaml/dune#3951, @tmattio)

- Avoid pager when running `$ git diff` (ocaml/dune#3912, @AltGr)

- Add `(root_module ..)` field to libraries & executables. This makes it
  possible to use library dependencies shadowed by local modules (ocaml/dune#3825,
  @rgrinberg)

- Allow `(formatting ...)` field in `(env ...)` stanza to set per-directory
  formatting specification. (ocaml/dune#3942, @nojb)

- [coq] In `coq.theory`, `:standard` for the `flags` field now uses the
  flags set in `env` profile flags (ocaml/dune#3931 , @ejgallego @rgrinberg)

- [coq] Add `-q` flag to `:standard` `coqc` flags , fixes ocaml/dune#3924, (ocaml/dune#3931 , @ejgallego)

- Add support for Coq's native compute compilation mode (@ejgallego, ocaml/dune#3210)

- Add a `SUFFIX` directive in `.merlin` files for each dialect with no
  preprocessing, to let merlin know of additional file extensions (ocaml/dune#3977,
  @vouillon)

- Stop promoting `.merlin` files. Write per-stanza Merlin configurations in
  binary form. Add a new subcommand `dune ocaml-merlin` that Merlin can use to
  query the configuration files. The `allow_approximate_merlin` option is now
  useless and deprecated. Dune now conflicts with `merlin < 3.4.0` and
  `ocaml-lsp-server < 1.3.0` (ocaml/dune#3554, @voodoos)

- Configurator: fix a bug introduced in 2.6.0 where the configurator V1 API
  doesn't work at all when used outside of dune. (ocaml/dune#4046, @aalekseyev)

- Fix `libexec` and `libexec-private` variables. In cross-compilation settings,
  they now point to the file in the host context. (ocaml/dune#4058, fixes ocaml/dune#4057,
  @TheLortex)

- When running `$ dune subst`, use project metadata as a fallback when package
  metadata is missing. We also generate a warning when `(name ..)` is missing in
  `dune-project` files to avoid failures in production builds.

- Remove support for passing `-nodynlink` for executables. It was bypassed in
  most cases and not correct in other cases in particular on arm32.
  (ocaml/dune#4085, fixes ocaml/dune#4069, fixes ocaml/dune#2527, @emillon)

- Generate archive rules compatible with 4.12. Dune longer attempt to generate
  an archive file if it's unnecessary (ocaml/dune#3973, fixes ocaml/dune#3766, @rgrinberg)

- Fix generated Merlin configurations when multiple preprocessors are defined
  for different modules in the same folder. (ocaml/dune#4092, fixes ocaml/dune#2596, ocaml/dune#1212 and
  ocaml/dune#3409, @voodoos)

- Add the option `use_standard_c_and_cxx_flags` to `dune-project` that 1.
  disables the unconditional use of the `ocamlc_cflags` and `ocamlc_cppflags`
  from `ocamlc -config` in C compiler calls, these flags will be present in the
  `:standard` set instead; and 2. enables the detection of the C compiler family
  and populates the `:standard` set of flags with common default values when
  building CXX stubs. (ocaml/dune#3875, ocaml/dune#3802, fix ocaml/dune#3718 and ocaml/dune#3528, @voodoos)
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.

4 participants