On Thursday 18 July 2013 10:23:34 Chen, Gui wrote:
> I tried the two ways here:
> 1. steps:
>     'select pattern base'
>     'deselect package A'
>     'resolve pool'
>  Result: A, B, C, D
...
> I attached my source code, which was adapted to use the first way.
> And I have tested in openSUSE 12.2, it runs successfully, my chroot
> includes A, B, C, D however, instead of printing resolve problems.

I modified your code to print out the return values of the status changes you 
apply in mark_poolitem. (restore the location of your repo)

    === U_Tu_(6)pattern:base-.noarch(tmp) applied setToBeInstalled
    This is currently selected:
      U_Tu_(6)pattern:base-.noarch(tmp)

    === U__s_(5)A-0.1-1.x86_64(tmp) applied resetTransact
    === U__s_(5)A-0.1-1.x86_64(tmp) FAILED TO APPLY setToBeUninstalled
    This is currently selected:
      U_Tu_(6)pattern:base-.noarch(tmp)

    This would have been commited:
      U_Ts_(2)D-1.0-1.x86_64(tmp)
      U_Ts_(3)C-0.1-1.x86_64(tmp)
      U_Ts_(4)B-1.0-1.x86_64(tmp)
      U_Ts_(5)A-0.1-1.x86_64(tmp)
      USTu_(6)pattern:base-.noarch(tmp)

Actually you did not deselect A. Only pattern:base is selected and resolved. 
That's why A,B,C,D are installed.



Why?

A PoolItem combines the basic data of a single package(pattern/patch/..) 
instance with a simple status. Package instances are found in the rpm 
database (@System) as well as in repositories.

On a typical system you will have multiple instances of a package called 
e.g. "kernel":

  installed:    kernel-1.0      (@System)
                kernel-1.1      (@System)
  available:    kernel-1.0      (repo: foo)
                kernel-1.1      (repo: foo-updates)
                kernel-1.2      (repo: foo-updates)
                kernel-2.0      (repo: experimental)

So you have 6 PoolItems named "kernel". The status of each PoolItem describes 
this instances fate:

  - unchanged:  nothing happens
  - transacts:  for an installed item it means it gets deleted;
                for an available item it means it gets installed;
  - locked:     not allowed to change the status; raises a resolver
                conflict if a change would be necessary.

In your case you requested setToBeUninstalled for an available PoolItem. As 
the item is not installed, the request can't be fulfilled.



What you wanted is to lock A, in fact all available instances of A if there 
were multiple, so the solver will not be allowed to select any of them and  
raise a conflict instead.

Obviously by looking at a single PoolItem you do not get the whole picture. 
You always need to consider all PoolItems for of given name in order to see 
what is, and what will happen. A 'normal' package update for example will 
cause the installed as well as one available PoolItem to 'transact'. Only in 
case multiple versions of a package can be installed in parallel (e.g. 
kernel), each PoolItem can be treated (more or less) independently.

The above collection of all PoolItems of the same name is available as 
ui::Selectable. This class also allows to get/set an overall status for 
this 'name' (translating this into solver requests and adjusting the 
individual PoolItems):

  ui::Selectable::Ptr sel = ui::Selectable::get( "A" );  // (package) A
  if ( ! sel )
    std::cout << "No package:A in the pool" << endl;
  else if ( ! sel->setStatus( ui::S_Taboo, ResStatus::USER ) )
    std::cout << "FAILED to set S_Taboo on " << sel << endl;
  
  //   ui::Selectable::get( ResKind::package, "A" );
  //   ui::Selectable::get( ResKind::pattern, "base" );
  //   ui::Selectable::get( IdString( "pattern:base" ) );
  //   also get from Solvable or PoolItem
 
With the above fix, you get the desired resolver conflict. Choosing to ignore 
it. you get D,C installed:
 
  Problem:
  ==============================
  pattern:base-.noarch requires A, but this requirement cannot be provided
  uninstallable providers: A-0.1-1.x86_64[tmp]
  ------------------------------
  Solution:
  do not forbid installation of A-0.1-1.x86_64[tmp]
  TransactionSolutionAction: Unlock U_Lu_(5)A-0.1-1.x86_64(tmp)
  Solution:
  do not install pattern:base-.noarch
  TransactionSolutionAction: Keep UBTu_(6)pattern:base-.noarch(tmp)
  Solution:
  break pattern:base-.noarch by ignoring some of its dependencies
  InjectSolutionAction: Weak UBTu_(6)pattern:base-.noarch(tmp)
  ==============================

  PICK LAST SOLUTION
  SOLVE AGAIN
  This would have been commited:
    U_Ts_(2)D-1.0-1.x86_64(tmp)
    U_Ts_(3)C-0.1-1.x86_64(tmp)
    UBTu_(6)pattern:base-.noarch(tmp)



JFYI: There is also a way to directly file solver requests. It is currently a 
bit limited, as it just supports Capabilities (you 'd not be be asking 
for 'package A' but for 'something providing A', which is close, but not 
exactly the same). This API will be enhanced as it's probably nice to usee 
for simple tasks.
        ResPool pool( ResPool::instance() );
        pool.resolver().addRequire( Capability("pattern:base") );
        pool.resolver().addConflict( Capability("A") );
        pool.resolver().resolvePool();
-- 

cu,
    Michael Andres

+------------------------------------------------------------------+
Key fingerprint = 2DFA 5D73 18B1 E7EF A862  27AC 3FB8 9E3A 27C6 B0E4
+------------------------------------------------------------------+
Michael Andres   SUSE LINUX Products GmbH, Development,   m...@suse.de
GF:Jeff Hawn,Jennifer Guild,Felix Imend├Ârffer, HRB16746(AG N├╝rnberg) 
Maxfeldstrasse 5, D-90409 Nuernberg, Germany, ++49 (0)911 - 740 53-0
+------------------------------------------------------------------+
#include <iostream>
#include <vector>
#include <list>

#include <zypp/ZYpp.h>
#include <zypp/ZYppFactory.h>
#include <zypp/PathInfo.h>
#include <zypp/RepoManager.h>
#include <zypp/PoolQuery.h>
#include <zypp/PoolItem.h>
#include <zypp/Package.h>
#include <zypp/Pattern.h>
#include <zypp/ResPool.h>
#include <zypp/ProblemTypes.h>

#include <zypp/repo/RepoType.h>
#include <zypp/sat/Pool.h>

////////////////////////////////////////////////////////////////////////////////
// just some convenience
#include <zypp/TmpPath.h>
#include <zypp/ui/Selectable.h>
using std::endl;

template <class Iterator>
std::ostream & printList( std::ostream & str, Iterator begin, Iterator end )
{
  for_( it, begin, end )
    str << "  " << *it << endl;
  return str;
}

std::ostream & showPool( std::ostream & str, const zypp::ResPool & pool )
{ return printList( str, pool.begin(), pool.end() ); }

template <class PoolFilter>
std::ostream & showPool( std::ostream & str, const zypp::ResPool & pool )
{ return printList( str, zypp::make_filter_begin<PoolFilter>(pool), zypp::make_filter_end<PoolFilter>(pool) ); }
////////////////////////////////////////////////////////////////////////////////

enum RESKIND {PATTERN, PACKAGE};
enum STATUS {INSTALL, UNINSTALL};

bool poolquery_interface(enum RESKIND kind, const char *name, zypp::PoolQuery &pq) {
    zypp::ResKind kinds[2] = {zypp::ResKind::pattern, zypp::ResKind::package};
    zypp::PoolQuery q;
    q.addKind(kinds[kind]);
    q.addAttribute(zypp::sat::SolvAttr::name, name);
    q.setMatchExact();

    if(q.empty())
        return false;

    pq = q;
    return true;
}

bool mark_poolitem( zypp::PoolItem pitem, enum STATUS status, bool reset=false) {
#if 0
    if(reset)
        pitem.status().resetTransact(zypp::ResStatus::USER);

    switch(status){
        case INSTALL:
            pitem.status().setToBeInstalled(zypp::ResStatus::USER);
            std::cout << pitem.resolvable() << std::endl;
            break;
        case UNINSTALL:
            pitem.status().setToBeUninstalled(zypp::ResStatus::USER);
            std::cout << pitem.resolvable() << std::endl;
            break;
        default:
            std::cout << "" << std::endl;
            return false;
    }
#else
// print the return value to show the problem
#define SetStaus( st, pi ) do {						\
   if ( pi.status().st(zypp::ResStatus::USER) )				\
     std::cout << "=== " << pi << " applied " << #st << std::endl;	\
   else									\
     std::cout << "=== " << pi << " FAILED TO APPLY " #st << std::endl;	\
} while( false )
    if(reset)
      SetStaus( resetTransact, pitem );

    switch(status){
        case INSTALL:
	    SetStaus( setToBeInstalled, pitem );
            break;
        case UNINSTALL:
	  SetStaus( setToBeUninstalled, pitem );
            break;
        default:
            std::cout << "" << std::endl;
            return false;
    }
#endif
    return true;
}

////////////////////////////////////////////////////////////////////////////////
// sat::Solvable can be explicitly converted into PoolItem
////////////////////////////////////////////////////////////////////////////////
inline bool mark_poolitem(zypp::sat::Solvable solvable, enum STATUS status, bool reset=false)
{ return mark_poolitem( zypp::PoolItem(solvable), status, reset ); }


int main(int argc, char **argv) {
    zypp::ZYpp::Ptr zyppPtr = zypp::ZYppFactory::instance().getZYpp();
#if 0
    zypp::Pathname sysRoot("/tmp");
#else
    // just to keep my /tmp clean
    zypp::filesystem::TmpDir root;
    zypp::Pathname sysRoot( root.path() );
#endif

    zyppPtr->initializeTarget( sysRoot, false );

    zypp::RepoManagerOptions moptions(sysRoot);
    zypp::RepoManager manager(moptions);

    // set repository info
    zypp::RepoInfo repo;
    repo.setAlias("tmp");
    repo.setEnabled(true);
    repo.setGpgCheck(false);
    repo.setType(zypp::repo::RepoType::RPMMD);
#if 0
    repo.addBaseUrl(zypp::Url("file:///home/test/base/"));
#else
    repo.addBaseUrl(zypp::Url("dir:///tmp/base/"));
#endif
    // add repository and refresh
    manager.addRepository(repo);
    manager.refreshMetadata(repo, zypp::RepoManager::RefreshIfNeededIgnoreDelay);
    manager.buildCache(repo, zypp::RepoManager::BuildIfNeeded);
    manager.loadFromCache(repo);

#if 0
    zypp::sat::Pool pool = zypp::sat::Pool::instance();
#else
    // don't need the lowlevel satpool. ResPool has the PoolItems, the Resolver, ...
    zypp::ResPool pool( zypp::ResPool::instance() );
#endif

    zypp::ui::Selectable::Ptr sel;
#if 0
    sel = zypp::ui::Selectable::get( zypp::ResKind::pattern, "base" );
    if ( ! sel )
      std::cout << "No pattern:base in the pool" << endl;
    else if ( ! sel->setStatus( zypp::ui::S_Install, zypp::ResStatus::USER ) )
      std::cout << "FAILED to set S_Install on " << sel << endl;
    std::cout << sel << endl;
#else
    // search pattern 'base'
    zypp::PoolQuery ptnquery;
    if(!poolquery_interface(PATTERN, "base", ptnquery)) {
        std::cout << "\npattern base no found" << std::endl;
        return -1;
    }
    //  Using a PoolQuery like you did also possible, but directly accessing
    //  the Selectable has far less overhead.
    //
    //  Note that the PoolQuery is a SolvIterMixin. The result can be iterated
    //  - as Solvable		solvableBegin/solvableEnd (begin/end)
    //  - as PoolItem 		poolItemBegin/poolItemEnd
    //  - ui::Selectable::Ptr	selectableBegin/selectableBegin

    // mark pattern 'base' to be installed
    mark_poolitem(*ptnquery.begin(), INSTALL);
#endif
    showPool<zypp::resfilter::ByTransact>( std::cout << "This is currently selected:" << endl, pool );

#if 1 // superfluous, just to demonstrate the error
    // search package 'A'
    zypp::PoolQuery pkgquery;
    if(!poolquery_interface(PACKAGE, "A", pkgquery)) {
        std::cout << "\npackage A no found" << std::endl;
        return -1;
    }

    // mark package 'A' to be uninstalled
    mark_poolitem(*pkgquery.begin(), UNINSTALL, true);
    showPool<zypp::resfilter::ByTransact>( std::cout << "This is currently selected:" << endl, pool );

    std::cout << endl << "=== The above does not work; going to lock A:" << endl;
#endif

    // Lock package A
    sel = zypp::ui::Selectable::get( "A" );  // (ResKind::package) A
    if ( ! sel )
      std::cout << "No package:A in the pool" << endl;
    else if ( ! sel->setStatus( zypp::ui::S_Taboo, zypp::ResStatus::USER ) )
      std::cout << "FAILED to set S_Taboo on " << sel << endl;
    std::cout << sel << endl;

    showPool<zypp::resfilter::ByTransact>( std::cout << "This is currently selected:" << endl, pool );
    showPool<zypp::resfilter::ByLock>( std::cout << "This is currently Locked:" << endl, pool );

    // resolve transactionset
#if 0
    zyppPtr->resolver()->resolvePool();
#else
    // actually the same, but the above may become deprecated.
    bool depsOK = pool.resolver().resolvePool();
#endif
    zypp::ResolverProblemList probs = zyppPtr->resolver()->problems();
    for (zypp::ResolverProblemList::const_iterator iter = probs.begin(); iter != probs.end(); ++iter)
        std::cout << (*iter) << std::endl;

    // resolve
    if ( ! depsOK )
    {
      zypp::ProblemSolutionList solutions;
      zypp::ResolverProblemList problems( pool.resolver().problems() );
      for_( pit, problems.begin(), problems.end() )
      {
	std::cout << *pit << endl;
	std::cout << "PICK LAST SOLUTION" << endl;
	solutions.push_back( *(--(*pit)->solutions().end()) );
      }
      pool.resolver().applySolutions( solutions );

      std::cout << "SOLVE AGAIN" << endl;
      depsOK = pool.resolver().resolvePool();
      if ( ! depsOK )
	std::cout << "FAILED AGAIN" << endl;
    }



#if 0
    // perform transactionset
    zypp::ZYppCommitPolicy policy;
    policy.restrictToMedia (0); // 0 == install all packages regardless to media
    policy.downloadMode (zypp::DownloadInHeaps);
    policy.syncPoolAfterCommit (true);

    // commit the task
   zypp::ZYppCommitResult result = zyppPtr->commit (policy);
   if(result.noError())
       std::cout << "install OK" << std::endl;
   else
       std::cout << "install FAIL" << std::endl;
#else
    // just a dry run:
    showPool<zypp::resfilter::ByTransact>( std::cout << "This would have been commited:" << endl, pool );
    return 0;
#endif
   return 0;
}

Reply via email to