Hello community,
here is the log from the commit of package libsatsolver
checked in at Fri Feb 22 15:34:33 CET 2008.
--------
--- libsatsolver/libsatsolver.changes 2008-02-20 13:26:23.000000000 +0100
+++ libsatsolver/libsatsolver.changes 2008-02-22 14:37:50.000000000 +0100
@@ -1,0 +2,10 @@
+Fri Feb 22 11:09:05 CET 2008 - coolo@suse.de
+
+- support rpmdb2solv -r for chroots
+
+-------------------------------------------------------------------
+Thu Feb 21 18:14:41 CET 2008 - coolo@suse.de
+
+- fix susetags parser for so called full trees
+
+-------------------------------------------------------------------
Old:
----
satsolver-0.0.17.tar.bz2
New:
----
satsolver-0.0.19.tar.bz2
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ libsatsolver.spec ++++++
--- /var/tmp/diff_new_pack.n15530/_old 2008-02-22 15:34:12.000000000 +0100
+++ /var/tmp/diff_new_pack.n15530/_new 2008-02-22 15:34:12.000000000 +0100
@@ -1,5 +1,5 @@
#
-# spec file for package libsatsolver (Version 0.0.17)
+# spec file for package libsatsolver (Version 0.0.19)
#
# Copyright (c) 2008 SUSE LINUX Products GmbH, Nuernberg, Germany.
# This file and all modifications and additions to the pristine
@@ -11,7 +11,7 @@
Name: libsatsolver
-Version: 0.0.17
+Version: 0.0.19
Release: 1
License: BSD 3-Clause
Url: http://svn.opensuse.org/svn/zypp/trunk/sat-solver
@@ -120,6 +120,10 @@
%{_libdir}/ruby/vendor_ruby/%{rb_ver}/satsolver.rb
%changelog
+* Fri Feb 22 2008 coolo@suse.de
+- support rpmdb2solv -r for chroots
+* Thu Feb 21 2008 coolo@suse.de
+- fix susetags parser for so called full trees
* Wed Feb 20 2008 coolo@suse.de
- no longer link against db43 but against rpmlib
* Tue Feb 19 2008 coolo@suse.de
++++++ satsolver-0.0.17.tar.bz2 -> satsolver-0.0.19.tar.bz2 ++++++
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/applayer/xsolvable.c new/satsolver-0.0.19/applayer/xsolvable.c
--- old/satsolver-0.0.17/applayer/xsolvable.c 2008-02-20 13:34:12.000000000 +0100
+++ new/satsolver-0.0.19/applayer/xsolvable.c 2008-02-22 14:37:48.000000000 +0100
@@ -18,6 +18,7 @@
*/
#include
+#include
#include
#include "xsolvable.h"
@@ -72,6 +73,70 @@
}
+/*
+ * equality
+ */
+
+int
+xsolvable_equal( XSolvable *xs1, XSolvable *xs2 )
+{
+/* fprintf(stderr, "%p[%p/%d] == %p[%p/%d] ?\n", xs1, xs1->pool, xs1->id, xs2, xs2->pool, xs2->id); */
+ if ((xs1 == xs2)
+ || ((xs1->pool == xs2->pool)
+ && (xs1->id == xs2->id)))
+ {
+ return 1;
+ }
+ return 0;
+}
+
+
+static void
+copy_deps( Repo *repo, Offset *ndeps, Id *odeps )
+{
+ while (*odeps) {
+ *ndeps = repo_addid( repo, *ndeps, *odeps );
+ ++odeps;
+ }
+}
+
+/*
+ * crude hack of adding an existing Solvable to another Repo
+ */
+
+XSolvable *
+xsolvable_add( Repo *repo, XSolvable *xs )
+{
+ Id sid;
+ Solvable *old_s, *new_s;
+
+ if (repo->pool != xs->pool)
+ return NULL; /* must be of same repo */
+
+ sid = repo_add_solvable( repo );
+
+ old_s = pool_id2solvable( xs->pool, xs->id );
+ new_s = pool_id2solvable( repo->pool, sid );
+
+ new_s->name = old_s->name;
+ new_s->evr = old_s->evr;
+ new_s->arch = old_s->arch;
+ new_s->vendor = old_s->vendor;
+
+ copy_deps( repo, &(new_s->provides), old_s->repo->idarraydata + old_s->provides );
+ copy_deps( repo, &(new_s->requires), old_s->repo->idarraydata + old_s->requires );
+ copy_deps( repo, &(new_s->obsoletes), old_s->repo->idarraydata + old_s->obsoletes );
+ copy_deps( repo, &(new_s->conflicts), old_s->repo->idarraydata + old_s->conflicts );
+
+ copy_deps( repo, &(new_s->recommends), old_s->repo->idarraydata + old_s->recommends );
+ copy_deps( repo, &(new_s->suggests), old_s->repo->idarraydata + old_s->suggests );
+ copy_deps( repo, &(new_s->supplements), old_s->repo->idarraydata + old_s->supplements );
+ copy_deps( repo, &(new_s->enhances), old_s->repo->idarraydata + old_s->enhances );
+ copy_deps( repo, &(new_s->freshens), old_s->repo->idarraydata + old_s->freshens );
+
+ return xsolvable_new( repo->pool, sid );
+}
+
/************************************************
* Pool/Repo
*
@@ -93,6 +158,8 @@
s = pool->solvables + i;
if (!pool_installable(pool, s))
continue;
+ if (repo && (s->repo != repo))
+ continue;
if (s->name == id)
queue_push(&plist, i);
}
@@ -263,13 +330,13 @@
{
Solvable *s;
Id p;
- for (p = 0, s = repo->pool->solvables + repo->start; p < repo->nsolvables; p++, s++)
+ FOR_REPO_SOLVABLES(repo, p, s)
{
if (!s)
continue;
if (!s->name)
continue;
- if (callback( xsolvable_new( repo->pool, repo->start + p ) ) )
+ if (callback( xsolvable_new( repo->pool, s - repo->pool->solvables ) ) )
break;
}
}
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/applayer/xsolvable.h new/satsolver-0.0.19/applayer/xsolvable.h
--- old/satsolver-0.0.17/applayer/xsolvable.h 2008-02-20 13:34:12.000000000 +0100
+++ new/satsolver-0.0.19/applayer/xsolvable.h 2008-02-22 14:37:48.000000000 +0100
@@ -49,11 +49,22 @@
void xsolvable_free( XSolvable *xs );
/*
+ * Check for equality
+ * Two XSolvables are equal if they both reference the same Id of the same pool.
+ */
+int xsolvable_equal( XSolvable *xs1, XSolvable *xs2 );
+
+/*
* Return the Solvable corresponding to the given XSolvable
*/
Solvable *xsolvable_solvable( const XSolvable *xs );
/*
+ * Add an existing solvable to Repo
+ */
+XSolvable *xsolvable_add( Repo *repo, XSolvable *xs );
+
+/*
* Find XSolvable by name in pool (and repo)
* If repo == NULL, search the complete pool
* If repo != NULL, limit search to the given repo
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/bindings/ruby/tests/gen_patches.rb new/satsolver-0.0.19/bindings/ruby/tests/gen_patches.rb
--- old/satsolver-0.0.17/bindings/ruby/tests/gen_patches.rb 1970-01-01 01:00:00.000000000 +0100
+++ new/satsolver-0.0.19/bindings/ruby/tests/gen_patches.rb 2008-02-22 14:37:48.000000000 +0100
@@ -0,0 +1,12 @@
+$:.unshift "../../../build/bindings/ruby"
+
+#
+# Generate 'code11 patches' from code10 patch repo
+#
+
+require 'satsolver'
+require '_patch'
+require '_solv2patches'
+
+patches = solv2patches "patches.solv", "x86_64"
+patches.each { |p| puts p }
Files old/satsolver-0.0.17/bindings/ruby/tests/packages.solv and new/satsolver-0.0.19/bindings/ruby/tests/packages.solv differ
Files old/satsolver-0.0.17/bindings/ruby/tests/patches.solv and new/satsolver-0.0.19/bindings/ruby/tests/patches.solv differ
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/bindings/ruby/tests/_patch.rb new/satsolver-0.0.19/bindings/ruby/tests/_patch.rb
--- old/satsolver-0.0.17/bindings/ruby/tests/_patch.rb 1970-01-01 01:00:00.000000000 +0100
+++ new/satsolver-0.0.19/bindings/ruby/tests/_patch.rb 2008-02-22 14:37:48.000000000 +0100
@@ -0,0 +1,52 @@
+
+#
+# Code11 Patch
+#
+
+class Item
+ attr_reader :name, :evr, :arch
+ def initialize name, evr, arch
+ @name = name
+ @evr = evr
+ @arch = arch
+ end
+ def to_s
+ "#{name} #{evr} #{arch}"
+ end
+end
+
+class Patch
+ attr_reader :name, :evr
+ attr_reader :timestamp, :category
+ attr_reader :summary, :description
+ attr_reader :contains
+
+ def initialize name, evr, category, timestamp
+ @name = name
+ @evr = evr
+ @category = category
+ @timestamp = timestamp
+ @contains = []
+ end
+
+ def summary= summary
+ @summary = summary
+ end
+ def description= description
+ @description = description
+ end
+
+ def add name,evr,arch
+ @contains << Item.new( name, evr, arch )
+ end
+
+ def to_s
+ s = "Name: #{@name}-#{@evr}\n" +
+ " Category: #{@category}\n" +
+ " Timestamp: #{@timestamp}\n" +
+ " Summary: #{@summary}\n" +
+ " Contains[#{@contains.size}]:\n"
+ @contains.each { |i| s += " #{i}\n" }
+ s
+ end
+end
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/bindings/ruby/tests/patch_updates.rb new/satsolver-0.0.19/bindings/ruby/tests/patch_updates.rb
--- old/satsolver-0.0.17/bindings/ruby/tests/patch_updates.rb 1970-01-01 01:00:00.000000000 +0100
+++ new/satsolver-0.0.19/bindings/ruby/tests/patch_updates.rb 2008-02-22 14:37:48.000000000 +0100
@@ -0,0 +1,136 @@
+$:.unshift "../../../build/bindings/ruby"
+
+# test Patch and Update
+
+require 'satsolver'
+require '_solv2patches'
+
+pool = SatSolver::Pool.new
+pool.arch = "x86_64"
+
+# load _all_ packages as 'installed'
+system = pool.add_solv( "packages.solv" )
+
+# now solve, fixing the system
+solver = SatSolver::Solver.new( pool, system )
+solver.allow_uninstall = true
+solver.fix_system = true
+
+pool.prepare
+solver.solve( pool.create_transaction )
+
+removals = []
+solver.each_to_remove { |s|
+ removals << s
+}
+
+puts "#{removals.size} solvables scheduled for removal"
+
+# create new 'system' from old, dropping removals
+
+new_system = pool.create_repo( "new_system" )
+system.each { |s|
+ new_system << s unless removals.include? s
+}
+
+#
+# now check new system
+#
+
+solver = SatSolver::Solver.new( pool, new_system )
+solver.allow_uninstall = true
+solver.fix_system = true
+
+pool.prepare
+solver.solve( pool.create_transaction )
+
+# these shouldn't print anything
+
+solver.each_to_install { |s|
+ puts "Install #{s}"
+}
+solver.each_to_remove { |s|
+ puts "Remove #{s}"
+}
+
+#
+# Add the repo with updates
+#
+
+updates = pool.add_solv( "updates.solv" )
+updates.name = "updates"
+
+pool.prepare
+solver = SatSolver::Solver.new( pool, new_system )
+solver.allow_uninstall = true
+solver.update_system = true
+solver.fix_system = true
+
+pool.prepare
+solver.solve( pool.create_transaction )
+
+#
+# Now match updates to patches
+#
+
+patches = solv2patches "patches.solv", "x86_64"
+
+#
+# Build a lookup hash
+#
+# Matching N patches to M updates would be a O(NxM) complexity
+# hashing patches by package name should reduce this to O(M)
+#
+
+updates = Hash.new
+patches.each { |patch|
+ next if patch.name[0,3] == "lib"
+ patch.contains.each { |c|
+ l = updates[c.name] || []
+ l << [c,patch]
+ updates[c.name] = l
+ }
+}
+
+output = []
+
+#
+# Now iterate over all computed package updates
+# and match them to patches
+# If a patch matches, collect it in 'output'
+# If no patch matches, create an artificial one (severty 'normal')
+#
+count = 0
+solver.each_to_update { |o,n|
+ count += 1
+ l = updates[n.name] # any patches known for this update ?
+ patch = nil
+ if l
+ l.each { |cp| # Y: find exact name,evr match
+ c = cp.first
+ if n.evr == c.evr
+ patch = cp[1]
+ break
+ end
+ }
+ end
+ unless patch
+# puts "Nothing patches #{n}"
+ buildtime = 0 # should be: n.buildtime
+ patch = Patch.new( n.name, n.evr, "normal", n.buildtime )
+ patch.summary = "Update of #{o.name}-#{o.evr}.#{o.arch}"
+ patch.add n.name, n.evr, n.arch
+ end
+ output << patch
+}
+
+final = output.uniq.sort { |a,b|
+ res = a.timestamp <=> b.timestamp
+ res = a.name <=> b.name if res == 0
+ res
+}
+puts "Reduced #{count} updates to #{final.size} patches"
+
+final.each { |p|
+ puts "#{p.category}: #{p.name}-#{p.evr} #{p.summary}"
+}
\ No newline at end of file
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/bindings/ruby/tests/_solv2patches.rb new/satsolver-0.0.19/bindings/ruby/tests/_solv2patches.rb
--- old/satsolver-0.0.17/bindings/ruby/tests/_solv2patches.rb 1970-01-01 01:00:00.000000000 +0100
+++ new/satsolver-0.0.19/bindings/ruby/tests/_solv2patches.rb 2008-02-22 14:37:48.000000000 +0100
@@ -0,0 +1,119 @@
+#
+# Convert .solv file to Code11 Patches
+#
+
+require 'satsolver'
+require '_patch'
+
+def store_it the_store, name, s
+ byname = the_store[name] || Hash.new
+ STDERR.puts "* known #{name}-#{s.evr}" if byname[s.evr]
+ byname[s.evr] = s
+ the_store[name] = byname
+ the_store
+end
+
+def solv2patches name, arch
+
+ pool = SatSolver::Pool.new( arch )
+ repo = pool.create_repo( "patches" )
+ repo.add_solv( name )
+ STDERR.puts "#{repo.size} patches and stuff"
+
+ atoms = Hash.new
+ patches = Hash.new
+
+
+ #
+ # Iterate through repo
+ # - ensure that it contains only patches and atoms
+ # - store all patches in 'patches'
+ # - store all atoms in 'atoms'
+ # - both stores are 2-dim hashed, first dim is name, second dim is evr
+ #
+
+ repo.each { |s|
+ sp = s.name.split ":"
+ unless sp.size == 2
+ STDERR.puts "** No kind #{s}"
+ exit
+ end
+ kind = sp[0]
+ case kind
+ when "atom"
+ atoms = store_it atoms, sp[1], s
+ when "patch"
+ patches = store_it patches, sp[1], s
+ else
+ STDERR.puts "** Unknown kind #{kind} of #{s}"
+ end
+ }
+
+ #
+ # Now iterate through all patches
+ # - ensure that it requires just atoms
+ # - by equality
+ # - find the atom
+ # - ensure that the atom has proper deps
+ #
+
+ out_patches = []
+
+ patches.each { |name,store|
+ store.each { |evr,patch|
+
+ p = Patch.new( name, evr, patch.category, patch.timestamp )
+ p.summary = patch.summary
+ p.description = patch.description
+
+ patch.requires.each { |req|
+ sp = req.name.split ":"
+
+ unless sp.size == 2
+ STDERR.puts "** Patch #{patch} requires #{req}"
+ exit
+ end
+ unless sp[0] == "atom"
+ STDERR.puts "** Patch #{patch} requires non-atom #{sp[0]}"
+ exit
+ end
+ name = sp[1]
+ byname = atoms[name]
+ unless byname
+ STDERR.puts "** Patch #{patch} requires unknown atom #{name}"
+ exit
+ end
+ unless req.op == SatSolver::REL_EQ
+ STDERR.puts "** Patch #{patch} requires non-equal atom #{name}"
+ exit
+ end
+ atom = byname[req.evr]
+ unless atom
+ STDERR.puts "** Patch #{patch} requires non-existing atom #{req.name}-#{req.evr}"
+ exit
+ end
+ atom.freshens.each { |fre|
+ unless fre.name == name
+ STDERR.puts "Atom #{atom} freshens #{fre}"
+ exit
+ end
+ }
+ atom.requires.each { |areq|
+ sp = areq.name.split ":"
+ unless sp.size == 1
+ STDERR.puts "Atom #{atom} requires #{areq}"
+ exit
+ end
+ unless areq.op == SatSolver::REL_GE
+ STDERR.puts "Atom #{atom} requires non-greater-equal #{areq}"
+ exit
+ end
+ p.add( areq.name, areq.evr, atom.arch )
+ }
+ }
+ out_patches << p
+ }
+ }
+
+ out_patches
+end
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/bindings/ruby/tests/updates.rb new/satsolver-0.0.19/bindings/ruby/tests/updates.rb
--- old/satsolver-0.0.17/bindings/ruby/tests/updates.rb 1970-01-01 01:00:00.000000000 +0100
+++ new/satsolver-0.0.19/bindings/ruby/tests/updates.rb 2008-02-22 14:37:48.000000000 +0100
@@ -0,0 +1,89 @@
+$:.unshift "../../../build/bindings/ruby"
+
+# test Updates
+
+require 'satsolver'
+
+pool = SatSolver::Pool.new
+pool.arch = "x86_64"
+
+# load _all_ packages as 'installed'
+system = pool.add_solv( "packages.solv" )
+
+# now solve, fixing the system
+solver = SatSolver::Solver.new( pool, system )
+solver.allow_uninstall = true
+solver.fix_system = true
+
+pool.prepare
+solver.solve( pool.create_transaction )
+
+removals = []
+solver.each_to_remove { |s|
+ removals << s
+}
+
+puts "#{removals.size} solvables scheduled for removal"
+
+# create new 'system' from old, dropping removals
+
+new_system = pool.create_repo( "new_system" )
+system.each { |s|
+ new_system << s unless removals.include? s
+}
+
+#
+# now check new system
+#
+
+solver = SatSolver::Solver.new( pool, new_system )
+solver.allow_uninstall = true
+solver.fix_system = true
+
+pool.prepare
+solver.solve( pool.create_transaction )
+
+# these shouldn't print anything
+
+solver.each_to_install { |s|
+ puts "Install #{s}"
+}
+solver.each_to_remove { |s|
+ puts "Remove #{s}"
+}
+
+#
+# Add the repo with updates
+#
+
+updates = pool.add_solv( "updates.solv" )
+updates.name = "updates"
+
+pool.prepare
+solver = SatSolver::Solver.new( pool, new_system )
+solver.allow_uninstall = true
+solver.update_system = true
+solver.fix_system = true
+
+pool.prepare
+solver.solve( pool.create_transaction )
+
+count = 0
+solver.each_to_install { |s|
+ count += 1
+}
+puts "#{count} installs"
+
+count = 0
+solver.each_to_remove { |s|
+ count += 1
+}
+puts "#{count} removals"
+
+
+count = 0
+solver.each_to_update { |o,n|
+ count += 1
+ puts n
+}
+puts "#{count} updates"
Files old/satsolver-0.0.17/bindings/ruby/tests/updates.solv and new/satsolver-0.0.19/bindings/ruby/tests/updates.solv differ
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/bindings/satsolver.i new/satsolver-0.0.19/bindings/satsolver.i
--- old/satsolver-0.0.17/bindings/satsolver.i 2008-02-20 13:34:12.000000000 +0100
+++ new/satsolver-0.0.19/bindings/satsolver.i 2008-02-22 14:37:48.000000000 +0100
@@ -591,6 +591,12 @@
XSolvable *create_solvable( const char *name, const char *evr, const char *arch = NULL )
{ return xsolvable_create( $self, name, evr, arch ); }
+#if defined(SWIGRUBY)
+ %alias add "<<";
+#endif
+ XSolvable *add( XSolvable *xs )
+ { return xsolvable_add( $self, xs ); }
+
void each()
{ repo_xsolvables_iterate( $self, generic_xsolvables_iterate_callback ); }
@@ -902,6 +908,45 @@
return solvable2str( $self->pool, xsolvable_solvable( $self ) );
}
+#if defined(SWIGRUBY)
+ %alias equal "==";
+ %typemap(out) int equal
+ "$result = ($1 != 0) ? Qtrue : Qfalse;";
+#endif
+ int equal( XSolvable *xs )
+ { return xsolvable_equal( $self, xs); }
+
+#if defined(SWIGRUBY)
+ %alias cmp "<=>";
+#endif
+ int cmp( XSolvable *xs )
+ {
+ Solvable *s1 = xsolvable_solvable( $self );
+ Solvable *s2 = xsolvable_solvable( xs );
+ const char *n1 = 0, *n2 = 0;
+ int i = 0;
+
+ if (($self->pool != xs->pool)
+ || (s1->name != s2->name))
+ {
+ n1 = id2str( $self->pool, s1->name );
+ n2 = id2str( xs->pool, s2->name );
+ i = strcmp( n1, n2 );
+ }
+ if (i == 0) /* names are equal */
+ {
+ if ($self->pool == xs->pool)
+ i = evrcmp( $self->pool, s1->evr, s2->evr, EVRCMP_COMPARE );
+ else
+ {
+ n1 = id2str( $self->pool, s1->evr );
+ n2 = id2str( xs->pool, s2->evr );
+ i = strcmp( n1, n2 );
+ }
+ }
+ return i;
+ }
+
/*
* Dependencies
*/
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/.emacs-dirvars new/satsolver-0.0.19/.emacs-dirvars
--- old/satsolver-0.0.17/.emacs-dirvars 1970-01-01 01:00:00.000000000 +0100
+++ new/satsolver-0.0.19/.emacs-dirvars 2008-02-22 14:37:48.000000000 +0100
@@ -0,0 +1,9 @@
+;; -*- emacs-lisp -*-
+;;
+;; This file is processed by the dirvars emacs package. Each variable
+;; setting below is performed when this dirvars file is loaded.
+;;
+c-file-style: "gnu";
+fill-column: 78
+indent-tabs-mode: nil
+tab-width: 8
Files old/satsolver-0.0.17/examples/ruby/10_3-x86_64-patterns.solv and new/satsolver-0.0.19/examples/ruby/10_3-x86_64-patterns.solv differ
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/examples/ruby/patterncheck.rb new/satsolver-0.0.19/examples/ruby/patterncheck.rb
--- old/satsolver-0.0.17/examples/ruby/patterncheck.rb 1970-01-01 01:00:00.000000000 +0100
+++ new/satsolver-0.0.19/examples/ruby/patterncheck.rb 2008-02-22 14:37:48.000000000 +0100
@@ -0,0 +1,230 @@
+#
+# patterncheck.rb
+#
+# First approach to 'rpm-only' pattern management
+# - seperate pattern into 'pattern deps' and 'package deps'
+# - solve patterns only
+# - use the resulting packages deps to check the system
+#
+
+#
+# Fully print dependency
+#
+
+def fullprint_deps name, deps
+ print "#{name}: "
+ first = true
+ deps.each do |d|
+ print "\t" unless first
+ puts "#{d}"
+ first = false
+ end
+ puts if first
+end
+
+#
+# Fully print solvable s, including dependencies
+#
+
+def fullprint_solvable solvable
+ puts "Solvable #{solvable}"
+ fullprint_deps "Provides", solvable.provides
+ fullprint_deps "Requires", solvable.requires
+ fullprint_deps "Conflicts", solvable.conflicts
+ fullprint_deps "Obsoletes", solvable.obsoletes
+ fullprint_deps "Recommends", solvable.recommends
+ fullprint_deps "Suggests", solvable.suggests
+ fullprint_deps "Supplements", solvable.supplements
+ fullprint_deps "Enhances", solvable.enhances
+ fullprint_deps "Freshens", solvable.freshens
+end
+
+
+#
+# strip 'pattern:' prefix from name
+#
+
+def strip_prefix name
+ return name[8..-1] if name =~ /^pattern:/
+ name
+end
+
+# split_dep( deps, pdeps )
+# add pattern: deps to pdeps
+# return non-pattern relations stripped of pool
+# (as array of [ name, op, evr ])
+#
+
+def split_dep deps, pdeps
+# puts "split_dep #{deps} [#{deps.size}], #{pdeps} [#{pdeps.size}]"
+ relations = Array.new
+ deps.each { |d|
+ name = strip_prefix d.name
+ if d.name =~ /^pattern:/
+# puts "Relation.new( #{name}, #{d.op}, #{d.evr})"
+ pdeps << Relation.new( pdeps.solvable.repo.pool, name, d.op, d.evr )
+ else
+ relations << [ name, d.op, d.evr ]
+ end
+ }
+ relations
+end
+
+# split_deps()
+# split dependencies of (pattern) p
+# into (pattern) new_pat for pattern: deps
+# and my_deps for non-pattern: deps
+# my_deps => Hash { string(name) => Hash { symbol(dependency) => Array [ name, op, evr ] } }
+#
+
+def split_deps p, new_pat
+# puts "split_deps pattern"
+# fullprint_solvable p
+# puts "new_pat"
+# fullprint_solvable new_pat
+# puts
+ my_deps = Hash.new
+ my_deps[:provides] = split_dep p.provides, new_pat.provides
+ my_deps[:requires] = split_dep p.requires, new_pat.requires
+ my_deps[:conflicts] = split_dep p.conflicts, new_pat.conflicts
+ my_deps[:obsoletes] = split_dep p.obsoletes, new_pat.obsoletes
+ my_deps[:recommends] = split_dep p.recommends, new_pat.recommends
+ my_deps[:suggests] = split_dep p.suggests, new_pat.suggests
+ my_deps[:supplements] = split_dep p.supplements, new_pat.supplements
+ my_deps[:enhances] = split_dep p.enhances, new_pat.enhances
+ my_deps[:freshens] = split_dep p.freshens, new_pat.freshens
+# puts "==> new_pat"
+# fullprint_solvable new_pat
+ my_deps
+end
+
+
+#
+# check status of solved.decision against system(Pool) and my_deps (Hash of package deps)
+#
+def check_status system, my_deps, solved
+ solver = system.create_solver
+ t = system.create_transaction
+ # collect dependencies
+ solved.each_decision do |d|
+ deps = my_deps[d.solvable.name]
+ case d.op
+ when DEC_INSTALL
+ deps[:requires].each do |r|
+ t.install system.create_relation( r[0], r[1], r[2] )
+ end
+ deps[:obsoletes].each do |r|
+ t.remove system.create_relation( r[0], r[1], r[2] )
+ end
+ deps[:conflicts].each do |r|
+ t.remove system.create_relation( r[0], r[1], r[2] )
+ end
+ when DEC_REMOVE
+ else
+ raise "******Unhandled #{d.op}"
+ end
+ end
+ solver.solve t unless t.empty?
+
+ if solver.problems?
+ return false
+ solver.each_problem t do |p|
+ puts "\t #{p}"
+ end
+ else
+ return true
+ end
+ if nil
+ t.each do |a|
+ print "\t"
+ puts (
+ case a.cmd
+ when SatSolver::INSTALL_SOLVABLE: "install #{a.solvable}"
+ when SatSolver::REMOVE_SOLVABLE: "remove #{a.solvable}"
+ when SatSolver::INSTALL_SOLVABLE_NAME: "install by name #{a.name}"
+ when SatSolver::REMOVE_SOLVABLE_NAME: "remove by name #{a.name}"
+ when SatSolver::INSTALL_SOLVABLE_PROVIDES: "install by relation #{a.relation}"
+ when SatSolver::REMOVE_SOLVABLE_PROVIDES: "remove by relation #{a.relation}"
+ else "<NONE>"
+ end
+ )
+ end
+ end
+end
+
+$: << "../../build/bindings/ruby"
+require 'satsolver'
+include SatSolver
+require 'problem'
+require 'decision'
+
+pool = Pool.new( "x86_64" )
+
+patterns = pool.create_repo 'patterns'
+patterns.add_solv '10_3-x86_64-patterns.solv'
+
+puts "Found #{patterns.size} patterns"
+
+#
+# Now split-off package deps from the patterns and
+# keep them separate.
+#
+
+my_pool = Pool.new
+my_pool.arch = "x86_64"
+
+my_pats = my_pool.create_repo 'patterns'
+my_deps = Hash.new
+
+i = 0
+patterns.each do |p|
+ i += 1
+# fullprint_solvable p
+ name = strip_prefix p.name
+ new_pat = my_pats.create_solvable name, p.evr, p.arch
+ my_deps[name] = split_deps p, new_pat
+# break if i > 1
+end
+
+my_pool.promoteepoch = 1
+
+puts "My pool has #{my_pool.count_repos} repos and #{my_pool.size} solvables"
+
+system = Pool.new "x86_64"
+system.add_rpmdb "/"
+
+i = 0
+my_pats.each do |p|
+ i += 1
+ solver = my_pool.create_solver
+
+ solver.fix_system = 0
+ solver.update_system = 0
+ solver.allow_downgrade = 0
+ solver.allow_uninstall = 0
+ solver.no_update_provide = 0
+
+ t = my_pool.create_transaction
+ t.install p
+ solver.solve t
+# fullprint_solvable p
+# print "Install #{p} "
+ if solver.problems?
+ puts "*** Failed"
+ solver.each_problem t do |p|
+# puts p
+ end
+ else
+# puts "Succeeded"
+ result = check_status system, my_deps, solver
+ if result
+ puts "Yes: #{p}"
+ else
+ puts "No: #{p}"
+ end
+ solver.each_decision do |d|
+# puts d
+ end
+ end
+# break if i > 1
+end
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/package/libsatsolver.changes new/satsolver-0.0.19/package/libsatsolver.changes
--- old/satsolver-0.0.17/package/libsatsolver.changes 2008-02-20 13:34:12.000000000 +0100
+++ new/satsolver-0.0.19/package/libsatsolver.changes 2008-02-22 14:37:48.000000000 +0100
@@ -1,4 +1,14 @@
-------------------------------------------------------------------
+Fri Feb 22 11:09:05 CET 2008 - coolo@suse.de
+
+- support rpmdb2solv -r for chroots
+
+-------------------------------------------------------------------
+Thu Feb 21 18:14:41 CET 2008 - coolo@suse.de
+
+- fix susetags parser for so called full trees
+
+-------------------------------------------------------------------
Wed Feb 20 13:23:31 CET 2008 - coolo@suse.de
- no longer link against db43 but against rpmlib
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/src/poolid.c new/satsolver-0.0.19/src/poolid.c
--- old/satsolver-0.0.17/src/poolid.c 2008-02-20 13:34:12.000000000 +0100
+++ new/satsolver-0.0.19/src/poolid.c 2008-02-22 14:37:48.000000000 +0100
@@ -28,7 +28,7 @@
{
int oldnstrings = pool->ss.nstrings;
Id id = stringpool_str2id (&pool->ss, str, create);
- if (create && oldnstrings != pool->ss.nstrings && (id & WHATPROVIDES_BLOCK) == 0)
+ if (create && pool->whatprovides && oldnstrings != pool->ss.nstrings && (id & WHATPROVIDES_BLOCK) == 0)
{
/* grow whatprovides array */
pool->whatprovides = sat_realloc(pool->whatprovides, (id + (WHATPROVIDES_BLOCK + 1)) * sizeof(Offset));
@@ -42,7 +42,7 @@
{
int oldnstrings = pool->ss.nstrings;
Id id = stringpool_strn2id (&pool->ss, str, len, create);
- if (create && oldnstrings != pool->ss.nstrings && (id & WHATPROVIDES_BLOCK) == 0)
+ if (create && pool->whatprovides && oldnstrings != pool->ss.nstrings && (id & WHATPROVIDES_BLOCK) == 0)
{
/* grow whatprovides array */
pool->whatprovides = sat_realloc(pool->whatprovides, (id + (WHATPROVIDES_BLOCK + 1)) * sizeof(Offset));
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/src/repo.h new/satsolver-0.0.19/src/repo.h
--- old/satsolver-0.0.17/src/repo.h 2008-02-20 13:34:12.000000000 +0100
+++ new/satsolver-0.0.19/src/repo.h 2008-02-22 14:37:48.000000000 +0100
@@ -132,7 +132,7 @@
}
#define FOR_REPO_SOLVABLES(r, p, s) \
- for (p = (r)->start, s = (r)->pool->solvables + p; p < (r)->end; p++, s++) \
+ for (p = (r)->start, s = (r)->pool->solvables + p; p < (r)->end; p++, s = (r)->pool->solvables + p) \
if (s->repo == (r))
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/src/repo_solv.c new/satsolver-0.0.19/src/repo_solv.c
--- old/satsolver-0.0.17/src/repo_solv.c 2008-02-20 13:34:12.000000000 +0100
+++ new/satsolver-0.0.19/src/repo_solv.c 2008-02-22 14:37:48.000000000 +0100
@@ -1375,6 +1375,7 @@
sat_free(parent->schemata);
sat_free(parent->schemadata);
sat_free(parent->keys);
+ sat_free(parent->location);
*parent = data;
}
else if (data.incoredatalen || data.fp)
@@ -1416,7 +1417,6 @@
return;
}
fp = pool->loadcallback(pool, data, pool->loadcallbackdata);
-
if (!fp)
{
data->state = REPODATA_ERROR;
@@ -1426,4 +1426,5 @@
data->state = REPODATA_ERROR;
else
data->state = REPODATA_AVAILABLE;
+ fclose(fp);
}
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/tools/common_write.c new/satsolver-0.0.19/tools/common_write.c
--- old/satsolver-0.0.17/tools/common_write.c 2008-02-20 13:34:12.000000000 +0100
+++ new/satsolver-0.0.19/tools/common_write.c 2008-02-22 14:37:48.000000000 +0100
@@ -92,14 +92,19 @@
fileinfo = fileinfoa;
FILE *fp = fopen (attrname, "w");
repo_write(repo, fp, keyfilter_attr, 0, fileinfo, 0);
- fclose (fp);
- fileinfo->location = strdup (attrname);
+ fclose(fp);
+ fileinfo->location = strdup(attrname);
fileinfo++;
nsubfiles = fileinfo - fileinfoa;
fileinfo = fileinfoa;
}
repo_write(repo, stdout, keyfilter_solv, 0, fileinfo, nsubfiles);
+ if (fileinfo)
+ {
+ free(fileinfo->location);
+ free(fileinfo->keys);
+ }
sat_free(filter);
return 0;
}
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/tools/repo_rpmdb.c new/satsolver-0.0.19/tools/repo_rpmdb.c
--- old/satsolver-0.0.17/tools/repo_rpmdb.c 2008-02-20 13:34:12.000000000 +0100
+++ new/satsolver-0.0.19/tools/repo_rpmdb.c 2008-02-22 14:37:48.000000000 +0100
@@ -718,6 +718,7 @@
unsigned int refmask, h;
int asolv;
Repodata *repodata;
+ char dbpath[PATH_MAX];
if (repo->start != repo->end)
abort(); /* FIXME: rpmdbid */
@@ -736,9 +737,10 @@
if (!ref)
{
- if (db->open(db, 0, "/var/lib/rpm/Packages", 0, DB_HASH, DB_RDONLY, 0664))
+ snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Packages", rootdir);
+ if (db->open(db, 0, dbpath, 0, DB_HASH, DB_RDONLY, 0664))
{
- perror("db->open /var/lib/rpm/Packages");
+ perror("db->open var/lib/rpm/Packages");
exit(1);
}
if (db->get_byteswapped(db, &byteswapped))
@@ -828,9 +830,10 @@
}
else
{
- if (db->open(db, 0, "/var/lib/rpm/Name", 0, DB_HASH, DB_RDONLY, 0664))
+ snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Name", rootdir);
+ if (db->open(db, 0, dbpath, 0, DB_HASH, DB_RDONLY, 0664))
{
- perror("db->open /var/lib/rpm/Name");
+ perror("db->open var/lib/rpm/Name");
exit(1);
}
if (db->get_byteswapped(db, &byteswapped))
@@ -955,9 +958,10 @@
perror("db_create");
exit(1);
}
- if (db->open(db, 0, "/var/lib/rpm/Packages", 0, DB_HASH, DB_RDONLY, 0664))
+ snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Packages", rootdir);
+ if (db->open(db, 0, dbpath, 0, DB_HASH, DB_RDONLY, 0664))
{
- perror("db->open /var/lib/rpm/Packages");
+ perror("db->open var/lib/rpm/Packages");
exit(1);
}
if (db->get_byteswapped(db, &byteswapped))
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/tools/repo_rpmmd.c new/satsolver-0.0.19/tools/repo_rpmmd.c
--- old/satsolver-0.0.17/tools/repo_rpmmd.c 2008-02-20 13:34:12.000000000 +0100
+++ new/satsolver-0.0.19/tools/repo_rpmmd.c 2008-02-22 14:37:48.000000000 +0100
@@ -608,7 +608,19 @@
case STATE_LOCATION:
str = find_attr("href", atts);
if (str)
- repodata_set_str(pd->data, entry, id_mediafile, str);
+ {
+ const char *str2 = strrchr(str, '/');
+ if (str2)
+ {
+ char *str3 = strdup (str);
+ str3[str2 - str] = 0;
+ repodata_set_poolstr(pd->data, entry, id_mediadir, str3);
+ free(str3);
+ repodata_set_str(pd->data, entry, id_mediafile, str2 + 1);
+ }
+ else
+ repodata_set_str(pd->data, entry, id_mediafile, str);
+ }
break;
case STATE_CHECKSUM:
pd->tmpattr = find_attr("type", atts);
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/tools/repo_write.c new/satsolver-0.0.19/tools/repo_write.c
--- old/satsolver-0.0.17/tools/repo_write.c 2008-02-20 13:34:12.000000000 +0100
+++ new/satsolver-0.0.19/tools/repo_write.c 2008-02-22 14:37:48.000000000 +0100
@@ -482,7 +482,6 @@
int oldoff = cbdata->needid[0].map;
int newoff = (id + 1 + NEEDED_BLOCK) & ~NEEDED_BLOCK;
int nrels = cbdata->repo->pool->nrels;
- fprintf(stderr, "growing needid...\n");
cbdata->needid = sat_realloc2(cbdata->needid, newoff + nrels, sizeof(NeedId));
if (nrels)
memmove(cbdata->needid + newoff, cbdata->needid + oldoff, nrels * sizeof(NeedId));
@@ -618,16 +617,20 @@
v[3] = u32;
data_addblob(xd, v, 4);
break;
+ case TYPE_NUM:
+ data_addid(xd, kv->num);
+ break;
case TYPE_DIR:
id = kv->id;
+ if (cbdata->owndirpool)
+ id = putinowndirpool(cbdata, data, &data->dirpool, id);
id = cbdata->dirused[id];
data_addid(xd, id);
break;
- case TYPE_NUM:
- data_addid(xd, kv->num);
- break;
case TYPE_DIRNUMNUMARRAY:
id = kv->id;
+ if (cbdata->owndirpool)
+ id = putinowndirpool(cbdata, data, &data->dirpool, id);
id = cbdata->dirused[id];
data_addid(xd, id);
data_addid(xd, kv->num);
@@ -635,6 +638,8 @@
break;
case TYPE_DIRSTRARRAY:
id = kv->id;
+ if (cbdata->owndirpool)
+ id = putinowndirpool(cbdata, data, &data->dirpool, id);
id = cbdata->dirused[id];
data_addideof(xd, id, kv->eof);
data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
@@ -756,6 +761,7 @@
setfileinfo = 1;
memset(&cbdata, 0, sizeof(cbdata));
+ cbdata.repo = repo;
/* go through all repodata and find the keys we need */
/* also unify keys */
@@ -842,7 +848,7 @@
cbdata.keymap[n++] = 0; /* key 0 */
idused = 0;
dirused = 0;
- for (j = 1; j < data->nkeys; j++)
+ for (j = 1; j < data->nkeys; j++, n++)
{
key = data->keys + j;
/* see if we already had this one, should use hash for fast miss */
@@ -858,22 +864,25 @@
if (k < cbdata.nmykeys)
{
repodataused[i] = 1;
- cbdata.keymap[n++] = k;
- continue;
+ cbdata.keymap[n] = k;
}
- cbdata.mykeys[cbdata.nmykeys] = *key;
- key = cbdata.mykeys + cbdata.nmykeys;
- key->storage = KEY_STORAGE_INCORE;
- if (key->type != TYPE_CONSTANT)
- key->size = 0;
- if (keyfilter)
+ else
{
- key->storage = keyfilter(repo, key, kfdata);
- if (key->storage == KEY_STORAGE_DROPPED)
+ cbdata.mykeys[cbdata.nmykeys] = *key;
+ key = cbdata.mykeys + cbdata.nmykeys;
+ key->storage = KEY_STORAGE_INCORE;
+ if (key->type != TYPE_CONSTANT)
+ key->size = 0;
+ if (keyfilter)
{
- cbdata.keymap[n++] = 0;
- continue;
+ key->storage = keyfilter(repo, key, kfdata);
+ if (key->storage == KEY_STORAGE_DROPPED)
+ {
+ cbdata.keymap[n] = 0;
+ continue;
+ }
}
+ cbdata.keymap[n] = cbdata.nmykeys++;
}
/* load repodata if not already loaded */
if (data->state == REPODATA_STUB)
@@ -886,17 +895,17 @@
{
/* redo this repodata! */
j = 0;
- n = cbdata.keymapstart[i] + 1;
+ n = cbdata.keymapstart[i];
continue;
}
}
if (data->state == REPODATA_ERROR)
{
/* too bad! */
- cbdata.keymap[n++] = 0;
+ cbdata.keymap[n] = 0;
continue;
}
- cbdata.keymap[n++] = cbdata.nmykeys++;
+
repodataused[i] = 1;
if (key->type != TYPE_STR && key->type != TYPE_U32)
idused = 1;
@@ -958,6 +967,17 @@
spool = &repo->pool->ss;
}
+ if (poolusage > 1)
+ {
+ /* convert global keys to new pool names */
+ for (i = 0; i < cbdata.nmykeys; i++)
+ {
+ if (cbdata.mykeys[i].storage == KEY_STORAGE_DROPPED)
+ continue;
+ cbdata.mykeys[i].name = stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[i].name), 1);
+ }
+ }
+
if (dirpoolusage == 3)
{
dirpool = &owndirpool;
@@ -974,7 +994,7 @@
fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
fprintf(stderr, "nmykeys: %d\n", cbdata.nmykeys);
for (i = 1; i < cbdata.nmykeys; i++)
- fprintf(stderr, " %2d: %d %d\n", i, cbdata.mykeys[i].name, cbdata.mykeys[i].type);
+ fprintf(stderr, " %2d: %d %d %d\n", i, cbdata.mykeys[i].name, cbdata.mykeys[i].type, cbdata.mykeys[i].storage);
#endif
/********************************************************************/
@@ -1172,8 +1192,7 @@
for (i = 1; i < dirpool->ndirs; i++)
{
#if 0
-if (cbdata.dirused)
- fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused[i]);
+fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
#endif
id = dirpool->dirs[i];
if (id <= 0)
@@ -1554,6 +1573,17 @@
sat_free(cbdata.extdata[i].buf);
sat_free(cbdata.extdata);
+ if (cbdata.ownspool)
+ {
+ sat_free(cbdata.ownspool->strings);
+ sat_free(cbdata.ownspool->stringspace);
+ sat_free(cbdata.ownspool->stringhashtbl);
+ }
+ if (cbdata.owndirpool)
+ {
+ sat_free(cbdata.owndirpool->dirs);
+ sat_free(cbdata.owndirpool->dirtraverse);
+ }
sat_free(needid);
sat_free(cbdata.solvschemata);
sat_free(cbdata.myschemadata);
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/tools/rpmdb2solv.c new/satsolver-0.0.19/tools/rpmdb2solv.c
--- old/satsolver-0.0.17/tools/rpmdb2solv.c 2008-02-20 13:34:12.000000000 +0100
+++ new/satsolver-0.0.19/tools/rpmdb2solv.c 2008-02-22 14:37:48.000000000 +0100
@@ -16,6 +16,7 @@
#include
#include
#include
+#include
#include "pool.h"
#include "repo.h"
@@ -29,22 +30,28 @@
Pool *pool = pool_create();
Repo *repo, *ref = 0;
FILE *fp;
-
- if (argc != 1)
- {
- Pool *refpool = pool;
- if ((fp = fopen(argv[1], "r")) == NULL)
- {
- perror(argv[1]);
- exit(0);
- }
- ref = repo_create(refpool, "ref");
- repo_add_solv(ref, fp);
- fclose(fp);
- }
-
+ Pool *refpool;
+ int g;
+ const char *root = "/";
+
+ while ((g = getopt (argc, argv, "-r:")) >= 0)
+ switch (g)
+ {
+ case 'r': root = optarg; break;
+ case 1:
+ refpool = pool;
+ if ((fp = fopen(argv[1], "r")) == NULL)
+ {
+ perror(argv[1]);
+ exit(0);
+ }
+ ref = repo_create(refpool, "ref");
+ repo_add_solv(ref, fp);
+ fclose(fp);
+ }
+
repo = repo_create(pool, "installed");
- repo_add_rpmdb(repo, ref, "/");
+ repo_add_rpmdb(repo, ref, root);
if (ref)
{
if (ref->pool != pool)
diff -urN --exclude=CVS --exclude=.cvsignore --exclude=.svn --exclude=.svnignore old/satsolver-0.0.17/VERSION.cmake new/satsolver-0.0.19/VERSION.cmake
--- old/satsolver-0.0.17/VERSION.cmake 2008-02-20 13:34:12.000000000 +0100
+++ new/satsolver-0.0.19/VERSION.cmake 2008-02-22 14:37:48.000000000 +0100
@@ -46,5 +46,5 @@
SET(LIBSATSOLVER_MAJOR "0")
SET(LIBSATSOLVER_MINOR "0")
-SET(LIBSATSOLVER_PATCH "17")
+SET(LIBSATSOLVER_PATCH "19")
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Remember to have fun...
---------------------------------------------------------------------
To unsubscribe, e-mail: opensuse-commit+unsubscribe@opensuse.org
For additional commands, e-mail: opensuse-commit+help@opensuse.org