[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

RFC: APT2 <-> underlying package manager interface plans



Hi everyone,

I have attached documentation code which describes the current
plans for the interface between APT2 and underlying package
managers such as dpkg.

It is based on a resolver object which takes the user request
and outputs a list of changes which should be applied (the
"changeset"). The changeset returned by the solver is complete,
which means that after all changes have been applied, there won't
be any broken packages on the system.

This changeset is then passed to PackageManager.apply_changeset(),
which orders and groups the changes to minimize the need to pass
--force-depends options to dpkg. It could also do no ordering and
grouping at all, and just pass --force-depends to every dpkg
call; which should result in a working system.

The whole code is intented to support multi-arch systems, and
systems where two versions of a package can be installed at
once (latter one is for future RPM support). This means a
P(name,version,architecture) identifies one package (we
may actually allow the solver to pass package IDs to the
package manager in case two packages with the same version
exist; but this is another story).

I should also note that the PackageManager classes will probably
be implemented by plugins, and that the interface has to be rather
independent from dpkg,rpm,etc.

If I missed something, please tell me. Next topic will most probably
be the cache design. Afterwards, we can talk about solving
dependencies. And once all theoretical work is done, I can start
implementing it.
-- 
Julian Andres Klode  - Debian Developer, Ubuntu Member

See http://wiki.debian.org/JulianAndresKlode and http://jak-linux.org/.
/*
 * PackageManagerDemo.vala - Example of APT2 <-> dpkg interface
 *
 * Copyright (C) 2009 Julian Andres Klode <jak@debian.org>
 * 
 * Copying and distribution of this file, with or without modification,
 * are permitted in any medium without royalty provided the copyright
 * notice and this notice are preserved.  This file is offered as-is,
 * without any warranty.
 *
 * This file is not meant to be used as a program; but just for documenting
 * the interface. It should be treated as pseudo-code, although it may compile.
 *
 * Description:
 *  This is an example of how the interface between APT2 and a low-level
 *  package manager such as dpkg could look.
 *
 *  We basically have on PackageManager object which we ask to carry out
 *  certain action/changes. The API is extremely different from the one
 *  in APT due to not treating changes completely separate from the cache,
 *  which only provides information about dependencies.
 *
 *  The PackageManager progress handling is designed like the InstallProgress
 *  classes in python-apt and is easy. Moving progress handling directly into
 *  the PackageManager unifies the progress handling regardless of the type of
 *  package manager used.
 */

namespace Apt {

    /**
     * Types of actions which can be carried out by a package manager.
     *
     * Available options:
     *  - INSTALL: Install a package
     *  - REMOVE: Remove a package from the system
     *  - PURGE: Remove a package and delete configuration files.
     *
     * If a package manager does not differentiate between remove and purge,
     * it must treat both as removal requests.
     */
    public enum Action {
        INSTALL,
        REMOVE,
        PURGE
    }

    /**
     * Object representing a change to be carried out by PackageManager.
     *
     * This objects describes requests to the package manager. There can be
     * two basic kinds of actions. The first one is an action regarding an
     * already installed package e.g. removal; the second one is the
     * installation of a new package. The first kind is uniquely described
     * by the quadruple (package-name, version, architecture, action). The
     * second one is described by the pair (filename, action); although the
     * (name, version, architecture) fields can be used to help sorting the
     * installation commands.
     *
     * A subclass of this could represent user requests to a dependency
     * resolver by providing an additional field for storing the version
     * comparison operator (e.g. <=, >=). Such an object would provide a
     * superset of the information provided by a debian dependency.
     */
    public class Change {
        /** The name of the package */
        public string name;
        /** The version of the package */
        public string? version;
        /** The architecture of the package the change should be applied to.*/
        public string? architecture;
        /** The action to be taken */
        public Action action;
        /** The filename of the package */
        public string? filename;

        public Change(string name, string? version, string? architecture,
                     Action action, string? filename) {
            this.name = name;
            this.version = version;
            this.architecture = architecture;
            this.action = action;
            this.filename = filename;
        }
    }


    /**
     * Interface to the native system package manager.
     *
     * This prepares the dpkg calls, calls dpkg; connects to its status fd
     * and emits the correct signal when it receives updates from dpkg.
     *
     * The PackageManager should be connected to the cache, so it can correctly
     * order package installations and removals. When no cache has been given,
     * PackageManager may abort installations due to unsatisfied dependencies.
     */
    public class PackageManager : Object {

        /**
         * The stages of installation, as recognized by dpkg.
         *
         * TODO: Generic replacement or move them into their own signals.
         */
        public enum Stage {
            UPGRADE,
            INSTALL,
            CONFIGURE,
            TRIGGER_PROCESSING,
            DISAPPEAR,
            REMOVE,
            PURGE
        }


        /*
         * Signals to be emited when we receive the status messages from dpkg.
         *
         * This is actually a bit to dpkg-specific and should be reworked to
         * support other package managers.
         */
        public signal void conffile(string file, string realold,
                                    string realnew, string useredited,
                                    string distedited);
        public signal void stage(string package, Stage stage);
        public signal void status(string package, string status);
        public signal void error(string package, string error);

        /**
         * This would read the changeset and order and group the dpkg calls
         * in a way that dependencies are satisfied most of the times.
         *
         * Then it would call dpkg, and read the status fd; and when it reads
         * something new, it would emit the corresponding signal.
         *
         * A changeset must be complete in a way that the dependencies of all
         * installed packages are satisfied. Using the information in the cache
         * and a non-recursive analysis of the dependency information stored in
         * the cache, we should be able to sort the commands in a way which
         * makes dpkg happy.
         *
         * If we can't make dpkg happy, we'll have to pass --force-depends to
         * it and process the changes in multiple steps. This may be required
         * under special circumstances.
         */
        public void apply_changeset(Gee.Collection<Change> changeset) {
            foreach(Change change in changeset) {
                if (change.action == Action.REMOVE)
                    print("dpkg --remove %s\n", change.name);
                else if (change.action == Action.PURGE)
                    print("dpkg --purge %s\n", change.name);
                else if (change.action == Action.INSTALL)
                    print("dpkg --install %s\n", change.filename);
            }
        }
    }

    public void main() {
        // Create the package manager object
        var manager = new PackageManager();
        // Create the changeset
        var changes = new Gee.ArrayList<Change>();
        // Add a change to remove all versions of pkg for all architectures.
        changes.add(new Change("pkg", null, null, Action.REMOVE, null));
        // Add a change to install the file pkg.deb
        changes.add(new Change("pkg", null, null, Action.INSTALL, "pkg.deb"));
        // Apply the changes
        manager.apply_changeset(changes);
    }
}

Attachment: signature.asc
Description: Digital signature


Reply to: