123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340(************************************************************************)(* * 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 *)moduleError=structtypet=LibUnmappedDir|LibNotFoundletunmapped_dirqid=letprefix,_=Libnames.repr_qualidqidinCErrors.user_errPp.(seq[str"Cannot load ";Libnames.pr_qualidqid;str":";spc();str"no physical path bound to";spc();Names.DirPath.printprefix;fnl()])letlib_not_founddir=letvos=!Flags.load_vos_librariesinletvos_msg=ifvosthen[Pp.str" (while searching for a .vos file)"]else[]inCErrors.user_errPp.(seq([str"Cannot find library ";Names.DirPath.printdir;str" in loadpath"]@vos_msg))letraisedp=function|LibUnmappedDir->unmapped_dir(Libnames.qualid_of_dirpathdp)|LibNotFound->lib_not_founddpend(* If [!Flags.load_vos_libraries]
and the .vos file exists
and this file is not empty
Then load this library
Else load the .vo file or raise error if both are missing *)letselect_vo_file~findbase=letfindext=tryletname=Names.Id.to_stringbase^extinletlpath,file=findnameinOk(lpath,file)withNot_found->ErrorError.LibNotFoundinif!Flags.load_vos_librariesthenbeginmatchfind".vos"with|Ok(_,vosasresvos)when(Unix.statvos).Unix.st_size>0->Okresvos|_->find".vo"endelsefind".vo"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_path,Error.t)Result.t=(* 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,Error.t)Result.t=(* 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|[]->ErrorError.LibUnmappedDir|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->e(** { 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