123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199(*****************************************************************************)(* libCUDF - CUDF (Common Upgrade Description Format) manipulation library *)(* Copyright (C) 2009-2012 Stefano Zacchiroli <zack@upsilon.cc> *)(* *)(* This library is free software: you can redistribute it and/or modify *)(* it under the terms of the GNU Lesser General Public License as *)(* published by the Free Software Foundation, either version 3 of the *)(* License, or (at your option) any later version. A special linking *)(* exception to the GNU Lesser General Public License applies to this *)(* library, see the COPYING file for more information. *)(*****************************************************************************)openExtLibopenPrintfopenCudf_typesopenCudfmodulePP=Cudf_types_pplet(!!)pred=funx->not(predx)typeinconsistency_reason=[`Unsat_depof(pkgname*version)*vpkgformula|`Conflictof(pkgname*version)*vpkglist]typebad_solution_reason=[inconsistency_reason|`Missing_installofvpkglist|`Missing_upgradeofvpkglist|`Unremovedofvpkglist|`Downgradeofvpkglist|`Multi_upgradeofpkgnamelist|`Not_keptofpkgname*version*enum_keep]letexplain_reason=function|`Unsat_dep((name,ver),fmla)->sprintf"Cannot satisfy dependencies %s of package %s (version %d)"(PP.string_of_vpkgformulafmla)namever|`Conflict((name,ver),pkgs)->sprintf"Unresolved conflicts %s of package %s (version %d)"(PP.string_of_vpkglistpkgs)namever|`Missing_installvpkgs->"Unmet installation request, missing packages: "^PP.string_of_vpkglistvpkgs|`Missing_upgradevpkgs->"Unmet upgrade request, missing packages: "^PP.string_of_vpkglistvpkgs|`Unremovedvpkgs->"Unmet remove request, still present packages: "^PP.string_of_vpkglistvpkgs|`Downgradevpkgs->"Unmet upgrade request, not-upgraded: "^PP.string_of_vpkglistvpkgs|`Multi_upgradepkgs->"Unmet upgrade request, not-unique: "^String.concat", "pkgs|`Not_kept(name,ver,keep)->sprintf"Unmet \"Keep\" request %s of package %s (version %d)"(PP.string_of_keepkeep)namever(* XXX not tail-recursive *)letsatisfy_formulaunivfmla=letreason=ref[]inletsat_pkg=mem_installed~include_features:trueunivinletsat=matchList.filter(!!(List.existssat_pkg))fmlawith[]->true|unsat->reason:=unsat;falseinsat,!reasonletdisjointuniv?ignorepkgs=matchList.filter(mem_installed?ignore~include_features:trueuniv)pkgswith|[]->true,[]|pkgs->false,pkgsletis_consistentuniv=letmsg=refNoneintryiter_packages(funpkg->ifpkg.installedthenbegin(matchsatisfy_formulaunivpkg.dependswithfalse,fmla->msg:=Some(`Unsat_dep((pkg.package,pkg.version),fmla));raiseExit|_->());(matchdisjointuniv~ignore:((=%)pkg)pkg.conflictswith|false,pkgs->msg:=Some(`Conflict((pkg.package,pkg.version),pkgs));raiseExit|_->());end)univ;true,!msgwithExit->false,!msg(* for reference, see CUDF §2.3.4, "semantics of requests" *)letis_solution(univ,req)sol=let_=ifuniverse_sizesol<>installed_sizesolthenprerr_endline("WARNING: solution contains not-installed packages,"^" they have been ignored")inletsatvpkg=fst(satisfy_formulasol[[vpkg]])inletand_formula=List.map(funvpkg->[(vpkg:>vpkg)])inletis_succ()=(* XXX not implemented, as it will be pointless with a
diff-like encoding of solutions *)true,[]inletis_cons()=(* check solution consistency (i.e., dep./conflicts) *)matchis_consistentsolwith|true,_->true,[]|false,None->assertfalse|false,Somereason->false,[reason]inletinstall_ok()=(* check "Install" property semantics *)matchList.filter(!!sat)req.installwith|[]->true,[]|l->false,[`Missing_installl]inletremove_ok()=(* check "Remove" property semantics *)matchdisjointsolreq.removewith|true,_->true,[]|false,pkgs->false,[`Unremovedpkgs]inletupgrade_ok()=(* check "Upgrade" property semantics *)matchList.filter(!!sat)req.upgradewith|(_::_)asl->false,[`Missing_upgradel]|[]->letversions_ofunivname=List.map(* real packages *)(funpkg->Somepkg.version)(get_installedunivname)@List.map(* virtual packages; "None" means "all versions" *)(fun(_pkg,version)->version)(who_providesuniv(name,None))inletres=List.fold_left(fun(ok,downgrades,multi)((name,_constr)asvpkg)->matchList.unique(versions_ofsolname)with|[Somev]->letold_installed=versions_ofunivnameinifnot(List.for_all(functionSomev'->v'<=v|None->false)(* XXX: this None will report attempted
upgrade of unversioned virtual packages
as downgrades. Maybe right, maybe not *)old_installed)thenfalse,vpkg::downgrades,multielsetrue&&ok,downgrades,multi|[]->(* impossible: cause the formula is satisfied *)assertfalse|_->false,downgrades,name::multi)(true,[],[])req.upgradein(matchreswith|true,_,_->true,[]|false,downgrades,multi->false,(ifdowngrades<>[]then[`Downgradedowngrades]else[])@(ifmulti<>[]then[`Multi_upgrademulti]else[]))inletkeep_ok()=(* check "Keep" property semantics *)letto_be_kept=get_packages~filter:(funpkg->pkg.installed&&pkg.keep<>`Keep_none)univinList.fold_left(fun(ok,reasons)pkg->letpkg_ok=matchpkg.keepwith|`Keep_version->(try(lookup_packagesol(pkg.package,pkg.version)).installedwithNot_found->false)|`Keep_package->mem_installed~include_features:falsesol(pkg.package,None)|`Keep_feature->fst(satisfy_formulasol(and_formulapkg.provides))|_->assertfalse(* [get_packages ~filter] is broken *)inifpkg_okthenok,reasonselsefalse,(`Not_kept(pkg.package,pkg.version,pkg.keep))::reasons)(true,[])to_be_keptinList.fold_left(fun(is_sol,msgs)test->letres,msg=test()inres&&is_sol,msg@msgs)(true,[])[is_succ;is_cons;install_ok;remove_ok;upgrade_ok;keep_ok]