- start of p5solv and perl bindings
authorMichael Schroeder <mls@suse.de>
Fri, 4 Mar 2011 17:30:58 +0000 (18:30 +0100)
committerMichael Schroeder <mls@suse.de>
Fri, 4 Mar 2011 17:30:58 +0000 (18:30 +0100)
examples/p5solv [new file with mode: 0755]
examples/pysolv
examples/solv.i

diff --git a/examples/p5solv b/examples/p5solv
new file mode 100755 (executable)
index 0000000..60e867b
--- /dev/null
@@ -0,0 +1,378 @@
+#!/usr/bin/perl
+
+use POSIX;
+use Config::IniFiles;
+use File::FnMatch;
+use Data::Dumper;
+use solv;
+use strict;
+
+package Repo::generic;
+
+sub new {
+  my ($class, $attr) = @_;
+  my $r = { %$attr };
+  return bless $r, $class;
+}
+
+sub cachepath {
+  my ($self, $ext) = @_;
+  my $path = $self->{'alias'};
+  $path =~ s/^\./_/s;
+  $path .= $ext ? "_$ext.solvx" : '.solv';
+  $path =~ s/\//_/gs;
+  return "/var/cache/solv/$path";
+}
+
+sub load {
+  my ($self, $pool) = @_;
+  $self->{'handle'} = $pool->add_repo($self->{'alias'});
+  $self->{'handle'}->{'appdata'} = $self;
+  $self->{'handle'}->{'priority'} = 99 - $self->{'priority'};
+  $self->{'cookie'} = '';
+  $self->usecachedrepo();
+}
+
+sub usecachedrepo {
+  my ($self, $ext, $mark) = @_;
+  my $cookie = $ext ? $self->{'extcookie'} : $self->{'cookie'};
+  my $handle = $self->{'handle'};
+  my $cachepath = $self->cachepath();
+  if (sysopen(my $f, $cachepath, POSIX::O_RDONLY)) {
+    $f = solv::xfopen_fd('', POSIX::dup(fileno($f)));
+    my $flags = $ext ? $solv::Repo::REPO_USE_LOADING|$solv::Repo::REPO_EXTEND_SOLVABLES : 0;
+    $flags |= $solv::Repo::REPO_LOCALPOOL if $ext && $ext ne 'DL';
+    if (!$self->{'handle'}->add_solv($f, $flags)) {
+      solv::xfclose($f);
+      return undef;
+    }
+    return 1;
+  }
+  return undef;
+}
+
+package Repo::rpmmd;
+
+our @ISA = ('Repo::generic');
+
+package Repo::system;
+
+our @ISA = ('Repo::generic');
+
+sub calc_cookie_file {
+  my ($self, $filename) = @_;
+  my $chksum = solv::Chksum->new($solv::REPOKEY_TYPE_SHA256);
+  $chksum->add("1.1");
+  $chksum->add_stat($filename);
+  return $chksum->raw();
+}
+
+sub load {
+  my ($self, $pool) = @_;
+
+  $self->{'handle'} = $pool->add_repo($self->{'alias'});
+  $self->{'handle'}->{'appdata'} = $self;
+  $pool->{'installed'} = $self->{'handle'};
+  print "rpm database: ";
+  $self->{'cookie'} = $self->calc_cookie_file('/var/lib/rpm/Packages');
+  if ($self->usecachedrepo()) {
+    print "cached\n";
+    return 1;
+  }
+  return undef;
+}
+
+package main;
+
+sub validarch {
+  my ($pool, $arch) = @_;
+  return undef unless $arch;
+  my $id = $pool->str2id($arch, 0);
+  return $id && $pool->isknownarch($id) ? 1 : undef;
+}
+
+sub depglob {
+  my ($pool, $name, $globname, $globdep) = @_;
+  my $id = $pool->str2id($name, 0);
+  if ($id) {
+    my $match;
+    for my $s ($pool->providers($id)) {
+      return $pool->Job($solv::Job::SOLVER_SOLVABLE_NAME, $id) if $globname && $s->{'nameid'} == $id;
+      $match = 1;
+    }
+    if ($match) {
+      print "[using capability match for '$name']\n" if $globname && $globdep;
+      my @j = $pool->Job($solv::Job::SOLVER_SOLVABLE_PROVIDES, $id);
+      return $pool->Job($solv::Job::SOLVER_SOLVABLE_PROVIDES, $id);
+    }
+  }
+  return unless $name =~ /[[*?]/;
+  if ($globname) {
+    my %idmatches;
+    for my $s (@{$pool->{'solvables_iter'}}) {
+      $idmatches{$s->{'nameid'}} = 1 if $s->installable() && File::FnMatch::fnmatch($name, $s->{'name'}, 0);
+    }
+    if (%idmatches) {
+      return map {$pool->Job($solv::Job::SOLVER_SOLVABLE_NAME, $_)} sort(keys %idmatches);
+    }
+  }
+  if ($globdep) {
+    my %idmatches;
+    for $id (@{$pool->allprovidingids()}) {
+      $idmatches{$id} = 1 if File::FnMatch::fnmatch($name, $pool->id2str($id), 0);
+    }
+    if (%idmatches) {
+      print "[using capability match for '$name']\n";
+      return map {$pool->Job($solv::Job::SOLVER_SOLVABLE_PROVIDES, $_)} sort(keys %idmatches);
+    }
+  }
+  return;
+}
+
+sub limitjobs {
+  my ($pool, $jobs, $flags, $evr) = @_;
+  my @jobs;
+  for my $j (@$jobs) {
+    my $how = $j->{'how'};
+    my $sel = $how & $solv::Job::SOLVER_SELECTMASK;
+    my $what = $pool->rel2id($j->{'what'}, $evr, $flags);
+    if ($flags == $solv::REL_ARCH) {
+      $how |= $solv::Job::SOLVER_SETARCH;
+    } elsif ($flags == $solv::REL_EQ && $sel == $solv::Job::SOLVER_SOLVABLE_NAME) {
+      $how |= $pool->id2str($evr) =~ /-/ ? $solv::Job::SOLVER_SETEVR : $solv::Job::SOLVER_SETEV;
+    }
+    push @jobs, $pool->Job($how, $what);
+  }
+  return @jobs;
+}
+
+sub limitjobs_arch {
+  my ($pool, $jobs, $flags, $evrstr) = @_;
+  if ($evrstr =~ /^(.+)\.(.+?)$/ && validarch($pool, $2)) {
+    my $evr = $pool->str2id($1);
+    my @jobs = limitjobs($pool, $jobs, $solv::solv::REL_ARCH, $pool->str2id($2));
+    return limitjobs($pool, \@jobs, $flags, $evr);
+  }
+  return limitjobs($pool, $jobs, $flags, $pool->str2id($evrstr));
+}
+
+sub mkjobs_rel {
+  my ($pool, $cmd, $name, $rel, $evr) = @_;
+  my $flags = 0;
+  $flags |= $solv::REL_LT if $rel =~ /</;
+  $flags |= $solv::REL_EQ if $rel =~ /=/;
+  $flags |= $solv::REL_GT if $rel =~ />/;
+  my @jobs = depglob($pool, $name, 1, 1);
+  return limitjobs($pool, \@jobs, $flags, $pool->str2id($evr)) if @jobs;
+  if (($name =~ /^(.+)\.(.+?)$/s) && validarch($pool, $2)) {
+    my $arch = $2;
+    @jobs = depglob($pool, $1, 1, 1);
+    if (@jobs) {
+      @jobs = limitjobs($pool, \@jobs, $solv::solv::REL_ARCH, $pool->str2id($arch));
+      return limitjobs($pool, \@jobs, $flags, $pool->str2id($evr));
+    }
+  }
+  return ();
+}
+
+sub mkjobs_nevra {
+  my ($pool, $cmd, $arg) = @_;
+  my @jobs = depglob($pool, $arg, 1, 1);
+  return @jobs if @jobs;
+  if (($arg =~ /^(.+)\.(.+?)$/s) && validarch($pool, $2)) {
+    my $arch = $2;
+    @jobs = depglob($pool, $1, 1, 1);
+    return limitjobs($pool, \@jobs, $solv::REL_ARCH, $pool->str2id($arch)) if @jobs;
+  }
+  if ($arg =~ /^(.+)-(.+?)$/s) {
+    my $evr = $2;
+    @jobs = depglob($pool, $1, 1, 0);
+    return limitjobs_arch($pool, \@jobs, $solv::REL_EQ, $evr) if @jobs;
+  }
+  if ($arg =~ /^(.+)-(.+?-.+?)$/s) {
+    my $evr = $2;
+    @jobs = depglob($pool, $1, 1, 0);
+    return limitjobs_arch($pool, \@jobs, $solv::REL_EQ, $evr) if @jobs;
+  }
+  return ();
+}
+
+sub mkjobs_filelist {
+  my ($pool, $cmd, $arg) = @_;
+  my $type = ($arg =~ /[[*?]/) ? $solv::Dataiterator::SEARCH_GLOB : $solv::Dataiterator::SEARCH_STRING;
+  $type |= $solv::Dataiterator::SEARCH_FILES | $solv::Dataiterator::SEARCH_COMPLETE_FILELIST;
+  my $di;
+  if ($cmd eq 'erase') {
+    $di = $pool->{'installed'}->dataiterator_new(0, $solv::SOLVABLE_FILELIST, $arg, $type);
+  } else {
+    $di = $pool->dataiterator_new(0, $solv::SOLVABLE_FILELIST, $arg, $type);
+  }
+  my @matches;
+  for my $d (@$di) {
+    my $s = $d->{'solvable'};
+    next unless $s && $s->installable();
+    push @matches, $s->{'id'};
+    tied(@$di)->iter()->skip_solvable();
+  }
+  return () unless @matches;
+  print "[using file list match for '$arg']\n";
+  if (@matches > 1) {
+    return $pool->Job($solv::Job::SOLVER_SOLVABLE_ONE_OF, $pool->towhatprovides(\@matches));
+  } else {
+    return $pool->Job($solv::Job::SOLVER_SOLVABLE | $solv::Job::NOAUTOSET, $matches[0]);
+  }
+}
+
+sub mkjobs {
+  my ($pool, $cmd, $arg) = @_;
+  if ($arg && $arg =~ /^\//) {
+    my @jobs = mkjobs_filelist($pool, $cmd, $arg);
+    return @jobs if @jobs;
+  }
+  if ($arg =~ /^(.+?)\s*([<=>]+)\s*(.+?)$/s) {
+    return mkjobs_rel($pool, $cmd, $1, $2, $3);
+  } else {
+    return mkjobs_nevra($pool, $cmd, $arg);
+  }
+}
+
+die("Usage: p5solv COMMAND [ARGS]\n") unless @ARGV;
+my $cmd = shift @ARGV;
+$cmd = 'list' if $cmd eq 'li';
+$cmd = 'install' if $cmd eq 'in';
+$cmd = 'erase' if $cmd eq 'rm';
+$cmd = 'verify' if $cmd eq 've';
+$cmd = 'search' if $cmd eq 'se';
+
+my $pool = solv::Pool->new();
+$pool->setarch((POSIX::uname())[4]);
+my @repos;
+for my $reposdir ('/etc/zypp/repos.d') {
+  next unless -d $reposdir;
+  next unless opendir(DIR, $reposdir);
+  for my $reponame (sort(grep {/\.repo$/} readdir(DIR))) {
+    my $cfg = new Config::IniFiles('-file' => "$reposdir/$reponame");
+    for my $alias ($cfg->Sections()) {
+      my $repoattr = {'alias' => $alias, 'enabled' => 0, 'priority' => 99, 'autorefresh' => 1, 'type' => 'rpm-md', 'metadata_expire' => 900};
+      for my $p ($cfg->Parameters($alias)) {
+       $repoattr->{$p} = $cfg->val($alias, $p);
+      }
+      my $repo;
+      if ($repoattr->{'type'} == 'rpm-md') {
+       $repo = Repo::rpmmd->new($repoattr);
+      }
+      push @repos, $repo;
+    }
+  }
+}
+
+my $sysrepo = Repo::system->new({'alias' => '@System', 'type' => 'system'});
+$sysrepo->load($pool);
+for my $repo (@repos) {
+  $repo->load($pool) if $repo->{'enabled'};
+}
+
+if ($cmd eq 'search') {
+  my %matches;
+  my $di = $pool->dataiterator_new(0, $solv::SOLVABLE_NAME, $ARGV[0], $solv::Dataiterator::SEARCH_SUBSTRING | $solv::Dataiterator::SEARCH_NOCASE);
+  for my $d (@$di) {
+    $matches{$d->{'solvid'}} = 1;
+  }
+  for my $solvid (sort keys %matches) {
+    my $s = $pool->{'solvables'}->[$solvid];
+    print "- ".$s->str()." [$s->{'repo'}->{'name'}] ".$s->lookup_str($solv::SOLVABLE_SUMMARY)."\n";
+  }
+  exit(0);
+}
+
+my $addedprovides =  $pool->addfileprovides_ids();
+$pool->createwhatprovides();
+
+my @jobs;
+for my $arg (@ARGV) {
+  my @njobs = mkjobs($pool, $cmd, $arg);
+  die("nothing matches '$arg'\n") unless @njobs;
+  push @jobs, @njobs;
+}
+
+if ($cmd eq 'list' || $cmd eq 'info') {
+  die("no package matched.\n") unless @jobs;
+  for my $job (@jobs) {
+    for my $s ($pool->jobsolvables($job)) {
+      if ($cmd eq 'info') {
+        printf "Name:        %s\n", $s->str();
+        printf "Repo:        %s\n", $s->{'repo'}->{'name'};
+        printf "Summary:     %s\n", $s->lookup_str($solv::SOLVABLE_SUMMARY);
+       my $str = $s->lookup_str($solv::SOLVABLE_URL);
+        printf "Url:         %s\n", $str if $str;
+       my $str = $s->lookup_str($solv::SOLVABLE_LICENSE);
+        printf "License:     %s\n", $str if $str;
+        printf "Description:\n%s\n", $s->lookup_str($solv::SOLVABLE_DESCRIPTION);
+      } else {
+        printf "  - %s [%s]\n", $s->str(), $s->{'repo'}->{'name'};
+        printf "    %s\n", $s->lookup_str($solv::SOLVABLE_SUMMARY);
+      }
+    }
+  }
+  exit 0;
+}
+
+if ($cmd eq 'install' || $cmd eq 'erase' || $cmd eq 'up' || $cmd eq 'dup' || $cmd eq 'verify') {
+  if (!@jobs) {
+    if ($cmd eq 'up' || $cmd eq 'verify') {
+      push @jobs, $pool->Job($solv::Job::SOLVER_SOLVABLE_ALL, 0);
+    } elsif ($cmd ne 'dup') {
+      die("no package matched.\n");
+    }
+  }
+  for my $job (@jobs) {
+    if ($cmd eq 'up') {
+      if ($job->{'how'} == $solv::Job::SOLVER_SOLVABLE_ALL || grep {$_->isinstalled()} @{$pool->jobsolvables($job)}) {
+        $job->{'how'} |= $solv::Job::SOLVER_UPDATE;
+      } else {
+        $job->{'how'} |= $solv::Job::SOLVER_INSTALL;
+      }
+    } elsif ($cmd eq 'install') {
+        $job->{'how'} |= $solv::Job::SOLVER_INSTALL;
+    } elsif ($cmd eq 'erase') {
+        $job->{'how'} |= $solv::Job::SOLVER_ERASE;
+    } elsif ($cmd eq 'dup') {
+        $job->{'how'} |= $solv::Job::SOLVER_DISTUPGRADE;
+    } elsif ($cmd eq 'verify') {
+        $job->{'how'} |= $solv::Job::SOLVER_VERIFY;
+    }
+  }
+  my $solver;
+  while (1) {
+    $solver = $pool->create_solver();
+    $solver->{'ignorealreadyrecommended'} = 1;
+    $solver->{'allowuninstall'} = 1 if $cmd eq 'erase';
+    if ($cmd eq 'dup' && !@jobs) {
+      $solver->{'distupgrade'} = 1;
+      $solver->{'updatesystem'} = 1;
+      $solver->{'allowdowngrade'} = 1;
+      $solver->{'allowvendorchange'} = 1;
+      $solver->{'allowarchchange'} = 1;
+      $solver->{'dosplitprovides'} = 1;
+    } elsif ($cmd eq 'up' and @jobs == 1 and $jobs[0]->{'how'} == ($solv::Job::SOLVER_UPDATE | $solv::Job::SOLVER_SOLVABLE_ALL)) {
+      $solver->{'dosplitprovides'} = 1;
+    }
+    my @problems = $solver->solve(\@jobs);
+    last unless @problems;
+    for my $problem (@problems) {
+      print "Problem $problem->{'id'}:\n";
+      my $r = $problem->findproblemrule();
+      my ($type, $source, $target, $dep) = $r->info();
+      if ($type == $solv::Solver::SOLVER_RULE_RPM_PACKAGE_CONFLICT) {
+        printf "package %s conflicts with %s provided by %s\n", $source->str(), $pool->dep2str($dep), $target->str();
+      }
+      print "TYPE: $type\n";
+      printf "SOURCE: %s\n", $source->str() if $source;
+      printf "TARGET: %s\n", $target->str() if $target;
+      printf "DEP: %s\n", $pool->dep2str($dep) if $dep;
+    }
+    exit(0);
+  }
+}
+
+exit 0;
index 1ebf931..c035ba1 100755 (executable)
@@ -574,7 +574,7 @@ def limitjobs(pool, jobs, flags, evr):
        what = pool.rel2id(j.what, evr, flags)
         if flags == solv.REL_ARCH:
            how |= Job.SOLVER_SETARCH
-       if flags == solv.REL_EQ and sel == Job.SOLVER_SOLVABLE_NAME:
+       elif flags == solv.REL_EQ and sel == Job.SOLVER_SOLVABLE_NAME:
            if pool.id2str(evr).find('-') >= 0:
                how |= Job.SOLVER_SETEVR
            else:
@@ -595,7 +595,7 @@ def mkjobs_filelist(pool, cmd, arg):
        type = Dataiterator.SEARCH_GLOB
     else:
        type = Dataiterator.SEARCH_STRING
-    if cmd == 'rm' or cmd == 'erase':
+    if cmd == 'erase':
        di = pool.installed.dataiterator_new(0, solv.SOLVABLE_FILELIST, arg, type | Dataiterator.SEARCH_FILES|Dataiterator.SEARCH_COMPLETE_FILELIST)
     else:
        di = pool.dataiterator_new(0, solv.SOLVABLE_FILELIST, arg, type | Dataiterator.SEARCH_FILES|Dataiterator.SEARCH_COMPLETE_FILELIST)
@@ -791,7 +791,11 @@ for arg in args:
     if cmdlinerepo and arg in cmdlinerepo['packages']:
        jobs.append(pool.Job(Job.SOLVER_SOLVABLE, cmdlinerepo['packages'][arg]))
     else:
-       jobs += mkjobs(pool, cmd, arg)
+       njobs = mkjobs(pool, cmd, arg)
+       if not njobs:
+           print "nothing matches '%s'" % arg
+           sys.exit(1)
+       jobs += njobs
 
 if cmd == 'list' or cmd == 'info':
     if not jobs:
@@ -853,6 +857,7 @@ if cmd == 'install' or cmd == 'erase' or cmd == 'up' or cmd == 'dup' or cmd == '
            solver.updatesystem = True
            solver.allowdowngrade = True
            solver.allowvendorchange = True
+           solver.allowarchchange = True
            solver.dosplitprovides = True
        if cmd == 'up' and len(jobs) == 1 and jobs[0].how == (Job.SOLVER_UPDATE | Job.SOLVER_SOLVABLE_ALL):
            solver.dosplitprovides = True
index 7924386..16aff49 100644 (file)
@@ -9,6 +9,7 @@
 
 %module solv
 
+#if defined(SWIGPYTHON)
 %typemap(in) Queue {
   /* Check if is a list */
   queue_init(&$1);
   queue_free(&$1);
   $result = o;
 }
+#endif
+#if defined(SWIGPERL)
+%typemap(in) Queue {
+  AV *av;
+  int i, size;
+  queue_init(&$1);
+  if (!SvROK($input) || SvTYPE(SvRV($input)) != SVt_PVAV)
+    SWIG_croak("Argument $argnum is not an array reference.");
+  av = (AV*)SvRV($input);
+  size = av_len(av);
+  for (i = 0; i <= size; i++) {
+    SV **sv = av_fetch(av, i, 0);
+    int v;
+    int e = SWIG_AsVal_int(*sv, &v);
+    if (!SWIG_IsOK(e)) {
+      SWIG_croak("list must contain only integers");
+    }
+    queue_push(&$1, v);
+  }
+}
+%typemap(out) Queue {
+  int i;
+  AV *o = newAV();
+  for (i = 0; i < $1.count; i++)
+    av_push(o, SvREFCNT_inc(SWIG_From_int($1.elements[i])));
+  queue_free(&$1);
+  $result = newRV_noinc((SV*)o); argvi++;
+}
+#endif
 %typemap(arginit) Queue {
   queue_init(&$1);
 }
   queue_free(&$1);
 }
 
+#if defined(SWIGPERL)
+%define perlmkarray(class,accessor,constructor)
+  %perlcode {
+    *solv::##class##::array::TIEARRAY = sub { bless $_[1], 'solv::class::array' };
+    *solv::##class##::array::FETCHSIZE = *solv::##class##::__len__;
+    *solv::##class##::array::FETCH = *solv::##class##::__getitem__;
+    undef *solv::##accessor##;
+    *solv::##accessor = sub { my @a; tie(@a, 'solv::class::array', ##constructor##(@_)); \@a };
+  }
+%enddef
+%define perlmkiter(class,accessor,constructor)
+  %perlcode {
+    *solv::##class##::iter::TIEARRAY = sub { bless [$_[1], 0], 'solv::class::iter' };
+    *solv::##class##::iter::FETCHSIZE = sub { ($_[0]->[2] = $_[0]->[0]->__next__()) ? ++$_[0]->[1] : 0 };
+    *solv::##class##::iter::FETCH = sub { $_[1] == $_[0]->[1] - 1 ? $_[0]->[2] : undef };
+    *solv::##class##::iter::iter = sub { return $_[0]->[0] };
+    undef *solv::##accessor##;
+    *solv::##accessor = sub { my @a; tie(@a, 'solv::class::iter', ##constructor##(@_)); \@a };
+  }
+%enddef
+#endif
+
+
+#if defined(SWIGPYTHON)
+typedef PyObject *AppObjectPtr;
+%typemap(out) AppObjectPtr {
+  $result = $1;
+  Py_INCREF($result);
+}
+#endif
+#if defined(SWIGPERL)
+typedef SV *AppObjectPtr;
+%typemap(out) AppObjectPtr {
+  $result = $1;
+  SvREFCNT_inc($result);
+}
+#endif
+#if defined(SWIGRUBY)
+typedef VALUE AppObjectPtr;
+#endif
 
 %include "cdata.i"
+#ifndef SWIGPERL
 %include "file.i"
+#endif
 %include "typemaps.i"
 
 %{
 #define SOLVER_SOLUTION_REPLACE -101
 typedef struct chksum Chksum;
 typedef int bool;
+typedef void *AppObjectPtr;
 
 typedef struct {
   Pool* pool;
@@ -186,6 +259,7 @@ typedef struct {
 %nodefaultctor Pool;
 %nodefaultdtor Pool;
 typedef struct {
+  AppObjectPtr appdata;
 } Pool;
 
 %nodefaultctor Repo;
@@ -196,12 +270,7 @@ typedef struct _Repo {
   int priority;
   int subpriority;
   int const nsolvables;
-#if defined(SWIGRUBY)
-  VALUE appdata;
-#endif
-#if defined(SWIGPERL)
-  SV *appdata;
-#endif
+  AppObjectPtr appdata;
 } Repo;
 
 %nodefaultctor Pool_solvable_iterator;
@@ -509,6 +578,10 @@ typedef struct {
     const unsigned char *b = pool_lookup_bin_checksum($self, entry, keyname, &type);
     return sat_chksum_create_from_bin(type, b);
   }
+
+#ifdef SWIGPERL
+  perlmkiter(Dataiterator, Pool::dataiterator_new, solvc::Pool_dataiterator_new)
+#endif
   %newobject dataiterator_new;
   Dataiterator *dataiterator_new(Id p, Id key,  const char *match, int flags) {
     return new_Dataiterator($self, 0, p, key, match, flags);
@@ -533,6 +606,13 @@ typedef struct {
   void createwhatprovides() {
     pool_createwhatprovides($self);
   }
+
+
+#if defined(SWIGPERL)
+  perlmkarray(Pool_solvable_iterator, Pool::swig_solvables_get, solvc::Pool_solvables_get)
+  perlmkiter(Pool_solvable_iterator, Pool::swig_solvables_iter_get, solvc::Pool_solvables_get)
+#endif
+
   %newobject solvables;
   Pool_solvable_iterator * const solvables;
   %{
@@ -544,6 +624,12 @@ typedef struct {
     return s;
   }
   %}
+
+#if defined(SWIGPERL)
+  perlmkarray(Pool_repo_iterator, Pool::swig_repos_get, solvc::Pool_repos_get)
+  perlmkiter(Pool_repo_iterator, Pool::swig_repos_iter_get, solvc::Pool_repos_get)
+#endif
+
   %newobject repos;
   Pool_repo_iterator * const repos;
   %{
@@ -555,6 +641,7 @@ typedef struct {
     return s;
   }
   %}
+
   Repo *installed;
   %{
   SWIGINTERN void Pool_installed_set(Pool *pool, Repo *installed) {
@@ -607,6 +694,18 @@ typedef struct {
       return [ self.solvables[id] for id in self.providerids(*args) ]
   }
 #endif
+#if defined(SWIGPERL)
+  %perlcode {
+    sub solv::Pool::jobsolvables {
+      my ($self, @args) = @_;
+      return map {$self->{'solvables'}->[$_]} @{$self->jobsolvids(@args)};
+    }
+    sub solv::Pool::providers {
+      my ($self, @args) = @_;
+      return map {$self->{'solvables'}->[$_]} @{$self->providerids(@args)};
+    }
+  }
+#endif
 
   Id towhatprovides(Queue q) {
     return pool_queuetowhatprovides($self, &q);
@@ -711,6 +810,11 @@ typedef struct {
     $self->nrepodata = oldnrepodata;
     return 1;
   }
+
+#ifdef SWIGPERL
+  perlmkiter(Dataiterator, Repo::dataiterator_new, solvc::Repo_dataiterator_new)
+#endif
+
   %newobject dataiterator_new;
   Dataiterator *dataiterator_new(Id p, Id key,  const char *match, int flags) {
     return new_Dataiterator($self->pool, $self, p, key, match, flags);
@@ -746,19 +850,6 @@ typedef struct {
     if (data->state != REPODATA_STUB)
       repodata_create_stubs(data);
   }
-#if defined(SWIGPYTHON)
-  PyObject *appdata;
-  %{
-  SWIGINTERN void Repo_appdata_set(Repo *repo, PyObject *o) {
-    repo->appdata = o;
-  }
-  SWIGINTERN PyObject *Repo_appdata_get(Repo *repo) {
-    PyObject *o = repo->appdata;
-    Py_INCREF(o);
-    return o;
-  }
-  %}
-#endif
   bool iscontiguous() {
     int i;
     for (i = $self->start; i < $self->end; i++)
@@ -798,6 +889,7 @@ typedef struct {
     dataiterator_free($self);
     sat_free($self);
   }
+#if defined(SWIGPYTHON)
   %newobject __iter__;
   Dataiterator *__iter__() {
     Dataiterator *ndi;
@@ -805,15 +897,17 @@ typedef struct {
     dataiterator_init_clone(ndi, $self);
     return ndi;
   }
-  %exception next {
+  %rename("next") __next__();
+  %exception __next__ {
     $action
     if (!result) {
       PyErr_SetString(PyExc_StopIteration,"no more matches");
       return NULL;
     }
   }
-  %newobject next;
-  Dataiterator *next() {
+#endif
+  %newobject __next__;
+  Dataiterator *__next__() {
     Dataiterator *ndi;
     if (!dataiterator_step($self)) {
       return 0;
@@ -868,8 +962,8 @@ typedef struct {
   }
 }
 
-
 %extend Pool_solvable_iterator {
+#if defined(SWIGPYTHON)
   %newobject __iter__;
   Pool_solvable_iterator *__iter__() {
     Pool_solvable_iterator *s;
@@ -877,15 +971,17 @@ typedef struct {
     *s = *$self;
     return s;
   }
-  %exception next {
+  %rename("next") __next__();
+  %exception __next__ {
     $action
     if (!result) {
       PyErr_SetString(PyExc_StopIteration,"no more matches");
       return NULL;
     }
   }
-  %newobject next;
-  XSolvable *next() {
+#endif
+  %newobject __next__;
+  XSolvable *__next__() {
     Pool *pool = $self->pool;
     XSolvable *s;
     if ($self->id >= pool->nsolvables)
@@ -902,9 +998,13 @@ typedef struct {
       return new_XSolvable(pool, key);
     return 0;
   }
+  int __len__() {
+    return $self->pool->nsolvables;
+  }
 }
 
 %extend Pool_repo_iterator {
+#if defined(SWIGPYTHON)
   %newobject __iter__;
   Pool_repo_iterator *__iter__() {
     Pool_repo_iterator *s;
@@ -912,14 +1012,17 @@ typedef struct {
     *s = *$self;
     return s;
   }
-  %exception next {
+  %rename("next") __next__();
+  %exception __next__ {
     $action
     if (!result) {
       PyErr_SetString(PyExc_StopIteration,"no more matches");
       return NULL;
     }
   }
-  Repo *next() {
+#endif
+  %newobject __next__;
+  Repo *__next__() {
     Pool *pool = $self->pool;
     if ($self->id >= pool->nrepos + 1)
       return 0;
@@ -936,9 +1039,13 @@ typedef struct {
       return pool_id2repo(pool, key);
     return 0;
   }
+  int __len__() {
+    return $self->pool->nrepos + 1;
+  }
 }
 
 %extend Repo_solvable_iterator {
+#if defined(SWIGPYTHON)
   %newobject __iter__;
   Repo_solvable_iterator *__iter__() {
     Repo_solvable_iterator *s;
@@ -946,15 +1053,17 @@ typedef struct {
     *s = *$self;
     return s;
   }
-  %exception next {
+  %rename("next") __next__();
+  %exception __next__ {
     $action
     if (!result) {
       PyErr_SetString(PyExc_StopIteration,"no more matches");
       return NULL;
     }
   }
-  %newobject next;
-  XSolvable *next() {
+#endif
+  %newobject __next__;
+  XSolvable *__next__() {
     Repo *repo = $self->repo;
     Pool *pool = repo->pool;
     XSolvable *s;
@@ -975,6 +1084,9 @@ typedef struct {
       return new_XSolvable(pool, key);
     return 0;
   }
+  int __len__() {
+    return $self->repo->pool->nsolvables;
+  }
 }
 
 %extend XSolvable {
@@ -1123,6 +1235,22 @@ typedef struct {
       return [ Solution(self, i) for i in range(1, self.solution_count() + 1) ];
   }
 #endif
+#if defined(SWIGPERL)
+  %perlcode {
+    sub solv::Problem::findproblemrule {
+      my ($self) = @_;
+      return solv::XRule->new($self->{'solv'}, $self->findproblemrule_helper());
+    }
+    sub solv::Problem::findallproblemrule {
+      my ($self, $unfiltered) = @_;
+      return map {solv::XRule->new($self->{'solv'}, $_)} @{$self->findallproblemrule_helper($unfiltered)};
+    }
+    sub solv::Problem::solutions {
+      my ($self) = @_;
+      return map {solv::Solution->new($self, $_)} 1..($self->solution_count());
+    }
+  }
+#endif
 }
 
 %extend Solution {
@@ -1143,6 +1271,14 @@ typedef struct {
       return [ Solutionelement(self, i) for i in range(1, self.element_count() + 1) ];
   }
 #endif
+#if defined(SWIGPERL)
+  %perlcode {
+    sub solv::Solution::elements {
+      my ($self) = @_;
+      return map {solv::Solutionelement->new($self, $_)} 1..($self->element_count());
+    }
+  }
+#endif
 }
 
 %extend Solutionelement {
@@ -1229,6 +1365,16 @@ typedef struct {
       return [ Problem(self, pid) for pid in range(1, nprob + 1) ]
   }
 #endif
+#if defined(SWIGPERL)
+  %perlcode {
+    sub solv::Solver::solve {
+      my ($self, $jobs) = @_;
+      my @j = map {($_->{'how'}, $_->{'what'})} @$jobs;
+      my $nprob = $self->solve_helper(\@j);
+      return map {solv::Problem->new($self, $_)} 1..$nprob;
+    }
+  }
+#endif
   int solve_helper(Queue jobs) {
     solver_solve($self, &jobs);
     return solver_problem_count($self);
@@ -1364,6 +1510,17 @@ typedef struct {
       return type, source, target, dep
   }
 #endif
+#if defined(SWIGPERL)
+  %perlcode {
+    sub solv::XRule::info {
+      my ($self) = @_;
+      my ($type, $source, $target, $dep) = $self->info_helper();
+      $source = $self->{'solv'}->{'pool'}->{'solvables'}->[$source] if $source;
+      $target = $self->{'solv'}->{'pool'}->{'solvables'}->[$target] if $target;
+      return ($type, $source, $target, $dep);
+    }
+  }
+#endif
 }
 
 
@@ -1435,3 +1592,4 @@ typedef struct {
     repodata_extend_block(data, data->repo->start, data->repo->end - data->repo->start);
   }
 }
+