-
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 support for loading libraries from toplevel #2952
Conversation
mbernat
commented
Dec 5, 2019
- Start work on loading dune libraries from toplevel
- Finish draft version of toplevel-init-file
- Add toplevel loading script
- Add install stanza for dune.mlt
- Remove dependency on Unix in the toplevel script
- Add documentation for toplevel integration
- Add a very basic test for toplevel-init-file
Hi! |
|
||
Second, to enhance usability, dune also provides a toplevel script, which does the above | ||
manual work for you. To use it, make sure to have `topfind` available in your toplevel by | ||
invoking `#use "topfind";;`. Afterwards you can run `#use "dune";;` and your |
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.
Do we actually need topfind
for this? At the moment we only generate #directory
and load
directives and these are available by default.
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.
My bad, I thought the Toplevel
module comes from topfind
, but apparently it's available by default. I'll update the docs.
| (name test) | ||
#directory "$TESTCASE_ROOT/_build/default/.test.objs/byte";; | ||
#directory "$TESTCASE_ROOT/_build/default/.test.objs/native";; | ||
#load "$TESTCASE_ROOT/_build/default/test.cma";; |
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.
Could you add a test testing the whole story? i.e. doing #use "..."
from a toplevel? And also one testing an error case, just to see what the error looks like. You can do the following:
$ ocaml -stdin <<EOF
> #use "..."
> ...use code that was just loaded...
> EOF
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.
Okay, I have several questions here:
- Even outside of tests, doing just
#use "dune.top"
doesn't work inocaml
(it can't find the file), but it does inutop
. Isocaml
usingOCAML_TOPLEVEL_PATH
or does it resolve paths in some other fashion? I wasn't able to figure this out by googling and I only use plainocaml
rarely. But of course, we want to support it, that's the whole point of this PR! :) - When running the tests,
OCAML_TOPLEVEL_PATH
still points to my user-local directory. Am I supposed to runexport OCAML_TOPLEVEL_PATH="_build/install/default/lib/toplevel/dune.top"
or is there a nicer way to refer to the local installation we're testing? - What error case do you have in mind?
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 did a bit of research and right now, packages that install toplevel scripts such as ocamlfind
or down
install them in both <libdir>/toplevel
and <libdir>/ocaml
. The latter is immediately available in the toplevel.
OCAML_TOPLEVEL_PATH
is only used by utop and not by the vanilla toplevel. However, since 4.08.0, there is a new variable OCAMLTOP_INCLUDE_PATH
that is supported natively. I opened a ticket to discuss setting this variable in opam: ocaml/opam-repository#15547. Let's see what happens there. At worse we could install dune.top
in <libdir>/ocaml
as well.
On the dune side, dune already extends a few environment variable so that when running dune or other tools inside dune, various tools will look for things in _build/install
in addition to things installed globally. We should do this for OCAMLTOP_INCLUDE_PATH
as well. More precisely, you can look for extend_var "OCAMLPATH"
in src/dune/context.ml
and add the following line: extend_var "OCAMLTOP_INCLUDE_PATH" (Path.relative local_lib_path "toplevel")
.
Then, in the test #use "dune.top"
will work out of the box with OCaml >= 4.08. We run the testsuite with 4.09 in the CI so having the test only work with OCaml >= 4.08 is fine.
Regarding the error case, I was thinking of a case where compilation fails. For instance, what does the user see if one of the ml file that dune
tries to compile doesn't compile? Or if a dune
file is invalid.
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.
Thanks for all this great info and sorry about my (usual) late response!
Here's what I've done for now. Since there are still several unknowns, I've also had to make several questionable choices.
- I've added
extend_var
as you suggested. - I've also installed
dune.top
into<libdir>/ocaml
, since we can't rely on the opam PR being finished anytime soon (right?). - I've put back the documentation bit that people need to do
#use "topfind"
first. Indeed, it turns out that without that I don't haveToplevel
module available indune.top
(I missed this because I had#use "topfind"
in my.ocamlinit
). Do you think it's possible to remove the dependency on#use "topfind"
or, even better, on theToplevel
module? - I've added a few tests, including failures. However, the failures expose compilation internals and I'm not sure whether the tests won't break randomly: https://github.com/mbernat/dune/blob/toplevel/test/blackbox-tests/test-cases/toplevel-integration/run.t#L35
Please let me know what you think about this.
In case I don't get around to this again for a week, I wish you a very Merry Christmas and thanks again for all your help! 🎄
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.
Looking at those errors more closely, they actually reference my local paths, so they're definitely wrong.
How do you handle test errors containing local paths? And more importantly, what kind of error behavior should we expect from running dune toplevel-init-file
and dune.top
?
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.
Just back from holidays. Thanks, hope you had a nice Christmas too!
For 2, that seems reasonable.
For 3, we can do the same as the topfind
script, i.e. temporary add the +compiler-libs
directory:
#directory "+compiler-libs";;
...
#remove_directory "+compiler-libs";;
For 4, I was surprised by this output as normally Dune doesn't print the full compilation command line if it detects that the error messages already points to a file, i.e. is of the form File "xxx", ...
. After a bit of digging, I found that we always print the full compilation command when dune is run inside dune because of this in src/dune/config.ml
:
let show_full_command_on_error () =
inside_dune || inside_ci || !Clflags.always_show_command_line
@rgrinberg, I don't remember the details. Was the inside_dune ||
part intentional?
Thanks! I left a few comments but overall it looks good to me. I can think of one issue related to findlib: if the user does The solution for this involves detecting if findlib is available and if yes issuing |
Thank you! I fixed what I could and asked about what I couldn't. I agree about the Btw, what is the relation between |
bin/dune
Outdated
@@ -23,6 +23,11 @@ | |||
(files | |||
(dune.exe as dune))) | |||
|
|||
(install | |||
(section lib_root) |
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.
Just realised that there is a toplevel
section available. We might as well use it and replace (dune.mlt as toplevel/dune)
by (dune.mlt as dune.top)
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.
Also, while we are it we could name the file dune.top
directly in the source tree.
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.
Makes sense, done.
Yes, that's the idea. But only if
Any dune library with a public name will be installed on the system as a findlib library. Findlib libraries can be consumed in dune projects the same way dune libraries can. So in short, findlib libraries are essentially equivalent to dune public libraries. On the other hand, dune private libraries are not visible to findlib. Although, it would be nice to change that so you could use dune private libraries the same way you use findlib libraries. Indeed, this would be especially useful here. But this is quite a bit more work. |
BTW, I don't see your latest changes. Did you push them? |
> EOF | ||
$ cat >dune <<EOF | ||
> (library | ||
> (name test) |
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.
1 space indentation please
src/dune/toplevel.ml
Outdated
let print_toplevel_init_file ~include_paths ~files_to_load = | ||
let includes = Path.Set.to_list include_paths in | ||
List.iter includes ~f:(fun p -> | ||
print_endline("#directory \"" ^ Path.to_absolute_filename p ^ "\";;")); |
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.
Please format your code with ocamlformat. $ make fmt
does taht for you
bin/toplevel_init_file.ml
Outdated
|
||
let link_deps link ~lib_config = | ||
List.map link ~f:(fun t -> Dune.Lib.link_deps t Dune.Link_mode.Byte lib_config) | ||
|> List.flatten |
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.
Can you use List.concat_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.
In fact, this function becomes unnecessary if it's just a call to concat_map
bin/toplevel_init_file.ml
Outdated
let* setup = Import.Main.setup common in | ||
let sctx = | ||
Dune.Context_name.Map.find setup.scontexts Dune.Context_name.default | ||
|> Option.value_exn |
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.
This should take a context argument like $ dune utop
.
src/dune/utop.mli
Outdated
@@ -8,4 +8,6 @@ val utop_exe : string | |||
|
|||
val is_utop_dir : Path.Build.t -> bool | |||
|
|||
val libs_under_dir : Super_context.t -> db:Lib.DB.t -> dir:Import.Path.t -> Lib.L.t |
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's not use Import
in the mli. It should just be Path.t
and Stdune
be opened.
bin/toplevel_init_file.ml
Outdated
let files_to_load = | ||
List.filter files ~f:(fun p -> | ||
match Path.extension p with | ||
| ".cma" | ".cmo" -> true |
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.
Should be Mode.compiled_lib_ext
and Cm_kind.ext
.
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 left some minor comments, but overall, the feature looks fine. Thank you for your hard work.
Was it discussed to have these .init scripts be generated by targets? It seems like it makes them usable in a few more contexts. Such as the ability to be auto updated in watch mode, and the ability to launch multiple top levels.
Please sign your commits by the way. See CONTRIBUTING.md for how |
All my commits are signed. Am I missing something? |
Hmm well the DCO bot isn’t happy. I’ll have a closer look later I guess |
I thought about it, but it would slightly slow down all builds systematically while the current method doesn't disturb existing builds. We can also launch multiple top levels. For watch mode, I'm not too sure what would be different. I mean, in any case the user has to manually restart the toplevel. And when they do so, they'll get the latest updates. |
@mbernat your commits are GPG signed. This is fine, but this is not what we require for Dune contributions. We need the DCO sign-off. |
@mbernat gentle ping, the signature is the last bit we need to merge this work :) BTW, I recently added support for |
@diml Sorry about the long silence, I have had very little energy for contributions because of a new job these past few months. I signed my commit. The DCO bot is happy, but the AppVeyor isn't. IIRC there were issues with the tests that we haven't resolved. |
No problem, hope your new job is going well! Thanks for signing the commits, I'll do the finishing touches. We are doing some other work on the toplevel so I'd like to merge this soon. |
I rebased the feature and replaced the # #use_output "dune toplevel-init-file" |
The command name looks a bit long to me now, I'm wondering if it shouldn't be just # #use_output "dune top" What do others think? |
* Start work on loading dune libraries from toplevel * Finish draft version of toplevel-init-file * Add toplevel loading script * Add install stanza for dune.top * Remove dependency on Unix in the toplevel script * Add documentation for toplevel integration * Add a very basic test for toplevel-init-file Signed-off-by: Marek Bernat <marek.bernat@gmail.com>
Signed-off-by: Jeremie Dimino <jeremie@dimino.org>
Signed-off-by: Jeremie Dimino <jeremie@dimino.org>
Signed-off-by: Jeremie Dimino <jeremie@dimino.org>
Alright, I renamed the command |
Add a "dune top" command one is expected to call via: # #use_output "dune top";; In the toplevel. This command should work in any toplevel.
…lugin, dune-private-libs and dune-glob (2.5.0) CHANGES: - Add a `--release` option meaning the same as `-p` but without the package filtering. This is useful for custom `dune` invocation in opam files where we don't want `-p` (ocaml/dune#3260, @diml) - Fix a bug introduced in 2.4.0 causing `.bc` programs to be built with `-custom` by default (ocaml/dune#3269, fixes ocaml/dune#3262, @diml) - Allow contexts to be defined with local switches in workspace files (ocaml/dune#3265, fix ocaml/dune#3264, @rgrinberg) - Delay expansion errors until the rule is used to build something (ocaml/dune#3261, fix ocaml/dune#3252, @rgrinberg, @diml) - [coq] Support for theory dependencies and compositional builds using new field `(theories ...)` (ocaml/dune#2053, @ejgallego, @rgrinberg) - From now on, each version of a syntax extension must be explicitely tied to a minimum version of the dune language. Inconsistent versions in a `dune-project` will trigger a warning for version <=2.4 and an error for versions >2.4 of the dune language. (ocaml/dune#3270, fixes ocaml/dune#2957, @voodoos) - [coq] Bump coq lang version to 0.2. New coq features presented this release require this version of the coq lang. (ocaml/dune#3283, @ejgallego) - Prevent installation of public executables disabled using the `enabled_if` field. Installation will now simply skip such executables instead of raising an error. (ocaml/dune#3195, @voodoos) - `dune upgrade` will now try to upgrade projects using versions <2.0 to version 2.0 of the dune language. (ocaml/dune#3174, @voodoos) - Add a `top` command to integrate dune with any toplevel, not just utop. It is meant to be used with the new `#use_output` directive of OCaml 4.11 (ocaml/dune#2952, @mbernat, @diml) - Allow per-package `version` in generated `opam` files (ocaml/dune#3287, @toots) - [coq] Introduce the `coq.extraction` stanza. It can be used to extract OCaml sources (ocaml/dune#3299, fixes ocaml/dune#2178, @rgrinberg) - Load ppx rewriters in dune utop and add pps field to toplevel stanza. Ppx extensions will now be usable in the toplevel (ocaml/dune#3266, fixes ocaml/dune#346, @stephanieyou) - Add a `(subdir ..)` stanza to allow evaluating stanzas in sub directories. (ocaml/dune#3268, @rgrinberg) - Fix a bug preventing one from running inline tests in multiple modes (ocaml/dune#3352, @diml) - Allow the use of the `%{profile}` variable in the `enabled_if` field of the library stanza. (ocaml/dune#3344, @mrmr1993) - Allow the use of `%{ocaml_version}` variable in `enabled_if` field of the library stanza. (ocaml/dune#3339, @voodoos) - Fix dune build freezing on MacOS when cache is enabled. (ocaml/dune#3249, fixes #ocaml/dune#2973, @artempyanykh)