123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362(************************************************************************)(* * 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) *)(************************************************************************)openUtilmoduleDP=Names.DirPath(** Load paths. Mapping from physical to logical paths. *)typet={path_physical:CUnix.physical_path;path_logical:DP.t;path_implicit:bool;(* true for -R, false for -Q in command line *)path_root:(CUnix.physical_path*DP.t);}letload_paths=Summary.ref([]:tlist)~stage:Summary.Stage.Synterp~name:"LOADPATHS"letlogicalp=p.path_logicalletphysicalp=p.path_physicalletppp=letdir=DP.printp.path_logicalinletpath=Pp.str(CUnix.escaped_string_of_physical_pathp.path_physical)inPp.(hov2(dir++spc()++path))letget_load_paths()=!load_pathsletanomaly_too_many_pathspath=CErrors.anomalyPp.(str"Several logical paths are associated with"++spc()++strpath++str".")letfind_load_pathphys_dir=letphys_dir=CUnix.canonical_path_namephys_dirinletfilterp=String.equalp.path_physicalphys_dirinletpaths=List.filterfilter!load_pathsinmatchpathswith|[]->raiseNot_found|[p]->p|_->anomaly_too_many_pathsphys_dir(* get the list of load paths that correspond to a given logical path *)letfind_with_logical_pathdirpath=List.filter(funp->Names.DirPath.equalp.path_logicaldirpath)!load_pathsletwarn_file_found_multiple_times=CWarnings.create~name:"ambiguous-extra-dep"~category:CWarnings.CoreCategories.filesystem(fun(file,from,other,extra)->Pp.(str"File "++strfile++str" found twice in "++Names.DirPath.printfrom++str":"++spc()++strother++str" (selected),"++spc()++strextra++str"."))letrecfirst_path_containing?locfromfileacc=function|[]->beginmatchaccwith|Somex->x|None->CErrors.user_errPp.(str"File "++strfile++str" not found in "++Names.DirPath.printfrom++str".")end|x::xs->letabspath=x^"/"^fileinifSys.file_existsabspaththenbeginmatchaccwith|None->first_path_containing?locfromfile(Someabspath)xs|Someother->warn_file_found_multiple_times?loc(file,from,other,abspath);first_path_containing?locfromfileaccxsendelsefirst_path_containing?locfromfileaccxsletfind_extra_dep_with_logical_path?loc~from~file()=matchfind_with_logical_pathfromwith|_::_aspaths->letpaths=List.mapphysicalpathsinfirst_path_containing?locfromfileNonepaths|[]->CErrors.user_errPp.(str"No LoadPath found for "++Names.DirPath.printfrom++str".")letremove_load_pathdir=letfilterp=not(String.equalp.path_physicaldir)inload_paths:=List.filterfilter!load_pathsletwarn_overriding_logical_loadpath=CWarnings.create~name:"overriding-logical-loadpath"~category:CWarnings.CoreCategories.filesystem(fun(phys_path,old_path,coq_path)->Pp.(seq[strphys_path;strbrk" was previously bound to ";DP.printold_path;strbrk"; it is remapped to ";DP.printcoq_path]))letadd_load_pathrootphys_pathcoq_path~implicit=letphys_path=CUnix.canonical_path_namephys_pathinletfilterp=String.equalp.path_physicalphys_pathinletbinding={path_logical=coq_path;path_physical=phys_path;path_implicit=implicit;path_root=root;}inmatchList.filterfilter!load_pathswith|[]->load_paths:=binding::!load_paths|[{path_logical=old_path;path_implicit=old_implicit}]->letreplace=ifDP.equalcoq_pathold_paththenimplicit<>old_implicitelselet()=(* Do not warn when overriding the default "-I ." path *)ifnot(DP.equalold_pathLibnames.default_root_prefix)thenwarn_overriding_logical_loadpath(phys_path,old_path,coq_path)intrueinifreplacethenbeginremove_load_pathphys_path;load_paths:=binding::!load_paths;end|_->anomaly_too_many_pathsphys_pathletfilter_pathf=letrecaux=function|[]->[]|p::l->iffp.path_logicalthen(p.path_physical,p.path_logical)::auxlelseauxlinaux!load_pathsleteq_root(phys,log_path)(phys',log_path')=String.equalphysphys'&&Names.DirPath.equallog_pathlog_path'letadd_pathrootfile=function|[]->[root,[file]]|(root',l)::l'asl''->ifeq_rootrootroot'then(root',file::l)::l'else(root,[file])::l''letexpand_path?rootdir=letexact_path=matchrootwithNone->dir|Someroot->Libnames.append_dirpathrootdirinletrecaux=function|[]->[],[]|{path_physical=ph;path_logical=lg;path_implicit=implicit;path_root}::l->letfull,others=auxlinifDP.equalexact_pathlgthen(* Most recent full match comes first *)(ph,lg)::full,otherselseletsuccess=matchrootwith|None->implicit&&Libnames.is_dirpath_suffix_ofdirlg|Someroot->Libnames.(is_dirpath_prefix_ofrootlg&&is_dirpath_suffix_ofdir(drop_dirpath_prefixrootlg))inifsuccessthen(* Only keep partial path in the same "-R" block *)full,add_pathpath_root(ph,lg)otherselsefull,othersinletfull,others=aux!load_pathsin(* Returns the dirpath matching exactly and the ordered list of
-R/-Q blocks with subdirectories that matches *)full,List.mapsndothersletlocate_filefname=letpaths=List.mapphysical!load_pathsinlet_,longfname=System.find_file_in_path~warn:(not!Flags.quiet)pathsfnameinlongfname(************************************************************************)(*s Locate absolute or partially qualified library names in the path *)typelocate_error=LibUnmappedDir|LibNotFoundtype'alocate_result=('a,locate_error)resultletwarn_several_object_files=CWarnings.create~name:"several-object-files"~category:CWarnings.CoreCategories.filesystemPp.(fun(vi,vo)->seq[str"Loading";spc();strvi;strbrk" instead of ";strvo;strbrk" because it is more recent."])letselect_vo_file~findbase=letfindext=tryletname=Names.Id.to_stringbase^extinletlpath,file=findnameinSome(lpath,file)withNot_found->Nonein(* If [!Flags.load_vos_libraries]
and the .vos file exists
and this file is not empty
Then load this library
Else load the most recent between the .vo file and the .vio file,
or if there is only of the two files, take this one,
or raise an error if both are missing. *)letload_most_recent_of_vo_and_vio()=matchfind".vo",find".vio"with|None,None->ErrorLibNotFound|Someres,None|None,Someres->Okres|Some(_,vo),Some(_,viasresvi)whenUnix.((statvo).st_mtime<(statvi).st_mtime)->warn_several_object_files(vi,vo);Okresvi|Someresvo,Some_->Okresvoinif!Flags.load_vos_librariesthenbeginmatchfind".vos"with|Some(_,vosasresvos)when(Unix.statvos).Unix.st_size>0->Okresvos|_->load_most_recent_of_vo_and_vio()endelseload_most_recent_of_vo_and_vio()letfind_firstloadpathbase=matchSystem.all_in_pathloadpathbasewith|[]->raiseNot_found|f::_->fletfind_uniquefullqidloadpathbase=matchSystem.all_in_pathloadpathbasewith|[]->raiseNot_found|[f]->f|_::_asl->CErrors.user_errPp.(str"Required library "++Libnames.pr_qualidfullqid++strbrk" matches several files in path (found "++pr_enumstr(List.mapsndl)++str").")letlocate_absolute_librarydir:CUnix.physical_pathlocate_result=(* Search in loadpath *)letpref,base=Libnames.split_dirpathdirinletloadpath=filter_path(fundir->DP.equaldirpref)inmatchloadpathwith|[]->ErrorLibUnmappedDir|_->matchselect_vo_file~find:(find_firstloadpath)basewith|Ok(_,file)->Okfile|Errorfail->Errorfailletlocate_qualified_library?rootqid:(DP.t*CUnix.physical_path)locate_result=(* Search library in loadpath *)letdir,base=Libnames.repr_qualidqidinmatchexpand_path?rootdirwith|[],[]->ErrorLibUnmappedDir|full_matches,others->letresult=(* Priority to exact matches *)matchselect_vo_file~find:(find_firstfull_matches)basewith|Ok_asx->x|Error_->(* Looking otherwise in -R/-Q blocks of partial matches *)letrecaux=function|[]->ErrorLibUnmappedDir|block::rest->matchselect_vo_file~find:(find_uniqueqidblock)basewith|Ok_asx->x|Error_->auxrestinauxothersinmatchresultwith|Ok(dir,file)->letlibrary=Libnames.add_dirpath_suffixdirbaseinOk(library,file)|Error_ase->eleterror_unmapped_dirqid=letprefix,_=Libnames.repr_qualidqidinCErrors.user_errPp.(seq[str"Cannot load ";Libnames.pr_qualidqid;str":";spc();str"no physical path bound to";spc();DP.printprefix;fnl()])leterror_lib_not_foundqid=letvos=!Flags.load_vos_librariesinletvos_msg=ifvosthen[Pp.str" (while searching for a .vos file)"]else[]inCErrors.user_errPp.(seq([str"Cannot find library ";Libnames.pr_qualidqid;str" in loadpath"]@vos_msg))lettry_locate_absolute_librarydir=matchlocate_absolute_librarydirwith|Okres->res|ErrorLibUnmappedDir->error_unmapped_dir(Libnames.qualid_of_dirpathdir)|ErrorLibNotFound->error_lib_not_found(Libnames.qualid_of_dirpathdir)(** { 5 Extending the load path } *)typevo_path={unix_path:string(** Filesystem path containing vo/ml files *);coq_path:DP.t(** Coq prefix for the path *);implicit:bool(** [implicit = true] avoids having to qualify with [coq_path]
true for -R, false for -Q in command line *);has_ml:bool(** If [has_ml] is true, the directory will also be added to the ml include path *);recursive:bool(** [recursive] will determine whether we explore sub-directories *)}letwarn_cannot_open_path=CWarnings.create~name:"cannot-open-path"~category:CWarnings.CoreCategories.filesystem(fununix_path->Pp.(str"Cannot open "++strunix_path))letwarn_cannot_use_directory=CWarnings.create~name:"cannot-use-directory"~category:CWarnings.CoreCategories.filesystem(fund->Pp.(str"Directory "++strd++strbrk" cannot be used as a Coq identifier (skipped)"))letconvert_stringd=tryNames.Id.of_stringdwith|CErrors.UserError_->letd=Unicode.escaped_if_non_utf8dinwarn_cannot_use_directoryd;raise_notraceExitletadd_vo_pathlp=letunix_path=lp.unix_pathinletimplicit=lp.implicitinletrecursive=lp.recursiveinifSystem.exists_dirunix_paththenletdirs=ifrecursivethenSystem.all_subdirs~unix_pathelse[]inletdirs=List.sort(funab->String.compare(fsta)(fstb))dirsinletprefix=DP.reprlp.coq_pathinletconvert_dirs(lp,cp)=tryletpath=List.rev_mapconvert_stringcp@prefixinSome(lp,DP.makepath)withExit->Noneinletdirs=List.map_filterconvert_dirsdirsinlet()=iflp.has_ml&¬lp.recursivethenMltop.add_ml_dirunix_pathelseiflp.has_ml&&lp.recursivethen(List.iter(fun(lp,_)->Mltop.add_ml_dirlp)dirs;Mltop.add_ml_dirunix_path)else()inletroot=(unix_path,lp.coq_path)inletadd(path,dir)=add_load_pathrootpath~implicitdirin(* deeper dirs registered first and thus be found last *)letdirs=List.revdirsinlet()=List.iteradddirsinadd_load_pathrootunix_path~implicitlp.coq_pathelsewarn_cannot_open_pathunix_path