123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430(************************************************************************)(* * 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) *)(************************************************************************)openPpopenUtilopenNamesletchk_pp=Feedback.msg_noticeletpr_dirpathdp=str(DirPath.to_stringdp)letdefault_root_prefix=DirPath.emptyletsplit_dirpathd=letl=DirPath.reprdin(DirPath.make(List.tll),List.hdl)letextend_dirpathpid=DirPath.make(id::DirPath.reprp)typesection_path={dirpath:stringlist;basename:string}typeobject_file=|PhysicalFileofCUnix.physical_path|LogicalFileofsection_pathletdir_of_pathp=DirPath.make(List.mapId.of_stringp.dirpath)letpath_of_dirpathdir=matchDirPath.reprdirwith[]->failwith"path_of_dirpath"|l::dir->{dirpath=List.mapId.to_stringdir;basename=Id.to_stringl}letpr_dirlistdp=prlist_with_sep(fun_->str".")str(List.revdp)letpr_pathsp=matchsp.dirpathwith[]->strsp.basename|sl->pr_dirlistsl++str"."++strsp.basename(************************************************************************)(*s Modules loaded in memory contain the following informations. They are
kept in the global table [libraries_table]. *)typecompilation_unit_name=DirPath.ttypeseg_proofs=Opaqueproof.opaque_prooftermoptionarraytypelibrary_t={library_name:compilation_unit_name;library_filename:CUnix.physical_path;library_compiled:Safe_typing.compiled_library;library_opaques:seg_proofs;library_deps:(compilation_unit_name*Safe_typing.vodigest)array;library_digest:Safe_typing.vodigest;library_vm:Vmlibrary.on_disk;}moduleLibraryOrdered=structtypet=DirPath.tletcompared1d2=compare(List.rev(DirPath.reprd1))(List.rev(DirPath.reprd2))endmoduleLibrarySet=Set.Make(LibraryOrdered)moduleLibraryMap=Map.Make(LibraryOrdered)(* This is a map from names to loaded libraries *)letlibraries_table=refLibraryMap.empty(* various requests to the tables *)letfind_librarydir=LibraryMap.finddir!libraries_tableletlibrary_full_filenamedir=(find_librarydir).library_filename(* If a library is loaded several time, then the first occurrence must
be performed first, thus the libraries_loaded_list ... *)letregister_loaded_librarym=libraries_table:=LibraryMap.addm.library_namem!libraries_table(* Map from library names to table of opaque terms *)letopaque_tables=refLibraryMap.emptyletaccess_opaque_tabledpi=lett=tryLibraryMap.finddp!opaque_tableswithNot_found->assertfalseinleti=Opaqueproof.repr_handleiinlet()=assert(0<=i&&i<Array.lengtht)int.(i)letindirect_accessoro=let(sub,ci,dp,i)=Opaqueproof.reproinletc=access_opaque_tabledpiinletc=matchcwith|None->CErrors.user_errPp.(str"Cannot access opaque delayed proof.")|Somec->cinlet(c,prv)=Discharge.cook_opaque_prooftermcicinletc=Mod_subst.subst_mps_listsubcin(c,prv)let()=Mod_checking.set_indirect_accessorindirect_accessorletcheck_one_libadmitsenv(dir,m)=letmd=m.library_compiledinletdig=m.library_digestin(* Look up if the library is to be admitted correct. We could
also check if it carries a validation certificate (yet to
be implemented). *)letsenv=ifLibrarySet.memdiradmitthen(Flags.if_verboseFeedback.msg_notice(str"Admitting library: "++pr_dirpathdir);Safe_checking.unsafe_import(fstsenv)mdm.library_vmdig),(sndsenv)else(Flags.if_verboseFeedback.msg_notice(str"Checking library: "++pr_dirpathdir);Safe_checking.import(fstsenv)(sndsenv)mdm.library_vmdig)inregister_loaded_librarym;senv(*************************************************************************)(*s Load path. Mapping from physical to logical paths etc.*)typelogical_path=DirPath.tletload_paths=ref([],[]:CUnix.physical_pathlist*logical_pathlist)letfind_logical_pathphys_dir=letphys_dir=CUnix.canonical_path_namephys_dirinletphysical,logical=!load_pathsinmatchList.filter2(funpd->p=phys_dir)physicallogicalwith|_,[dir]->dir|_,[]->default_root_prefix|_,l->CErrors.anomaly(Pp.str("Two logical paths are associated to "^phys_dir^"."))letremove_load_pathdir=letphysical,logical=!load_pathsinload_paths:=List.filter2(funpd->p<>dir)physicallogicalletadd_load_path(phys_path,coq_path)=ifCDebug.(get_flagmisc)thenFeedback.msg_notice(str"path: "++pr_dirpathcoq_path++str" ->"++spc()++strphys_path);letphys_path=CUnix.canonical_path_namephys_pathinletphysical,logical=!load_pathsinmatchList.filter2(funpd->p=phys_path)physicallogicalwith|_,[dir]->ifcoq_path<>dir(* If this is not the default -I . to coqtop *)&¬(phys_path=CUnix.canonical_path_nameFilename.current_dir_name&&coq_path=default_root_prefix)thenbegin(* Assume the user is concerned by library naming *)ifdir<>default_root_prefixthenFeedback.msg_warning(strphys_path++strbrk" was previously bound to "++pr_dirpathdir++strbrk"; it is remapped to "++pr_dirpathcoq_path);remove_load_pathphys_path;load_paths:=(phys_path::fst!load_paths,coq_path::snd!load_paths)end|_,[]->load_paths:=(phys_path::fst!load_paths,coq_path::snd!load_paths)|_->CErrors.anomaly(Pp.str("Two logical paths are associated to "^phys_path^"."))letload_paths_of_dir_pathdir=letphysical,logical=!load_pathsinfst(List.filter2(funpd->d=dir)physicallogical)(************************************************************************)(*s Locate absolute or partially qualified library names in the path *)exceptionLibUnmappedDirexceptionLibNotFoundletlocate_absolute_librarydir=(* Search in loadpath *)letpref,base=split_dirpathdirinletloadpath=load_paths_of_dir_pathprefinifloadpath=[]thenraiseLibUnmappedDir;tryletname=Id.to_stringbase^".vo"inlet_,file=System.where_in_path~warn:falseloadpathnamein(dir,file)withNot_found->(* Last chance, removed from the file system but still in memory *)try(dir,library_full_filenamedir)withNot_found->raiseLibNotFoundletlocate_qualified_libraryqid=try(* we assume qid is an absolute dirpath *)letloadpath=load_paths_of_dir_path(dir_of_pathqid)inifloadpath=[]thenraiseLibUnmappedDir;letname=qid.basename^".vo"inletpath,file=System.where_in_pathloadpathnameinletdir=extend_dirpath(find_logical_pathpath)(Id.of_stringqid.basename)in(* Look if loaded *)try(dir,library_full_filenamedir)withNot_found->(dir,file)withNot_found->raiseLibNotFoundleterror_unmapped_dirqid=letprefix=qid.dirpathinCErrors.user_err(str"Cannot load "++pr_pathqid++str":"++spc()++str"no physical path bound to"++spc()++pr_dirlistprefix++str"."++fnl())leterror_lib_not_foundqid=CErrors.user_err(str"Cannot find library "++pr_pathqid++str" in loadpath.")lettry_locate_absolute_librarydir=trylocate_absolute_librarydirwith|LibUnmappedDir->error_unmapped_dir(path_of_dirpathdir)|LibNotFound->error_lib_not_found(path_of_dirpathdir)lettry_locate_qualified_librarylib=matchlibwith|PhysicalFilef->let()=ifnot(System.file_exists_respecting_case""f)thenerror_lib_not_found{dirpath=[];basename=f;}inletdir=Filename.dirnamefinletbase=Filename.chop_extension(Filename.basenamef)inletdir=extend_dirpath(find_logical_pathdir)(Id.of_stringbase)in(dir,f)|LogicalFileqid->trylocate_qualified_libraryqidwith|LibUnmappedDir->error_unmapped_dirqid|LibNotFound->error_lib_not_foundqid(************************************************************************)(*s Low-level interning of libraries from files *)letraw_intern_libraryf=ObjFile.open_in~file:f(************************************************************************)(* Internalise libraries *)typelibrary_infotypesummary_disk={md_name:compilation_unit_name;md_deps:(compilation_unit_name*Safe_typing.vodigest)array;md_ocaml:string;md_info:library_info;}typelibrary_objectstypelibrary_disk={md_compiled:Safe_typing.compiled_library;md_syntax_objects:library_objects;md_objects:library_objects;}letmk_librarysdmdftabledigestvm={library_name=sd.md_name;library_filename=f;library_compiled=md.md_compiled;library_opaques=table;library_deps=sd.md_deps;library_digest=digest;library_vm=vm;}letname_clash_messagedirmdirf=str("The file "^f^" contains library")++spc()++pr_dirpathmdir++spc()++str"and not library"++spc()++pr_dirpathdirtypeintern_mode=Rec|Root|Dep(* Rec = standard, Root = -norec, Dep = dependency of norec *)(* Dependency graph *)letdepgraph=refLibraryMap.emptyletmarshal_in_segment(typea)~validate~value~(segment:aObjFile.segment)fch:a=let()=LargeFile.seek_inchsegment.ObjFile.posinifvalidatethenletv=tryletv=Analyze.parse_channelchinletdigest=Digest.inputchinlet()=ifnot(String.equaldigestsegment.ObjFile.hash)thenraise_notraceExitinvwith_->CErrors.user_err(str"Corrupted file "++quote(strf))inlet()=Validate.validatevaluevinletv=Analyze.instantiatevinObj.objvelseSystem.marshal_infchletsummary_seg:summary_diskObjFile.id=ObjFile.make_id"summary"letlibrary_seg:library_diskObjFile.id=ObjFile.make_id"library"letopaques_seg:seg_proofsObjFile.id=ObjFile.make_id"opaques"letvm_seg=Vmlibrary.vm_segmentletintern_from_file~intern_mode~enable_VM(dir,f)=letvalidate=intern_mode<>DepinFlags.if_verbosechk_pp(str"[intern "++strf++str" ...");let(sd,md,table,vmlib,digest)=try(* First pass to read the metadata of the file *)letch=System.with_magic_number_checkraw_intern_libraryfinletseg_sd=ObjFile.get_segmentch~segment:summary_seginletseg_md=ObjFile.get_segmentch~segment:library_seginletseg_opaque=ObjFile.get_segmentch~segment:opaques_seginletseg_vmlib=ObjFile.get_segmentch~segment:vm_seginlet()=ObjFile.close_inchin(* Actually read the data *)letch=open_in_binfinletsd=marshal_in_segment~validate~value:Values.v_libsum~segment:seg_sdfchinletmd=marshal_in_segment~validate~value:Values.v_lib~segment:seg_mdfchinlettable=marshal_in_segment~validate~value:Values.v_opaquetable~segment:seg_opaquefchinletvmlib=ifenable_VMthenmarshal_in_segment~validate~value:Values.v_vmlib~segment:seg_vmlibfchelseVmlibrary.(export(set_pathdirempty))in(* Verification of the final checksum *)let()=close_inchinletch=open_in_binfinlet()=close_inchinlet()=System.check_caml_version~caml:sd.md_ocaml~file:finifdir<>sd.md_namethenCErrors.user_err(name_clash_messagedirsd.md_namef);Flags.if_verbosechk_pp(str" done]"++fnl());letdigest=Safe_typing.Dvo_or_viseg_md.hashinsd,md,table,vmlib,digestwithe->Flags.if_verbosechk_pp(str" failed!]"++fnl());raiseeindepgraph:=LibraryMap.addsd.md_namesd.md_deps!depgraph;opaque_tables:=LibraryMap.addsd.md_nametable!opaque_tables;mk_librarysdmdftabledigest(Vmlibrary.injectvmlib)(* Read a compiled library and all dependencies, in reverse order.
Do not include files that are already in the context. *)letrecintern_library~intern_mode~enable_VMseen(dir,f)needed=ifLibrarySet.memdirseenthenfailwith"Recursive dependencies!";(* Look if in the current logical environment *)trylet_=find_librarydirinneededwithNot_found->(* Look if already listed and consequently its dependencies too *)ifList.mem_assoc_fDirPath.equaldirneededthenneededelse(* [dir] is an absolute name which matches [f] which must be in loadpath *)letm=intern_from_file~intern_mode~enable_VM(dir,f)inletseen'=LibrarySet.adddirseeninletdeps=Array.map(fun(d,_)->try_locate_absolute_libraryd)m.library_depsinletintern_mode=matchintern_modewithRec->Rec|Root|Dep->Depin(dir,m)::Array.fold_right(intern_library~intern_mode~enable_VMseen')depsneeded(* Compute the reflexive transitive dependency closure *)letrecfold_depsseenff(dir,f)(s,acc)=ifLibrarySet.memdirseenthenfailwith"Recursive dependencies!";ifLibrarySet.memdirsthen(s,acc)elseletdeps=matchLibraryMap.find_optdir!depgraphwith|Somedeps->deps|None->CErrors.anomalyPp.(str"missing dep when computing closure ("++DirPath.printdir++str")")inletdeps=Array.map(fun(d,_)->try_locate_absolute_libraryd)depsinletseen'=LibrarySet.adddirseeninlet(s',acc')=Array.fold_right(fold_depsseen'ff)deps(s,acc)in(LibrarySet.adddirs',ffdiracc')andfold_deps_listseenffmodlneeded=List.fold_right(fold_depsseenff)modlneededletfold_deps_listffmodlacc=snd(fold_deps_listLibrarySet.emptyffmodl(LibrarySet.empty,acc))letrecheck_librarysenv~norec~admit~check=letenable_VM=(Environ.typing_flags(Safe_typing.env_of_safe_envsenv)).enable_VMinletml=List.maptry_locate_qualified_librarycheckinletnrl=List.maptry_locate_qualified_librarynorecinletal=List.maptry_locate_qualified_libraryadmitinletneeded=List.fold_right(intern_library~intern_mode:Rec~enable_VMLibrarySet.empty)ml[]inletneeded=List.fold_right(intern_library~intern_mode:Root~enable_VMLibrarySet.empty)nrlneededinletneeded=List.revneededin(* first compute the closure of norec, remove closure of check,
add closure of admit, and finally remove norec and check *)letnochk=fold_deps_listLibrarySet.addnrlLibrarySet.emptyinletnochk=fold_deps_listLibrarySet.removemlnochkinletnochk=fold_deps_listLibrarySet.addalnochkin(* explicitly required modules cannot be skipped... *)letnochk=List.fold_rightLibrarySet.remove(List.mapfst(nrl@ml))nochkin(* *)Flags.if_verboseFeedback.msg_notice(fnl()++hv2(str"Ordered list:"++fnl()++prlist(fun(dir,_)->pr_dirpathdir++fnl())needed));letsenv=List.fold_left(check_one_libnochk)(senv,Cmap.empty)neededinFlags.if_verboseFeedback.msg_notice(str"Modules were successfully checked");senv