Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file mltop.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477(************************************************************************)(* * The Coq Proof Assistant / The Coq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)openUtilopenPp(* Code to interact with ML "toplevel", in particular, handling ML
plugin loading.
We use Fl_dynload to load plugins, which can correctly track
dependencies, and manage path for us.
A bit of infrastructure is still in place to support a "legacy"
mode where Coq used to manage the OCaml include paths and directly
load .cmxs/.cma files itself.
We also place here the required code for interacting with the
Summary and Libobject, and provide an API so plugins can interact
with this loading/unloading for Coq-specific purposes adding their
own init functions, given that OCaml cannot unlink a dynamic module.
*)(* This path is where we look for .cmo/.cmxs using the legacy method *)letcoq_mlpath_copy=ref[Sys.getcwd()]letkeep_copy_mlpathpath=letcpath=CUnix.canonical_path_namepathinletfilterpath'=not(String.equalcpathpath')incoq_mlpath_copy:=cpath::List.filterfilter!coq_mlpath_copymoduleFl_internals=struct(* Check that [m] is a findlib library name *)letvalidate_lib_namem=String.index_optm'.'<>None(* Fl_split.in_words is not exported *)letfl_split_in_wordss=(* splits s in words separated by commas and/or whitespace *)letl=String.lengthsinletrecsplitij=ifj<lthenmatchs.[j]with|(' '|'\t'|'\n'|'\r'|',')->ifi<jthen(String.subsi(j-i))::(split(j+1)(j+1))elsesplit(j+1)(j+1)|_->spliti(j+1)elseifi<jthen[String.subsi(j-i)]else[]insplit00(* simulate what fl_dynload does *)letfl_find_pluginslib=letbase=Findlib.package_directorylibinletpreds=Findlib.recorded_predicates()inletarchive=tryFindlib.package_propertypredslib"plugin"withNot_found->tryfst(Findlib.package_property_2("plugin"::preds)lib"archive")withNot_found->""infl_split_in_wordsarchive|>List.map(Findlib.resolve_path~base)(* We register errors at for Dynlink and Findlib, it is possible to
do so Symtable too, as we used to do in the bytecode init code.
*)let()=CErrors.register_handler(function|Dynlink.Errormsg->Some(hov0(str"Dynlink error: "++str(Dynlink.error_messagemsg)))|Fl_package_base.No_such_package(p,msg)->letpaths=Findlib.search_path()inSome(hov0(str"Findlib error: "++strp++str" not found in:"++cut()++v0(prlist_with_sepcutstrpaths)++fnl()++strmsg))|_->None)endmodulePluginSpec:sigtypet(* Main constructor, takes the format used in Declare ML Module *)valof_declare_ml_format:string->t(* repr/unrepr are internal and only needed for the summary and other low-level stuff *)valrepr:t->stringoption*stringvalunrepr:stringoption*string->t(* Load a plugin, low-level, that is to say, will directly call the
loading mechanism in OCaml/findlib *)valload:t->unit(* Compute a digest, a findlib library name have more than one
plugin .cmxs, however this is not the case in Coq. Maybe we
should strengthen this invariant. *)valdigest:t->Digest.tlistvalpp:t->stringmoduleSet:CSet.Swithtypeelt=tmoduleMap:CMap.ExtSwithtypekey=tandmoduleSet:=Setend=structtypet={file:stringoption;lib:string}moduleErrors=structletplugin_name_should_contain_dotm=CErrors.user_errPp.(strFormat.(asprintf"%s is not a valid plugin name anymore."m)++spc()++str"Plugins should be loaded using their public name"++spc()++str"according to findlib, for example package-name.foo and not "++str"foo_plugin.")letplugin_name_invalid_formatm=CErrors.user_errPp.(strFormat.(asprintf"%s is not a valid plugin name."m)++spc()++str"It should be a public findlib name, e.g. package-name.foo,"++spc()++str"or a legacy name followed by a findlib public name, e.g. "++spc()++str"legacy_plugin:package-name.plugin.")endletlegacy_mapping=Core_plugins_findlib_compat.legacy_to_findlibletof_declare_ml_formatm=matchString.split_on_char':'mwith|[file]whenList.mem_assocfilelegacy_mapping->{file=Somefile;lib=String.concat"."("coq-core"::List.assocfilelegacy_mapping)}|[x]whennot(Fl_internals.validate_lib_namex)->Errors.plugin_name_should_contain_dotm|[file;lib]->{file=Somefile;lib}|[lib]->{file=None;lib}|[]->assertfalse|_::_::_->Errors.plugin_name_invalid_formatm(* Adds the corresponding extension .cmo/.cma or .cmxs. Dune and
coq_makefile byte plugins do differ in the choice of extension,
hence the probing. *)letselect_plugin_versionbase=ifSys.(backend_type=Native)thenbase^".cmxs"elseletname=base^".cmo"inifSystem.is_in_path!coq_mlpath_copynamethennameelsebase^".cma"letload=function|{file=None;lib}->Fl_dynload.load_packages[lib]|{file=Somefile;lib}->letfile=select_plugin_versionfileinlet_,gname=System.find_file_in_path~warn:false!coq_mlpath_copyfileinDynlink.loadfilegname;Findlib.(record_packageRecord_load)libletdigests=matchswith|{file=Somefile;_}->letfile=select_plugin_versionfileinlet_,gname=System.find_file_in_path~warn:false!coq_mlpath_copyfilein[Digest.filegname]|{file=None;lib}->letplugins=Fl_internals.fl_find_pluginslibinList.mapDigest.filepluginsletrepr{file;lib}=(file,lib)letunrepr(file,lib)={file;lib}letcompare{lib=l1;_}{lib=l2;_}=String.comparel1l2letpp=function|{file=None;lib}->lib|{file=Somefile;lib}->letfile=select_plugin_versionfileinFilename.basenamefile^" (using legacy method)"moduleSelf=structtypenonrect=tletcompare=compareendmoduleSet=CSet.Make(Self)moduleMap=CMap.Make(Self)end(* If there is a toplevel under Coq *)typetoplevel={load_plugin:PluginSpec.t->unit(** Load a findlib library, given by public name *);load_module:string->unit(** Load a cmxs / cmo module, used by the native compiler to load objects *);add_dir:string->unit(** Adds a dir to the module search path *);ml_loop:unit->unit(** Implementation of Drop *)}(* Determines the behaviour of Coq with respect to ML files (compiled
or not) *)typekind_load=|WithTopoftoplevel|WithoutTop(* Must be always initialized *)letload=refWithoutTop(* Sets and initializes a toplevel (if any) *)letset_toptoplevel=load:=WithToptoplevel;Nativelib.load_obj:=toplevel.load_module(* Removes the toplevel (if any) *)letremove()=load:=WithoutTop;Nativelib.load_obj:=(funx->():string->unit)(* Tests if an Ocaml toplevel runs under Coq *)letis_ocaml_top()=match!loadwith|WithTop_->true|_->false(* Tests if we can load ML files *)lethas_dynlink=Coq_config.has_natdynlink||notSys.(backend_type=Native)(* Runs the toplevel loop of Ocaml *)letocaml_toploop()=match!loadwith|WithTopt->t.ml_loop()|_->()letml_loadp=match!loadwith|WithTopt->t.load_pluginp|WithoutTop->PluginSpec.loadp(* Adds a path to the ML paths *)letadd_ml_dirs=match!loadwith|WithTopt->t.add_dirs;keep_copy_mlpaths|WithoutTopwhenhas_dynlink->keep_copy_mlpaths|_->()(** Is the ML code of the standard library placed into loadable plugins
or statically compiled into coqtop ? For the moment this choice is
made according to the presence of native dynlink : even if bytecode
coqtop could always load plugins, we prefer to have uniformity between
bytecode and native versions. *)(* [known_loaded_module] contains the names of the loaded ML modules
* (linked or loaded with load_object). It is used not to load a
* module twice. It is NOT the list of ML modules Coq knows. *)(* TODO: Merge known_loaded_module and known_loaded_plugins *)letknown_loaded_modules:PluginSpec.Set.tref=refPluginSpec.Set.emptyletadd_known_modulemname=ifnot(PluginSpec.Set.memmname!known_loaded_modules)thenknown_loaded_modules:=PluginSpec.Set.addmname!known_loaded_modulesletmodule_is_knownmname=PluginSpec.Set.memmname!known_loaded_modulesletplugin_is_knownmname=PluginSpec.Set.memmname!known_loaded_modules(** Init time functions *)letinitialized_plugins=Summary.ref~stage:Synterp~name:"inited-plugins"PluginSpec.Set.emptyletplugin_init_functions:(unit->unit)listPluginSpec.Map.tref=refPluginSpec.Map.emptyletadd_init_functionnamef=letname=PluginSpec.of_declare_ml_formatnameinifPluginSpec.Set.memname!initialized_pluginsthenCErrors.anomalyPp.(str"Not allowed to add init function for already initialized plugin "++str(PluginSpec.ppname));plugin_init_functions:=PluginSpec.Map.updatename(function|None->Some[f]|Someg->Some(f::g))!plugin_init_functions(** Registering functions to be used at caching time, that is when the Declare
ML module command is issued. *)letcache_objs=refPluginSpec.Map.emptyletdeclare_cache_objfname=letname=PluginSpec.of_declare_ml_formatnameinletobjs=tryPluginSpec.Map.findname!cache_objswithNot_found->[]inletobjs=f::objsincache_objs:=PluginSpec.Map.addnameobjs!cache_objsletperform_cache_objname=letobjs=tryPluginSpec.Map.findname!cache_objswithNot_found->[]inletobjs=List.revobjsinList.iter(funf->f())objs(** ml object = ml module or plugin *)letdinit=CDebug.create~name:"mltop-init"()letinit_ml_objectmname=ifPluginSpec.Set.memmname!initialized_pluginsthendinitPp.(fun()->str"already initialized "++str(PluginSpec.ppmname))elsebegindinitPp.(fun()->str"initing "++str(PluginSpec.ppmname));letn=matchPluginSpec.Map.findmname!plugin_init_functionswith|l->List.iter(funf->f())(List.revl);List.lengthl|exceptionNot_found->0ininitialized_plugins:=PluginSpec.Set.addmname!initialized_plugins;dinitPp.(fun()->str"finished initing "++str(PluginSpec.ppmname)++str" ("++intn++str" init functions)")endletload_ml_objectmname=ml_loadmname;add_known_modulemname;init_ml_objectmnameletadd_known_modulename=letname=PluginSpec.of_declare_ml_formatnameinadd_known_modulenameletmodule_is_knownmname=letmname=PluginSpec.of_declare_ml_formatmnameinmodule_is_knownmname(* Summary of declared ML Modules *)(* List and not String.Set because order is important: most recent first. *)letloaded_modules=ref[]letget_loaded_modules()=List.rev!loaded_modules(* XXX: It seems this should be part of trigger_ml_object, and
moreover we should check the guard there *)letadd_loaded_modulemd=ifnot(List.memmd!loaded_modules)thenloaded_modules:=md::!loaded_modulesletreset_loaded_modules()=loaded_modules:=[]letif_verbose_loadverbfname=ifnotverbthenfnameelseletinfo=str"[Loading ML file "++str(PluginSpec.ppname)++str" ..."intryletpath=fnameinFeedback.msg_info(info++str" done]");pathwithreraise->Feedback.msg_info(info++str" failed]");raisereraise(** Load a module for the first time (i.e. dynlink it)
or simulate its reload (i.e. doing nothing except maybe
an initialization function). *)lettrigger_ml_object~verbose~cache~reinitplugin=let()=ifplugin_is_knownpluginthen(ifreinittheninit_ml_objectplugin)elsebeginifnothas_dynlinkthenCErrors.user_err(str"Dynamic link not supported (module "++str(PluginSpec.ppplugin)++str").")elseif_verbose_load(verbose&¬!Flags.quiet)load_ml_objectpluginendinadd_loaded_moduleplugin;ifcachethenperform_cache_objpluginletunfreeze_ml_modulesx=reset_loaded_modules();List.iter(funname->letname=PluginSpec.unreprnameintrigger_ml_object~verbose:false~cache:false~reinit:falsename)xlet()=Summary.declare_ml_modules_summary{stage=Summary.Stage.Synterp;Summary.freeze_function=(fun~marshallable->get_loaded_modules()|>List.mapPluginSpec.repr);Summary.unfreeze_function=unfreeze_ml_modules;Summary.init_function=reset_loaded_modules}(* Liboject entries of declared ML Modules *)typeml_module_object={mlocal:Vernacexpr.locality_flag;mnames:PluginSpec.tlist;mdigests:Digest.tlist}letcache_ml_objectsmnames=letiterobj=trigger_ml_object~verbose:true~cache:true~reinit:trueobjinList.iteritermnamesletload_ml_objects_{mnames;_}=letiterobj=trigger_ml_object~verbose:true~cache:false~reinit:trueobjinList.iteritermnamesletclassify_ml_objects{mlocal=mlocal}=ifmlocalthenLibobject.DisposeelseLibobject.SubstituteletinMLModule:ml_module_object->Libobject.obj=letopenLibobjectindeclare_object{(default_object"ML-MODULE")withcache_function=(fun_->());load_function=load_ml_objects;subst_function=(fun(_,o)->o);classify_function=classify_ml_objects}letdeclare_ml_moduleslocall=letmnames=List.mapPluginSpec.of_declare_ml_formatlinifGlobal.sections_are_opened()thenCErrors.user_errPp.(str"Cannot Declare ML Module while sections are opened.");(* List.concat_map only available in 4.10 *)letmdigests=List.mapPluginSpec.digestmnames|>List.concatinLib.add_leaf(inMLModule{mlocal=local;mnames;mdigests});(* We can't put this in cache_function: it may declare other
objects, and when the current module is required we want to run
the ML-MODULE object before them. *)cache_ml_objectsmnamesletprint_ml_path()=letl=!coq_mlpath_copyinstr"ML Load Path:"++fnl()++str" "++hv0(prlist_with_sepfnlstrl)(* Printing of loaded ML modules *)letprint_ml_modules()=letl=get_loaded_modules()instr"Loaded ML Modules: "++pr_vertical_liststr(List.mapPluginSpec.ppl)letprint_gc()=letstat=Gc.stat()inletmsg=str"minor words: "++realstat.Gc.minor_words++fnl()++str"promoted words: "++realstat.Gc.promoted_words++fnl()++str"major words: "++realstat.Gc.major_words++fnl()++str"minor_collections: "++intstat.Gc.minor_collections++fnl()++str"major_collections: "++intstat.Gc.major_collections++fnl()++str"heap_words: "++intstat.Gc.heap_words++fnl()++str"heap_chunks: "++intstat.Gc.heap_chunks++fnl()++str"live_words: "++intstat.Gc.live_words++fnl()++str"live_blocks: "++intstat.Gc.live_blocks++fnl()++str"free_words: "++intstat.Gc.free_words++fnl()++str"free_blocks: "++intstat.Gc.free_blocks++fnl()++str"largest_free: "++intstat.Gc.largest_free++fnl()++str"fragments: "++intstat.Gc.fragments++fnl()++str"compactions: "++intstat.Gc.compactions++fnl()++str"top_heap_words: "++intstat.Gc.top_heap_words++fnl()++str"stack_size: "++intstat.Gc.stack_sizeinhv0msgletinit_known_plugins()=()letadd_known_plugin__=()