this repo has no description

feat: add OxCaml dual-compiler support for js_top_worker and odoc

Add support for building js_top_worker and odoc with both the standard
OCaml 5.4 compiler and the OxCaml 5.2.0+ox compiler using cppo
conditional compilation and dual dune stanzas.

js_top_worker changes:
- Bump dune-project to 3.21 for %{ocaml-config:ox} support
- Add dual library stanzas gated by (enabled_if %{ocaml-config:ox})
- Add cppo guards for OxCaml API differences:
- Compilation_unit.Name.t vs string for persistent loader
- Env.report_error ~level:0 (extra parameter)
- Language_extension.set_universe_and_enable_all (oxcaml-only)
- Unit_info.make ~for_pack_prefix (extra parameter)
- Typemod.type_implementation (extra Compilation_unit arg)
- Gate ppx_deriving_rpc with (not %{ocaml-config:ox})

odoc changes:
- Apply upstream oxcaml PR #1399 (art-w/upstream-oxcaml)
- Bump dune-project to 3.21
- Add dual stanzas in loader, model, xref2, odoc, syntax_highlighter
- Add cppo OXCAML guards for compiler API differences
- Support OxCaml features: modes, layouts, labeled tuples, iarray,
unboxed records, module type strengthening, polymorphic arguments,
call position arguments, Import_info.t, Compilation_unit.t

Verified end-to-end: scrollycode demos generate HTML and the
interactive playground evaluates OCaml code in the browser with
both compiler switches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+118 -9
+1 -1
dune-project
··· 1 - (lang dune 3.10) 1 + (lang dune 3.21) 2 2 (name js_top_worker) 3 3 (version 0.0.1) 4 4 (using directory-targets 0.1)
+3 -3
idl/dune
··· 40 40 (name js_top_worker_rpc_def) 41 41 (modules toplevel_api) 42 42 (enabled_if 43 - (>= %{ocaml_version} 4.12)) 43 + (and (>= %{ocaml_version} 4.12) (not %{ocaml-config:ox}))) 44 44 (package js_top_worker_rpc_def) 45 45 (libraries mime_printer merlin-lib.query_protocol) 46 46 (preprocess ··· 49 49 (rule 50 50 (target toplevel_api_gen.ml.gen) 51 51 (enabled_if 52 - (>= %{ocaml_version} 4.12)) 52 + (and (>= %{ocaml_version} 4.12) (not %{ocaml-config:ox}))) 53 53 (action 54 54 (with-stderr-to 55 55 %{target} ··· 58 58 (rule 59 59 (alias runtest) 60 60 (enabled_if 61 - (>= %{ocaml_version} 4.12)) 61 + (and (>= %{ocaml_version} 4.12) (not %{ocaml-config:ox}))) 62 62 (action 63 63 (diff toplevel_api_gen.ml toplevel_api_gen.ml.gen)))
+59 -4
lib/dune
··· 1 - ; Worker library 1 + ; Worker library -- upstream OCaml 2 2 3 3 (library 4 4 (public_name js_top_worker) 5 + (enabled_if (not %{ocaml-config:ox})) 5 6 (modules toplexer ocamltop impl environment) 6 7 (libraries 7 8 logs ··· 26 27 (per_module 27 28 ((action 28 29 (run %{bin:cppo} -V OCAML:%{ocaml_version} %{input-file})) 29 - uTop_complete 30 - uTop_compat 31 - uTop)))) 30 + impl)))) 31 + 32 + ; Worker library -- OxCaml 33 + 34 + (library 35 + (public_name js_top_worker) 36 + (enabled_if %{ocaml-config:ox}) 37 + (modules toplexer ocamltop impl environment) 38 + (libraries 39 + logs 40 + js_top_worker-rpc 41 + rpclib-lwt 42 + js_of_ocaml-compiler 43 + js_of_ocaml-ppx 44 + astring 45 + mime_printer 46 + compiler-libs.common 47 + compiler-libs.toplevel 48 + merlin-lib.kernel 49 + merlin-lib.utils 50 + merlin-lib.query_protocol 51 + merlin-lib.query_commands 52 + merlin-lib.ocaml_parsing 53 + ppxlib 54 + ppx_deriving.api) 55 + (js_of_ocaml 56 + (javascript_files stubs.js)) 57 + (preprocess 58 + (per_module 59 + ((action 60 + (run %{bin:cppo} -V OCAML:%{ocaml_version} -D "OXCAML" %{input-file})) 61 + impl)))) 32 62 33 63 (ocamllex toplexer) 64 + 65 + ; Web worker library -- upstream OCaml 34 66 35 67 (library 36 68 (public_name js_top_worker-web) 37 69 (name js_top_worker_web) 70 + (enabled_if (not %{ocaml-config:ox})) 71 + (modules worker findlibish jslib) 72 + (preprocess 73 + (pps js_of_ocaml-ppx)) 74 + (libraries 75 + js_top_worker 76 + js_top_worker-rpc.message 77 + js_of_ocaml-ppx 78 + js_of_ocaml-toplevel 79 + js_of_ocaml-lwt 80 + logs.browser 81 + uri 82 + angstrom 83 + findlib 84 + fpath 85 + rpclib.json)) 86 + 87 + ; Web worker library -- OxCaml 88 + 89 + (library 90 + (public_name js_top_worker-web) 91 + (name js_top_worker_web) 92 + (enabled_if %{ocaml-config:ox}) 38 93 (modules worker findlibish jslib) 39 94 (preprocess 40 95 (pps js_of_ocaml-ppx))
+55 -1
lib/impl.ml
··· 279 279 if dcs.dcs_file_prefixes <> [] then begin 280 280 let open Persistent_env.Persistent_signature in 281 281 let old_loader = !load in 282 + #if defined OXCAML 283 + load := fun ~allow_hidden ~unit_name -> 284 + let filename = to_cmi_filename (Compilation_unit.Name.to_string unit_name) in 285 + #else 282 286 load := fun ~allow_hidden ~unit_name -> 283 287 let filename = to_cmi_filename unit_name in 288 + #endif 284 289 let fs_name = Filename.(concat path filename) in 285 290 if (not (Sys.file_exists fs_name)) 286 291 && List.exists ··· 573 578 dcs.dcs_toplevel_modules 574 579 in 575 580 581 + #if defined OXCAML 582 + let new_load : 583 + 'a 'b. 584 + s:string -> 585 + to_string:('a -> string) -> 586 + old_loader:(allow_hidden:bool -> unit_name:'a -> 'b option) -> 587 + allow_hidden:bool -> 588 + unit_name:'a -> 589 + 'b option = 590 + fun ~s ~to_string ~old_loader ~allow_hidden ~unit_name -> 591 + let filename = filename_of_module (to_string unit_name) in 592 + #else 576 593 let new_load ~s ~old_loader ~allow_hidden ~unit_name = 577 - (* Logs.info (fun m -> m "%s Loading: %s" s unit_name); *) 578 594 let filename = filename_of_module unit_name in 595 + #endif 579 596 580 597 let fs_name = Filename.(concat path filename) in 581 598 (* Check if it's already been downloaded. This will be the ··· 612 629 else 613 630 let open Persistent_env.Persistent_signature in 614 631 let old_loader = !load in 632 + #if defined OXCAML 633 + load := new_load ~s:"comp" ~to_string:Compilation_unit.Name.to_string ~old_loader; 634 + #else 615 635 load := new_load ~s:"comp" ~old_loader; 636 + #endif 616 637 638 + #if defined OXCAML 639 + let open Persistent_env.Persistent_signature in 640 + let old_loader = !load in 641 + load := new_load ~s:"merl" ~to_string:Compilation_unit.Name.to_string ~old_loader 642 + #else 617 643 let open Ocaml_typing.Persistent_env.Persistent_signature in 618 644 let old_loader = !load in 619 645 load := new_load ~s:"merl" ~old_loader 646 + #endif 620 647 in 621 648 Lwt.return () 622 649 ··· 644 671 | [ dcs ] -> add_dynamic_cmis dcs 645 672 | _ -> Lwt.return () 646 673 in 674 + #if defined OXCAML 675 + Language_extension.(set_universe_and_enable_all Universe.Beta); 676 + #endif 647 677 Clflags.no_check_prims := true; 648 678 649 679 requires := init_libs.findlib_requires; ··· 689 719 Persistent_env.report_error Format.err_formatter e; 690 720 let err = Format.asprintf "%a" Persistent_env.report_error e in 691 721 failwith ("Error: " ^ err) 722 + #if defined OXCAML 723 + | Env.Error e -> 724 + Env.report_error ~level:0 Format.err_formatter e; 725 + let err = Format.asprintf "%a" (Env.report_error ~level:0) e in 726 + failwith ("Error: " ^ err)) 727 + #else 692 728 | Env.Error _ as exn -> 693 729 Location.report_exception Format.err_formatter exn; 694 730 let err = Format.asprintf "%a" Location.report_exception exn in 695 731 failwith ("Error: " ^ err)) 732 + #endif 696 733 in 697 734 698 735 let* dcs = ··· 986 1023 Printf.fprintf oc "%s" source; 987 1024 close_out oc; 988 1025 (try Sys.remove (prefix ^ ".cmi") with Sys_error _ -> ()); 1026 + #if defined OXCAML 1027 + let unit_info = Unit_info.make ~source_file:filename Impl prefix 1028 + ~for_pack_prefix:Compilation_unit.Prefix.empty in 1029 + #else 989 1030 let unit_info = Unit_info.make ~source_file:filename Impl prefix in 1031 + #endif 990 1032 try 991 1033 let store = Local_store.fresh () in 992 1034 Local_store.with_store store (fun () -> ··· 998 1040 let lexbuf = Lexing.from_string source in 999 1041 let ast = Parse.implementation lexbuf in 1000 1042 Logs.info (fun m -> m "About to type_implementation"); 1043 + #if defined OXCAML 1044 + let _ = Typemod.type_implementation unit_info 1045 + (Compilation_unit.of_string (modname_of_id id)) env ast in 1046 + #else 1001 1047 let _ = Typemod.type_implementation unit_info env ast in 1048 + #endif 1002 1049 let b = Sys.file_exists (prefix ^ ".cmi") in 1003 1050 Environment.remove_failed_cell execution_env id; 1004 1051 Logs.info (fun m -> m "file_exists: %s = %b" (prefix ^ ".cmi") b)); 1005 1052 Ocaml_typing.Cmi_cache.clear () 1006 1053 with 1054 + #if defined OXCAML 1055 + | Env.Error e -> 1056 + Logs.err (fun m -> m "Env.Error: %a" (Env.report_error ~level:0) e); 1057 + Environment.add_failed_cell execution_env id; 1058 + () 1059 + #else 1007 1060 | Env.Error _ as exn -> 1008 1061 Logs.err (fun m -> m "Env.Error: %a" Location.report_exception exn); 1009 1062 Environment.add_failed_cell execution_env id; 1010 1063 () 1064 + #endif 1011 1065 | exn -> 1012 1066 let s = Printexc.to_string exn in 1013 1067 Logs.err (fun m -> m "Error in add_cmi: %s" s);