Pacchettizzare da zero

[Importante]

Requisiti: build-essential, automake, gnupg, lintian, fakeroot e pbuilder.

In questo esempio useremo il programma GNU hello come esempio. Puoi scaricare l'archivio dei sorgenti da ftp.gnu.org. Per questo esempio, useremo la directory ~/hello/.

mkdir ~/hello
cd ~/hello
wget http://ftp.gnu.org/gnu/hello/hello-2.1.1.tar.gz

We will also compare our package to one that is already packaged in the Ubuntu repository. For now, we will place it in the ubuntu directory so we can look at it later. To get the source package, make sure you have a "deb-src" line in your /etc/apt/sources.list file for the Main repository. Then, simply execute:

mkdir ubuntu
cd ubuntu
apt-get source hello
cd ..
[Nota]

A differenza di molti comandi di apt-get, non hai bisogno dei permessi di root per scaricare il pacchetto sorgente perchè è scaricato nella directory corrente. Infatti è consigliabile usare apt-get source solo come utente normale, in modo da poter modificare i filie nel pacchetto sorgente senza aver bisogno dei privilegi di root.

Questo è quanto compie il comando apt-get source:

  1. Scarica il pacchetto sorgente. Un pacchetto sorgente di solito contiene un file .dsc che descrive il pacchetto e fornisce gli hash MD5 del pacchetto sorgente, un file .orig.tar.gz che contiene il codice sorgente degli autori originali e un file .diff.gz che contiene le modifiche applicate al codice sorgente comprensive delle informazioni di pacchettizzazione.

  2. Scompatta il file .orig.tar.gz nella directory corrente.

  3. Applica il file .diff.gz decompresso nella directory dei sorgenti.

Se hai scaricato manualmente il pacchetto sorgente (i file .dsc, .orig.tar.gz e .diff.gz), puoi scompattarli nello stesso modo di apt-get source usando dpkg-source in questo modo:

dpkg-source -x *.dsc

La prima cosa che dovrai fare è fare una copia dell'archivio originale (a volte chiamato "upstream") nel seguente formato: <nomepacchetto>_<versione>.orig.tar.gz. Questo passaggio fa due cose. In primo luogo, crea due copie del codice sorgente. Se per sbaglio modifichi o cancelli la copia su cui stai lavorando puoi usare quella che hai scaricato. In secondo luogo è considerata non una buona tecnica di pacchettizzazione modificare l'archivio dei sorgenti originale se non strettamente necessario. Si veda la sezione chiamata “Errori comuni” per i motivi.

cp hello-2.1.1.tar.gz hello_2.1.1.orig.tar.gz
tar -xzvf hello_2.1.1.orig.tar.gz
[Avvertimento]

L'underscore, "_", tra il nome del pacchetto (hello) e la versione (2.1.1), in contrapposizione al trattino, "-", è molto importante. Il tuo pacchetto sorgente non sarà compilato come un pacchetto nativo di Debian.

Ora abbiamo una directory hello-2.1.1 contenente i file sorgente. Ora dobbiamo creare la solita directory debian dove sono contenute tutte le informazioni di pacchettizzazione, consentendoci di separare i file di pacchettizzazione da quelli dell'applicazione.

mkdir hello-2.1.1/debian
cd hello-2.1.1/debian/

We now need to create the essential files for any Ubuntu source package: changelog, control, copyright, and rules. These are the files needed to create the binary packages (.deb files) from the original (upstream) source code. Let us look at each one in turn.

changelog

Il file changelog, come suggerisce il suo nome, è un elenco dei cambiamenti effettuati in ogni versione. Ha un formato predefinito che fornisce il nome del pacchetto, la versione, la distribuzione, le modifiche e chi e quando ha fatto le modifiche. Se hai una chiave GPG, assicurati di usare lo stesso nome e indirizzo di posta elettronica nel changelog di quello riportato nella tua chiave. Il seguente è un modello di changelog:

package (version) distribution; urgency=urgency

  * change details
    more change details
  * even more change details

 -- maintainer name <email address>[two spaces]  date

Il formato (in particolar modo dedlla data) è importante. La data dovrebbe essere in formato RFC822, la quale può essere ottenuta dal programma 822-date.

Ecco un esempio del file changelog per hello:

hello (2.1.1-1) edgy; urgency=low

   * New upstream release with lots of bug fixes.

 -- Captain Packager <packager@coolness.com>  Wed,  5 Apr 2006 22:38:49 -0700

Osserva che la versione contiene un -1 in coda ad essa e si definisce revisione di Debian, la quale è usata in modo tale che i pacchetti possano essere aggiornati (per correggere bug per esempio) con nuovi caricamenti all'interno del medesima versione del file sorgente.

[Nota]

Ubuntu and Debian have slightly different package versioning schemes to avoid conflicting packages with the same source version. If a Debian package has been changed in Ubuntu, it has ubuntuX (where X is the Ubuntu revision number) appended to the end of the Debian version. So if the Debian hello package was changed by Ubuntu, the version string would be 2.1.1-1ubuntu1. If a package for the application does not exist in Debian, then the Debian revision is 0 (e.g., 2.1.1-0ubuntu1).

Now look at the changelog for the Ubuntu source package that we downloaded earlier:

less ../../ubuntu/hello-2.1.1/debian/changelog

Notice that in this case the distribution is unstable (a Debian branch), because the Debian package has not been changed by Ubuntu. Remember to set the distribution to your target distribution release.

A questo punto crea un file changelog nella directory debian, dove dovresti già essere.

control

Il file control contiene le informazioni che il gestore di pacchetti (come apt-get, synaptic e aptitude) usa, le dipendenze di compilazioni, le informazioni del manutentore, e molto altro.

For the Ubuntu hello package, the control file looks something like:

Source: hello
Section: devel
Priority: optional
Maintainer: Captain Packager <packager@coolness.com> 
Standards-Version: 3.6.1

Package: hello
Architecture: any
Depends: ${shlibs:Depends}
Description: The classic greeting, and a good example
 The GNU hello program produces a familiar, friendly greeting.  It
 allows non-programmers to use a classic computer science tool which
 would otherwise be unavailable to them.
 .
 Seriously, though: this is an example of how to do a Debian
 package.
 It is the Debian version of the GNU Project's `hello world' program
 (which is itself an example for the GNU Project).

Crea il control usando le informazioni elencate (accertandoti di fornire i tuoi dati per il campo Maintainer).

Il primo paragrafo fornisce informazioni sul pacchetto sorgente. Analizziamo ogni riga:

  • Source: Questo è il nome del pacchetto sorgente, hello, in questo caso.

  • Section: I repository di apt sono suddivisi in sezioni per facilitare la consultazione e la catalogazione del software. In questo caso, hello appartiene alla sezione devel.

  • Priority: Questo campo assegna l'importanza del pacchetto per gli utenti. Dovrebbe essere una delle seguenti:

    • Required - pacchetti essenziali per il funzionamento del sistema. Se vengono rimossi, è altamente probabile che il tuo sistema verrà danneggiato in maniera irreparabile.

    • Important - un insieme minimale di pacchetti per avere un sistema utilizzabile. Rimuovendo questi pacchetti non causerà un malfunzionamento irreparabile del sistema, ma di solito sono considerati strumenti importanti senza i quali qualsiasi distribuzione GNU/Linux sarebbe incompleta. Nota bene: questa categoria on include pacchetti come Emacs o persino il sistema X Window.

    • Standard - Autoesplicativo.

    • Optional - questa categoria è dedicata in pratica per i pacchetti non richiesti, o per la maggior parte di essi. Comunque, questi pacchetti non devono essere in conflitto tra loro.

    • Extra - pacchetti che possono essere in conflitto con i pacchetti di una delle altre categorie. Viene inoltre usata per pacchetti specifici che potrebbero essere utili a persono che già conoscono l'utilità del pacchetto.

  • Maintainer: Il manutentore del pacchetto e l'indirizzo di posta elettronica.

  • Standards-Version: La versione della Policy di Debian alla quale il pacchetto è assoggettato (in questo caso, la versione 3.6.1). Un modo rapido per visualizzare la versione corrente è apt-cache show debian-policy | grep Version.

  • Build-Depends: Uno dei campi più importanti e spesso causa di bug, questa riga elenca i pacchetti binari (con versione, se necessario) che devono essere installati per creare il pacchetto binario a partire da quello sorgente. I pacchetti essenziali sono richiesti da build-essential e non devono essere inclusi nella riga Build-Depends. Nel caso di hello, tutti i pacchetti che servono fanno parte di build-essential, così la riga Build-Depends non è necessaria. L'elenco dei pacchetti di build-essentiaò può essere reperita in /usr/share/doc/build-essential/list.

Il secondo paragrafo è relativo ai pacchetti binari che saranno creati a partire dai sorgenti. Se più pacchetti binari saranno creati dal pacchetto sorgente, dovrebbe esserci una sezione per emphasis>ognuno

  • Package: Il nome del pacchetto binario. Il più delle volte per programmi semplici (come hello), il nome dei pacchetti binario e sorgente sono identici.

  • Architecture: L'architettura per la quale saranno costruiti i pacchetti binari. Esempi possono essere:

    • all - I sorgenti non dipendono da una specifica architettura. I programmi che usano Python o altri linguaggi interpretati lo usano. Il pachetto binario risultante dovrebbe terminare in _all.deb.

    • any - I sorgenti sono dipendono da una specifica architettura e dovrebbero compilarsi su tutte le architetture supportate. Ci sarà un pacchetto .deb per ciascuna architettura ( _i386.deb per esempio)

    • A subset of architectures (i386, amd64, ppc, etc.) can be listed to indicate that the source is architecture-dependent and does not work for all architectures supported by Ubuntu.

  • Depends: L'elenco dei pacchetti che il pacchetto binario dipende per poter funzionare. Per hello, notiamo ${shlibs:Depends}, una variabile che sostituisce le necessarie librerie condivise. Si veda la pagina di manuale di dpkg-source per maggiori informazioni.

  • Recommends: Usato per pacchetti che sono caldamente raccomandati e solitamente sono installati con il pacchetto. Alcuni gestori di pacchetti, più precisamente aptitude, installano automaticamente i pacchetti raccomandati.

  • Suggests: Usato per pacchetti simili o utili al pacchetto che deve essere installato.

  • Conflicts: Usato per pacchetti che vanno in conflitto con questo pacchetto. Entrambi non possono essere installati contemporaneamente. Se uno di essi sta per essere installato, l'altro verrà rimosso.

  • Description: Sia la descrizione breve, sia quella lunga sono usate dai gestori di pacchetti. Il formato è:

    Description: <singola riga>
     <descrizione estesa su più righe>

    Osserva che c'è uno spazio all'inizio di ogni riga della descrizione lunga. Maggiori informazioni su come realizzare una buona descrizione possono essere trovate su http://people.debian.org/~walters/descriptions.html.

copyright

Questo file elenca le informazioni sul copyright. Di solito, le informazioni sul copyright sono riportate nel file COPYING nella directory dei sorgenti del programma. Questo file dovrebbe includere informazioni quali il nome dell'autore e del creatore del pacchetto, l'URL dal quale provengono i sorgenti, una riga Copyright con l'anno e il detentore dei diritti d'autore e il testo del copyright stesso. Un modello d'esempio è simile a:

This package was debianized by {Il tuo nome} <la tua email>
{Data}

It was downloaded from: {URL o pagina Web} 

Upstream Author(s): {Nomi e indirizzi email degli autori}

Copyright:
	Copyright (C) {Anno/i} by {Autore} {Indirizzo email}

License:

As one can imagine, hello is released under the GPL license. In this case it is easiest to just copy the copyright file from the Ubuntu package:

cp ../../ubuntu/hello-2.1.1/debian/copyright .

Devi inserire il copyright completo a meno che non sia GPL, LGPL, BSD o Licenza Artistica, nel qual caso puoi fare riferimento al file corrispondente nella directory /usr/share/common-licenses/.

Notice that the Ubuntu package's copyright includes a license statement for the manual. It is important that all the files in the source be covered by a license statement.

rules

The rules file is an executable Makefile that has rules for building the binary package from the source packages. For hello, it will be easier to use the rules from the Ubuntu package:

#!/usr/bin/make -f
# Semplice file debian/rules - for GNU Hello.
# Copyright 1994,1995 by Ian Jackson.
# Ti concedo il permesso perpetuo e illimitato di copiare,
# modificare e modificare la licenza di questo programma,
# a patto che non rimuova il mio nome dal file medesimo.
# (Asserisco i miei diritti morali di paternità sotto il decreto
# Copyright, Designs and Patents Act del 1988.)
# Questo file può essere modificato pesantemente

package = hello
docdir = debian/tmp/usr/share/doc/$(package)

CC = gcc
CFLAGS = -g -Wall
INSTALL_PROGRAM = install

ifeq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
  CFLAGS += -O2
endif
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
  INSTALL_PROGRAM += -s
endif

build:
        $(checkdir)
        ./configure --prefix=/usr
        $(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)"
        touch build

clean:
        $(checkdir)
        rm -f build
        -$(MAKE) -i distclean
        rm -rf *~ debian/tmp debian/*~ debian/files* debian/substvars

binary-indep:   checkroot build
        $(checkdir)
# Non ci sono file dipendenti da una specifica architettura da caricare
# generati da questo pacchetto. Se ce ne fossero, sarebbero dovuti
# essere fatti qui

binary-arch:    checkroot build
        $(checkdir)
        rm -rf debian/tmp
        install -d debian/tmp/DEBIAN $(docdir)
        install -m 755 debian/postinst debian/prerm debian/tmp/DEBIAN
        $(MAKE) INSTALL_PROGRAM="$(INSTALL_PROGRAM)" \
                prefix=$$(pwd)/debian/tmp/usr install
        cd debian/tmp && mv usr/info usr/man usr/share
        cp -a NEWS debian/copyright $(docdir)
        cp -a debian/changelog $(docdir)/changelog.Debian
        cp -a ChangeLog $(docdir)/changelog
        cd $(docdir) && gzip -9 changelog changelog.Debian
        gzip -r9 debian/tmp/usr/share/man
        gzip -9 debian/tmp/usr/share/info/*
        dpkg-shlibdeps debian/tmp/usr/bin/hello
        dpkg-gencontrol -isp
        chown -R root:root debian/tmp
        chmod -R u+w,go=rX debian/tmp
        dpkg --build debian/tmp ..

define checkdir
        test -f src/$(package).c -a -f debian/rules
endef

binary: binary-indep binary-arch

checkroot:
        $(checkdir)
        test $$(id -u) = 0

.PHONY: binary binary-arch binary-indep clean checkroot

Procediamo su questo file in dettaglio. Una delle prime componenti che vedrai è la dichiarazione di alcune variabili:

package = hello
docdir = debian/tmp/usr/share/doc/$(package)

CC = gcc
CFLAGS = -g -Wall
INSTALL_PROGRAM = install

ifeq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
  CFLAGS += -O2
endif
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
  INSTALL_PROGRAM += -s
endif

Questa sezione definisce i CFLAGS per il compilatore e gestisce anche le DEB_BUILD_OPTIONS noopt e nostrip per il debug.

Poi c'è la regola build:

build:
	$(checkdir)
	./configure --prefix=/usr
	$(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)"
	touch build

Questa regola esegue ./configure con il prefix appropriato, esegue make e crea un file build che indica il momento in cui è stato creato il pacchetto per prevenire compilazioni multiple errate.

La regola seguente è clean, la quale esegue make -i distclean che rimuove i file creati durante il processo di creazione del pacchetto.

clean:
	$(checkdir)
	rm -f build
	-$(MAKE) -i distclean
	rm -rf *~ debian/tmp debian/*~ debian/files* debian/substvars

Poi c'è una regola binary-indep vuota, dato che non sono stati creati file dipendenti da una specifica architettura in questo pacchetto.

Ci sono, però, diversi file dipendenti da una specifica architettura, quindi viene usata binary-arch:

binary-arch:    checkroot build
		$(checkdir)
		rm -rf debian/tmp
		install -d debian/tmp/DEBIAN $(docdir)
		install -m 755 debian/postinst debian/prerm debian/tmp/DEBIAN
		$(MAKE) INSTALL_PROGRAM="$(INSTALL_PROGRAM)" \
		prefix=$$(pwd)/debian/tmp/usr install
		cd debian/tmp && mv usr/info usr/man usr/share
		cp -a NEWS debian/copyright $(docdir)
		cp -a debian/changelog $(docdir)/changelog.Debian
		cp -a ChangeLog $(docdir)/changelog
		cd $(docdir) && gzip -9 changelog changelog.Debian
		gzip -r9 debian/tmp/usr/share/man
		gzip -9 debian/tmp/usr/share/info/*
		dpkg-shlibdeps debian/tmp/usr/bin/hello
		dpkg-gencontrol -isp
		chown -R root:root debian/tmp
		chmod -R u+w,go=rX debian/tmp
		dpkg --build debian/tmp ..

Per prima cosa, questa regola richiama checkroot per assicurarsi che il pacchetto venga costruito come root e richiama la regola build per compilare i sorgenti. Quindi vengono creati i file debian/tmp/DEBIAN e debian/tmp/usr/share/doc/hello e gli script postinst e prerm> vengono installati in debian/tmp/DEBIAN. Quindi viene eseguito make install con un prefix apposito che installa il pacchetto nella directory debian/tmp/usr. In seguito i file della documentazione (NEWS, ChangeLog e il changelog di Debian) vengono compressi e installati. Viene invocato dpkg-shlibdeps per trovare le librerie condivise da cui dipende l'eseguibile hello, e ne registra l'elenco nel file debian/substvars per la variabile ${shlibs:Depends} in control. Quindi viene eseguito dpkg-gencontrol per creare un file di controllo per il pacchetto binario, applicando le modifiche create da dpkg-shlibdeps. Infine, dopo che i permessi di debian/tmp sono stati assegnati, viene eseguito dpkg --build per costruire il pacchetto binario .deb che verrà collocato nella directory superiore.

postinst e prerm

The postinst and prerm files are examples of maintainer scripts. They are shell scripts that are executed after installation and before removal, respectively, of the package. In the case of the Ubuntu hello package, they are used to install (and remove) the info file. Go ahead and copy them into the current debian directory.

cp ../../ubuntu/hello-2.1.1/debian/postinst .
cp ../../ubuntu/hello-2.1.1/debian/prerm .

Creare il pacchetto sorgente

Ora che abbiamo analizzato in dettaglio i file nella directory debian per hello, possiamo costruire il pacchetto sorgente (e binario). Per prima cosa entriamo nella directory principale dei sorgenti estratti:

cd ..

Ora costruiamo il pacchetto sorgente usando dpkg-buildpackage:

dpkg-buildpackage -S -rfakeroot

Il flag -S flag indica a dpkg-buildpackage di creare un pacchetto sorgente, mentre il flag -r flag gli indica di usare fakeroot per permettergli di avere privilegi di root apparenti durante la creazione del pacchetto. dpkg-buildpackage prenderà il file .orig.tar.gz e produrrà un .diff.gz (le differenze tra l'archivio originale dell'autore e la directory che abbiamo creato, debian/ and its contents) e un file .dsc che contiene la descrizione e gli hash MD5 del pacchetto sorgente. I file .dsc e *_source.changes (utilizzato per caricare i pacchetti sorgente) sono firmati usando la tua chiave GPG.

[Avvertimento]

Se non hai una chiave gpg disponibile, otterrai un errore da debuild. Puoi creare sia una chiave gpg. o usare le chiavi -us -uc di debuild per disabilitare la firma. Però non sarai in grado di caricare i tuoi pacchetti in Ubuntu senza firmarli.

[Suggerimento]

Per assicurarti che debuild la chiave gpg corretta, dovresti impostare le veriabili d'ambiente DEBFULLNAME e DEBEMAIL (nel tuo ~/.bashrc per esempio) con il nome e l'indirizzo di posta elettronica che usi per la tua chiave gpg e in debian/changelog

Alcune persone hanno riportato che non sono state in grado di far trovare a debuild la loro chiave gpg, anche dopo aver configurato le variabili d'ambiente citate. Per risolvere il problema, puoi indicare a debuild il flag -k<keyid> dove <keyid> è l'ID della tua chiave gpg.

In aggiunta al pacchetto sorgente, possiamo costruire il pacchetto binario con pbuilder:

sudo pbuilder build ../*.dsc

E' molto importante usare pbuilder per costruire il pacchetto binario. Si assicura che le dipendenze di compilazione siano corrette, dato che pbuilder: prevede solo un ambiente minimale, così tutte le dipendeze di compilazione vengono determinate dal file control.

Possiamo controllare il pacchetto sorgente per errori comuni usando lintian:

cd ..
lintian -i *.dsc