1.0.3 2010-07-13
Introduzione alle eclass L'idea che sta dietro le eclass

Le eclass sono moduli di codice condiviso. Sono scritte in bash, hanno la stessa sintassi dei normali ebuild e vengono derivate ('ereditate') dagli ebuild e altre eclass, in modo da fornire impostazioni e funzionalità predefinite attraverso molti ebuild simili tra loro.

Questo serve per assicurare il massimo riutilizzo del codice tra ebuild simili.

Questo capitolo mostra brevemente come scrivere una eclass incorporando i trucchi standard e le tecniche usate nelle eclass esistenti.

Esempio di una semplice eclass

Questa è una eclass fittizia sourceforge.eclass, progettata per fornire l'homepage e l'ubicazione dei download per i progetti presenti su sourceforge.net:

# Copyright 2009 Gentoo Foundation
# Distributed under the terms of the GNU General Public License, v2 or later
# Author Dan Armak
# <danarmak@gentoo.org> $Header: $

# questa eclass imposta ${HOMEPAGE} e ${SRC_URI} ai valori standard per
# sourceforge.net - progetti ospitati.

HOMEPAGE="http://${PN}.sourceforge.net/"
SRC_URI="http://download.sourceforge.net/${PN}/${P}.tar.gz"

Le prime quattro linee sono intestazioni (headers) proprio come quelle presenti in ogni ebuild. Le seguenti due linee sono una breve descrizione dell'eclass. Il resto del codice fa il lavoro effettivo - impostando SRC_URI e HOMEPAGE.

la maggior parte delle eclass va oltre l'impostare variabili e fornire funzioni d'aiuto; esse contengono delle versioni predefinite di funzioni speciali degli ebuild (src_unpack, src_compile e così via). Prima di scrivere una funzione predefinita in una eclass, bisogna essere a conoscenza delle funzioni predefinite già contenute in ebuild.sh, che saranno quelle che verranno eseguite se non viene messa alcuna funzione nel proprio ebuild (non sempre tramite una eclass); è usata spesso la predefinita src_unpack(). Se non è ancora fatto, guardare le implementazioni predefinite in ebuild.sh.

Questo è tutto quello che serve sapere per scrivere delle eclass. Posizionare la propria nuova eclass in ${PORTDIR}/eclass/, e inserire questa linea all'inizio del proprio ebuild:

inherit sourceforge

A questo punto il contenuto delle eclass verrà derivato. Ricordarsi che tutte le variabili o funzioni definite nella eclass possono essere sovrascritte nell'ebuild, il cui codice viene eseguito dopo qualsiasi eclass. Pertanto, bisogna cercare di mettere nelle proprie eclass il maggior numero possibile di impostazioni predefinite e codice comune. Qualsiasi impostazione non standard o modifica può quindi essere messa negli ebuild.

Si possono ereditare più eclass contemporaneamente tramite la sintassi:

inherit eclass1 eclass2 [...]

...ma guardare bene il loro ordine! Ricordarsi che le eclass possono ereditarsi una dall'altra e sovrascriversi a vicenda le relative impostazione, per questo bisogna fare molta attenzione quando si ha a che fare con eclass multiple, le quali potrebbero influenzarsi l'una con l'altra.

Ora verranno spiegati tutti i trucchi nella scrittura delle eclass, prima di passare alle attuali eclass in portage.

inherit()

Questa funzione risiede in ebuild.sh e gestisce l'ereditarietà (derivazione) delle eclass. Viene invocata con una lista di nomi di eclass da ereditare: inherit <eclass1> [eclass2 eclass3...].

Oltre all'effettiva derivazione dei file delle eclass, questa funziona imposta le variabili ECLASS e INHERITED che vengono usate da portage per memorizzare i gli orari (timestamp) di modifica delle eclass. La variabile INHERITED potrebbe essere d'aiuto nella scrittura delle eclass: essa contiene una lista di tutte le eclass ereditate (derivate) fino a quel punto, in ordine. In questo modo una eclass può usare questa variabile per determinare se essa stessa viene chiamata o meno da un'altra eclass.

EXPORT_FUNCTIONS

Delle buone funzioni predefinite di un'eclass possono essere spesso usate così come sono; in questo modo l'ebuild conterrà pochissimo codice (che è una buona cosa). Qualche volta, ciononostante, l'eclass non fa esattamente quello di cui si ha bisogno. In questo caso si può scrivere una nuova funzione nel proprio ebuild, sovrascrivendo la funzione definita nell'eclass. tuttavia questo minimizzerà i benefici del riutilizzo del codice. Per cui si proverà ad 'estendere' la funzione dell'eclass.

Si supponga di voler estendere src_compile(). Si può scrivere una definizione per src_compile() nel proprio ebuild, che includerà solo le parti mancanti della funzione src_compile() presente nell'eclass. A questo punto si invocherà la funzione src_compile() dell'eclass dall'interno del codice della propria funzione personalizzata.

Tuttavia, se si crea una nuova funzione chiamata src_compile(), bash si dimenticherà quella vecchia e non sarà più capace di chiamarla! Qui entra in gioco la macro EXPORT_FUNCTIONS.

Ora verrà analizzato momentaneamente un altro problema. Si supponga che entrambe le eclass foo.eclass e bar.eclass definiscano src_compile(). Ereditando sia foo che bar si otterrà una diversa funzione src_compile() in base all'ordine in cui esse vengono ereditate. Questo è normale, in quanto si suppone di tener conto dell'ordine dell'ereditarietà. Ma si potrebbe voler invocare entrambe le dichiarazioni di src_compile() esplicitamente.

Così, ogni eclass aggiunge un prefisso alle funzioni che definisce . Per esempio, foo.eclass definirà una funzione chiamata foo_src_compile(), e rispettivamente bar.eclass definirà bar-src_compile(). In questo modo, l'ebuild può chiamare entrambe le funzioni sapendo cosa otterrà.

Tuttavia, si può voler disporre di una qualche funzione predefinita chiamata semplicemente src_compile(), altrimenti l'ebuild dovrà definirne una. La macro EXPORT_FUCTIONS risolve sia questo problema che quello presentato prima.

EXPORT_FUNCTIONS() {
    while [ "$1" ]; do
        eval "$1() { ${ECLASS}_$1 ; }" > /dev/null
        shift
    done
}

La funzione inherit() imposta ${ECLASS} con il nome dell'eclass prima di derivarla. Alla fine l'eclass chiama EXPORT_FUNCTIONS(), passando come parametri la lista delle funzioni predefinite che fornisce. Per esempio, se si chiama

EXPORT_FUNCTIONS src_compile src_install

allora EXPORT_FUNCTIONS chiamerà eval() sulla seguente stringa:

src_compile() { foo_src_compile ; }
src_install() { foo_src_install ; }

Ora, qualsivoglia eclass venga ereditato per ultimo, definirà la funzione predefinita src_compile(), ma, se necessario, entrambe le funzioni potranno essere chiamate direttamente dall'ebuild.

È possibile anche estendere la funzione predefinita src_compile() chiamando la funzione dell'eclass dall'interno della propria funzione. In questo modo si dovrà usare il nome completo della funzione predefinita foo_src_compile. Un esempio:

#in foo.eclass:
foo_src_compile() {
        [qui codice predefinito]
}

EXPORT_FUNCTIONS src_compile

#fine codice eclass

#in un ebuild:

inherit foo

src_compile() {
        [qui codice personalizzato]
        foo_src_compile
        [altro codice personalizzato]
}
Sezioni delle funzioni

A volte, estendere le funzioni predefinite eseguendo il codice prima e dopo non è abbastanza flessibile. Quando si ha a che fare con funzioni lunghe e complesse, spesso ci sarà la necessità di voler eseguire il proprio codice personalizzato all'interno di queste funzioni.

Le sezioni delle funzioni provvedono a fornire una maggiore flessibilità, richiesta in questo caso. Esse suddividono le funzioni in sezioni e permettono di eseguire il codice tra due sezioni qualsiasi.

L'implementazione è semplice. Viene presa come esempio la funzione src_compile() da base.eclass. (Nota: non esiste più, ma è un buon esempio :-) Essa è fatta così:

base_src_compile() {
    econf || die
    emake || die
}

Qui c'è la stessa funzione, divisa in sezioni:

base_src_compile() {

    [ -z "$1" ] && base_src_compile all

    while [ "$1" ]; do
        case $1 in
            configure)
                econf || die;;
            make)
                emake || die;;
            all)
                base_src_compile configure make;;
        esac
    shift
    done

}

Il codice è stato suddiviso in due sezioni: configure e make. In questo semplice esempio, esse corrispondono ai due comandi presenti nella funzione originale.

Nel centro della nuova funzione c'è un blocco while;case...esac;shift;done. Questo blocco esegue un controllo di corrispondenza dei parametri della funzione con i nomi di sezioni definiti ed esegue le corrispondenti linee di codice.

Il caso speciale all chiama la stessa funzione ricorsivamente con una lista ordinata di sezioni. È compito dello sviluppatore mantenere questa lista.

La prima linea del blocco dice che una chiamata senza parametri dovrà essere trattata come una chiamata con il singolo parametro all. Come si può notare, questa funzione è molto ricorsiva. Notare, però, che anche la chiamata base_src_compile configure all make è legale; essa eseguirà base_src_compile configure configure make make.

A questo punto nel proprio ebuild (o eclass) che eredita da base.eclass, si avrà la funzione fittizia src_compile, la quale chiamerà base_src_compile senza parametri. Questo fa eseguire base_src_compile all, che significa eseguire ogni sezione. Si può lasciarla così com'è. Se si vuole estenderla, è possibile definire una nuova funzione src_compile e chiamare base_src_compile una sezione alla volta:

src_compile() {
    esegui_mio_codice1
    base_src_compile configure
    esegui_mio_codice2
    base_src_compile make
    esegui_mio_codice3
}

Come si può vedere, le sezioni aggiungono flessibilità in quanto ora è possibile inserire codice tra le due sezioni, come anche poterle eseguire in un ordine diverso oppure eseguire solo alcune delle sezioni fornite. Questo permette un maggiore riutilizzo complessivo del codice.

Le funzioni debug-print-*

Ci sono molte altre funzioni fornite da ebuild.sh. Esse aggiungono alle eclass un output prolisso in fase di debug, per permettere di seguire più facilmente la loro esecuzione senza dover leggere i lunghi messaggi forniti dal metodo di debug di bash. Tutte le eclass dell'autore di questa guida usano molto spesso queste funzioni.

debug-print() stampa semplicemente tutti i suoi parametri con il prefisso 'debug:'. Viene chiamata ogni volta che c'è qualcosa di interessante da mettere nel log del debug.

debug-print-function() stampa 'debug: entering function $1, parameters: $2 [$3 ....]. Viene chiamata all'inizio di una funzione.

debug-print-section() mostra 'debug: now in section $1'. Viene chiamata all'inizio di una sezione di funzione.

L'output di debug, normalmente va in ${T}/eclass-debug.log. È possibile impostare la variabile d'ambiente ECLASS_DEBUG_OUTPUT (in make.globals/conf o nell'ambiente) e l'output sarà inviato pure lì. Si può anche impostarla con lo speciale valore 'on', in modo da mostrare l'output insieme a tutti gli altri messaggi di emerge.

Ecco come aggiungere delle tipiche dichiarazioni di output di debug alla precedente funzione di esempio:

base_src_compile() {

    debug-print function
    [ -z "$1" ] && base_src_compile all

    while [ "$1" ]; do
        case $1 in
            configure)
                debug-print-section configure
                ./configure || die;;
            make)
                debug-print-section make
                make || die;;
            all)
                debug-print-section all
                base_src_compile configure make;;
        esac
    shift
    done

    debug-print "${FUNCNAME}: il risultato è ${RESULT}"
}

${FUNCNAME} è una varibile incorporata in bash che restituisce il nome corrente delle funzione.

Eclass esistenti eclass-manpages

È possibile effettuare l'emerge di app-portage/eclass-manpages per ottenere la documentazione delle eclass esistenti.