123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652(**************************************************************************)(* *)(* Copyright 2012-2020 OCamlPro *)(* Copyright 2012 INRIA *)(* *)(* All rights reserved. This file is distributed under the terms of the *)(* GNU Lesser General Public License version 2.1, with the special *)(* exception on linking described in the file LICENSE. *)(* *)(**************************************************************************)typerelop=[`Eq|`Neq|`Geq|`Gt|`Leq|`Lt]letneg_relop=function|`Eq->`Neq|`Neq->`Eq|`Geq->`Lt|`Gt->`Leq|`Leq->`Gt|`Lt->`Geqletstring_of_relop=OpamPrinter.FullPos.relop_kindtypeversion_constraint=relop*OpamPackage.Version.ttypeatom=OpamPackage.Name.t*version_constraintoptionletstring_of_atom=function|n,None->OpamPackage.Name.to_stringn|n,Some(r,c)->Printf.sprintf"%s (%s %s)"(OpamPackage.Name.to_stringn)(string_of_relopr)(OpamPackage.Version.to_stringc)letshort_string_of_atom=function|n,None->OpamPackage.Name.to_stringn|n,Some(`Eq,c)->Printf.sprintf"%s.%s"(OpamPackage.Name.to_stringn)(OpamPackage.Version.to_stringc)|n,Some(r,c)->Printf.sprintf"%s%s%s"(OpamPackage.Name.to_stringn)(string_of_relopr)(OpamPackage.Version.to_stringc)letstring_of_atomsatoms=OpamStd.List.concat_map" & "short_string_of_atomatomsletatom_of_stringstr=letre=lazyRe.(compile@@whole_string@@seq[group@@rep1@@diffany(set">=<.!");group@@alt[seq[set"<>";opt@@char'='];set"=.";str"!=";];group@@rep1any;])intryletsub=Re.exec(Lazy.forcere)strinletsname=Re.Group.getsub1inletsop=Re.Group.getsub2inletsversion=Re.Group.getsub3inletname=OpamPackage.Name.of_stringsnameinletsop=ifsop="."then"="elsesopinletop=OpamLexer.FullPos.relopsopinletversion=OpamPackage.Version.of_stringsversioninname,Some(op,version)withNot_found|Failure_|OpamLexer.Error_->OpamPackage.Name.of_stringstr,Nonetype'aconjunction='alistletstring_of_conjunctionstring_of_atomc=Printf.sprintf"(%s)"(OpamStd.List.concat_map" & "string_of_atomc)type'adisjunction='alistletstring_of_disjunctionstring_of_atomc=Printf.sprintf"(%s)"(OpamStd.List.concat_map" | "string_of_atomc)type'acnf='alistlistletstring_of_cnfstring_of_atomcnf=letstring_of_clausec=letleft,right=matchcwith[_]->"",""|_->"(",")"inOpamStd.List.concat_map~left~right" | "string_of_atomcinOpamStd.List.concat_map" & "string_of_clausecnftype'adnf='alistlistletstring_of_dnfstring_of_atomcnf=letstring_of_clausec=letleft,right=matchcwith[_]->"",""|_->"(",")"inOpamStd.List.concat_map~left~right" & "string_of_atomcinOpamStd.List.concat_map" | "string_of_clausecnftype'aformula=|Empty|Atomof'a|Blockof'aformula|Andof'aformula*'aformula|Orof'aformula*'aformulaletmake_andab=matcha,bwith|Empty,r|r,Empty->r|a,b->And(a,b)letmake_orab=matcha,bwith|Empty,r|r,Empty->r(* we're not assuming Empty is true *)|a,b->Or(a,b)letstring_of_formulastring_of_af=letrecaux?(in_and=false)f=letparen_if?(cond=false)s=ifcond||OpamFormatConfig.(!r.all_parens)thenPrintf.sprintf"(%s)"selsesinmatchfwith|Empty->"0"|Atoma->paren_if(string_of_aa)|Blockx->Printf.sprintf"(%s)"(auxx)|And(x,y)->paren_if(Printf.sprintf"%s & %s"(aux~in_and:truex)(aux~in_and:truey))|Or(x,y)->paren_if~cond:in_and(Printf.sprintf"%s | %s"(auxx)(auxy))inauxfletrecmapf=function|Empty->Empty|Atomx->fx|And(x,y)->make_and(mapfx)(mapfy)|Or(x,y)->make_or(mapfx)(mapfy)|Blockx->matchmapfxwith|Empty->Empty|x->Blockx(* Maps top-down *)letrecmap_formulaft=lett=ftinmatchtwith|Blockx->Block(map_formulafx)|And(x,y)->make_and(map_formulafx)(map_formulafy)|Or(x,y)->make_or(map_formulafx)(map_formulafy)|x->xletrecmap_up_formulaft=lett=matchtwith|Blockx->f(Block(map_up_formulafx))|And(x,y)->f(make_and(map_up_formulafx)(map_up_formulafy))|Or(x,y)->f(make_or(map_up_formulafx)(map_up_formulafy))|Atomx->f(Atomx)|Empty->Emptyinftletnegneg_atom=map_formula(function|And(x,y)->Or(x,y)|Or(x,y)->And(x,y)|Atomx->Atom(neg_atomx)|x->x)letreciterf=function|Empty->()|Atomx->fx|Blockx->iterfx|And(x,y)->iterfx;iterfy|Or(x,y)->iterfx;iterfyletrecfold_leftfi=function|Empty->i|Atomx->fix|Blockx->fold_leftfix|And(x,y)->fold_leftf(fold_leftfix)y|Or(x,y)->fold_leftf(fold_leftfix)yletrecfold_rightfi=function|Empty->i|Atomx->fix|Blockx->fold_rightfix|And(x,y)->fold_rightf(fold_rightfiy)x|Or(x,y)->fold_rightf(fold_rightfiy)xtypeversion_formula=version_constraintformulatypet=(OpamPackage.Name.t*version_formula)formulaletreccompare_formulafxy=letreccompare_atomx=function|Empty->1|Atomy->fxy|Blocky->compare_atomxy|And(y,z)|Or(y,z)->letr=compare_atomxyinifr<>0thenrelsecompare_atomxzinmatchx,ywith|Empty,Empty->0|Empty,_->-1|_,Empty->1|Atomx,Atomy->fxy|Atomx,y->compare_atomxy|x,Atomy->-1*(compare_atomyx)|Blockx,y|x,Blocky->compare_formulafxy|(And(x,y)|Or(x,y))aslhs,((And(x',y')|Or(x',y'))asrhs)->letl=compare_formulafxx'inifl<>0thenlelseletr=compare_formulafyy'inifr<>0thenrelse(matchlhs,rhswith|And_,And_|Or_,Or_->0|And_,Or_->1|Or_,And_->-1|_->assertfalse)letcompare_relopop1op2=matchop1,op2with|`Lt,`Lt|`Leq,`Leq|`Neq,`Neq|`Eq,`Eq|`Geq,`Geq|`Gt,`Gt->0|`Lt,_->-1|_,`Lt->1|`Leq,_->-1|_,`Leq->1|`Neq,_->-1|_,`Neq->1|`Eq,_->-1|_,`Eq->1|`Geq,_->-1|_,`Geq->1letcompare_version_formula=compare_formula(fun(op1,v1)(op2,v2)->letc=comparev1v2inifc<>0thencelsecompare_relopop1op2)letcompare_nc(n1,c1)(n2,c2)=letc=OpamPackage.Name.comparen1n2inifc<>0thencelsecompare_version_formulac1c2letcompare=compare_formulacompare_ncletrecevalatom=function|Empty->true|Atomx->atomx|Blockx->evalatomx|And(x,y)->evalatomx&&evalatomy|Or(x,y)->evalatomx||evalatomyletrecpartial_evalatom=function|Empty->`FormulaEmpty|Atomx->atomx|And(x,y)->(matchpartial_evalatomx,partial_evalatomywith|`False,_|_,`False->`False|`True,f|f,`True->f|`Formulax,`Formulay->`Formula(And(x,y)))|Or(x,y)->(matchpartial_evalatomx,partial_evalatomywith|`True,_|_,`True->`True|`False,f|f,`False->f|`Formulax,`Formulay->`Formula(Or(x,y)))|Blockx->partial_evalatomxletcheck_reloprelopc=matchrelopwith|`Eq->c=0|`Neq->c<>0|`Geq->c>=0|`Gt->c>0|`Leq->c<=0|`Lt->c<0leteval_reloprelopv1v2=check_reloprelop(OpamPackage.Version.comparev1v2)letcheck_version_formulafv=eval(fun(relop,vref)->eval_reloprelopvvref)fletcheck(name,cstr)package=name=OpamPackage.namepackage&&matchcstrwith|None->true|Some(relop,v)->eval_reloprelop(OpamPackage.versionpackage)vletpackages_of_atoms?(disj=false)pkgsetatoms=(* Conjunction for constraints over the same name (unless [disj] is
specified), but disjunction on the package names *)letffilter=ifdisjthenList.existselseList.for_allinletby_name=List.fold_left(funacc(n,_asatom)->OpamPackage.Name.Map.updaten(funa->atom::a)[]acc)OpamPackage.Name.Map.emptyatomsinOpamPackage.Name.Map.fold(funnameatomsacc->OpamPackage.Set.unionacc@@OpamPackage.Set.filter(funnv->ffilter(funa->checkanv)atoms)(OpamPackage.packages_of_namepkgsetname))by_nameOpamPackage.Set.emptyletsatisfies_dependspkgsetf=eval(fun(name,cstr)->OpamPackage.Set.exists(funnv->check_version_formulacstrnv.version)(OpamPackage.packages_of_namepkgsetname))fletto_stringt=letstring_of_constraint(relop,version)=Printf.sprintf"%s %s"(string_of_reloprelop)(OpamPackage.Version.to_stringversion)inletstring_of_pkg=function|n,Empty->OpamPackage.Name.to_stringn|n,(Atom_asc)->Printf.sprintf"%s %s"(OpamPackage.Name.to_stringn)(string_of_formulastring_of_constraintc)|n,c->Printf.sprintf"%s (%s)"(OpamPackage.Name.to_stringn)(string_of_formulastring_of_constraintc)instring_of_formulastring_of_pkgt(* convert a formula to a CNF *)letcnf_of_formulat=letrecmk_leftxy=matchywith|Blocky->mk_leftxy|And(a,b)->And(mk_leftxa,mk_leftxb)|Empty->x|_->Or(x,y)inletrecmk_rightxy=matchxwith|Blockx->mk_rightxy|And(a,b)->And(mk_rightay,mk_rightby)|Empty->y|_->mk_leftxyinletrecmk=function|Empty->Empty|Blockx->mkx|Atomx->Atomx|And(x,y)->And(mkx,mky)|Or(x,y)->mk_right(mkx)(mky)inmkt(* convert a formula to DNF *)letdnf_of_formulat=letrecmk_leftxy=matchywith|Blocky->mk_leftxy|Or(a,b)->Or(mk_leftxa,mk_leftxb)|_->And(x,y)inletrecmk_rightxy=matchxwith|Blockx->mk_rightxy|Or(a,b)->Or(mk_rightay,mk_rightby)|_->mk_leftxyinletrecmk=function|Empty->Empty|Blockx->mkx|Atomx->Atomx|Or(x,y)->Or(mkx,mky)|And(x,y)->mk_right(mkx)(mky)inmktletverifiesfnv=letname_formula=map(fun((n,_)asa)->ifn=OpamPackage.namenvthenAtomaelseEmpty)(dnf_of_formulaf)inname_formula<>Empty&&eval(fun(_name,cstr)->check_version_formulacstr(OpamPackage.versionnv))name_formulaletpackagespkgsetf=letnames=fold_left(funacc(name,_)->OpamPackage.Name.Set.addnameacc)OpamPackage.Name.Set.emptyfin(* dnf allows us to transform the formula into a union of intervals, where
ignoring atoms for different package names works. *)letdnf=dnf_of_formulafinOpamPackage.Name.Set.fold(funnameacc->(* Ignore conjunctions where [name] doesn't appear *)letname_formula=map(fun((n,_)asa)->ifn=namethenAtomaelseEmpty)dnfinOpamPackage.Set.unionacc@@OpamPackage.Set.filter(funnv->letv=OpamPackage.versionnvineval(fun(_name,cstr)->check_version_formulacstrv)name_formula)(OpamPackage.packages_of_namepkgsetname))namesOpamPackage.Set.empty(* Convert a t an atom formula *)letto_atom_formula(t:t):atomformula=map(fun(x,c)->matchcwith|Empty->Atom(x,None)|cs->map(func->Atom(x,Somec))cs)t(* Convert an atom formula to a t-formula *)letof_atom_formula(a:atomformula):t=letatom(n,v)=matchvwith|None->Atom(n,Empty)|Some(r,v)->Atom(n,Atom(r,v))inmapatomaletandsl=List.fold_leftmake_andEmptylletrecands_to_list=function|Empty->[]|And(e,f)->List.rev_append(rev_ands_to_liste)(ands_to_listf)|Blockf->ands_to_listf|x->[x]andrev_ands_to_list=function|Empty->[]|Blockf->rev_ands_to_listf|And(e,f)->List.rev_append(ands_to_listf)(rev_ands_to_liste)|x->[x]letof_conjunctionc=of_atom_formula(ands(List.rev_map(funx->Atomx)c))letorsl=List.fold_leftmake_orEmptylletrecors_to_list=function|Empty->[]|Or(e,f)->List.rev_append(rev_ors_to_liste)(ors_to_listf)|Blockf->ors_to_listf|x->[x]andrev_ors_to_list=function|Empty->[]|Or(e,f)->List.rev_append(ors_to_listf)(rev_ors_to_liste)|Blockf->rev_ors_to_listf|x->[x]letis_conjunctiont=letrecaux=function|Or_->false|And(a,b)->auxa&&auxb|Blocka->auxa|_->trueinauxtletis_disjunctiont=letrecaux=function|And_->false|Or(a,b)->auxa&&auxb|Blocka->auxa|_->trueinauxtletrecsortcompf=matchfwith|(Empty|Atom_)asf->f|Blockf->Block(sortcompf)|And_asf->ands_to_listf|>List.rev_map(sortcomp)|>List.sort(compare_formulacomp)|>ands|Or_asf->ors_to_listf|>List.rev_map(sortcomp)|>List.sort(compare_formulacomp)|>orsletatomst=fold_right(funaccux->x::accu)[](to_atom_formulat)letto_cnft=letatf=to_atom_formulatinletatoms=fold_right(funacca->a::acc)[]inletconj=rev_ands_to_listatfinifList.for_allis_disjunctionconjthenList.rev_mapatomsconj(* this gives a nice speedup *)elseList.rev_mapatoms@@rev_ands_to_list@@cnf_of_formulaatfletto_dnft=letatf=to_atom_formulatinletatoms=fold_right(funacca->a::acc)[]inletdisj=rev_ors_to_listatfinifList.for_allis_conjunctiondisjthenList.rev_mapatomsdisjelseList.rev_mapatoms@@rev_ors_to_list@@dnf_of_formulaatfletto_conjunctiont=ifis_conjunctiontthenatomstelsefailwith(Printf.sprintf"%s is not a valid conjunction"(to_stringt))letto_disjunctiont=ifis_disjunctiontthenatomstelsefailwith(Printf.sprintf"%s is not a valid disjunction"(to_stringt))letof_disjunctiond=of_atom_formula(ors(List.rev_map(funx->Atomx)d))letget_disjunction_formulaversion_setcstr=(* rev_ors_to_list cstr |>
* List.fold_left *)List.rev_map(funff->matchands_to_listffwith|[]->assertfalse|[Atom_]asat->at|_->OpamPackage.Version.Set.filter(check_version_formulaff)version_set|>OpamPackage.Version.Set.elements|>List.map(funv->Atom(`Eq,v)))(rev_ors_to_listcstr)|>List.flattenletset_to_disjunctionsett=List.map(function|And_->failwith(Printf.sprintf"%s is not a valid disjunction"(to_stringt))|Or_|Block_|Empty->assertfalse|Atom(name,Empty)->[name,None]|Atom(name,Atoma)->[name,Somea]|Atom(name,cstr)->get_disjunction_formula(OpamPackage.versions_of_namesetname)cstr|>List.map(function|Atom(relop,v)->name,Some(relop,v)|_->assertfalse))(ors_to_listt)|>List.flattenletsimplify_ineq_formulavcompf=letvals=fold_left(funacc(_op,x)->x::acc)[]finletvals=List.sort_uniqvcompvalsinletvals_a=Array.of_listvalsinletval_of_inti=vals_a.(i/2)inletint_of_val=letm=List.mapi(funiv->v,2*i+1)valsinfunv->List.assocvmin(* One integer for each value appearing in f, plus one for each interval *)letrecmk_rangesaccn=ifn<0thenaccelsemk_ranges(n::acc)(n-1)inletranges=mk_ranges[](2*Array.lengthvals_a+2)inletint_formula=map(fun(op,x)->Atom(op,int_of_valx))finletvals=List.map(funi->eval(fun(relop,iref)->check_reloprelop(i-iref))int_formula,i)rangesinifList.for_all(fun(t,_)->nott)valsthenNoneelseletrecaux=function|(true,_)::((true,_)::_asr)->auxr|(false,_)::((false,_)::_asr)->auxr|(true,_)::(false,x)::((true,_)::_asr)whenxmod2=1->(`Neq,x)::auxr|(false,_)::(true,x)::((false,_)::_asr)whenxmod2=1->(`Eq,x)::auxr|(true,_)::((false,x)::_asr)->(ifxmod2=1then`Lt,xelse`Leq,x-1)::auxr|(false,_)::((true,x)::_asr)->(ifxmod2=1then`Geq,xelse`Gt,x-1)::auxr|[_]|[]->[]inletrecaux2=function|(`Geq|`Gt|`Neqasop,i)::r->letrecfind_upperacc=function|(`Leq|`Ltasop,i)::r->ands(List.rev_appendacc[Atom(op,val_of_inti)])::aux2r|(`Neq,i)::r->find_upper(Atom(`Neq,val_of_inti)::acc)r|r->ands(List.revacc)::aux2rinfind_upper[Atom(op,val_of_inti)]r|(op,i)::r->Atom(op,val_of_inti)::aux2r|[]->[Empty]inSome(ors(aux2(auxvals)))letsimplify_version_formulaf=simplify_ineq_formulaOpamPackage.Version.comparef(** Takes an ordered list of atoms and a predicate, returns a formula describing
the subset of matching atoms *)letgen_formulalf=letl=List.map(funx->fx,x)linletrecaux(t,xasbound)l=matcht,lwith|true,(false,y)::(true,_)::r|false,(true,y)::(false,_)::r->leta=(iftthen`Neqelse`Eq),yin(matchauxboundrwith|b::r->b::a::r|r->a::r)|true,(true,_)::r|false,(false,_)::r->auxboundr|true,(false,_asbound')::r|false,(true,_asbound')::r->((iftthen`Geqelse`Lt),x)::auxbound'r|_,[]->[(iftthen`Geqelse`Lt),x]inletrecaux2=function|(`Geq|`Neq),_asa::r->letrecfind_upperacc=function|`Lt,_asa::r->ands(List.rev_appendacc[Atoma])::aux2r|`Neq,_asa::r->find_upper(Atoma::acc)r|r->ands(List.revacc)::aux2rinfind_upper[Atoma]r|a::r->Atoma::aux2r|[]->[Empty]inmatchlwith|[]->SomeEmpty|(t,x)::r->matchaux(t,x)rwith|[]->assertfalse|[`Geq,_]->SomeEmpty|[`Lt,_]->None|_::r->Some(ors(aux2r))letformula_of_version_setsetsubset=letmoduleS=OpamPackage.Version.Setinmatchgen_formula(S.elementsset)(funx->S.memxsubset)with|Somef->f|None->invalid_arg"Empty subset"letsimplify_version_setsetf=letmoduleS=OpamPackage.Version.SetinifS.is_emptysetthenEmptyelseletset=fold_left(funset(_relop,v)->S.addvset)setfingen_formula(S.elementsset)(check_version_formulaf)|>OpamStd.Option.defaultf