[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [volume] [parte]


Capitolo 59.   Applicativi distribuiti in forma sorgente o compilata

La maggior parte dei programmi per Unix il cui utilizzo viene concesso gratuitamente, viene distribuita in forma sorgente. Ciò significa che per poterli utilizzare, questi programmi devono essere compilati. Fortunatamente, nella maggior parte dei sistemi Unix è disponibile il compilatore ANSI C GNU che permette di uniformare questo procedimento di compilazione.

Alcuni programmi vengono distribuiti in forma già compilata (e senza sorgenti) soprattutto quando si tratta di prodotti proprietari. Anche in questi casi si possono incontrare problemi nell'installazione.

In generale, i problemi che derivano dall'installazione di questi applicativi nasce dalla mancanza di sostegno da parte del sistema di gestione dei pacchetti della propria distribuzione GNU/Linux.

59.1   Struttura tipica di un pacchetto sorgente

Un programma distribuito in forma sorgente si trova di solito confezionato in un archivio il cui nome ha un'estensione .tar.gz o .tgz (ottenuto attraverso tar e gzip), oppure ancora con un'estensione .tar.bz2 (tar e bzip2). Prima di poter procedere con la sua compilazione deve essere estratto il suo contenuto. Solitamente si fa questo in una directory di lavoro. Nell'esempio che segue, si fa riferimento a un pacchetto ipotetico archiviato nel file pacch.tar.gz:

cd ~/tmp[Invio]

tar xzvf pacch.tar.gz[Invio]

Oppure:

cat pacch.tar.gz | gunzip | tar xvf -[Invio]

Se invece si trattasse dell'archivio pacch.tar.bz2, sarebbe necessario decomprimerlo attraverso un comando leggermente diverso:

cd ~/tmp[Invio]

tar xjvf pacch.tar.bz2[Invio]

Oppure:

cat pacch.tar.bz2 | bunzip2 | tar xvf -[Invio]

Di solito si ottiene una struttura ad albero più o meno articolata in sottodirectory, dove nella directory principale di questa struttura si trovano:

Seguendo l'esempio visto poco prima, dovrebbe essere stata creata la directory pacch/.

cd pacch[Invio]

ls[Invio]

59.1.1   Documentazione necessaria alla compilazione

I file di testo che si trovano nella directory principale del pacchetto contenente il programma sorgente, servono per presentare brevemente il programma e per riassumere le istruzioni necessarie alla sua compilazione. Di solito, queste ultime sono contenute nel file INSTALL. In ogni caso, tutti questi file vanno letti, in particolare quello che spiega il procedimento per la compilazione e l'installazione.

Il modo più semplice per leggere un file è l'utilizzo del programma less:

less INSTALL[Invio]

In sua mancanza si può usare more:

more INSTALL[Invio]

59.1.2   script configure

La composizione classica di un pacchetto distribuito in forma sorgente, prevede la presenza di uno script il cui scopo è quello di costruire un file-make adatto all'ambiente in cui si vuole compilare il programma. Ciò è necessario perché i vari sistemi Unix sono diversi tra loro per tanti piccoli dettagli.

Spesso questo script è in grado di accettare argomenti. Ciò può permettere, per esempio, di definire una directory di destinazione del programma, diversa da quella predefinita.

Quando questo script di preparazione manca, occorre modificare manualmente il file-make in modo che sia predisposto correttamente per la compilazione nel proprio sistema.

Per evitare ambiguità, questo script viene sempre avviato indicando un percorso preciso: ./configure.

59.1.3   File-make

Il file-make, o makefile, è quel file che viene letto da Make, precisamente dall'eseguibile make, allo scopo di coordinare le varie fasi della compilazione ed eventualmente anche per l'installazione del programma compilato. Il nome di questo file può essere diverso, generalmente si tratta di Makefile, oppure di makefile.

Questo file viene generato frequentemente da uno script, di solito si tratta di ./configure, ma se manca deve essere controllato e, se necessario, modificato prima della compilazione.

Spesso è bene controllare il contenuto del file-make anche quando questo è stato generato automaticamente.

59.2   Fasi tipiche di una compilazione e installazione

I pacchetti più comuni si compilano e si installano con tre semplici operazioni.

  1. ./configure[Invio]

    Genera automaticamente il file-make.

  2. make[Invio]

    Esegue la compilazione generando i file eseguibili.

  3. make install[Invio]

    Installa gli eseguibili e gli altri file necessari nella loro destinazione prevista per il funzionamento: l'ultima fase deve essere eseguita con i privilegi dell'utente root.

59.3   Problemi

Le note di questo capitolo valgono solo in linea di massima: è sempre indispensabile leggere le istruzioni che si trovano nei file di testo distribuiti insieme ai sorgenti dei programmi.

I problemi maggiori si hanno quando non è stato predisposto uno script ./configure o simile e si è costretti a modificare il file-make.

In altri casi, il file-make potrebbe non prevedere la fase di installazione (make install), per cui si deve installare il programma copiando pezzo per pezzo nella destinazione giusta.

Spesso l'installazione non rispetta la struttura standard del proprio file system. Ciò nel senso che magari vengono piazzati file che devono poter essere modificati, all'interno di una zona che si voleva riservare per l'accesso in sola lettura.

Quando si utilizza una distribuzione GNU/Linux ben organizzata, si trova una gestione dei pacchetti installati che permette l'eliminazione e l'aggiornamento di questi senza rischiare di lasciare file inutilizzati in giro. Quando si installa un programma distribuito in forma originale, viene a mancare questo supporto della gestione dei pacchetti. In questi casi si cerca di installare tali pacchetti al di sotto della directory /opt/, o almeno al di sotto di /usr/local/.

59.4   Installazione di programmi già compilati

L'installazione di programmi già compilati per GNU/Linux, anche se potrebbe sembrare più semplice rispetto a un procedimento che richiede la compilazione, potrebbe creare qualche problema a chi non conosce perfettamente l'interdipendenza che c'è tra le varie parti del sistema operativo.

I problemi e le soluzioni che si descrivono nelle sezioni seguenti, riguardano a volte anche i programmi distribuiti in forma sorgente. Infatti, alcune volte, i programmi distribuiti in questo modo non sono stati preparati per un'installazione soddisfacente, di conseguenza bisogna provvedere da soli a collocare i file nelle posizioni corrette e a sistemare tutto quello che serve.

59.4.1   Scelta della piattaforma

Quando si cerca del software per il proprio sistema che può essere ottenuto solo in forma già compilata, occorre fare attenzione alla piattaforma. Infatti, non basta che si tratti di programmi compilati per GNU/Linux, occorre che gli eseguibili siano adatti al tipo di elaboratore su cui GNU/Linux è in funzione.

Normalmente, per identificare l'architettura dei «PC» (x86), si utilizza la sigla i386 nel nome dei file degli archivi. Sigle come i486, i586, i686,... rappresentano la stessa architettura basata però su un livello particolare del microprocessore.

59.4.2   Eseguibili e variabili di ambiente

Un programma distribuito in forma binaria, deve essere estratto normalmente dall'archivio compresso che lo contiene. A volte è disponibile uno script o un programma di installazione, altre volte è necessario copiare manualmente i file nelle varie destinazioni finali. Quando si può scegliere, è preferibile collocare tutto quanto a partire da un'unica directory discendente da /opt/.

A volte, perché il programma possa funzionare è necessario predisporre o modificare il contenuto di alcune variabili di ambiente. Il caso più comune è costituito da PATH che deve (o dovrebbe) contenere anche il percorso necessario ad avviare il nuovo programma. Spesso, i file di documentazione che accompagnano il software indicano chiaramente tutte le variabili che devono essere presenti durante il loro funzionamento.

La dichiarazione di queste variabili può essere collocata direttamente in uno dei file di configurazione della shell utilizzata (per esempio /etc/profile, oppure ~/.bash_profile o altri ancora a seconda di come è organizzato il proprio sistema).

59.4.3   Librerie dinamiche

Alcuni programmi utilizzano delle librerie non standard, che spesso vengono collocate al di fuori delle directory predisposte per contenerle. Per fare in modo che queste librerie risultino disponibili, ci sono due modi possibili:

  1. modificare la configurazione di /etc/ld.so.cache;

  2. utilizzare la variabile di ambiente LD_LIBRARY_PATH.

Per agire secondo la prima possibilità, occorre comprendere come è organizzato questo sistema. Il file /etc/ld.so.cache viene creato a partire da /etc/ld.so.conf che contiene semplicemente un elenco di directory destinate a contenere librerie. Il programma ldconfig serve proprio a ricreare il file /etc/ld.so.cache leggendo /etc/ld.so.conf, pertanto viene avviato solitamente dalla stessa procedura di inizializzazione del sistema, allo scopo di garantire che questo file sia sempre aggiornato.

Dovrebbe essere chiaro, ormai, il modo giusto di includere nuovi percorsi di librerie nel file /etc/ld.so.cache: occorre indicare questa o queste directory nel file /etc/ld.so.conf e quindi basta avviare il programma ldconfig.

L'utilizzo della variabile di ambiente LD_LIBRARY_PATH è meno impegnativo, ma soprattutto, in questo modo si può intervenire facilmente attraverso dei semplici script. Ciò permette, per esempio, di fare in modo che solo un certo programma «veda» certe librerie. In ogni caso, quando si intende usare questa variabile di ambiente, è importante ricordare di includere tra i vari percorsi anche quelli standard: /lib/, /usr/lib/ e /usr/local/lib/. L'esempio seguente rappresenta un pezzo di uno script (potrebbe trattarsi di /etc/profile) in cui viene assegnata la variabile di ambiente in questione.

LD_LIBRARY_PATH=/lib:/usr/lib:/usr/local/lib:/opt/mio_prog/lib:/opt/tuo_prog/lib
export LD_LIBRARY_PATH

Se un certo programma richiede determinate librerie che potrebbero entrare in conflitto con altri programmi, è indispensabile l'utilizzo della variabile di ambiente LD_LIBRARY_PATH, configurandola esclusivamente nell'ambito del processo di quel programma. In pratica, si tratta di avviare il programma attraverso uno script che genera l'ambiente adatto, in modo che non si rifletta negli altri processi, come mostrato nell'esempio seguente:

#! /bin/sh
#
# Modifica il percorso di ricerca delle librerie.
#
LD_LIBRARY_PATH="/opt/mio_programma/lib:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH
#
# Avvia il programma.
#
mio_programma
#
# Al termine dello script non restano tracce dei cambiamenti.
#

Avviando lo script, viene modificata la variabile di ambiente LD_LIBRARY_PATH per quel processo e per i suoi discendenti (viene esportata), quindi, al termine del programma termina lo script e con lui anche gli effetti di queste modifiche.

Si osservi in particolare il fatto che nella nuova definizione del percorso delle librerie, viene posto all'inizio quello per le librerie specifiche del programma, in modo che venga utilizzato per primo; subito dopo, viene inserito l'elenco dei percorsi eventualmente già esistente.

Teoricamente, è necessario soltanto che i file delle librerie siano accessibili in lettura, perché possano essere utilizzate dai programmi; tuttavia, per motivi di sicurezza, potrebbe essere richiesto anche il permesso di esecuzione attivo. In particolare, con i sistemi GNU/Linux è indispensabile che le librerie utilizzate da Init (/sbin/init) dispongano dei permessi di esecuzione.

59.4.4   Verifica delle librerie utilizzate dagli eseguibili

Il programma ldd emette l'elenco delle librerie condivise richieste dai programmi indicati come argomenti della riga di comando:

ldd [opzioni] programma...

Si utilizza ldd per determinare le dipendenze di uno o più programmi dalle librerie. Naturalmente, si può verificare anche la dipendenza di un file di libreria da altre librerie.

L'esempio seguente mostra le dipendenze dalle librerie di /bin/bash. Il risultato ottenuto indica il nome delle librerie e la collocazione effettiva nel sistema, risolvendo anche eventuali collegamenti simbolici.

ldd /bin/bash[Invio]

        libncurses.so.5 => /lib/libncurses.so.5 (0x40019000)
        libdl.so.2 => /lib/libdl.so.2 (0x40059000)
        libc.so.6 => /lib/libc.so.6 (0x4005c000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

59.5   File di differenze, o «patch»

Quando si ha a che fare con programmi il cui aggiornamento è frequente, come avviene nel caso del kernel, si possono anche trovare aggiornamenti in forma di file di differenze (patch o «pezze»), cioè di file che contengono solo le variazioni da una versione all'altra. Queste variazioni si applicano ai file di una versione per ottenerne un'altra.

Se la versione da aggiornare è stata espansa a partire dall'ipotetica directory ~/tmp/, per applicarvi una modifica è necessario posizionarsi sulla stessa directory e poi eseguire il comando seguente:

patch < file_di_differenze

Può darsi che la posizione in cui ci si deve trovare sia diversa o che i sorgenti da aggiornare debbano trovarsi in una posizione precisa. Per capirlo, dovrebbe bastare l'osservazione diretta del contenuto del file di differenze.

Spesso, il file di differenze è realizzato considerando che i file da aggiornare si trovino all'interno di una certa directory. Di solito, si raggira il problema usando l'opzione -p. Per esempio, si può immaginare di avere la directory /tmp/prova/esempio/ che contiene qualcosa da aggiornare attraverso il file di differenze /tmp/prova/esempio-007.diff, che fa riferimento a file contenuti nella directory prova-007/ (come percorso relativo). Per fare in modo che l'aggiornamento venga eseguito correttamente, è necessario spostarsi nella directory /tmp/prova/esempio/:

cd /tmp/prova/esempio[Invio]

Quindi si esegue l'aggiornamento ignorando la prima directory del percorso:

patch -p1 < /tmp/prova/esempio-007.diff[Invio]

L'applicazione delle variazioni, può fallire. Se non si vuole perdere il rapporto degli errori, questi possono essere ridiretti in un file specifico.

patch < file_di_differenze 2> file_degli_errori

Se gli aggiornamenti sono più d'uno, occorre applicare le modifiche in sequenza.

Il capitolo 194 tratta meglio questo problema.

59.6   Aggiornamento delle librerie standard

Oltre al problema delle librerie specifiche di un programma particolare, cosa già descritta in questo capitolo, ci può essere la necessità di aggiornare le librerie standard del sistema operativo, a seguito di qualche aggiornamento di altro software.

Normalmente, la propria distribuzione GNU dovrebbe offrire questi aggiornamenti in forma di archivi già pronti, installabili attraverso il proprio sistema di gestione dei pacchetti. Ciò garantendo la sistemazione di una serie di dettagli importanti.

Quando si è costretti a fare da soli è importante fare attenzione. In particolare, quando si interviene in ciò che risiede nella directory /lib/, se si commette un errore, si rischia di bloccare il sistema senza possibilità di rimedio.

I file delle librerie sono organizzati normalmente con un numero di versione piuttosto articolato. Quando ciò accade, è normale che a questi file siano affiancati una serie di collegamenti simbolici strutturati in modo che si possa accedere a quelle librerie anche attraverso l'indicazione di versioni meno dettagliate, oppure semplicemente attraverso nomi differenti. Questi collegamenti sono molto importanti perché ci sono dei programmi che dipendono da questi; quando si aggiorna una libreria occorre affiancare la nuova versione a quella vecchia, quindi si devono modificare questi collegamenti. Solo alla fine, sperando che tutto sia andato bene, si può eliminare eventualmente il vecchio file di libreria.

Per fare un esempio pratico, si suppone di disporre della libreria libc.so.9.8.7, articolata nel modo seguente:

libc.so -> libc.so.9
libc.so.9 -> libc.so.9.8.7
libc.so.9.8.7

Volendo sostituire questa libreria con la versione 9.8.10, il cui file ha il nome libc.so.9.8.10, occorre procedere come segue:

ln -s -f libc.so.9.8.10 libc.so.9[Invio]

Come si vede, per generare il collegamento, è stato necessario utilizzare l'opzione -f che permette di sovrascrivere il collegamento preesistente. Infatti, non sarebbe stato possibile eliminare prima il collegamento vecchio, perché così si sarebbe rischiato il blocco del sistema.

libc.so -> libc.so.9
libc.so.9 -> libc.so.9.8.10
libc.so.9.8.7
libc.so.9.8.10

In generale, per sicurezza, è meglio lasciare le librerie vecchie, perché ci potrebbero essere ugualmente dei programmi che ne hanno ancora bisogno.

Quando la libreria da aggiornare ha subito un aggiornamento molto importante, per cui i numeri delle versioni sono molto distanti rispetto a quanto si utilizzava prima, conviene evitare di sostituirle, mentre è solo il caso di affiancarle. Volendo ritornate all'esempio precedente, si può supporre che la libreria da aggiornare sia arrivata alla versione 10.1.1, con il file libc.so.10.1.1. Intuitivamente si comprende che il collegamento simbolico libc.so.9 non può puntare a questa nuova libreria, mentre resta il dubbio per libc.so.

In generale è meglio lasciare stare le cose come sono, a meno di scoprire che qualche programma cerca proprio la libreria libc.so e si lamenta perché non si tratta della versione adatta a lui. Comunque, sempre seguendo l'esempio, sarebbe il caso di riprodurre un collegamento equivalente a libc.so.9, denominato ovviamente libc.so.10.

libc.so -> libc.so.9
libc.so.9 -> libc.so.9.8.7
libc.so.9.8.7
libc.so.10 -> libc.so.10.1.1
libc.so.10.1.1

Appunti di informatica libera 2007.02 --- Copyright © 2000-2007 Daniele Giacomini -- <daniele (ad) swlibero·org>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome applicativi_distribuiti_in_forma_sorgente_o_compilata.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico]

Valid ISO-HTML!

CSS validator!