Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file env.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260openOdoc_compat(*
* Copyright (c) 2014 Leo White <leo@lpw25.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)(* We are slightly more flexible here than OCaml usually is, and allow
'linking' of modules that have the same name. This is because we do
documentation at a package level - it's perfectly acceptable to have
libraries within a package that are never meant to be linked into the same
binary, however package-level documents such as module and type indexes
effectively have to link those libraries together. Hence we may find
ourselves in the unfortunate situation where there are multiple modules with the same
name in our include path. We therefore maintain a mapping of module/page
name to Root _list_. Where we've already made a judgement about which module
we're looking for we have a digest, and can pick the correct module. When we
don't (for example, when handling package-level mld files), we pick the
first right now. The ocamldoc syntax doesn't currently allow for specifying
more accurately than just the module name anyway.
Where we notice this ambiguity we warn the user to wrap their libraries,
which will generally fix this issue. *)openOr_errortypet={expander:Odoc_xref.expander;resolver:Odoc_xref.resolver;}moduleAccessible_paths=structtypet={root_map:Fs.File.tOdoc_model.Root.Hash_table.t;file_map:(string,Odoc_model.Root.tlist)Hashtbl.t;directories:Fs.Directory.tlist;}letcreate~directories={root_map=Odoc_model.Root.Hash_table.create42;file_map=Hashtbl.create42;directories}letfind_file_by_nametname=letuname=name^".odoc"inletlname=String.uncapitalize_asciiname^".odoc"inletrecloopacc=function|[]->acc|directory::dirs->letlfile=Fs.File.create~directory~name:lnameinmatchUnix.stat(Fs.File.to_stringlfile)with|_->loop(lfile::acc)dirs|exceptionUnix.Unix_error_->letufile=Fs.File.create~directory~name:unameinmatchUnix.stat(Fs.File.to_stringufile)with|_->loop(ufile::acc)dirs|exceptionUnix.Unix_error_->loopaccdirsinloop[]t.directories(* If there's only one possible file we've discovered in the search path
we can check the digest right now. If there's more than one, we defer
until further up the call stack *)letcheck_optional_digest?digestfilename(roots:Odoc_model.Root.tlist)=matchroots,digestwith|[root],SomedwhenDigest.comparedroot.digest<>0->letwarning=Odoc_model.Error.filename_only"Digest mismatch"filenameinprerr_endline(Odoc_model.Error.to_stringwarning);roots|_->rootsletfind_roott~filename=matchHashtbl.findt.file_mapfilenamewith|roots->roots|exceptionNot_found->letpaths=find_file_by_nametfilenamein(* This could be the empty list *)letfilter_mapfl=List.fold_right(funxacc->matchfxwith|Somey->y::acc|None->acc)l[]inletsafe_readfile=matchRoot.readfilewith|Okroot->Some(root,file)|Error(`Msgmsg)->letwarning=Odoc_model.Error.filename_onlymsg(Fs.File.to_stringfile)inprerr_endline(Odoc_model.Error.to_stringwarning);None|exceptionEnd_of_file->letwarning=Odoc_model.Error.filename_only"End_of_file while reading"(Fs.File.to_stringfile)inprerr_endline(Odoc_model.Error.to_stringwarning);Noneinletroots_paths=filter_mapsafe_readpathsinletroots=List.mapfstroots_pathsinHashtbl.addt.file_mapfilenameroots;List.iter(fun(root,path)->Odoc_model.Root.Hash_table.addt.root_maprootpath)roots_paths;rootsletfile_of_roottroot=tryOdoc_model.Root.Hash_table.findt.root_maprootwithNot_found->let_roots=matchroot.filewith|Pagepage_name->letfilename="page-"^page_nameincheck_optional_digest~digest:root.digestfilename@@find_roott~filename|Compilation_unit{name;_}->check_optional_digest~digest:root.digestname@@find_roott~filename:nameinOdoc_model.Root.Hash_table.findt.root_maprootendletreclookup_unit~important_digestsaptarget_name=lethandle_root(root:Odoc_model.Root.t)=matchroot.filewith|Compilation_unit{hidden;_}->Odoc_xref.Found{root;hidden}|Page_->assertfalseinletfind_root~digest=matchAccessible_paths.find_rootap~filename:target_name,digestwith|[],_->Odoc_xref.Not_found|[r],_->handle_rootr(* Already checked the digest, if one's been specified *)|r::rs,None->Printf.fprintfstderr"Warning, ambiguous lookup. Please wrap your libraries. Possible files:\n%!";letfiles_strs=List.map(funroot->Accessible_paths.file_of_rootaproot|>Fs.File.to_string|>Printf.sprintf" %s")(r::rs)inprerr_endline(String.concat"\n"files_strs);(* We've not specified a digest, let's try the first one *)handle_rootr|roots,Somed->(* If we can't find a module that matches the digest, return Not_found *)tryhandle_root@@List.find(funroot->root.Odoc_model.Root.digest=d)rootswithNot_found->Odoc_xref.Not_foundinfunction|[]whenimportant_digests->Odoc_xref.Not_found|[]->find_root~digest:None|import::imports->matchimportwith|Odoc_model.Lang.Compilation_unit.Import.Unresolved(name,digest)whenname=target_name->beginmatchdigestwith|Nonewhenimportant_digests->Forward_reference|_->find_root~digestend|Odoc_model.Lang.Compilation_unit.Import.ResolvedrootwhenOdoc_model.Root.Odoc_file.nameroot.file=target_name->beginmatchroot.filewith|Compilation_unit{hidden;_}->Found{root;hidden}|Page_->assertfalseend|_->lookup_unit~important_digestsaptarget_nameimportsletlookup_pageaptarget_name=matchAccessible_paths.find_rootap~filename:("page-"^target_name)with|[]->None|[root]->Someroot|root::_roots->Somerootletfetch_pageaproot=matchAccessible_paths.file_of_rootaprootwith|path->Page.loadpath|exceptionNot_found->letmsg=Printf.sprintf"No unit for root: %s\n%!"(Odoc_model.Root.to_stringroot)inError(`Msgmsg)letfetch_unitaproot=matchAccessible_paths.file_of_rootaprootwith|path->Compilation_unit.loadpath|exceptionNot_found->letmsg=Printf.sprintf"No unit for root: %s\n%!"(Odoc_model.Root.to_stringroot)inError(`Msgmsg)typebuilder=[`UnitofCompilation_unit.t|`PageofPage.t]->tletcreate?(important_digests=true)~directories:builder=letap=Accessible_paths.create~directoriesinfununit_or_page->letlookup_unittarget_name:Odoc_xref.lookup_result=matchunit_or_pagewith|`Page_->lookup_unit~important_digests:falseaptarget_name[]|`Unitunit->letlookup_result=lookup_unit~important_digestsaptarget_nameunit.Odoc_model.Lang.Compilation_unit.importsinmatchlookup_resultwith|Not_found->beginletroot=Compilation_unit.rootunitinmatchroot.filewith|Page_->assertfalse|Compilation_unit{name;hidden}whentarget_name=name->Found{root;hidden}|Compilation_unit_->Not_foundend|x->xinletfetch_unitroot:(Odoc_model.Lang.Compilation_unit.t,_)Result.result=matchunit_or_pagewith|`Page_->fetch_unitaproot|`Unitunit->letcurrent_root=Compilation_unit.rootunitinifOdoc_model.Root.equalrootcurrent_rootthenOkunitelsefetch_unitaprootinletlookup_pagetarget_name=lookup_pageaptarget_nameinletfetch_pageroot:(Odoc_model.Lang.Page.t,_)Result.result=matchunit_or_pagewith|`Unit_->fetch_pageaproot|`Pagepage->letcurrent_root=Page.rootpageinifOdoc_model.Root.equalrootcurrent_rootthenOkpageelsefetch_pageaprootinletresolver=Odoc_xref.build_resolverlookup_unitfetch_unitlookup_pagefetch_pageinletexpander=(* CR trefis: what is the ~root param good for? *)letfetch~root:_root=fetch_unitrootinletlookup_s=lookup_unitsinOdoc_xref.build_expander(lookup())fetchin{expander;resolver}letbuildbuilderunit=builderunitletresolvert=t.resolverletexpandert=t.expander