(* $Id$ * ---------------------------------------------------------------------- * *) let predicates = ref ("toploop" :: Findlib.recorded_predicates());; (* We also want things like "syntax" here which are not allowed in Findlib, hence we maintain our own list *) let ocaml_stdlib = lazy (Findlib.ocaml_stdlib ());; let directories = ref [ ] ;; (* Note: Sys.interactive is always _true_ during toploop startup. * When a script is executed, it is set to false just before the * script starts. This is important for ocamlmktop-generated toploops: * For initialization code linked into the toploop, Sys.interactive * is _true_. It is set to false just before the script starts. *) let real_toploop = !Sys.interactive;; let log = ref (if real_toploop then prerr_endline else ignore) let rec remove_dups l = match l with x :: l' -> if List.mem x l' then remove_dups l' else x::remove_dups l' | [] -> [] ;; let add_predicates pl = predicates := remove_dups (pl @ !predicates); Findlib.record_package_predicates !predicates;; let syntax s = add_predicates [ "syntax"; s ];; let standard_syntax () = syntax "camlp4o";; let revised_syntax () = syntax "camlp4r";; let add_dir d = let d = Fl_split.norm_dir d in let ocaml_stdlib = Lazy.force ocaml_stdlib in if d <> ocaml_stdlib && not (List.mem d !directories) then begin Topdirs.dir_directory d; directories := d :: !directories; !log (d ^ ": added to search path") end ;; let exec_string s = let l = Lexing.from_string s in let ph = !Toploop.parse_toplevel_phrase l in (* PPXOPT_BEGIN *) let ph = Toploop.preprocess_phrase Format.err_formatter ph in (* PPXOPT_END *) let fmt = Format.make_formatter (fun _ _ _ -> ()) (fun _ -> ()) in try Toploop.execute_phrase false fmt ph with _ -> false ;; let load pkglist = List.iter (fun pkg -> let _stdlibdir = Findlib.ocaml_stdlib() in let loaded = Findlib.is_recorded_package pkg && Findlib.type_of_recorded_package pkg = Findlib.Record_load in let incore = Findlib.is_recorded_package pkg && Findlib.type_of_recorded_package pkg = Findlib.Record_core in if not loaded then begin (* Determine the package directory: *) let d = Findlib.package_directory pkg in add_dir d; (* Leave pkg out if mentioned in !forbidden *) if not incore then begin (* Determine the 'archive' property: *) let archive = try Findlib.package_property !predicates pkg "archive" with Not_found -> "" in (* Split the 'archive' property and load the files: *) let archives = Fl_split.in_words archive in List.iter (fun arch -> let arch' = Findlib.resolve_path ~base:d arch in !log (arch' ^ ": loaded"); Topdirs.dir_load Format.std_formatter arch') archives; (* Determine the 'ppx' property: *) let ppx = try Some(Findlib.resolve_path ~base:d ~explicit:true (Findlib.package_property !predicates pkg "ppx") ) with Not_found -> None and ppxopts = try List.map (fun opt -> match Fl_split.in_words opt with | pkg :: opts -> pkg, List.map (Findlib.resolve_path ~base:d ~explicit:true) opts | _ -> assert false) (Fl_split.in_words_ws (Findlib.package_property !predicates pkg "ppxopt")) with Not_found -> [] in (* Feed the 'ppx' property into the toplevel. To remain compatible with pre-4.01 OCaml, construct and execute a phrase instead of directly altering Clflags. *) begin match ppx with | Some ppx -> begin try match Hashtbl.find Toploop.directive_table "ppx" with | Toploop.Directive_string fn -> fn ppx; !log (ppx ^ ": activated") | _ -> assert false with Not_found -> failwith "Package defines a ppx preprocessor, but OCaml is too old. \ Use OCaml >= 4.02.0 for ppx support." end | None -> () end; (* Feed the 'ppxopt' property into the toplevel. *) match ppxopts with | [] -> () | _ -> (* PPXOPT_BEGIN *) List.iter (fun (pkg, opts) -> ignore (exec_string ("[@@@findlib.ppxopt " ^ (String.concat ", " (List.map (Printf.sprintf "%S") (pkg :: opts))) ^ "];;")); !log (pkg ^ ": " ^ (String.concat " " opts) ^ ": option added")) ppxopts (* (* PPXOPT_END *) failwith "Package defines a ppx preprocessor option, but OCaml is too old. \ Use OCaml >=4.02.1 for ppxopt support." (* PPXOPT_BEGIN *) *) (* PPXOPT_END *) end; (* The package is loaded: *) Findlib.record_package Findlib.Record_load pkg end) pkglist ;; let load_deeply pkglist = (* Get the sorted list of ancestors *) let eff_pkglist = Findlib.package_deep_ancestors !predicates pkglist in List.iter (fun pkg -> try let error = Findlib.package_property !predicates pkg "error" in failwith ("Error from package `" ^ pkg ^ "': " ^ error) with Not_found -> ()) eff_pkglist ; (* Load the packages in turn: *) load eff_pkglist ;; let check_existence pkglist = List.iter (fun pkg -> let _ = Findlib.package_directory pkg in () ) pkglist ;; let don't_load pkglist = check_existence pkglist; List.iter (Findlib.record_package Findlib.Record_core) pkglist ;; let don't_load_deeply pkglist = (* Check if packages exist: *) check_existence pkglist; (* Get the sorted list of ancestors *) let eff_pkglist = Findlib.package_deep_ancestors !predicates pkglist in (* Add this to the list of core packages: *) List.iter (Findlib.record_package Findlib.Record_core) eff_pkglist ;; let reset() = Findlib.reset_recordings() ;; let have_mt_support() = Findlib.package_property [] "threads" "type_of_threads" = "posix" ;; let load_mt_support() = (* Load only if package "threads" is not yet loaded. *) if not(Findlib.is_recorded_package "threads") then ( (* This works only for POSIX threads. *) if have_mt_support() then ( add_predicates ["mt"; "mt_posix"]; add_dir (Filename.concat (Findlib.ocaml_stdlib()) "threads"); load_deeply ["unix"]; load_deeply ["threads"]; ) else ( failwith "It is not possible to load support for vmthreads dynamically. Use\n 'ocamlfind ocamlmktop -o vmtop -package threads,findlib -linkpkg -vmthread'\n to create a toploop with integrated vmthreads library." ) ) ;; let list_packages() = Findlib.list_packages stdout; flush stdout ;; let protect f arg = try let _ = f arg in () with Failure s -> print_endline s | Fl_package_base.No_such_package(pkg, reason) -> print_endline ("No such package: " ^ pkg ^ (if reason <> "" then " - " ^ reason else "")) | Fl_package_base.Package_loop pkg -> print_endline ("Package requires itself: " ^ pkg) ;; (* Add "#require" directive: *) Hashtbl.add Toploop.directive_table "require" (Toploop.Directive_string (fun s -> protect load_deeply (Fl_split.in_words s) )) ;; (* Add "#predicates" directive: *) Hashtbl.add Toploop.directive_table "predicates" (Toploop.Directive_string (fun s -> protect add_predicates (Fl_split.in_words s) )) ;; (* Add "#camlp4o" directive: *) Hashtbl.add Toploop.directive_table "camlp4o" (Toploop.Directive_none (fun () -> protect (fun () -> standard_syntax(); load_deeply ["camlp4"]) () )) ;; (* Add "#camlp4r" directive: *) Hashtbl.add Toploop.directive_table "camlp4r" (Toploop.Directive_none (fun () -> protect (fun () -> revised_syntax(); load_deeply ["camlp4"]) () )) ;; (* Add "#list" directive: *) Hashtbl.add Toploop.directive_table "list" (Toploop.Directive_none (fun () -> protect list_packages () )) ;; (* Add "#thread" directive: *) Hashtbl.add Toploop.directive_table "thread" (Toploop.Directive_none (fun () -> protect load_mt_support () )) ;; let announce() = if real_toploop then begin (* Assume we are in a toploop and not a script *) let msg_thread = " #thread;; to enable threads\n" in print_endline ("Findlib has been successfully loaded. Additional directives:\n" ^ " #require \"package\";; to load a package\n" ^ " #list;; to list the available packages\n" ^ " #camlp4o;; to load camlp4 (standard syntax)\n" ^ " #camlp4r;; to load camlp4 (revised syntax)\n" ^ " #predicates \"p,q,...\";; to set these predicates\n" ^ " Topfind.reset();; to force that packages will be reloaded\n" ^ (if have_mt_support() then msg_thread else "")) end ;;