123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971letis_dir_sep=ifSys.win32||Sys.cygwinthenfunc->c='/'||c='\\'||c=':'elsefunc->c='/'letexplode_path=letrecstartaccpathi=ifi<0thenaccelseifis_dir_sep(String.unsafe_getpathi)thenstartaccpath(i-1)elsecomponentaccpathi(i-1)andcomponentaccpathend_i=ifi<0thenString.takepath(end_+1)::accelseifis_dir_sep(String.unsafe_getpathi)thenstart(String.subpath~pos:(i+1)~len:(end_-i)::acc)path(i-1)elsecomponentaccpathend_(i-1)infunpath->ifpath=Filename.current_dir_namethen[path]elsematchstart[]path(String.lengthpath-1)with|"."::xs->xs|xs->xsmoduleExternal:sigtypetvalto_sexp:tSexp.Encoder.tvalcompare:t->t->Ordering.tvalcompare_val:t->t->Ordering.tvalto_string:t->stringvalof_string:string->tvalrelative:t->string->tvalmkdir_p:t->unitvalbasename:t->stringvalparent:t->tvalinitial_cwd:tvalcwd:unit->tvalextend_basename:t->suffix:string->tvalextension:t->stringvalis_suffix:t->suffix:string->boolvalsplit_extension:t->t*stringvalas_local:t->stringend=structincludeInterned.No_interning(structletinitial_size=512letresize_policy=Interned.Greedyletorder=Interned.Naturalend)()letcompare_valxy=String.compare(to_stringx)(to_stringy)letas_stringx~f=to_stringx|>f|>makeletextend_basenamet~suffix=as_stringt~f:(funt->t^suffix)letis_suffixt~suffix=String.is_suffix(to_stringt)~suffixletof_stringt=ifFilename.is_relativetthenExn.code_error"Path.External.of_string: relative path given"["t",Sexp.Encoder.stringt];maketletto_sexpt=Sexp.Encoder.string(to_stringt)(*
let rec cd_dot_dot t =
match Unix.readlink t with
| exception _ -> Filename.dirname t
| t -> cd_dot_dot t
let relative initial_t path =
let rec loop t components =
match components with
| [] | ["." | ".."] ->
die "invalid filename concatenation: %s / %s" initial_t path
| [fn] -> Filename.concat t fn
| "." :: rest -> loop t rest
| ".." :: rest -> loop (cd_dot_dot t) rest
| comp :: rest -> loop (Filename.concat t comp) rest
in
loop initial_t (explode_path path)
*)letrelativexy=matchywith|"."->x|_->make(Filename.concat(to_stringx)y)letrecmkdir_pt=lett_s=to_stringtinletp_s=Filename.dirnamet_sinletp=makep_sinifp<>tthentryUnix.mkdirt_s0o777with|Unix.Unix_error(EEXIST,_,_)->()|Unix.Unix_error(ENOENT,_,_)->mkdir_pp;Unix.mkdirt_s0o777letbasenamet=Filename.basename(to_stringt)letparentt=as_string~f:Filename.dirnametletextensiont=Filename.extension(to_stringt)letsplit_extensiont=lets,ext=Filename.split_extension(to_stringt)in(makes,ext)letcwd()=make(Sys.getcwd())letinitial_cwd=cwd()letas_localt=lets=to_stringtin"."^sendmoduleLocal:sigtypetvalto_sexp:tSexp.Encoder.tvalroot:tvalis_root:t->boolvalcompare:t->t->Ordering.tvalcompare_val:t->t->Ordering.tvalequal:t->t->boolvalof_string:?error_loc:Loc.t->string->tvalto_string:t->stringvalrelative:?error_loc:Loc.t->t->string->tvalappend:t->t->tvalparent:t->tvalmkdir_p:t->unitvaldescendant:t->of_:t->toptionvalis_descendant:t->of_:t->boolvalreach:t->from:t->stringvalbasename:t->stringvalextend_basename:t->suffix:string->tvalextension:t->stringvalis_suffix:t->suffix:string->boolvalsplit_extension:t->t*stringmoduleSet:Set.Swithtypeelt=tmodulePrefix:sigtypelocal=ttypetvalmake:local->tvaldrop:t->local->localoption(* for all local path p, drop (invalid p = None) *)valinvalid:tendwithtypelocal:=tend=struct(* either "." for root, either a '/' separated list of components
other that ".", ".." and not containing '/'. *)includeInterned.No_interning(structletinitial_size=512letresize_policy=Interned.Greedyletorder=Interned.Naturalend)()letcompare_valxy=String.compare(to_stringx)(to_stringy)letequalxy=matchcomparexywith|Eq->true|Gt|Lt->falseletroot=make"."letis_roott=t=rootletis_suffixt~suffix=String.is_suffix(to_stringt)~suffixletto_list=letreclooptaccij=ifi=0thenString.taketj::accelsematcht.[i-1]with|'/'->loopt(String.subt~pos:i~len:(j-i)::acc)(i-1)(i-1)|_->looptacc(i-1)jinfunt->ifis_roottthen[]elselett=to_stringtinletlen=String.lengthtinloopt[]lenlenletparentt=ifis_roottthenExn.code_error"Path.Local.parent called on the root"[]elselett=to_stringtinmatchString.rindex_fromt(String.lengtht-1)'/'with|exceptionNot_found->root|i->make(String.taketi)letbasenamet=ifis_roottthenExn.code_error"Path.Local.basename called on the root"[]elselett=to_stringtinletlen=String.lengthtinmatchString.rindex_fromt(len-1)'/'with|exceptionNot_found->t|i->String.subt~pos:(i+1)~len:(len-i-1)letto_sexpt=Sexp.Encoder.string(to_stringt)letrelative?error_loctpath=ifnot(Filename.is_relativepath)then(Exn.code_error"Local.relative: received absolute path"["t",to_sexpt;"path",Sexp.Encoder.stringpath]);letreclooptcomponents=matchcomponentswith|[]->Result.Okt|"."::rest->looptrest|".."::rest->ifis_roottthenResult.Error()elseloop(parentt)rest|fn::rest->ifis_roottthenloop(makefn)restelseloop(make(to_stringt^"/"^fn))restinmatchloopt(explode_pathpath)with|Result.Okt->t|Error()->Exn.fatalf?loc:error_loc"path outside the workspace: %s from %s"path(to_stringt)letis_canonicalized=letrecbefore_slashsi=ifi<0thenfalseelsematchs.[i]with|'/'->false|'.'->before_dot_slashs(i-1)|_->in_components(i-1)andbefore_dot_slashsi=ifi<0thenfalseelsematchs.[i]with|'/'->false|'.'->before_dot_dot_slashs(i-1)|_->in_components(i-1)andbefore_dot_dot_slashsi=ifi<0thenfalseelsematchs.[i]with|'/'->false|_->in_components(i-1)andin_componentsi=ifi<0thentrueelsematchs.[i]with|'/'->before_slashs(i-1)|_->in_components(i-1)infuns->letlen=String.lengthsinlen=0||before_slashs(len-1)letof_string?error_locs=matchswith|""|"."->root|_whenis_canonicalizeds->makes|_->relativeroots?error_locletrecmkdir_pt=ifis_roottthen()elselett_s=to_stringtintryUnix.mkdirt_s0o777with|Unix.Unix_error(EEXIST,_,_)->()|Unix.Unix_error(ENOENT,_,_)ase->letparent=parenttinifis_rootparentthenraiseeelsebeginmkdir_pparent;Unix.mkdirt_s0o777endletappendab=matchis_roota,is_rootbwith|true,_->b|_,true->a|_,_->make((to_stringa)^"/"^(to_stringb))letdescendantt~of_=ifis_rootof_thenSometelseift=of_thenSomerootelselett=to_stringtinletof_=to_stringof_inletof_len=String.lengthof_inlett_len=String.lengthtinif(t_len>of_len&&t.[of_len]='/'&&String.is_prefixt~prefix:of_)thenSome(make(String.dropt(of_len+1)))elseNoneletis_descendantt~of_=is_rootof_||t=of_||(lett=to_stringtinletof_=to_stringof_inletof_len=String.lengthof_inlett_len=String.lengthtin(t_len>of_len&&t.[of_len]='/'&&String.is_prefixt~prefix:of_))letreacht~from=letreclooptfrom=matcht,fromwith|a::t,b::fromwhena=b->looptfrom|_->matchList.fold_leftfrom~init:t~f:(funacc_->".."::acc)with|[]->"."|l->(String.concatl~sep:"/")inloop(to_listt)(to_listfrom)letextend_basenamet~suffix=make(to_stringt^suffix)letextensiont=Filename.extension(to_stringt)letsplit_extensiont=lets,ext=Filename.split_extension(to_stringt)in(makes,ext)modulePrefix=structletmake_path=maketypet={len:int;path:string;path_slash:string}letmakep=ifis_rootpthenExn.code_error"Path.Local.Prefix.make"["path",to_sexpp];letp=to_stringpin{len=String.lengthp;path=p;path_slash=p^"/"}letdroptp=letp=to_stringpinletlen=String.lengthpiniflen=t.len&&p=t.paththenSomerootelseString.drop_prefixp~prefix:t.path_slash|>Option.map~f:make_pathletinvalid={len=-1;path="/";path_slash="/"}endendlet(abs_root,set_root)=letroot_dir=refNoneinletset_rootnew_root=match!root_dirwith|None->root_dir:=Somenew_root|Someroot_dir->Exn.code_error"set_root: cannot set root_dir more than once"["root_dir",External.to_sexproot_dir;"new_root_dir",External.to_sexpnew_root]inletabs_root=lazy(match!root_dirwith|None->Exn.code_error"root_dir: cannot use root dir before it's set"[]|Someroot_dir->root_dir)in(abs_root,set_root)moduleKind=structtypet=|ExternalofExternal.t|LocalofLocal.tletto_absolute_filenamet=matchtwith|Externals->External.to_strings|Locall->External.to_string(External.relative(Lazy.forceabs_root)(Local.to_stringl))letto_string=function|Localt->Local.to_stringt|Externalt->External.to_stringtletto_sexpt=Sexp.Encoder.string(to_stringt)letof_strings=ifFilename.is_relativesthenLocal(Local.of_strings)elseExternal(External.of_strings)let_=letroot=LocalLocal.rootinassert(of_string""=root);assert(of_string"."=root)let_relative?error_loctfn=matchtwith|Localt->Local(Local.relative?error_loctfn)|Externalt->External(External.relativetfn)letmkdir_p=function|Localt->Local.mkdir_pt|Externalt->External.mkdir_ptletappend_localxy=matchxwith|Localx->Local(Local.appendxy)|Externalx->External(External.relativex(Local.to_stringy))endlet(build_dir_kind,build_dir_prefix,set_build_dir)=letbuild_dir=refNoneinletbuild_dir_prefix=refNoneinletset_build_dir(new_build_dir:Kind.t)=match!build_dirwith|None->(matchnew_build_dirwith|External_->()|Localp->ifLocal.is_rootp||Local.parentp<>Local.rootthenExn.fatalf"@{<error>Error@}: Invalid build directory: %s\n\
The build directory must be an absolute path or \
a sub-directory of the root of the workspace."(Local.to_stringp|>String.maybe_quoted));build_dir:=Somenew_build_dir;build_dir_prefix:=Some(matchnew_build_dirwith|Localp->Local.Prefix.makep|External_->Local.Prefix.invalid)|Somebuild_dir->Exn.code_error"set_build_dir: cannot set build_dir more than once"["build_dir",Kind.to_sexpbuild_dir;"new_build_dir",Kind.to_sexpnew_build_dir]inletbuild_dir=lazy(match!build_dirwith|None->Exn.code_error"build_dir: cannot use build dir before it's set"[]|Somebuild_dir->build_dir)inletbuild_dir_prefix=lazy(match!build_dir_prefixwith|None->Exn.code_error"build_dir: cannot use build dir before it's set"[]|Someprefix->prefix)in(build_dir,build_dir_prefix,set_build_dir)moduleT:sigtypet=private|ExternalofExternal.t|In_source_treeofLocal.t|In_build_dirofLocal.tvalcompare:t->t->Ordering.tvalequal:t->t->boolvalhash:t->intvalin_build_dir:Local.t->tvalin_source_tree:Local.t->tvalexternal_:External.t->tend=structtypet=|ExternalofExternal.t|In_source_treeofLocal.t|In_build_dirofLocal.tletcomparexy=matchx,ywith|Externalx,Externaly->External.comparexy|External_,_->Lt|_,External_->Gt|In_source_treex,In_source_treey->Local.comparexy|In_source_tree_,_->Lt|_,In_source_tree_->Gt|In_build_dirx,In_build_diry->Local.comparexyletequal(x:t)(y:t)=x=ylethash=Hashtbl.hashletin_build_dirs=In_build_dirsletin_source_trees=In_source_treesletexternal_e=ExternaleendincludeTletbuild_dir=in_build_dirLocal.rootletis_root=function|In_source_trees->Local.is_roots|In_build_dir_|External_->falsemoduleMap=Map.Make(T)letkind=function|In_build_dirp->Kind.append_local(Lazy.forcebuild_dir_kind)p|In_source_trees->Kind.Locals|Externals->Kind.Externalsletis_managed=function|In_build_dir_|In_source_tree_->true|External_->falseletto_stringt=matchtwith|In_source_treep->Local.to_stringp|Externalp->External.to_stringp|In_build_dirp->matchLazy.forcebuild_dir_kindwith|Localb->Local.to_string(Local.appendbp)|Externalb->ifLocal.is_rootpthenExternal.to_stringbelseFilename.concat(External.to_stringb)(Local.to_stringp)letto_string_maybe_quotedt=String.maybe_quoted(to_stringt)letroot=in_source_treeLocal.rootletmake_local_pathp=matchLocal.Prefix.drop(Lazy.forcebuild_dir_prefix)pwith|None->in_source_treep|Somep->in_build_dirpletof_local=make_local_pathletrelative?error_loctfn=matchfnwith|""|"."->t|_whennot(Filename.is_relativefn)->external_(External.of_stringfn)|_->matchtwith|In_source_treep->make_local_path(Local.relativepfn?error_loc)|In_build_dirp->in_build_dir(Local.relativepfn?error_loc)|Externals->external_(External.relativesfn)letof_string?error_locs=matchswith|""|"."->in_source_treeLocal.root|s->ifnot(Filename.is_relatives)thenexternal_(External.of_strings)elsemake_local_path(Local.of_strings?error_loc)letto_sexpt=letconstrfxy=Sexp.Encoder.(pairstringf)(x,y)inmatchtwith|In_build_dirs->constrLocal.to_sexp"In_build_dir"s|In_source_trees->constrLocal.to_sexp"In_source_tree"s|Externals->constrExternal.to_sexp"External"sletof_filename_relative_to_initial_cwdfn=external_(ifFilename.is_relativefnthenExternal.relativeExternal.initial_cwdfnelseExternal.of_stringfn)letto_absolute_filenamet=Kind.to_absolute_filename(kindt)letexternal_of_localx~root=External.to_string(External.relativeroot(Local.to_stringx))letexternal_of_in_source_treex=external_of_localx~root:(Lazy.forceabs_root)letreacht~from=matcht,fromwith|Externalt,_->External.to_stringt|In_source_treet,In_source_treefrom|In_build_dirt,In_build_dirfrom->Local.reacht~from|In_source_treet,In_build_dirfrom->beginmatchLazy.forcebuild_dir_kindwith|Localb->Local.reacht~from:(Local.appendbfrom)|External_->external_of_in_source_treetend|In_build_dirt,In_source_treefrom->beginmatchLazy.forcebuild_dir_kindwith|Localb->Local.reach(Local.appendbt)~from|Externalb->external_of_localt~root:bend|In_source_treet,External_->external_of_in_source_treet|In_build_dirt,External_->matchLazy.forcebuild_dir_kindwith|Localb->external_of_in_source_tree(Local.appendbt)|Externalb->external_of_localt~root:bletreach_for_running?(from=root)t=letfn=reacht~frominmatchFilename.analyze_program_namefnwith|In_path->"./"^fn|_->fnletdescendantt~of_=matchkindt,kindof_with|Localt,Localof_->Option.map~f:in_source_tree(Local.descendantt~of_)|_,_->Noneletis_descendantt~of_=matchkindt,kindof_with|Localt,Localof_->Local.is_descendantt~of_|_,_->falseletappend_localab=matchawith|In_source_treea->in_source_tree(Local.appendab)|In_build_dira->in_build_dir(Local.appendab)|Externala->external_(External.relativea(Local.to_stringb))letappendab=matchbwith|In_build_dir_|External_->Exn.code_error"Path.append called with directory that's \
not in the source tree"["a",to_sexpa;"b",to_sexpb]|In_source_treeb->append_localabletbasenamet=matchkindtwith|Localt->Local.basenamet|Externalt->External.basenametletparent=function|Externals->letparent=External.parentsinifparent=sthenNoneelseSome(external_parent)|In_source_treep|In_build_dirpwhenLocal.is_rootp->None|In_source_treel->Some(in_source_tree(Local.parentl))|In_build_dirl->Some(in_build_dir(Local.parentl))letparent_exnt=matchparenttwith|Somep->p|None->Exn.code_error"Path.parent:exn t is root"["t",to_sexpt]letis_strict_descendant_of_build_dir=function|In_build_dirp->not(Local.is_rootp)|In_source_tree_|External_->falseletis_in_build_dir=function|In_build_dir_->true|In_source_tree_|External_->falseletis_in_source_tree=function|In_source_tree_->true|In_build_dir_|External_->falseletis_alias_stamp_file=function|In_build_dirs->String.is_prefix(Local.to_strings)~prefix:".aliases/"|In_source_tree_|External_->falseletextract_build_context=function|In_source_tree_|External_->None|In_build_dirpwhenLocal.is_rootp->None|In_build_dirt->lett=Local.to_stringtinbeginmatchString.lsplit2t~on:'/'with|None->Some(t,in_source_treeLocal.root)|Some(before,after)->Some(before,after|>Local.of_string|>in_source_tree)endletextract_build_context_dir=function|In_source_tree_|External_->None|In_build_dirt->lett_str=Local.to_stringtinbeginmatchString.lsplit2t_str~on:'/'with|None->Some(in_build_dirt,in_source_treeLocal.root)|Some(before,after)->Some(in_build_dir(Local.of_stringbefore),after|>Local.of_string|>in_source_tree)endletdrop_build_contextt=Option.map(extract_build_contextt)~f:sndletdrop_build_context_exnt=matchextract_build_contexttwith|None->Exn.code_error"Path.drop_build_context_exn"["t",to_sexpt]|Some(_,t)->tletdrop_optional_build_contextt=matchextract_build_contexttwith|None->t|Some(_,t)->tletlocal_src=Local.of_string"src"letlocal_build=Local.of_string"build"letsandbox_managed_paths~sandbox_dirt=matchtwith|External_->t|In_source_treep->append_localsandbox_dir(Local.appendlocal_srcp)|In_build_dirp->append_localsandbox_dir(Local.appendlocal_buildp)letsplit_first_componentt=matchkindt,is_roottwith|Localt,false->lett=Local.to_stringtinbeginmatchString.lsplit2t~on:'/'with|None->Some(t,root)|Some(before,after)->Some(before,after|>Local.of_string|>in_source_tree)end|_,_->Noneletexplodet=matchkindtwith|LocalpwhenLocal.is_rootp->Some[]|Locals->Some(String.split(Local.to_strings)~on:'/')|External_->Noneletexplode_exnt=matchexplodetwith|Somes->s|None->Exn.code_error"Path.explode_exn"["path",to_sexpt]letexistst=trySys.file_exists(to_stringt)withSys_error_->falseletreaddir_unsortedt=Sys.readdir(to_stringt)|>Array.to_listletis_directoryt=trySys.is_directory(to_stringt)withSys_error_->falseletrmdirt=Unix.rmdir(to_stringt)letwin32_unlinkfn=tryUnix.unlinkfnwithUnix.Unix_error(Unix.EACCES,_,_)ase->(* Try removing the read-only attribute *)tryUnix.chmodfn0o666;Unix.unlinkfnwith_->raiseeletunlink_operation=ifSys.win32thenwin32_unlinkelseUnix.unlinkletunlinkt=unlink_operation(to_stringt)letunlink_no_errt=tryunlinktwith_->()letbuild_dir_exists()=is_directorybuild_dirletensure_build_dir_exists()=matchkindbuild_dirwith|Localp->Local.mkdir_pp|Externalp->letp=External.to_stringpintryUnix.mkdirp0o777with|Unix.Unix_error(EEXIST,_,_)->()|Unix.Unix_error(ENOENT,_,_)->Exn.fatalf"Cannot create external build directory %s. \
Make sure that the parent dir %s exists."p(Filename.dirnamep)letextend_basenamet~suffix=matchtwith|In_source_treet->in_source_tree(Local.extend_basenamet~suffix)|In_build_dirt->in_build_dir(Local.extend_basenamet~suffix)|Externalt->external_(External.extend_basenamet~suffix)letinsert_after_build_dir_exn=leterrorab=Exn.code_error"Path.insert_after_build_dir_exn"["path",to_sexpa;"insert",Sexp.Encoder.stringb]infunab->matchawith|In_build_dira->in_build_dir(Local.append(Local.of_stringb)a)|In_source_tree_|External_->errorabletrm_rf=letrecloopdir=Array.iter(Sys.readdirdir)~f:(funfn->letfn=Filename.concatdirfninmatchUnix.lstatfnwith|{st_kind=S_DIR;_}->loopfn|_->unlink_operationfn);Unix.rmdirdirinfunt->ifnot(is_managedt)then(Exn.code_error"Path.rm_rf called on external dir"["t",to_sexpt]);letfn=to_stringtinmatchUnix.lstatfnwith|exceptionUnix.Unix_error(ENOENT,_,_)->()|_->loopfnletmkdir_p=function|Externals->External.mkdir_ps|In_source_trees->Local.mkdir_ps|In_build_dirk->Kind.mkdir_p(Kind.append_local(Lazy.forcebuild_dir_kind)k)letcomparexy=matchx,ywith|Externalx,Externaly->External.compare_valxy|External_,_->Lt|_,External_->Gt|In_source_treex,In_source_treey->Local.compare_valxy|In_source_tree_,_->Lt|_,In_source_tree_->Gt|In_build_dirx,In_build_diry->Local.compare_valxyletextensiont=matchtwith|Externalt->External.extensiont|In_build_dirt|In_source_treet->Local.extensiontletsplit_extensiont=matchtwith|Externalt->lett,ext=External.split_extensiontin(external_t,ext)|In_build_dirt->lett,ext=Local.split_extensiontin(in_build_dirt,ext)|In_source_treet->lett,ext=Local.split_extensiontin(in_source_treet,ext)letppppft=Format.pp_print_stringppf(to_string_maybe_quotedt)letpp_debugppf=function|In_source_trees->Format.fprintfppf"(In_source_tree %S)"(Local.to_strings)|In_build_dirs->Format.fprintfppf"(In_build_dir %S)"(Local.to_strings)|Externals->Format.fprintfppf"(External %S)"(External.to_strings)moduleSet=structincludeSet.Make(T)letto_sexpt=Sexp.Encoder.(listto_sexp)(to_listt)letof_string_setss~f=String.Set.to_listss|>List.map~f|>of_listendletin_sources=in_source_tree(Local.of_strings)letis_suffixp~suffix=matchpwith|In_build_dirl|In_source_treel->Local.is_suffixl~suffix|Externalp->External.is_suffixp~suffixmoduleTable=Hashtbl.Make(T)moduleInternal=structletraw_kind=function|In_build_dirl->Kind.Locall|In_source_treel->Locall|Externall->ExternallendmoduleL=struct(* TODO more efficient implementation *)letrelativet=List.fold_left~init:t~f:relativeendletlocal_part=function|Externale->Local.of_string(External.as_locale)|In_source_treel->l|In_build_dirl->l