Skip to content

Commit

Permalink
Add tests of building toplevel that can load plugins
Browse files Browse the repository at this point in the history
One test show successful plugin loading in a toplevel.
The other test shows the error when you use
Dynlink.loadfile (via dune-site.dynlink) in a toplevel.

Also, handle moved load_file function.
Prior to OCaml 4.13.0, the load_file function was in Topdirs.
Starting with OCaml 4.13.0, the load_file function moved to
Toploop. In order to find it open both these modules,
suppressing the warning for unused open, and then
reference load_file unqualified.

Also add change log entry.

Signed-off-by: Richard L Ford <[email protected]>
  • Loading branch information
richardlford committed Mar 6, 2023
1 parent c5ea567 commit 9078cac
Show file tree
Hide file tree
Showing 4 changed files with 388 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
Unreleased
----------

- Adds support for loading plugins in toplevels (#6082, fixes #6081,
@ivg, @richardlford)

- Support commands that output 8-bit and 24-bit colors in the terminal (#7188,
@Alizter)

Expand Down
5 changes: 4 additions & 1 deletion otherlibs/site/src/plugins/linker/toplevel/linker.ml
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
open Topdirs [@@ocaml.warning "-33"]
open Toploop [@@ocaml.warning "-33"]

let load filename =
let buf = Buffer.create 16 in
let ppf = Format.formatter_of_buffer buf in
match Toploop.load_file ppf filename with
match load_file ppf filename with
| true -> ()
| false ->
Format.pp_print_flush ppf ();
Expand Down
185 changes: 185 additions & 0 deletions test/blackbox-tests/test-cases/toplevel-plugin-fail.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
Testsuite for (toplevel that loads plugins). This version
uses dune-site.dynlink which uses Dynlink.loadfile.
This is not allowed in top-levels, so it fails.

$ cat > dune-project <<EOF
> (lang dune 3.7)
> (using dune_site 0.1)
> (name top_with_plugins)
> (wrapped_executables false)
> (map_workspace_root false)
>
> (package
> (name top_with_plugins)
> (sites (lib top_plugins)))
> EOF

$ cat > dune <<EOF
> (executable
> (public_name top_with_plugins)
> (name top_with_plugins)
> (modes byte)
> (flags :standard -safe-string)
> (modules sites top_with_plugins)
> (link_flags (-linkall))
> (libraries compiler-libs.toplevel
> top_with_plugins.register dune-site dune-site.plugins
> dune-site.dynlink findlib.dynload))
>
> (library
> (public_name top_with_plugins.register)
> (modes byte)
> (name registration)
> (modules registration))
>
> (generate_sites_module
> (module sites)
> (plugins (top_with_plugins top_plugins)))
> EOF

$ cat > top_with_plugins.ml <<EOF
> let main () =
> print_endline "\nMain app really starts...";
> (* load all the available plugins *)
> Sites.Plugins.Top_plugins.load_all ();
> print_endline "Main app after loading plugins...";
> (* Execute the code registered by the plugins *)
> print_endline "Main app executing registered plugins...";
> Queue.iter (fun f -> f ()) Registration.todo;
> print_endline "Main app after executing registered plugins...";
> exit (Topmain.main ())
>
> let () =
> main()
> EOF

$ cat > registration.ml <<EOF
> let todo : (unit -> unit) Queue.t = Queue.create ()
> let register f =
> print_endline "In register";
> Queue.add f todo;
> print_endline "Done in register";
> EOF

$ mkdir plugin1
$ cat > plugin1/dune-project <<EOF
> (lang dune 3.7)
> (using dune_site 0.1)
>
> (generate_opam_files true)
> (wrapped_executables false)
> (map_workspace_root false)
> (package
> (name top-plugin1))
> EOF

$ cat > plugin1/dune <<EOF
> (library
> (public_name top-plugin1.plugin1_impl)
> (modes byte)
> (name plugin1_impl)
> (modules plugin1_impl)
> (libraries top_with_plugins.register))
>
> (plugin
> (name plugin1)
> (libraries top-plugin1.plugin1_impl)
> (site (top_with_plugins top_plugins)))
> EOF

$ cat > plugin1/plugin1_impl.ml <<EOF
> let myfun () =
> print_endline "Plugin1 is doing something..."
>
> let () =
> print_endline "Registration of Plugin1";
> Registration.register myfun;
> print_endline "Done with registration of Plugin1";
> EOF

$ mkdir plugin2
$ cat > plugin2/dune-project <<EOF
> (lang dune 3.7)
> (using dune_site 0.1)
>
> (generate_opam_files true)
> (wrapped_executables false)
> (map_workspace_root false)
> (package
> (name top-plugin2))
> EOF

$ cat > plugin2/dune <<EOF
> (library
> (public_name top-plugin2.plugin2_impl)
> (modes byte)
> (name plugin2_impl)
> (modules plugin2_impl)
> (libraries top_with_plugins.register))
>
> (plugin
> (name plugin2)
> (libraries top-plugin2.plugin2_impl)
> (site (top_with_plugins top_plugins)))
> EOF

$ cat > plugin2/plugin2_impl.ml <<EOF
> let myfun () =
> print_endline "Plugin2 is doing something..."
>
> let () =
> print_endline "Registration of Plugin2";
> Registration.register myfun;
> print_endline "Done with registration of Plugin2";
> EOF

$ dune build --display short @all 2>&1 | dune_cmd sanitize
ocamldep .top_with_plugins.eobjs/top_with_plugins.impl.d
ocamlc .registration.objs/byte/registration.{cmi,cmo,cmt}
ocamlc .top_with_plugins.eobjs/byte/dune_site__Dune_site_data.{cmo,cmt}
ocamlc .top_with_plugins.eobjs/byte/dune_site_plugins__Dune_site_plugins_data.{cmo,cmt}
ocamlc .top_with_plugins.eobjs/byte/findlib_initl.{cmi,cmo,cmt}
ocamldep .top_with_plugins.eobjs/sites.impl.d
ocamlc registration.cma
ocamlc plugin1/.plugin1_impl.objs/byte/plugin1_impl.{cmi,cmo,cmt}
ocamlc plugin2/.plugin2_impl.objs/byte/plugin2_impl.{cmi,cmo,cmt}
ocamlc .top_with_plugins.eobjs/byte/sites.{cmi,cmo,cmt}
ocamldep .top_with_plugins.eobjs/top_with_plugins.intf.d
ocamlc plugin1/plugin1_impl.cma
ocamlc plugin2/plugin2_impl.cma
ocamlc .top_with_plugins.eobjs/byte/top_with_plugins.{cmi,cmti}
ocamlc .top_with_plugins.eobjs/byte/top_with_plugins.{cmo,cmt}
ocamlc top_with_plugins.bc
ocamlc top_with_plugins.exe
$ dune install --prefix _install --display short
Installing _install/lib/top-plugin1/META
Installing _install/lib/top-plugin1/dune-package
Installing _install/lib/top-plugin1/plugin1_impl/plugin1_impl.cma
Installing _install/lib/top-plugin1/plugin1_impl/plugin1_impl.cmi
Installing _install/lib/top-plugin1/plugin1_impl/plugin1_impl.cmt
Installing _install/lib/top-plugin1/plugin1_impl/plugin1_impl.ml
Installing _install/lib/top_with_plugins/top_plugins/plugin1/META
Installing _install/lib/top-plugin2/META
Installing _install/lib/top-plugin2/dune-package
Installing _install/lib/top-plugin2/plugin2_impl/plugin2_impl.cma
Installing _install/lib/top-plugin2/plugin2_impl/plugin2_impl.cmi
Installing _install/lib/top-plugin2/plugin2_impl/plugin2_impl.cmt
Installing _install/lib/top-plugin2/plugin2_impl/plugin2_impl.ml
Installing _install/lib/top_with_plugins/top_plugins/plugin2/META
Installing _install/lib/top_with_plugins/META
Installing _install/lib/top_with_plugins/dune-package
Installing _install/lib/top_with_plugins/register/registration.cma
Installing _install/lib/top_with_plugins/register/registration.cmi
Installing _install/lib/top_with_plugins/register/registration.cmt
Installing _install/lib/top_with_plugins/register/registration.ml
Installing _install/bin/top_with_plugins
$ export OCAMLPATH=$PWD/_install/lib
$ ./_install/bin/top_with_plugins -no-version <<EOF
> 2+2;;
> #quit;;
> EOF

Main app really starts...
Fatal error: exception Invalid_argument("The dynlink.cma library cannot be used inside the OCaml toplevel")
[2]

Loading

0 comments on commit 9078cac

Please sign in to comment.