openSUSE:Linee guida per i file Spec

(Reindirizzamento da openSUSE:Specfile guidelines)
Il formato RPM, se considerato a sé stante, impone essenzialmente ben poche limitazioni su quello che si può fare nei file spec, per cui è possibile renderli assai complessi. Queste linee guida puntano a ridurre al minimo le variazioni in alcune aree specifiche, in modo tale che i file spec restino agevoli da mantenere.

Regole generali

Tutti i file spec devono risultare agevoli da leggere. Se altri creatori di pacchetti non riescono a leggere il file spec, sarà allora impossibile eseguirne la verifica di controllo (review) e contribuire al pacchetto.

File spec modello

Quando si realizza un pacchetto da zero, sarebbe opportuno scrivere il corrispondente file spec basandosi sul file spec modello (si veda la pagina su rpmdevtools). Si può generare una configurazione di base per un generico pacchetto MYPACK con

[1] osc-plugin-install devel:tools rpmdevtools

oppure con

sudo zypper -p http://download.opensuse.org/repositories/devel:/tools/openSUSE_12.3/ -v in osc rpmdevtools (modificare l'indirizzo in accordo con la versione di (open)SUSE in uso)

cd .../MYPROJECT
osc mkpac MYPACK
cd MYPACK
wget http://upstream.example.org/source/.../MYPACK-1.0.tar.gz
rpmdev-newspec -t lib MYPACK
osc vc
vi MYPACK.spec

in seguito:

osc build
osc ci
osc in

Si prega di lasciare da parte le proprie preferenze riguardo a formattazione e organizzazione dei file spec, e di cercare di mantenersi conformi a questo modello il più possibile. Questo non perché riteniamo che quel modello sia l'unico modo corretto di scrivere un file spec, piuttosto perché rende spesso più facile a chi fa il controllo di qualità individuare gli errori e capire rapidamente quali siano le intenzioni del creatore di pacchetti (packager).

Spesso è possibile creare file spec per particolari linguaggi usando strumenti come cpanspec o gem2rpm-opensuse. Vedere anche

Codifica dei file spec

Tranne il caso in cui sia necessario utilizzare caratteri al di fuori del repertorio ASCII, non sarà necessario preoccuparsi della codifica dei file spec. Se si ha bisogno di caratteri diversi da quelli ASCII, salvare il proprio file spec con codifica UTF-8. Se si dovessero avere dubbi su quali caratteri siano ASCII, consultare una tabella ASCII.


Assegnare una licenza ai file spec

Per ragioni legali, i file spec devono riportare una licenza nell'intestazione. È possibile servirsi del seguente modello (naturalmente in inglese), se si desidera:

 #
 # spec file for package python-$FOO
 #
 # Copyright (c) $CURRENT_YEAR $YOUR_NAME_WITH_MAIL_ADDRESS
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
 # upon. The license for this file, and modifications and additions to the
 # file, is the same license as for the pristine package itself (unless the
 # license for the pristine package is not an Open Source License, in which
 # case the license is the MIT License). An "Open Source License" is a
 # license that conforms to the Open Source Definition (Version 1.9)
 # published by the Open Source Initiative.
 
 # Please submit bugfixes or comments via http://bugs.opensuse.org/
 #

Macro

Utilizzare le macro al posto di scrivere direttamente il nome delle directory per esteso (si vedano le Convenzioni sulle macro RPM nella creazione di pacchetti). Avere delle macro in una riga introdotta da Source: o Patch: è una questione di stile. Alcune persone gradiscono la leggibilità immediata di una riga di codice sorgente priva di macro. Altre preferiscono invece la facilità di aggiornamento nel caso di nuove versioni, resa possibile dall'utilizzo delle macro. In ogni caso, è importante ricordarsi di essere coerenti durante la stesura dell'intero file spec e di verificare che gli indirizzi (URL) e percorsi elencati siano validi. Se fosse necessario conoscere l'effettiva stringa restituita dalla macro (se sono presenti macro nel file spec), si usi il comando rpm. Per esempio, per risalire al valore effettivo di Source:, si può eseguire:

rpm -q --specfile foo.spec --qf "$(grep -i ^Source foo.spec)\n"

Attenzione: il comando qui sopra funziona con %name e %version, ma potrebbe fallire con macro definite dall'utente.

Macro a confronto con variabili RPM

Esistono due stili per definire la root del build (Build Root) e i flag di ottimizzazione (Optimization Flags) in un file spec.

stile "macro" stile "variabili"
Build Root  %{buildroot} $RPM_BUILD_ROOT
Opt. Flags  %{optflags} $RPM_OPT_FLAGS

Ci sono davvero ben pochi motivi per preferire uno stile all'altro, dato che entrambi daranno origine ai medesimi valori (restituiti), in tutti gli scenari. Sarebbe opportuno scegliere uno stile ed usarlo in modo consistente per tutta quanta la propria attività di creazione pacchetti. Mescolare i due stili, benché valido, è sbagliato dal punto di vista della Garanzia di Qualità e della usabilità, lo si dovrebbe perciò evitare nei pacchetti per openSUSE.

Le macro RPM vengono espanse prima che lo script di shell, che una sezione include al suo interno, venga eseguito. Dato che sh è notoriamente lenta, si può concludere che lo stile con "%" sia da preferirsi, anche perché già si staranno usando altre macro come %_bindir, per le quali non esiste una variabile equivalente.

Condizionali (versione obsoleta)

Template:Obsolete Se il file spec contiene istruzioni di build condizionali, selezionate in base alla presenza degli argomenti opzionali --with(out) foo per il comando rpmbuild, si deve creare il file RPM sorgente da caricare usando le opzioni predefinite, ovvero in modo tale che nessuno degli argomenti suddetti sia presente nella riga di comando iniziata da rpmbuild. Il motivo è che quei requisiti vengono introdotti "di serie" nel risultante RPM sorgente, in breve le istruzioni condizionate non sono più in uso.

Condizionali (versione corrente)

Vedere la pagina sulle istruzioni build condizionali.

Preambolo

Qui sono raccolte le regole per la sezione preambolo (preamble) di un file spec.

Assegnazione di nome e versione

Si vedano le Linee guida per i nomi di pacchetto.

Tag dei metadati

  • Il tag Packager non deve essere usato nei file spec. Se si ricrea il pacchetto RPM altrove, ovvero in un ambiente diverso da quello di quel packager (il creatore del pacchetto prima accennato), allora il valore del tag "packager" legato a quel primo creatore di pacchetto verrà imposto su quello di terzi packager, creando in seguito confusione su chi contattare in caso di necessità. Le identità dei packager dovrebbero invece essere specificate assieme alle aggiunte al log delle modifiche.
  • Il tag Vendor non deve essere usato per la medesima ragione. Nel Build Service, infatti, quest'ultimo viene impostato automaticamente; se lo si vuole sovrascrivere utilizzare la macro %define, operante a livello di prjconf.
  • Il tag Copyright è deprecato. Usare invece la tag License, come esposto dettagliatamente in openSUSE:Linee_guida_per_la_creazione_dei_pacchetti#Licenza.
  • Se il tag Source contiene il collegamento ad un indirizzo URL non raggiungibile, la creazione del pacchetto potrebbe non andare a buon fine, anche nel caso in cui il file sia presente localmente. Perciò, converrà piuttosto fornire la directory di download, o l'indirizzo di pagina, in un commento aggiuntivo.
  • Il tag Group dovrebbe contenere soltanto i gruppi di pacchetti che seguono le Linee guida per i gruppi di pacchetti.
  • Il tag BuildRoot si dovrebbe utilizzare sempre, anche se i nuovi rpm lo sovrascriveranno comunque. Il percorso da preferirsi è %{_tmppath}/%{name}-%{version}-build.
  • Il tag AutoReqProv è impostato su on per default, è quindi superfluo specificarlo, tranne quando si voglia realmente disattivarlo (impostandolo su off).

Sommario

Il sommario (o Summary):

  • Non dovrebbe terminare con il punto fermo. Se questo creasse degli scrupoli dal punto di vista grammaticale, non resta che rilassarsi e farsene una ragione.
  • Dovrebbe essere una breve e concisa descrizione del pacchetto. La sua massima lunghezza è di 80 caratteri.
  • Dovrebbe essere adatto a tutte le situazioni normalmente riscontrabili, e non supporre per scontato alcun contesto particolare.
  • Dovrebbe essere utile di per sé, in elenchi disordinati o ordinati alfabeticamente di alcuni pacchetti scelti, nonché in elenchi disordinati o ordinati alfabeticamente di tutti i pacchetti.
  • Dovrebbe descrivere le funzionalità principali del pacchetto e mettere in risalto tutte le proprietà peculiari del pacchetto, al fine di supportare l'utente nel fare confronti con pacchetti simili. Per esempio, le due parole "Browser Web" ("Web Browser", in inglese) identificano qualunque browser web, ma l'accostamento di attributi opportuni (come "minimalistico", "basato su testo" o "veloce" - questi ultimi da usarsi nella localizzazione in italiano, vedere oltre) contribuisce a definire un pacchetto specifico.

Descrizione

La descrizione (Description) riprende ed amplia il sommario. Nella descrizione non si devono includere istruzioni per l'installazione. La descrizione non è un manuale. Se il pacchetto richiede alcune operazioni di configurazione manuale, o ci sono alcune importanti istruzioni da comunicare all'utente, rimandare l'utente alla documentazione contenuta nel pacchetto. Aggiungere quindi, se lo si ritiene necessario, un file README.SUSE, o similare.

La limitazione di 70 caratteri per la lunghezza di riga (dovuta alle dimensioni limitate della finestra di descrizione con YaST in modalità testo) non è più necessaria. Come il front end in Qt, anche il selettore di pacchetti in ncurses crea testo in html puro, a partire dalla descrizione in testo semplice (disponibile in Factory da marzo 2012).

Si prega di rispettare queste regole: Le linee vuote separano i capoversi. In modalità elenco, una linea vuoto termina l'elenco. Le interruzioni di linea si risolvono in un spazio singolo, questo significa che una interruzione di linea non ha alcun effetto particolare. La modalità elenco non numerato inizia con una linea introdotta da un asterisco o da un segno meno seguito da almeno uno spazio. Una linea che contiene un elemento dell'elenco inizia con almeno due spazi.

Inoltre, la descrizione non dovrebbe eccedere un'ampiezza ragionevole, più di venti righe sono probabilmente eccessive.

La descrizione del pacchetto dovrebbe aiutare l'utente a prendere una decisione corretta sul pacchetto adeguato ai suoi scopi, senza che debba prima provare altri pacchetti dello stesso tipo. Questo è lo spazio adeguato per informare l'utente in modo più preciso sulle funzionalità di un pacchetto. Dovrebbe contenere informazioni ulteriori su caratteristiche e differenze da altri pacchetti, con quello confrontabili. Se un pacchetto fosse in grado di danneggiare l'installazione di un utente, la descrizione dovrebbe contenere avvisi chiari e visibili sui possibili rischi o effetti collaterali, ad esso connessi.

Sarà necessario accantonare le preferenze personali e usare l'ortografia dell'inglese americano, sia nel sommario che nella descrizione. Il file RPM spec contiene soltanto la versione in inglese di Summary e Description, per limitare le dimensioni del database RPM; le localizzazioni sono gestite da YaST.

L'elenco di autori al termine della descrizione non dovrebbe venire più incluso [N.B.:a partire dall'estate 2011]. Per i riconoscimenti ci serviamo di un file separato chiamato "AUTHORS". Se il file non è già incluso nel pacchetto originale (anche noto come pacchetto upstream), sarà necessario crearne uno e utilizzarlo in questo modo:

Source2:        AUTHORS
...
%setup
cp %{S:2} .
...
%files
%doc AUTHORS

Marchi commerciali in Summary o Description

I creatori di pacchetti (packager) dovrebbero essere cauti nel modo in cui usano i marchi commerciali all'interno di Summary o Description. A riguardo ci sono alcune regole da seguire:

  • Mai usare le scritte "(TM)" o "(R)" (o gli equivalenti in unicode: ™ e ®). Usarli in modo appropriato è incredibilmente complesso, per cui è davvero più sicuro, per noi, non usarli del tutto.
  • Usare i marchi commerciali in modo non ambiguo. Evitare espressioni come "similar to" o "like" (e le equivalenti tradotte). Alcuni esempi:
  • Male: It is similar to Adobe Photoshop. (trad.: è simile ad Adobe Photoshop)
  • Bene: It supports Adobe Photoshop PSD files, ... (trad.: supporta i file PSD di Adobe Photoshop)
  • Male: A Linux version of Microsoft Office (trad.: Versione per Linux di Microsoft Office)
  • Bene: A word-processor with support for Microsoft Office DOC files (trad.: word-processor con supporto per i file DOC di Microsoft Office)

In caso di dubbi sarà sufficiente chiedersi se esista la possibilità che qualche utente possa confondersi e pensare che il pacchetto in questione sia il prodotto oggetto del marchio commerciale. Se l'eventuale appaia possibile sarà meglio cercare di non citare il marchio commerciale.

Dipendenze

Richieste con Requires

Il gestore RPM è in grado, con grande efficacia, di reperire automaticamente le dipendenze per librerie e anche moduli Perl, per esempio. In breve, è inutile reinventare la ruota e conviene lasciar fare ad rpm il suo lavoro. Di solito non c'è quindi alcuna necessità di includere in elenco esplicitamente richieste come Requires: libqt4, quando la dipendenza è già stata rilevata da rpm sotto forma di dipendenza da librerie condivise nel pacchetto libqt4.

Se il proprio pacchetto è suddiviso in sotto-pacchetti, questi ultimi potrebbero dover richiedere il pacchetto base, il che si ottiene indicando una dipendenza di versione (cioè da una specifica versione di un pacchetto): Requires: %{name} = %{version}.

Per i sotto-pacchetti -devel si deve indicare esplicitamente la dipendenza dai pacchetti con le librerie condivise verso cui puntano i collegamenti simbolici contenuti nel pacchetto devel, cioè libfoobar-devel necessita, per esempio, di Requires: libfoo2 = %{version}, libbar5 = %{version}

Prerequisiti con PreReq

I pacchetti non dovrebbero utilizzare il tag PreReq. Una volta, in cicli infiniti di dipendenze, PreReq era solito "avere la meglio" sui convenzionali Requires quando, durante una transazione, RPM determinava l'ordine di installazione. Oggi questo non è più il caso.

BuildArch

Utilizzo tipico:

BuildArch: noarch

Usare questo tag con quei pacchetti per i quali nell'elenco dei file da installare non è presente alcun file dipendente dal tipo di architettura del processsore.

Notare che se il pacchetto principale è ti tipo noarch, allora anche tutti i sotto-pacchetti devono essere noarch.

Richieste con BuildRequires

Diversamente che con i Requires, per i BuildRequires non esiste una procedura automatica per trovare le dipendenze. È necessario allora indicare esplicitamente un elenco con i pacchetti che il proprio pacchetto richiede per poter essere creato con successo. Riportare correttamente i requisito per il build del pacchetto permette di risparmiare tempo a tutti gli sviluppatori e ai tester, poiché non avranno bisogno di andare manualmente alla ricerca dei requisiti mancati per la creazione del pacchetto. Il tag assicura inoltre che il processo di build sia riproducibile e utilizzi sempre le medesime caratteristiche. Gli script configure, per esempio, potrebbero escludere il supporto a PNG, in base alla disponibilità di libpng nel sistema usato per il building. Con BuildRequires: libpng-devel (o a partire da openSUSE 11.4: BuildRequires: pkgconfig(libpng14)), ci si assicura che la creazione del pacchetto non andrà a buon fine se libpng non è presente.

Precauzioni con OBS

I BuildRequires condizionali sono limitati all'uso con variabili semplici. In un'espressione introdotta da %if RPM supporta di solito costrutti complessi come questo:

%if 0%(test "%something" = "enabled" && echo 1)

Ma quando sta calcolando i BuildRequires, il parser RPM del BuildService viene eseguito in un ambiente in cui l'espansione delle subshell è disabilitata. Verrà mostrato invece un avviso come questo:

Warning: spec file parser line 109: can't expand %(...)

Nemmeno gli script %lua vengono interpretati dal dispatcher; in tal caso non viene emesso alcun avviso.

L'esempio che segue utilizza soltanto una variabile semplice, il che funziona con BuildRequires:

%if ! %{with own_mpfr}
BuildRequires: mpfr-devel
%endif
Eccezioni

Non è necessario includere nel BuildRequires i seguenti pacchetti o le loro dipendenze, dal momento che li si dovrebbe inserire troppo spesso. Questi pacchetti sono considerati l'ambiente il più ridotto all'essenziale per la creazione di pacchetti.

Per un elenco si può anche provare questo comando:

osc meta prjconf openSUSE:Factory | egrep '^(Preinstall:|Support:|Required:)'
Preinstall: aaa_base acl attr bash coreutils diffutils
Preinstall: filesystem fillup glibc grep insserv libacl libattr
Preinstall: libbz2-1 libgcc%{gcc_version} libxcrypt m4 libncurses5 pam
Preinstall: permissions libreadline6 rpm sed tar zlib libselinux1
Preinstall: liblzma5 libcap2 libpcre0
Preinstall: libpopt0 libelf1 liblua5_1
Required: gcc gcc%{gcc_version} glibc perl rpm tar patch
Support: autoconf automake binutils bzip2 gcc gcc%{gcc_version}
Support: gettext-runtime glibc libtool perl rpm zlib
Support: libncurses5
Support: libaudit1 cpio cpp cpp%{gcc_version} cracklib cvs
Support: file findutils gawk gdbm gettext-tools
Support: glibc-devel glibc-locale groff gzip info less
Support: libbz2-devel libdb-4_8
Support: libstdc++%{gcc_version}
Support: udev
Support: libxcrypt libzio
Support: linux-glibc-devel make man netcfg
Support: net-tools pam-modules patch perl-base sysvinit-tools
Support: texinfo timezone util-linux libmount1 login
Support: libgomp%{gcc_version} libuuid1 psmisc
Support: terminfo-base update-alternatives pwdutils build-mkbaselibs
Support: brp-check-suse post-build-checks rpmlint-Factory
Support: build-compare
Support: libunwind libunwind-devel

Conflitti

Tutte le volte che è possibile, si dovrebbe evitare che i pacchetti per openSUSE entrino in conflitto tra di loro. Sfortunatamente, questo non sempre è possibile. Per i dettagli completi sulla linea di condotta di openSUSE per i conflitti tra pacchetti, consultare: en:openSUSE:Packaging conflicts .

Dipendenze da file

RPM offre la possibilità di far dipendere il pacchetto da certi file, anziché dai pacchetti. Tutte le volte che è possibile, si dovrebbero evitare le dipendenze dai file al di fuori di /etc, /bin, /sbin, /usr/bin, o /usr/sbin. L'utilizzo di dipendenze da file che si trovano in percorsi diversi da quelle directory richiede a yum (e ad altri risolutori di dipendenze che usano il formato repomd) di scaricare e analizzare un grosso file XML, alla ricerca delle dipendenze. Consentire ai solutori di dipendenze di evitare questo procedimento, facendo sì che le dipendenze siano rispetto ad altri pacchetti e non dai file, permette ai nostri utenti finali di risparmiare un bel po' di tempo. Ci sono volte in cui altre considerazioni di natura tecnica prevalgono su queste considerazioni. Un esempio in merito è rappresentato dall'installazione di pacchetti all'interno di %{_libdir}/mozilla/plugins. In questo caso, imporre un particolare browser tra i pacchetti da installare, solo per avere questa directory potrebbe condurre ad una grande quantità di pacchetti inutili. Richiedere alla directory di risolvere la dipendenza è la scelta migliore.

Patch

Tutte le patch nei file spec di openSUSE è opportuno che riportino, prima della patch stessa, un commento relativo al loro stato. Per i dettagli si veda la pagina sulle linee guida per la creazione di pacchetti patch.

Sezione preparazione (%prep)

Qui si trovano le regole per la sezione di preparazione (preparation) del file spec.

Rendere %setup silenzioso

Si consiglia di passare l'opzione -q (quiet) alla macro %setup. Questo ridurrà significativamente le dimensioni del file log generato durante la creazione del pacchetto, in particolare con i file archivio di sorgenti che contengono molti file da estrarre.

Sezione build (%build)

Queste sono le regole per la sezione build del file spec.

Flag per il compilatore

I compilatori utilizzati per creare i pacchetti dovrebbero rispettare gli appropriati flag per il compilatore, impostati nella configurazione di rpm dal sistema. In pratica significa: %{optflags} (o la variabile: $RPM_OPT_FLAGS, si veda il paragrafo più sopra) per compilatori C, C++ e Fortran. "Rispettare" (il flag) significa che il contenuto di quella variabile viene usato come base di partenza per i flag effettivamente usati dal compilatore durante la creazione del pacchetto. È consentito sovrascrivere, eliminare (filtrandole) o fare aggiunte a parti di questi flag, a condizione che sussista una ragione valida per farlo; la ragione fondamentale per cui si è scelto di farlo dovrebbe essere oggetto di resoconto e documentazione da aggiungere nel file spec, soprattutto nei casi in cui i flag siano stati modificati o eliminati (parzialmente).

Parallelizzare make

Ovunque possibile, il comando make dovrebbe essere richiamato con:

make %{?_smp_mflags}

Questo in genere velocizza i build, specialmente su macchine SMP (multiprocessore, multicore). Questo è da preferirsi all'uso di %{?jobs:-j%jobs}, poiché consente di usare flag alternativi per il comando make, come ad esempio make -lN, a differenza dell'immutabile -jN. Converrà, tuttavia, accertarsi che con questo modalità il pacchetto venga creato senza errori, dato che alcuni file di make non supportano la compilazione parallela, o danno problemi con le dipendenze.

Nel caso in cui un pacchetto sorgente non supporti il building in parallelo, sarebbe opportuno scrivere esplicitamente -j1 per facilitare la ricerca e individuazione di tali pacchetti, all'interno di un gruppo di file spec. Possono esserci altri motivi per usare -j1, per esempio perché altrimenti si avrebbe un utilizzo (soggettivamente) eccessivo di memoria, dovuto a unità C++ di grandi dimensioni. In ogni caso, aggiungere una nota che spieghi cosa ha portato ad usare -j1.

Sezione installazione (%install)

Si passa ora alle regole per la sezione di installazione (install) del file spec.

Dato che rpmbuild viene eseguito senza i privilegi di super-utente, durante l'esecuzione `make install` non deve cercare di modificare il proprietario dei file (sia eseguendo direttamente chown, sia utilizzando, per esempio, `install -o root...`). Permessi e pacchetto proprietario dei file saranno invece da impostare più avanti, nella sezione %files.

Esistono due macro di installazione dai nomi simili, e perciò causa di possibile confusione, fornite da rpm a dalle macro rpm di SUSE:

Il make install compagno di %configure per autotools moderni e la precedente versione, presente per compatibilità e per pacchetti vecchi o guasti che non supportano correttamente DESTDIR (estratto da /usr/lib/rpm/macros):

# The make install analogue of %configure for modern autotools:
%make_install make install DESTDIR=%{?buildroot}

#------------------------------------------------------------------------------
# Former make install analogue, kept for compatibility and for old/broken
#  packages that don't support DESTDIR properly.
%makeinstall \
  make \\\
        prefix=%{?buildroot:%{buildroot}}%{_prefix} \\\
        exec_prefix=%{?buildroot:%{buildroot}}%{_exec_prefix} \\\
[...]

La collezione di macro rpm di SUSE, cioè il file /usr/lib/rpm/suse_macros, le riassegna entrambe a praticamente lo stesso comando:

%make_install           make install DESTDIR=%{?buildroot}
%makeinstall            make DESTDIR=%{?buildroot:%{buildroot}} install

Dato che spesso i pacchetti sono concepiti per essere compilati non soltanto per openSUSE, converrà probabilmente utilizzare soltanto make install DESTDIR="%buildroot".

Rimozione del buildroot

openSUSE segna come bad coding style (brutto stile di scrittura) avere la riga rm -rf %{buildroot} (o rm -rf $RPM_BUILD_ROOT) nella parte iniziale della sezione %install come in questo esempio:

%install
rm -Rf "%buildroot"
mkdir -p "%buildroot/%_prefix/..."  o make install

Perché?

La cartella di creazione temporanea del pacchetto, %buildroot, normalmente si trova dentro /var/tmp e, così facendo, è stata appena aperta la strada ad una facile race condition per un attacker locale, sulla tua macchina, che gli permetterà di prendere il controllo del tuo account (o anche quello di root, se compili come root). È meglio evitare del tutto "rm -rf %buildroot" in %install (e affidarsi a %clean per farlo).

Se si intende ripulire il build-root, è meglio farlo con:

%install
rm -Rf "%buildroot";
mkdir "%buildroot";
mkdir -p "%buildroot/%_prefix/..." oppure make install

In questo caso il "mkdir %buildroot" non avrà successo e il build si interromperà se un attacker stesse cercando di sostituire il build-root con un collegamento simbolico sotto il suo controllo.

Sezione clean (%clean)

La sezione %clean (di pulitura), se presente, verrà eseguita dopo che sono stati generati i pacchetti RPM binari e sorgente. Quando si usa Open Build Service, questa sezione non è necessaria poiché gli ambienti creati con chroot e con macchie virtuali, che sono usati per creare i pacchetti, solitamente vengono eliminati in ogni caso. La creazione di pacchetti in ambienti non creati da zero ad hoc, comunemente, non è supportata per i pacchetti di openSUSE (rif.: bnc#176528 c4)

a partire dalla versione 4.7 di 4.7, ovvero da openSUSE_11.3, rpm ricorre automaticamente a "%clean: rm -Rf %buildroot" se la sezione %clean manca del tutto dal file spec.

In tempi passati, alcuni pacchetti, nella sezione %clean, controllavano che %{buildroot} non fosse / prima di eliminarlo. Questo non è più necessario in openSUSE.

Scriptlet (%post* / %pre*)

Qui sono esposte le regole per le sezioni scriptlet del file spec. Quando si usano gli scriptlet bisogna essere molto cauti. Bisognerà quindi accertarsi che gli scriptlet utilizzati non contendono codice dannoso o che siano in ordine. Alcuni scriptlet di uso comune sono documentati in en:openSUSE:Packaging scriptlet snippets.

Requisiti per gli scriptlet

Il tuo pacchetto deve richiedere esplicitamente tutto le dipendenze presenti negli scriptlet. La sintassi è la seguente:

Requires(pre): ...
Requires(post): ...

Gli scriptlet possono scrivere soltanto in alcune directory

Agli script per il build dei pacchetti (%prep, %build, %install, %check e %clean) è permesso di alterare i file (creare, modificare, eliminare) dentro a %buildroot, %_builddir e directory temporanee valide come /tmp, /var/tmp (oppure $TMPDIR (valido?) o %_tmppath così come vengono impostate dal processo di creazione del pacchetto, via rpmbuild) in accordo con la seguente tabella:

/tmp, /var/tmp, $TMPDIR, %_tmppath  %_builddir  %buildroot
%prep no
%build no
%install
%check no
%clean

Nota aggiuntiva: questo dovrebbe valere indipendentemente dall'UID con cui è in esecuzione l'operazione di build.

Sezione files (%files)

Qui sotto sono riportate le regole per la sezione files del file spec. Per quanto riguarda l'organizzazione del filesystem openSUSE segue il Filesystem Hierarchy Standard. Lo standard FHS definisce la posizione in cui collocare i file nel sistema. Ogni scostamento dallo standard (FHS) dovrebbe essere giustificato con un commento nel file spec.

File e cartelle appartenenti ad un pacchetto

Il pacchetto che si va a creare dovrebbe possedere tutti quanti i file che sono installati come parte del processo di installazione (con %install). I pacchetti non devono possedere file che già appartengono ad altri pacchetti. La regola pratica da seguire, qui, è che al primo pacchetto ad essere installato dovrebbero essere assegnati i file che potrebbero essere di competenza anche degli altri pacchetti. Se si ritiene di aver un valido motivo per cui il proprio pacchetto dovrebbe possedere un file che è appartenente ad un altro pacchetto, si prega allora di farlo presente al momento della verifica del pacchetto (fase di review).

Assegnare una directory ad un pacchetto è più complesso che decidere l'appartenenza di un file ad un pacchetto. Benché la regola pratica sia la stessa: "possiedi tutte le directory che crei ma nessuna delle directory dei pacchetti da cui dipendi", esistono varie situazioni in cui è desiderabile per più pacchetti possedere una stessa directory. Esempi di questo sono:

  1. Al pacchetto da cui si dipende per fornire una certa directory può venir assegnata, in una versione più recente, una directory diversa dalla precedente e il proprio pacchetto resterà funzionante, senza modifiche, con quella versione più recente. Un esempio frequente di questo è un modulo Perl. Si supponga che perl-A-B dipenda da perl-A e installi dei file in /usr/lib/perl5/vendor_perl/5.8.8/i386-linux-thread-multi/A/B. Il pacchetto base del Perl assicura che possiederà /usr/lib/perl5/vendor_perl/5.8.8/i386-linux-thread-multi per tutto il tempo in cui resterà compatibile con la versione 5.8.8, ma un futuro aggiornamento del pacchetto perl-A potrebbe installare dentro a (e perciò possedere) /usr/lib/perl5/vendor_perl/5.9.0/i386-linux-thread-multi/A. Così il pacchetto perl-A-B deve essere il possessore di /usr/lib/perl5/vendor_perl/5.8.8/i386-linux-thread-multi/A e anche di /usr/lib/perl5/vendor_perl/5.8.8/i386-linux-thread-multi/A/B al fine di conservare il corretto possesso delle directory.
  2. I pacchetti multipli hanno file in una directory comune ma nessuno di essi necessita degli altri. Ecco un esempio in cui
Foo-Animal-Emu colloca alcuni dei suoi file dentro a /usr/share/Foo/Animal/Emu
Foo-Animal-Llama colloca alcuni dei suoi file dentro a /usr/share/Foo/Animal/Llama

Nessuno dei pacchetti dipende dall'altro. Nessuno dei pacchetti dipende da alcun altro pacchetto che possieda la directory /usr/share/Foo/Animal. In questo caso, ciascun pacchetto deve essere il possessore della directory /usr/share/Foo/Animal.

In ogni caso, in openSUSE viene effettuato un controllo contro la presenza nel sistema di directory non appartenenti ad alcun pacchetto (ovvero "orfane"). Fare riferimento a en:Packaging/Unowned_Directories per i dettagli (se non disponibile provare qui.

Un pacchetto per openSUSE non deve contenere nessun file duplicato nell'elenco in %files, sia per quanto riguarda uno stesso file pacchettizzato due o più volte (in due o più sotto-pacchetti), sia per quanto concerne il suo contenuto. È possibile far eseguire %fdupes %buildroot (necessita di aggiungere BuildRequires: fdupes) per evitare in modo intelligente contenuti duplicati, sostituendo quei file con hardlink ai loro doppioni.

Permessi

I permessi sui file devono essere impostati in modo appropriato. Ai file eseguibili dovrebbero essere impostati come eseguibili, per esempio. Ogni sezione %files deve includere una riga con %defattr(...). Ecco un buon comportamento predefinito:

%files
%defattr(-,root,root)

Tranne il caso in cui si abbia un più che valido motivo per fare diversamente, si dovrebbe utilizzare %defattr(-,root,root) per tutte le sezioni %files nel proprio pacchetto. Per risolvere problemi di permessi relativi alle directory si può usare %defattr(-,root,root,0755) (per esempio se umask era troppo limitante al momento di scompattare).

Bit di SUID

Su openSUSE i permessi critici per la sicurezza sono gestiti per mezzo di /etc/permissions*. Per conoscere i concetti di base si possono leggere i file /etc/permissions*.

Se si desidera creare un pacchetto che contenga programmi con il suid impostato, si può fare come qui di seguito:

  • Valuta d'apprima se è davvero necessario. Di solito l'uso dei bit di setuid non è bene per la sicurezza del sistema e sintomo di progettazione (o pacchettizzazione) non accurata.
  • Aggiungi un'intestazione con Requires(post): permissions al tuo file spec.
  • Nella sezione %files, definisci gli attributi dei file elencati nei tuoi file di permessi (i permissions) così come sono definiti per permissions.secure. Dai istruzioni a rpmverify di non controllare il proprietario e neppure i permessi.
  • Aggiungi uno scriptlet %post che imposti i permessi del tuo pacchetto in accordo con il corrente livello di sicurezza del sistema.
  • Aggiungi uno scriptlet %verifyscript che controlli i permessi del tuo pacchetto in accordo con il livello di sicurezza normalmente applicato dal sistema.

E dato che le istruzioni qui sopra contengono alcuni dettagli non banali, ecco uno schema di esempio:

[...]
PreReq:         permissions
[...]

%if 0%{?suse_version} >= 1120
%verifyscript
%verify_permissions -e %_bindir/programma_con_suid
%endif

%post
%if 0%{?set_permissions:1}
    %set_permissions %name
%else
    %run_permissions
%endif

%files
%defattr(-,root,root)
[...]
%verify(not user group mode) %attr(0711,root,root) %_bindir/programma_con_suid
[...]

Se hai creato nuovi programmi con impostati i bit di setuid e desideri che vengano inclusi nella distribuzione openSUSE, considera che dovranno essere esaminati dal gruppo Sicurezza di SUSE. Apri un bugreport destinato a security-team@suse.de e, o in alternativa, invia una e-mail a security@suse.de


Per creare dei pacchetti temporanei nella home del tuo progetto:

  • Usa i permessi specifici per il pacchetto, i quali possono essere impostati mediante file in /etc/permissions.d/.
  • Crea i file permissions{,.easy,.secure,.paranoid} per il tuo pacchetto. permissions viene usato se non viene trovato alcun file permissions.* corrispondente alle impostazioni di sicurezza correnti del sistema. Come regola pratica, permissions.easy dovrebbe contenere i permessi come se l'installazione fosse fatta da make install, permissions.paranoid dovrebbe rimuovere tutti i suid bit (anche se questo dovesse danneggiarne il funzionamento), e permissions.secure si può considerare qualcosa a metà strada.
  • Aggiungi al file spec questi file di permessi (permissions) come sorgenti, e fai sì che vengano installati (con %install) in %{buildroot}%{_sysconfdir}/permissions.d/nomepacchetto[.suffisso].
  • Al fine di evitare messaggi di errore da parte di rpmlint relativi a file di permessi non ammessi, crea il file nomepacchetto-rpmlintrc contenente la linea setBadness('permissions-unauthorized-file', 333), a aggiungile in elenco come parte di Source nel tuo spec file. (Modifica l'argomento "333" se il numero di file non ammessi è diverso da tre.)

Ricorda per favore che tutti questi espedienti devono essere eliminati prima di inoltrare richiesta per la distribuzione openSUSE.

File di documentazione

Tutta la documentazione utile, inclusa nell'insieme dei sorgenti dovrebbe essere inclusa nel pacchetto. La documentazione non rilevante include le istruzioni per creare i pacchetti, l'onnipresente file INSTALL contenente istruzioni per generare una installazione generica, per esempio, e documentazione per sistemi diversi da Linux, come ad esempio README.MSDOS. Prestare inoltre attenzione a in quali sotto-pacchetti si include la documentazione, per esempio la documentazione sulle interfacce API appartiene al sotto-pacchetto -devel, non a quello principale. Oppure se è disponibile molta documentazione, si può valutare se metterla in un pacchetto dedicato e separato. In questo caso, si raccomanda di usare il suffisso *-doc per il nome del sotto-pacchetto, e Documentation come valore da usare con il tag Group.

Inoltre, quando un pacchetto include contenuto indicato come %doc, questo contenuto non deve avere nulla a che fare con l'esecuzione dell'applicazione. Ovvero si deve poter eseguire correttamente il programma anche se %doc non dovesse più esserci.

File di configurazione

I file di configurazione devono essere segnati come tali, nei pacchetti. La regola pratica prevede in tal caso di usare %config(noreplace) al posto del solo %config, a meno che ci siano solidi e tecnicamente fondati motivi che così facendo si arrecherebbe danno al sistema. In altre parole, conviene riflettere bene prima di sovrascrivere, durante gli aggiornamenti di pacchetto, eventuali modifiche personali ai file di configurazione. Un caso tipico in cui non usare noreplace è quando il file di configurazione di un pacchetto cambia in modo tale che la nuova revisione del pacchetto non sarebbe compatibile con il file di configurazione proveniente dalla precedente revisione di pacchetto. Ogni volta che viene usato %config da solo, aggiungere un breve commento al file spec per spiegarne il motivo.

I prefissi %config o %config(noreplace) non devono essere usati in /usr. In openSUSE si dà infatti per assodato che /usr non contenga file di configurazione.

File di sviluppo

Se il software che si sta pacchettizzando contenesse file pensati esclusivamente per lo sviluppo, questi file dovrebbero essere inseriti in un sotto-pacchetto -devel. Qui di seguito si riportano esempi di tipologie di file che dovrebbero stare in un pacchetto -devel:

  • File di header (p.es. file .h)
  • File di librerie condivise senza numero di versione (p.es. libfoo.so). Le librerie condivise dotate di numero di versione, come ad esempio libfoo.so.3, libfoo.so.3.0.0 non si dovrebbero invece mettere nel pacchetto -devel.
  • File pkgconfig (metadati per pkg-config). Una ragionevole eccezione è data da quando lo stesso pacchetto principale è uno strumento di sviluppo, come ad esempio gcc o gdb.

Per i pacchetti contenenti file pkgconfig (.pc) si deve utilizzare BuildRequires: pkg-config, in modo tale che venga aggiunta la corretta dipendenza di runtime Requires: pkg-config.

File di localizzazione

openSUSE include una macro rpm chiamata %find_lang. Tale macro rileverà tutti quanti i file di localizzazione (i file locale) che appartengono al proprio pacchetto (cercandoli in base al nome), e salverà questo elenco in un file. Si può quindi usare questo file per includere tutti quanti i file di localizzazione. %find_lang dovrebbe essere eseguita nella sezione %install del file spec, dopo che tutti i file sono stati installati nel buildroot. L'utilizzo di %find_lang contribuisce a mantenere chiaro e semplice il file spec, aiuta inoltre ad evitare altri errori nella creazione di pacchetti.

  • Ai pacchetti che usano %_datadir/* per recuperare tutti i file di localizzazione (di tipo locale) in una sola riga viene anche assegnato il possesso delle varie directory di localizzazione (le directory locale), il che non è consentito.
  • Gran parte dei pacchetti localizzati contiene numerosi file di localizzazione. Utilizzare %find_lang, nel file spec, è assai più comodo che dover scrivere:
%_datadir/locale/ar/LC_MESSAGES/%name.mo
%_datadir/locale/be/LC_MESSAGES/%name.mo
%_datadir/locale/cs/LC_MESSAGES/%name.mo
%_datadir/locale/de/LC_MESSAGES/%name.mo
%_datadir/locale/es/LC_MESSAGES/%name.mo
...
  • Quando nuovi file di localizzazione saranno disponibili in revisioni del pacchetto più recenti, %find_lang farà sì che vengano automaticamente inclusi mentre viene eseguito, non costringendo così il packager ad aggiornare il file spec più spesso di quanto effettivamente necessario.

Nomi di file con codifica diversa da ASCII

I nomi di file che contengono caratteri non facenti parte del repertorio ASCII devono essere codificati in UTF-8. Dato che non c'è modo di indicare la codifica usata per i caratteri del nome di file, utilizzare la stessa codifica per tutti i nomi di file è il modo migliore per garantire che gli utenti saranno in grado di leggere i nomi dei file senza problemi. Se il progetto originale (cioè upstream) distribuisce file con nomi non codificati in UTF-8, si può usare una utilità da riga di comando come convmv (dal pacchetto convmv) per convertire i nomi di file dalla propria sezione %install.

Libexecdir

Il Filesystem Hierarchy Standard non include alcuna raccomandazione per libexecdir, ma ai pacchetti di openSUSE è consentito di salvare file, se appropriati, in quella directory. Libexecdir (che nei sistemi openSUSE è reimpostata a /usr/lib) dovrebbe servire come directory per quei programmi eseguibili che sono stati progettati principalmente per essere eseguiti da altri programmi, anziché dagli utenti. Il gestore rpm di openSUSE include una macro per libexecdir: %_libexecdir. I creatori di pacchetti sono fortemente invitati a salvare i file per libexecdir in una sottodirectory, specifica del pacchetto, di %_libexecdir, come ad esempio: %_libexecdir/%name.

Sezione log delle modifiche (%changelog)

Vedi Come scrivere buoni file delle modifiche