Projekte

PPA - The easy way

PaketEiner der grossen Vorteile von Linux Distributionen sind die verwalteten Repositories für die Bereitstellung und Aktualisierung von Softwarepaketen. Neben den offiziellen Paketquellen wie 'main, universe, restricted, multiverse', gibt es die 'Persönlichen Paket Archive' (PPA), mit denen Entwickler eigene Software an die Community verteilen können.

Leider ist der Weg dorthin - gerade für Anfänger - dornig und mit Fehlschlägen gespickt. Dies liegt nicht an fehlender Dokumentation oder Hilfsbereitschaft in den Foren; im Gegenteil, die Informationen zum Paketbau und PPA-Befüllen sind so vielfältig, dass man vor lauter Bäumen den Wald nicht mehr sieht. Dieser Artikel gibt eine einfache Anleitung zum Erstellen und Publizieren eines Softwarepakets über ein PPA.

 

Voraussetzungen

Bevor die eigene Software in einem PPA angeboten werden kann, gilt es einige Voraussetzungen zu erfüllen. Diese sind:

  • ein vollständiges Konto bei Launchpad
  • wissen, wie man ein einfaches Programm in Python erstellt
  • Grundkenntnisse der Linux Verzeichnisse
  • Grundkenntnisse im Umgang mit einem Terminal

Keine Angst, diese Voraussetzung sind einfach und schnell zu erlernen. Beim persönlichen Konto in Launchpad werden benötigt: OpenPGP Key, SSH Key, Signed Ubuntu Code of Conduct und natürlich ein leeres PPA. In Launchpad selbst ist sehr gut erklärt wie man das alles macht. Ein PPA (oder mehrere) gehören zur persönlichen Launchpad Seite und nicht etwa zur Projektseite im Launchpad. Die Verbindung zwischen dem Projekt und dem PPA kann (zur Zeit) nur über einen Link von der Launchpad Projektseite zur persönlichen Seite hergestellt werden.

Bekanntlich führen viele Wege nach Rom. Das hier beschriebene Vorgehen und die verwendeten Strukturen für Dateien und Verzeichnisse stellen nur einen möglichen Weg von hunderten dar. Vermutlich hält der Vorschlag nicht der kritischen Betrachtung eines erfahrenen Paketbauers stand, insbesondere wenn grosse bzw. Plattform-übergreifende Software paketiert werden soll. Hier geht es in erster Linie um Einfachheit und Nachvollziehbarkeit.


Hello World

Bei den gängigen Anleitungen zum Paketbau wird meistens irgendein fertiges Paket als Beispiel verwendet. Dabei fällt es schwer, die Dateien und Verzeichnisstruktur nachzuvollziehen. Aus diesem Grund wird hier ein ganz einfaches Pythonprogramm als Beispiel verwendet. Es besteht lediglich auch einer Datei und heisst 'Hello'. Die Datei hat den Namen 'hello.py' und folgenden Inhalt:

{code}
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pygtk
pygtk.require('2.0')
import gtk

class HelloWindow:
    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("destroy", self.destroy)
        self.button = gtk.Button("Hello World")
        self.button.connect("clicked", self.destroy)
        self.window.add(self.button)
        self.button.show()
        self.window.show()
   
    def destroy(self, widget, data=None):
        gtk.main_quit()

def main():
    hellowindow = HelloWindow()
    gtk.main()
    return 0

main()
{/code}

Hello ScreenDas Beispielprogramm macht nichts anderes als ein kleines Fenster mit einem Button zu zeigen. Nach einem Klick auf den Button beendet sich das Programm. Wer das ausprobieren möchte, kann folgende Verzeichnisstruktur anlegen und die Programmdatei dort ablegen:

{code}
home user
└── dev
    └── hello
        └── hello.py
{/code}

Das Verzeichnis 'dev' dient als allgemeines Verzeichnis für die eigenen Softwareprojekte; sein Name kann beliebig gewählt werden. 'hello' ist das Unterverzeichnis für das gleichnamige Projekt. Darin befindet sich die Programmdatei 'hello.py'. In einem Termin kann nun das Programm in diesem Verzeichnis gestartet werden:

{code}
python hello.py
{/code}


Verzeichnisse

Bisher gibt es lediglich ein ganz normales Projektverzeichnis mit einer einzelnen Programmdatei. Von einem Paket oder PPA ist noch nichts zu sehen. In einem nächsten Schritt wird die Verzeichnisstruktur für den Paketbau wie folgt erweitert:

{code}
dev
└── hello
    ├── hello.py
    └── ppa
        └── hello_0.1.1
            ├── applications
            ├── debian
            ├── hello
            └── locale
                └── de
                    └── LC_MESSAGES
{/code}

Was ist hinzugekommen? Im Projektverzeichnis 'hello' existiert jetzt ein Unterverzeichnis 'ppa' in dem alle für den Paketbau und die Publikation als PPA nötigen Information stehen werden. Das Verzeichnis muss nicht 'ppa' heissen sondern kann beliebig genannt werden. Darin gibt es das Verzeichnis für die Version der Software: 'hello' in Version 0.1.1. Bitte auf die Syntax des Verzeichnisnamens achten. Darunter kommt das Verzeichnis 'applications' in dem später die 'desktop' Datei für die Anwendung stehen wird. Das Verzeichnis 'debian' wird alle für den Paketbau relevanten Steuerdateien enthalten. Im Verzeichnis 'hello' wird der Programmcode stehen und im Unterbaum 'locale' befinden sich die Dateien für die Internationalisierung. Hier gehören die *.mo Dateien hinein. Um die Sache so einfach wie möglich zu halten, wird im Beispiel auf die Internationalisierung verzichtet. Deshalb kann das Verzeichnis 'locale' nun wieder gelöscht werden.

Für die Versionsnummer wird das übliche Schema verwendet:

{code}
2.3.5-0041
│ │ │  └────── Buildnummer
│ │ └───────── Revisionsnummer
│ └─────────── Nebenversionsnummer
└───────────── Hauptversionsnummer
{/code}

Das Beispiel beschränkt sich auf Haupt-, Neben- und Revisionsnummer. Die Hauptversion '0' deutet an, dass sich das Projekt noch in der anfänglichen Entwicklungsphase befindet. Die Nebenversion kann für die Nummerierung der Anwendungs-relevanten Veröffentlichungen genutzt werden, z.B. würde der Anwender in den Versionen 0.1, 0.2, 0.3 usw. inhaltliche Änderungen oder Weiterentwicklungen vorfinden. Die Revisionsnummer dient im Beispiel für die Nummerierung der Pakete und hat für den Anwender keine Bedeutung weil sich inhaltlich am Programm nichts Wesentliches ändert.


Desktop-Datei

Diese Datei trägt im Beispiel den Namen 'hello.desktop' und dient als Starter für das Programm 'hello'. Die Spezifikation ist hier zu finden. Ihr Inhalt erklärt sich weitestgehend selbst. Name und Comment müssen natürlich nur in den Sprachen angegeben werden, die man unterstützen will. Hinter 'Exec=' befindet sich der Pfad auf die Programmdatei bzw. im Beispiel auf das Shell-Skript 'hello.sh' mit dem die 'hello.py' gestartet wird. Ausserdem enthält 'Icon=' den Pfad zum Programmicon 'hello.png' im gleichen Verzeichnis wie 'hello.py'. Das ist nicht ganz korrekt, für dieses Beispiel jedoch einfacher und ausreichend.

{code}
[Desktop Entry]
Name=Hello
Name[de]=Hallo
Name[de_CH]=Hallo
Name[de_AT]=Hallo
Comment=Ein Beispielprogramm
Comment[de]=Ein Beispielprogramm
Comment[de_CH]=Ein Beispielprogramm
Comment[de_AT]=Ein Beispielprogramm
Exec=/usr/share/hello/hello.sh
Icon=/usr/share/hello/hello.png
Terminal=false
Type=Application
Categories=GNOME;GTK;
StartupNotify=false
{/code}


Die Debian Dateien

Das Verzeichnis 'debian' enthält fünf wichtige Steuerdateien für den Paketbau, sie heissen: changelog, compat, control, copyright und rules.

Die Datei 'changelog' sieht so aus:

{code}
hello (0.1.1-0ubuntu1) natty; urgency=low

  * Initial release (Closes: #nnnn)

 -- Otto Pohl <otto.pohl@web.de>  Tue, 27 Apr 2011 20:44:36 +0200
{/code}

Hierin findet man den Programmnamen 'hello', die Versionsnummer '0.1.1', die Distribution '-0ubuntu1' und die Ubuntu-Version 'natty' für die das Programm erstellt wurde. Weiter unten muss der Name des Paketbauers incl. Email-Adresse und Datums/Zeit-Stempel angegeben werden. Die Einträge müssen den Angaben bei Launchpad entsprechen.

Die Datei 'compat' enthält nichts anderes als die Zahl '7'.

Die Datei 'control' sieht folgendermassen aus:

{code}
Source: hello
Section: python
Priority: optional
Maintainer: Otto Pohl <otto.pohl@web.de>
Build-Depends: debhelper (>= 7), python-support, python (>=2.6)
Standards-Version: 3.9.1
Homepage: https://launchpad.net/hello

Package: hello
Architecture: all
Depends: ${misc:Depends}, ${python:Depends}
Description: Hello - Ein Beispielprogramm
 Hier folgen Details zum Programm.
 Jede Zeile muss mit einem Leerzeichen beginnen.
{/code}

Auch in dieser Datei sind die Angaben zumeist selbsterklärend. Weiterführende Informationen sollen hier nicht gegeben werden; wer es genauer wissen möchte kann im Packaging Guide nachlesen.

Die Datei 'copyright' hat folgenden Inhalt:

{code}
This work was packaged for Debian by:

    Otto Pohl <otto.pohl@web.de> on Tue, 27 Apr 2011 20:44:36 +0200

It was downloaded from:

    https://launchpad.net/hello

Upstream Author(s):

    Otto Pohl, otto.pohl@web.de

Copyright:

    Copyright (C) 2011 Otto Pohl

License:

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License with
the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
if not, write to the Free Software Foundation, Inc., 59 Temple Place,
Suite 330, Boston, MA  02111-1307  USA

On Debian systems, the complete text of the GNU General Public
License, version 3, can be found in /usr/share/common-licenses/GPL-3.
{/code}

Ausser dem eigenen Namen, der Email-Adresse und dem Datum muss an dieser Datei nicht viel geändert werden.

Die Datei 'rules' ist die wichtigste beim ganzen Paketbau. Sie bestimmt wie das Paket erstellt wird und wohin die Dateien bei der Installation kopiert werden. Hier ist ihr Inhalt:

{code}
#!/usr/bin/make -f
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.

# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1

%:
    dh $@

override_dh_install:
    dh_install hello/* /usr/share/hello/
    dh_install locale/de/LC_MESSAGES/*.mo /usr/share/locale/de/LC_MESSAGES/
    dh_install applications/hello.desktop /usr/share/applications/
{/code}

Beim Paketbau wird durch diese Datei eine ganze Kette von Verarbeitungsschritten ausgelöst, die sich hinter dem Befehl 'dh $' befinden ('dh' steht für debhelper). Einige Kommandos von debhelper werden durch den Befehl 'override_dh_install' überschrieben. Es handelt sich um die Angaben, was wohin kopiert werden soll. Das Programmverzeichnis 'hello' wird mit seinem gesamten Inhalt nach '/usr/share/hello/' kopiert. Die Desktop-Datei wird in das Verzeichnis '/usr/share/applications/' kopiert. Die mittlere Zeile (locale) kann weggelassen werden weil das Beispiel keine Internationalisierung verwendet.


Die Programmdateien

Das Verzeichnis 'ppa/hello' enthält einfach eine Kopie aller für das eigentliche Programm benötigten Dateien. Im Beispiel sind dies die Python Datei, das Icon und das Shell-Skript welches z.B. so aussehen könnte:

{code}
#!/bin/bash
cd `dirname $0`
python hello.py
{/code}

Die aktuelle Verzeichnisstruktur sieht nun so aus:

{code}
dev
└── hello
    ├── hello.png
    ├── hello.py
    ├── hello.sh
    └── ppa
        └── hello_0.1.1
            ├── applications
            │   └── hello.desktop
            ├── debian
            │   ├── changelog
            │   ├── compat
            │   ├── control
            │   ├── copyright
            │   └── rules
            └── hello
                ├── hello.png
                ├── hello.py
                └── hello.sh
{/code}


Der Paketbau

Um nun das Paket zu estellen, sind einige einfache Schritte durchzuführen. Im Verzeichnis 'ppa' erstellt man ein Archiv des Unterverzeichnisses 'hello_0.1.1' das dann so lautet: 'hello_0.1.1.tar.gz'. Dieses benennt man um in 'hello_0.1.1.orig.tar.gz'.

Nun wechselt man in das Verzeichnis 'hello_0.1.1' und führt diesen Befehl aus:

{code}
debuild -S -k"0x1AB13C45"
{/code}

Bei der Angabe '1AB13C45' handelt es sich um den 'OpenPGP Key' den man beim eigenen Profil im Launchpad oder auf dem eigenen PC unter 'Passwörter und Verschlüsselungen' ablesen kann. Man darf nicht vergessen dem Schlüssel die Zeichen '0x' voranzustellen.

Da 'debuild' nicht zur Standardinstallation gehört, wird man beim ersten Aufruf zur Installation aufgefordert:

{code}
sudo apt-get install devscripts
{/code}

Nach erfolgreicher Erstinstallation kann der Befehl dann ausgeführt werden. Die Ausgaben von 'debuild' können Fehlermeldungen oder Warnungen enthalten, die man genauer anschauen sollte. Falls die bisherigen Schritte in diesem Artikel befolgt wurden, sollte 'debuild' ohne Schwierigkeiten durchlaufen.

Im Verzeichnisbaum erscheinen nun die Dateien des Source Packages:

{code}
dev
└── hello
    ├── hello.png
    ├── hello.py
    ├── hello.sh
    └── ppa
        ├── hello_0.1.1-0ubuntu1.diff.gz
        ├── hello_0.1.1-0ubuntu1.dsc
        ├── hello_0.1.1-0ubuntu1_source.build
        ├── hello_0.1.1-0ubuntu1_source.changes
        ├── hello_0.1.1-0ubuntu1_source.ppa.upload
        ├── hello_0.1.1.orig.tar.gz
        └── hello_0.1.1
            ├── applications
            │   └── hello.desktop
            ├── debian
            │   ├── changelog
            │   ├── compat
            │   ├── control
            │   ├── copyright
            │   └── rules
            └── hello
                ├── hello.png
                ├── hello.py
                └── hello.sh
{/code}


Das PPA befüllen

Jetzt kann das PPA auf Launchpad mit dem Paket befüllt werden. Dazu wird im Verzeichnisbaum auf die nächst höhere Ebene gewechselt ( /dev/hello/ppa ) und dieser Befehl ausgeführt:

{code}
dput ppa:otto.pohl/opohl-ppa hello_0.1.1-0ubuntu1_source.changes
{/code}

Kurze Zeit später erhält man von Launchpad eine Email in der mitgeteilt wird, ob das Paket akzeptiert wurde oder nicht. Dies ist ein rein technischer Test der das Paket auf Vollständigkeit und Korrektheit überprüft. Den weiteren Verlauf kann man auf der eigenen PPA Seite im Launchpad verfolgen. Nach erfolgreichem Hochladen und Prüfen, wird das Paket für die entsprechende Zielarchitektur und Linux Version erstellt. Ja nach Verkehrsaufkommen im Launchpad kann dieser Vorgang bis zu 24 Stunden dauern; der aktuelle Status kann jederzeit im Launchpad überprüft werden. Sobald der grüne Haken neben 'Build Status' erscheint, ist das Paket fertig für das Verteilen an die Anwender.


Installation

Nun wird es spannend; jeder frisch gebackene Paketbauer möchte natürlich wissen, ob sich das erste eigene Paket installieren lässt und ob es korrekt funktioniert. Dazu fügt man im Software-Center das eigene PPA als Paketquelle hinzu: 'ppa:otto.pohl/opohl-ppa'. Das Software-Center braucht nun eine Weile bis die neue Quelle gelesen und die darin enthaltenen Pakete für die Installation ausgewählt werden können. In einem PPA können sich beliebig viele Pakete befinden die nichts miteinander zu tun haben müssen.

Nach der Installation können folgende Punkte überprüft werden:

  1. befindet sich das Programm 'hello' im Verzeichnis '/usr/share/hello'?
  2. befindet sich die Datei 'hello.desktop' im Verzeichnis '/usr/share/applications'?
  3. läuft das Programm nachdem man die 'hello.desktop' in den Launcher gezogen und geklickt hat?


Wenn nun das 'Hello World' Fenster erscheint, darf man sich zum erfolgreichen Erstellen eines PPAs gratulieren.


PPA aktualisieren

Die bisherigen Schritte haben gezeigt, wie ein Source-Package erstellt, in ein PPA geschoben und installiert wurde. Nun geht es darum das Programm zu erweitern oder Fehler zu beheben und das PPA mit der neuen Version zu aktualisieren.

Zunächst wird das Programm erweitert indem man in der Datei 'dev/hello/hello.py' diese Zeile leicht ändert:

{code}
self.button = gtk.Button("Hello World 2")
{/code}

Nun gibt es zwei Möglichkeiten; möchte man die alte Version behalten, so kann das gesamte Verzeichnis 'hello_0.1.1' mitsamt seiner Unterverzeichnisse kopiert und in 'hello_0.1.2' umbenannt werden. Sinnvoller ist es, lediglich das Archiv 'hello_0.1.1.orig.tar.gz' zu sichern und das Verzeichnis 'hello_0.1.1' in 'hello_0.1.2' umzubenennen. Möchte man die alte Version nicht behalten, kann einfach nur das alte Verzeichnis umbenannt werden.

Jetzt müssen einige Dateien aktualisiert und die Schritt wie oben erklärt wiederholt werden:

  • 'dev/hello/hello.py' nach 'dev/hello/hello_0.1.2/hello/hello.py' kopieren
  • in der 'debian/changelog' die Versionsnummer und das Datum anpassen
  • in der 'debian/copyright' das Datum anpassen
  • ein neues Archive des Verzeichnisses 'dev/hello/hello_0.1.2' erstellen
  • das neue Archive umbenennen in 'hello_0.1.2.orig.tar.gz'
  • erneut mit 'debuild' das Source Paket bauen (Anleitung siehe oben)
  • das neue Paket mit 'dput' ins PPA hochladen (siehe oben)

Der Anwender erhält nun bei der nächsten Softwareaktualisierung automatisch die neue Version von 'Hello'.

Viel Spass beim Ausprobieren.