Sappiate che non si impara a compilare in cinque minuti. Serve determinazione, studio e un po' di cervello. Se però imparerete ad affacciarvi a questa strana "arte" avrete compiuto un passo in più verso la vera indipendenza nella manutenzione di un sistema operativo, specialmente GNU/Linux.
La distribuzione per eccellenza che fa estensivo uso di questa tecnica è la Slackware, creata nel 1992, tuttora esistente e mantenuta da Patrick Volkerding. Essa è una tra le distribuzioni più vecchie e stabili, e fa della semplicità (intesa come semplicità strutturale e non di utilizzo) uno dei suoi punti di forza, a prezzo dell'usabilità. E il modo migliore di manutenere e aggiornare la Slackware è attraverso la compilazione.
In questo articolo cercherò di spiegarvi le basi della compilazione, a partire dai concetti generali.
Il cuore pulsante di un computer è la CPU (Central Processing Unit), che nei sistemi di oggi può essere un processore AMD Athlon X2, un Intel Core 2 Duo o un processore ARM. Essi sono centraline di controllo che possiedono un certo insieme (chiamato in gergo "set") di istruzioni macchina, manipolato in modo diretto dal proprio linguaggio macchina.
Il linguaggio macchina è a tutti gli effetti un linguaggio di programmazione, ma non è un linguaggio portabile [1], ovvero non può essere impiegato su architetture diverse [2]. Esso è valido soltanto sui processori compatibili con l'architettura per la quale il programma è stato scritto. Per esempio, l'architettura ARM è fondamentalmente differente dall'architettura x86, usata da AMD e Intel, in quanto sono fisicamente diverse e usano diversi set di istruzioni. Quindi un programma scritto in linguaggio macchina (chiamato anche "assembly") x86 non potrà girare su un'architettura ARM, e un programma scritto in assembly ARM non funzionerà su un processore x86 come AMD o Intel. [3]
Entrano quindi in scena i compilatori e i linguaggi portabili.
Un compilatore x86 può generare codice x86 partendo da un semplice file contenente codice sorgente scritto in un linguaggio portabile come il C. Lo stesso codice sorgente può essere compilato con modifiche nulle o minime su qualsiasi architettura purchè si disponga del compilatore adatto, che possa girare sull'architettura "target".
I primi computer richiedevano che i software da far girare su di essi fossero scritti nel loro set di istruzioni specifico. Quindi all'inizio non si poteva parlare di "compilazione" in senso stretto, proprio perché non c'era niente da compilare, e il codice sorgente era lo stesso linguaggio macchina.
Ciononostante esistevano i... "compilatori", che non erano programmi, ma persone in carne ed ossa che compilavano una scheda o un nastro perforato, più o meno in un modo analogo alla compilazione di... una dichiarazione dei redditi. Questi supporti venivano punzonati e fatti leggere da una macchina apposita, che provvedeva a trasmettere le istruzioni macchina corrispondenti al computer. Esso eseguiva poi il codice, ovvero il programma.
Con il passare del tempo i costi dei grandi computer delle aziende aumentavano, assieme alla varietà delle architetture, e programmare software in linguaggio macchina cominciò a diventare sempre più dispendioso. Si fece dunque prioritaria la creazione di strumenti in grado di generare codice macchina compatibile con una grande quantità di processori, partendo da una serie di istruzioni generiche e valide per ogni tipo di hardware. Fu così che i compilatori videro la luce. [4]
I primi compilatori erano scritti in linguaggio macchina, e verso gli anni 70 fecero la loro comparsa i primi compilatori in grado di compilare se stessi nel linguaggio che possono comprendere (chiamati anche "autocontenuti", o self-hosting). Fra essi, il Pascal alla fine degli anni 60 e il famoso e diffusissimo C nel 1972, creato dai progettisti di UNIX per UNIX stesso.
Con il passare del tempo i compilatori si sono evoluti e perfezionati per supportare una sempre più ampia varietà di architetture, e fra tutti i compilatori C vorrei ricordare GCC, acronimo di GNU Compiler Collection (inizialmente GNU C Compiler), un progetto avviato da Richard Stallman e mantenuto da Free Software Foundation, che ha di fatto gettato le basi degli standard C attualmente adottati dai compilatori C oggi in circolazione.
Potete dare un'occhiata a quest'articolo per qualche informazione in più sulla storia dei computer e dei sistemi operativi liberi prima degli anni 90.
La vita di un programma comincia nel momento in cui viene ideato e progettato, con diagrammi di flusso, problematiche da risolvere, pseudocodice, raffinazioni successive, strutture di funzioni, eccetera.
Dopo la teoria arriva la pratica, e giunge il momento in cui viene scritto. Il programma può comprendere da uno a migliaia, o decine di migliaia, di file sorgente, che devono essere compilati per poter essere usati in qualche modo. Riassumo di seguito i passi fondamentali che un file sorgente fa nella sua trasformazione in eseguibile.
Il cammino dei sorgenti comincia quando essi vengono letti da un primo stadio chiamato "analisi di lessico". Viene usato un programma che suddivide il codice in piccoli frammenti chiamati "token" [5].
Dopo l'analisi del lessico c'è il preprocessing, in cui alcuni linguaggi come il C sostituiscono le invocazioni degli header [6] dichiarati nel codice sorgente con istruzioni relative, generando un codice intermedio completo.
Avviene poi l'analisi della sintassi e della semantica, in cui il compilatore (o un programma ottimizzato per questa funzione, chiamato "parser") controlla che non esistano errori di battitura come istruzioni sbagliate, variabili non referenziate, parametri non corretti e così via. Questa fase è strettamente legata all'analisi lessicale.
Dopo l'analisi sintattica e semantica comincia la vera e propria elaborazione del linguaggio, con una fase intermedia di ottimizzazione del codice, per fare in modo che il programma compilato richieda meno cicli di processore e meno memoria possibile, e quindi meno risorse possibili per la sua esecuzione.
Alla fine si ha la generazione del codice macchina, ovvero del programma eseguibile, in una procedura chiamata assembling [7] [7a].
Se ancora non siete abbastanza spaventati, potete continuare a leggere mentre vi descriverò brevemente come compilare un software semplice in un sistema operativo GNU/Linux, per la precisione Ubuntu. Notate che le istruzioni per l'installazione dell'ambiente di compilazione sono valide per qualsiasi distribuzione derivata da Debian.
La primissima cosa da tenere in considerazione è che quello che vi farò scaricare è un ambiente di compilazione standard, in cui potrebbero mancare strumenti meno conosciuti. Ma generalmente parlando si tratta comunque di un ambiente di compilazione con cui potrete generare applicazioni scritte in C e C++, ovvero la stragrande maggioranza del software disponibile per GNU/Linux.
Prima di scaricare gli strumenti verificate se avete gli strumenti con questo comando:
gcc
Qualsiasi output diverso da questo
gcc: no input files
Vorrà dire che gcc, ovvero il compilatore di cui avete bisogno, non sarà installato nel vostro sistema, e dovrete installarlo con il seguente comando:
sudo apt-get install build-essential flex bison libtool autoconf automake1.9
C'è poi da tener presente il fatto, molto importante, che nella compilazione è da ritenere fondamentale: le versioni dei software che installate tramite la compilazione potrebbero richiedere versioni di pacchetti e di dipendenze che nei repository Ubuntu ancora non sono comprese. Per questo dovete sempre tenere d'occhio tutte le fasi della compilazione. Ricordate che compilare è come costruire una macchina da zero, avendo solo i componenti: ogni componente deve essere compatibile con tutti gli altri ed ognuno deve essere controllato affinché funzioni al meglio.
Vorrei anche precisare che la compilazione di software è un procedimento in alcuni casi tecnico e delicato che potrà richiedere delle correzioni di volta in volta, a seconda del pacchetto che intendete installare. Ricordate sempre di leggere la documentazione prima di cominciare, specialmente se siete alle prime armi.
yasm è un piccolo assembler che potrà tornarvi utile per compilare dei software particolari, e in ogni caso la sua costruzione e installazione è molto semplice e richiede pochi comandi.
Confermate ogni comando come utente normale. Potrete usare sudo per operazioni che richiederanno consenso da superuser.
(1) Recatevi sul sito http://www.tortall.net/projects/yasm/wiki/Download;
(2) Scaricate (preferibilmente nella vostra directory /home) il file al link "Source.tar.gz";
(3) Entrate nella vostra home (se non ci siete) con cd ~ o cd $HOME;
(4) Estraete il pacchetto con tar -xzf source.tar.gz;
Personalmente vi consiglio di creare una sottocartella "source" o "sorgenti" in cui sarete liberi di fare tutti gli esperimenti che vorrete. Quindi il comando precedente si trasforma in tar -xzf source.tar.gz -C source.
(5) Entrate nella cartella appena creata con cd source/yasm-x.x.x, dove x.x.x è il numero di versione di yasm.
Siete in ciò che d'ora in poi chiameremo "albero dei sorgenti" (in inglese "source tree"), e la cartella in cui siete appena entrati si chiama "radice dei sorgenti" ("source root"). Vedrete un gruppo di file e/o di cartelle, tra cui uno script bash chiamato configure. Esso è il nostro punto di partenza, ma prima di andare avanti è bene leggere la documentazione del software, solitamente contenuta interamente in un file di testo README e le istruzioni di installazione nel file INSTALL nei progetti più piccoli. In quelli più voluminosi (come il kernel Linux o OpenOffice) la documentazione potrà essere situata in una sottocartella distinta, di solito nominata docs o documentation.
Potete poi fare visita alle singole cartelle per vederne i contenuti, o provare a guardare direttamente il codice sorgente leggendo i singoli file con un editor di testo ASCII come jed, nano, vim, gedit, kedit, kate, mousepad, emacs...
Quando avrete finito la visita all'albero dei sorgenti potrete entrare nello specifico evocando una lista di opzioni possibili di configure con il seguente comando dalla radice dei sorgenti:
./configure --help | less
(Il carattere tra --help e less è una pipe.)
Nella stragrande maggioranza dei casi il comando valido sarà quello che ho appena scritto ma, siccome non tutti i sorgenti sono uguali, potranno esistere diversi configuratori possibili (x264, perl, tcl, anche il kernel Linux stesso), con diverse opzioni, o potranno anche non esistere.
La configurazione, infatti, generalmente serve solo a generare i Makefile, ovvero delle liste di "azioni" da far fare a make, che è il manutentore principale di un blocco di sorgenti e richiama i compilatori con una serie di opzioni definite nei Makefile, e quindi nella configurazione.
In alcuni casi configure potrà non esistere del tutto nell'albero dei sorgenti e dovrete quindi andare a cercare direttamente Makefile (esattamente con questo nome), modificarlo affinché assecondi i vostri bisogni e far partire direttamente la compilazione.
Può anche darsi che configure e/o Makefile risiedano in una sottocartella (nel caso di OpenOffice o XviD), e potete cercarli con un semplice
find . -name "configure" -o -name "Makefile"
Se non riuscite a trovare né configure né Makefile da nessuna parte assicuratevi che esista un file chiamato configure.ac o configure.in. In questo caso dovete anteporre alla chiamata di configure il comando
autoconf
Che, in caso di successo, provvederà a crearlo per voi. Riferitevi all'appendice 2, Software di controllo versione, per altre informazioni su autoconf e sulla preconfigurazione.
Se non avrete trovato neppure configure.ac o configure.in rileggete la documentazione, o cercate informazioni nel sito degli sviluppatori del pacchetto che volete compilare.
Stiamo per compilare yasm, e nella lista delle opzioni richiamate dall'evocazione dell'aiuto di configure troviamo una opzione che si chiama --disable-nls.
NLS (Native Language Support) è il supporto di internazionalizzazione di un software. Normalmente è abilitato, se invece viene usata l'opzione --disable-nls esso viene disabilitato e, oltre a risparmiare spazio su disco per il programma si risparmia tempo per la sua compilazione. Personalmente lo trovo superfluo, ciononostante potete lasciarlo attivo (omettendo quindi l'opzione) e se in fase di comfigurazione o compilazione sorgono errori dovuti all'internazionalizzazione dovete disabilitare NLS per forza di cosa.
Assicuratevi quindi di essere nella radice dei sorgenti (o, più precisamente, allo stesso livello di directory di configure) e lanciate questo comando:
./configure --disable-nls
Premete Invio e osservate lo script configure che raccoglie informazioni sull'ambiente operativo e abilita o disabilita alcune opzioni, a seconda che ne trovi disponibili le opzioni o le librerie, o che vengano abilitate o disabilitate da riga di comando. [8] [9] [10] [11] [12]
Se la configurazione avrà avuto successo non si saranno verificati blocchi o errori di dipendenze, e vedrete tra e ultime righe alcune linee che diranno creating percorso/sottocartella/sorgenti/Makefile. In caso di insuccesso vi consiglio di rivedere le opzioni di configure o di verificare che effettivamente il sistema possieda tutti i requisiti necessari per compilare il vostro software. Leggete l'appendice 1, Troubleshooting, per ulteriori informazioni.
La configurazione è andata a buon fine, oppure avete trovato Makefile ma non avete visto configure. È ancora possibile modificare qualcosa nella configurazione dei sorgenti agendo direttamente sui Makefile, ma quasi sempre non è necessario, e se avrete aggiunto parametri alla riga di comando (consultate l'appendice 5, Parametri aggiuntivi, per altre informazioni) saranno stati collocati automaticamente in tutti i Makefile.
Tutto ciò che dovete fare ora è scrivere
make
Confermate con Invio e lasciatelo lavorare.
Vedrete una serie di linee di comando, anche molto complesse (in un terminale da 80 colonne per 25 linee le invocazioni di gcc posso arrivare anche a coprire tutto lo schermo, per un totale di poco meno di 2000 caratteri per ogni invocazione...), che avranno il compito di generare per voi il software, pezzo per pezzo, seguendo le regole dettate nei Makefile sotto la direzione di make. Ed è proprio in questa fase che possono verificarsi gli errori più subdoli e inafferrabili. Fate riferimento all'appendice 1, Troubleshooting, per informazioni in merito.
Ciononostante, se avrete seguito i miei consigli e avrete installato tutti gli strumenti necessari, yasm si compilerà perfettamente e senza errori. Una regola valida, seppur empirica, per capire se la compilazione ha avuto successo è controllare i messaggi di make: se nel suo output non esistono righe che comincino per error o Error è lecito pensare che non vi sia stato alcun errore. Se la compilazione si blocca dovete ritornare indietro e rivedere che cosa può essere andato storto, a cominciare dalla configurazione, seguendo le tracce fornite nell'appendice 1.
Possiamo quindi passare ad installare il software nella locazione di default (/usr/local con tutte le sottocartelle) [13] con questo comando:
sudo make install
Che è l'unico comando che richieda necessariamente sudo per funzionare, se siete utenti normali. Potrete poi richiamare yasm come qualsiasi programma installato, semplicemente digitandone il nome dal prompt di bash.
Ben fatto, avete appena compilato il vostro primo programma!
make install può non essere l'unica opzione disponibile per il comando make. L'uso di default per make è la compilazione, a seconda delle regole scritte nei Makefile. Nello stesso Makefile potrebbero esserci altre regole, tra cui:
uninstall: i file installati dopo la compilazione con make install vengono rimossi. Di solito non vengono rimossi eventuali file di configurazione nelle directory home degli utenti, come nel caso di Mozilla Firefox o aMule. Sappiate che in alcuni sorgenti la regola uninstall non esiste.
clean: l'albero dei sorgenti viene ripulito da tutti i file originati dalla compilazione, ma non dalla configurazione: di seguito sarà necessario ricompilare il software. È consigliato solo in caso di modifiche manuali ai Makefile, o qualora non esistano script configure nell'albero dei sorgenti originario.
distclean: l'albero dei sorgenti viene completamente ripulito, anche da tutti i file generati in fase di configurazione, riportando la radice ad uno "stato neutrale" come era appena dopo l'estrazione dei file dall'archivio, eccetto quando siano state applicate delle patch. È consigliato praticamente sempre: in occasione di aggiornamenti o installazioni di librerie o header, cambi di processore/RAM/scheda madre...
Questi sono i più diffusi. Molti software contengono Makefile con regole personalizzate, ma generalmente le regole install, uninstall, clean e distclean sono le più usate.
yasm è molto semplice da compilare in confronto a molti altri software in circolazione. Una volta compreso il procedimento la compilazione diventa abbastanza naturale, ma richiede un minimo di coscienza e responsabilità, tenendo presente alcuni punti fondamentali.
-- 1 -- Tenete sempre d'occhio le versioni dei software. Molto spesso la fase di configurazione controlla che esistano determinati programmi o header di funzioni, e se si tratta di dipendenze fondamentali devono essere sempre installate. Nel caso di Ubuntu fate sempre riferimento ai pacchetti che hanno un suffisso "-dev" nel nome. Questi pacchetti contengono file di librerie e di header generalmente utilizzati solo nella compilazione, e che nelle installazioni standard da repository non sono necessari. Se il pacchetto è presente ma la sua versione è troppo vecchia, e nei repository Ubuntu ancora non esiste, la compilazione della dipendenza diventa l'unica alternativa.
-- 2 -- Leggete sempre la documentazione dei sorgenti prima di tentare la compilazione. Spesso nella documentazione gli sviluppatori scrivono non solo il procedimento di installazione più adatto per il loro software, ma anche descrizioni e notifiche di aggiornamenti, architetture valide e dettagli tecnici che nel manuale o nel pacchetto precompilato non sono presenti.
-- 3 -- Non tentate di installare librerie di sistema come glibc o libX11, specialmente se siete agli inizi. Avreste altissime probabilità di combinare disastri, rimediabili solamente con una reinstallazione dell'intera distribuzione. Compilate software di sistema soltanto se sapete quello che state facendo. Se volete semplicemente aggiornarli, fatelo con gli strumenti di update in dotazione alla distro in uso.
-- 4 -- Se ricompilate una libreria usata da uno o più programmi, è altamente consigliato ricompilare successivamente il programma o i programmi che la usano per sfruttare le nuove funzioni. In più, alcuni software si rifiutano di funzionare se aggiornate delle librerie tramite compilazione: cercano un numero di versione di libreria che non esiste più, e a questo punto è necessario ricompilare i software sulle nuove librerie.
-- 5 -- Non tentate di ricompilare il kernel Linux con questo procedimento. La configurazione e l'installazione di Linux sono completamente differenti, ed in particolare la sua configurazione è molto più complessa.
-- 6 -- Assicuratevi sempre di avere lo spazio necessario su disco per ospitare il pacchetto compresso, i sorgenti e i software compilati. Vi consiglio di creare una directory "source" o "sorgenti" in cui depositare questi file.
-- 7 -- Alcuni pacchetti compressi di sorgenti potrebbero essere di formato differente dai comuni .tar.gz o .tar.bz2. Di seguito riporto i comandi e i programmi richiesti per scompattare i formati di archivi più comuni.
GNU tar (default, parte di tar): .tar.gz, .tgz, .tar.bz2, .tbz2, .tar
tar -xzf <file>.tar.gz -C <destinazione>
tar -xzf <file>.tgz -C <destinazione>
tar -xjf <file>.tar.bz2 -C <destinazione>
tar -xjf <file>.tbz2 -C <destinazione>
tar -xf <file>.tar -C <destinazione>
Gli archivi creati con tar sono anche chiamati tarball.
UnZip (InfoZip, http://www.info-zip.org): .zip
unzip -q <file>.zip -d <destinazione>
Gunzip (default, parte della suite gzip): .gz
gunzip <file>.gz
È equivalente a
gzip -d <file>.gz
Per default gunzip non conserva l'archivio originale. Dopo l'estrazione troverete l'albero dei sorgenti ma non più l'archivio originario. Per conservare l'archivio dovete farne un backup con un altro nome. Sorgenti compressi solo con gzip sono assai rari e solitamente corrispondono alle patch.
Può capitare che ogni tanto la configurazione o la compilazione di un pacchetto si blocchino. Questi processi sono complessi e implicano che tutta una serie di condizioni siano prima soddisfatte. Il loro troubleshooting copre una varietà di errori così ampia che è impossibile stendere delle "regole fisse" per rimediare a tutti i problemi. Per cui mi limiterò a proporre una serie di errori "standard" che potrebbero fermare la compilazione o la configurazione con le più alte probabilità.
Per tentale di analizzare più o meno a fondo la questione, senza andare troppo nel particolare o senza causare troppa confusione, suddividerò questa appendice in due parti, una dedicata alla configurazione ed una alla compilazione, in cui elencherò una serie di problematiche relative ad ognuna di esse, ed alcuni suggerimenti su come risolverle.
Questa non è una guida, non è completa e non è garantita per tutte le architetture o tutti i software che vorrete compilare. Lascio soltanto un insieme di tracce, e due consigli "supremi" che valgono per ogni cosa che riguardi la compilazione:
Suggerimento: se, nonostante i vostri sforzi, "quel pacchetto" ancora non si vuole compilare, installatelo automaticamente con il packet manager del vostro sistema. Vale per software e librerie.
Lo script di configurazione solitamente vi comunica che cosa non va, molto spesso in modo chiaro. Ciononostante in alcuni software riuscite a capire soltanto in fase di compilazione che la riga di evocazione di configure è sbagliata. Per questo vi suggerisco sempre di studiare il vostro sistema, installare quello che c'è da installare e controllare che le dichiarazioni date a configure corrispondano a verità.
Suggerimento: i procedimenti di configurazione e compilazione possono essere interrotti in qualsiasi momento con Ctrl+C.
Gli errori più comuni in fase di configurazione sono tre:
Gli errori derivanti da opzioni forzate possono avere due conseguenze distinte. La prima, il fallimento della configurazione in seguito a dei test di presenza di header e librerie, e la seconda è il termine della configurazione senza problemi e il fallimento della compilazione perché non c'è stato alcun test di presenza: a causa di questo l'errore viene "ereditato" dalla compilazione.
Nello screenshot riportato ho tentato di configurare faad2, un codec audio, abilitando il plugin per xmms, un programma per leggere flussi audio come MP3 o wav. Ho forzato l'opzione con questa invocazione di configure:
./configure --with-xmms
Soluzioni possibili
Ciò vale per qualsiasi libreria, applicazione o header mancante.
I cosiddetti "errori di dipendenza" possono scaturire da un'opzione forzata o da una dipendenza fondamentale (o requisito fondamentale) insoddisfatta. Nel caso dell'opzione forzata il requisito non è necessario per il funzionamento del programma, ma se è assente ne preclude una o più funzionalità. L'assenza di un requisito fondamentale, che causa l'errore di dipendenza che vedremo adesso, fa invece fallire la configurazione, o la compilazione, e rende obbligatoria l'installazione del pacchetto mancante.
Nell'esempio ho tentato di compilare rtorrent, un client torrent testuale, senza disporre di libsigc++, uno dei suoi requisiti fondamentali. L'invocazione di configure è semplice:
./configure
E questo è il risultato:
L'errore è ben visibile dalla dicitura Package requirements (XXXXXXXXXXX) were not met con il nome del pacchetto mancante tra parentesi.
Soluzioni possibili
L'errore di versione è più subdolo e difficile da scovare e da correggere. Esso è un particolare errore di dipendenza dove non è l'intera libreria a mancare, ma alcune funzioni della libreria stessa. L'errore può manifestarsi quando l'utente aggiorna un programma ma non le sue dipendenze: il programma aggiornato potrebbe dipendere da alcune funzioni non presenti nella vecchia versione della libreria, e gli effetti potrebbero essere imprevedibili, dal mancato caricamento del software al crash del programma stesso.
Nell'esempio ho installato nel sistema la versione 0.4.4 di una libreria che si chiama glitz e si occupa di rendering grafico. cairo è un'altra libreria grafica che genera caratteri a video tramite metodi simili a quelli usati per il rendering di caratteri in documenti PDF o PostScript. La versione di cairo che ho voluto compilare era la 1.8.10, e se ne viene abilitato glitz essa ne richiede la versione 0.5.1 o superiore.
Usando pkg-config e verificando la versione di glitz installata con
$ pkg-config --modversion glitz
L'output è 0.4.4. Ciononostante la configurazione di cairo si ferma alla verifica della versione di glitz:
./configure --enable-glitz
Di primo acchito potrebbe sembrare un semplice errore di dipendenza, ma siccome abbiamo verificato la versione con pkg-config e la richiesta di glitz è la versione 0.5.1, è evidente che dobbiamo agire su glitz.
Soluzioni possibili
Questo tipo di errore potrebbe essere molto comune se installerete librerie da repository e tenterete di compilare software che usano versioni più recenti delle stesse librerie.
In genere, se la configurazione è andata a buon fine, la compilazione farà altrettanto. Ma se in fase di compilazione trovate degli errori rimediare ad essi può essere più difficile che risolvere i problemi della configurazione. Tuttavia la grande maggioranza degli errori "standard" di compilazione dipendono dalla configurazione, e la loro risoluzione corrisponde alla risoluzione dei relativi errori in configurazione. Un solo tipo di errore si differenzia da essi, ed è l'errore del codice sorgente. [14]
Abbiamo quindi queste comuni tipologie:
Come già detto nella parte sulla configurazione, le opzioni forzate possono influire negativamente sulla compilazione se lo script configure non fa test specifici sulla presenza di determinate librerie o programmi. Nel caso delle opzioni forzate, qui ho tentato di compilare glitz con supporto egl, la cui libreria principale, GLES, non era presente sul mio sistema.
Abilitando egl con
./configure --enable-egl
Lo script da per scontato che le librerie GLES siano installate senza fare alcun test di presenza, e ne conferma l'abilitazione dal sommario:
Tuttavia, provando a compilare glitz con egl l'esito del processo è un evidente fallimento:
Scorrendo le righe del processo troviamo la causa:
dove potete distinguere la causa che da il via alla catena di errori:
error: GLES/egl.h: No such file or directory
Che indica l'assenza nel sistema delle librerie GLES. [15]
Soluzioni possibili
Esattamente come le opzioni forzate, gli errori di dipendenza si possono riversare dalla configurazione alla compilazione, e come nella configurazione gli errori di dipendenza possono dipendere da opzioni forzate. Gli effetti di questo problema sono molto più evidenti durante la compilazione.
Una caratteristica evidente che accomuna errori di dipendenze, opzioni forzate e errori di versione in fase di compilazione consiste nel fatto che si risolvono tutte e tre allo stesso modo. Se fate caso all'esempio capirete infatti che nella compilazione lo stesso problema può essere originato da cause diverse. Ho tentato di compilare MPlayer, un software per leggere audio e video, abilitando un codec audio, musepack. Ma, non disponendo di musepack ed essendo lo script di configurazione di MPlayer piuttosto "permissivo" è molto probabile un errore di dipendenza come questo:
(MPC o mpc è un'abbreviazione di musepack, basta fare un breve googling per capirlo. Qui risulta quindi che l'errore riguarda l'assenza di librerie o header musepack.)
Gli esempi di questo tipo sono moltissimi, e come avrete capito errori di dipendenza veri e propri in compilazione non esistono, poiché nel 99% dei casi vengono tutti "smaltiti" e corretti durante le verifiche nella configurazione, se configure è progettato per fare tutti i controlli necessari.
Soluzioni possibili
Questi errori sono molto più rari, ma possono capitare. Succedono quando il codice sorgente contiene degli errori sintattici o lessicali, e causano l'interruzione della compilazione.
Nell'esempio ho volutamente causato l'interruzione della compilazione di MPlayer con questa invocazione:
./configure --enable-werror
Non è esattamente un errore di codice sorgente, ma simula una situazione di errore del codice. La struttura del messaggio d'errore è più o meno la stessa. L'opzione --enable-werror causa l'interruzione della compilazione in presenza di semplici warning e non di errori veri e propri.
(Potete distinguere la riga cc1: warnings being treated as errors tra i messaggi.)
I warning non sono fatali, ma in alcuni software potrebbero essere indispensabili per controllare che vengano compilati correttamente.
Soluzioni possibili
Non tutto è navigabile tramite browser, e la prova sono protocolli che non appartengono né al tipo "http" né "ftp". Alcuni dei protocolli "extra" vengono gestiti dagli sviluppatori attraverso particolari software chiamati "software di controllo versione", o sistemi di "source code management" (gestione di codice sorgente).
Questi protocolli particolari non contengono pagine web, e sono paragonabili a delle directory di rete: contengono cartelle e file, e sono indicizzati in modo che questi file e queste cartelle possano essere scaricati attraverso gli stessi software con cui sono stati depositati.
I tre software di controllo che andremo ad esaminare usano rispettivamente i protocolli CVS, SVN e GIT, ed hanno lo stesso nome. [16] [17] [18] [19] [20] [21]
Homepage: http://www.nongnu.org/cvs
CVS è probabilmente uno degli SCM (Source Code Manager, gestore di codice sorgente) meno intuitivi in circolazione. Esso infatti per un semplice accesso in download richiede nome utente e password, un po' come un accesso FTP. Una riga base consiste in:
cvs -d:pserver:anonymous@server.cvs.org:/directory/cvs/pacchetto login
(e premere Invio alla richiesta della password, ma dipende dal tipo di progetto e da come è mantenuto) che effettua unicamente l'operazione di accesso come utente anonimo, in sola lettura. Per scaricare il software poi bisogna lanciare questa riga:
cvs -z3 -d:pserver:anonymous@server.cvs.org:/directory/cvs/pacchetto co -P modulo
In entrambe le righe server.cvs.org è il server CVS che ospita i sorgenti del software; il percorso dopo @ è il percorso della directory che può essere vista come la radice dei sorgenti, co è l'abbreviazione di checkout, che aggiorna l'albero locale e modulo è il pacchetto da scaricare, specificato nella homepage del progetto.
CVS scaricherà i sorgenti aggiornati dopo aver creato una directory con lo stesso nome del modulo scaricato, senza numero di versione.
Homepage (vecchia): http://subversion.tigris.org
Homepage (nuova): http://subversion.apache.org
Subversion è un SCM un po' più "user-friendly" di CVS. Il processo di reperimento dei sorgenti è analogo, ciononostante non implica login o autenticazioni di sorta. L'invocazione di SVN è molto più semplice:
svn co svn://svn.server.org/branca/progetto
Come CVS, SVN creerà una directory con lo stesso nome del progetto progetto, e ne riverserà all'interno i sorgenti come in una copia locale.
Homepage: http://git-scm.com
Git è un progetto ideato da Linus Torvalds, creato apposta per essere veloce, scalabile e semplice da usare. Esso è largamente utilizzato nella gestione dei sorgenti del kernel Linux, e di software del calibro di X.org, VLC, Qt, Perl e GNOME, o sistemi operativi come Fedora, Debian e Android.
Git è molto più veloce di SVN e CVS, ha la semplicità di utilizzo di SVN e la versatilità di CVS. Supporta la generazione e la sottomissione di patch e la creazione e sottomissione di interi pacchetti.
Si può usare l'opzione clone per scaricare una copia esatta dell'albero dei sorgenti:
git clone git://server.com/git/nomeprog.git
Funziona come gli altri SCM: crea una directory in cui riversa i sorgenti, pronti per essere lavorati.
Le patch sono piccoli frammenti di codice sorgente generate da un programma chiamato diff, e sono come piccoli aggiornamenti al codice del software che avete scaricato. Di solito le trovate sottoforma di archivi dai formati simili a quelli usati per gli archivi dei pacchetti, oppure con estensione .diff o .patch. Fino a che non le applicherete non avranno alcun effetto, e per poterle applicare dovete entrare nella radice dei sorgenti e lanciare il seguente comando:
patch -Np1 < file_patch
Dove file_patch è il file patch scaricato e scompattato [22a], con il suo percorso completo. Oppure potete applicare la patch da qualsiasi punto del filesystem con
patch -Np1 -d directory < patchfile
Con questo comando entra prima nella directory directory ed effettua successivamente tutti gli aggiornamenti. Volendo è possibile effettuare dei backup dei file modificati aggiungendo l'opzione -b:
patch -Nbp1 < patchfile
Oppure
patch -Np1 -b < patchfile
I backup avranno estensioni .orig; se vorrete rimuovere tutti i nuovi file e tornare alle versioni precedenti potete lanciare questa riga di comando:
Esso cercherà nell'intero albero dei sorgenti tutti i file che hanno estensione .orig e la rimuoverà. [22]
Infine, ricordate di tenere sempre da parte l'archivio scaricato: se fate disastri non rimediabili potete sempre rimuovere completamente l'albero dei sorgenti, estrarre nuovamente il pacchetto e ricominciare daccapo [23].
Distinguiamo prima di tutto gli applicativi in due grandi categorie, a seconda di come viene effettuato il linking del programma alle librerie di cui ha bisogno:
I programmi statically linked (collegati staticamente) vengono generati includendo nel proprio spazio di memoria individuale tutte le funzioni necessarie per il loro corretto funzionamento. Viceversa, i software collegati dinamicamente (dinamically linked) hanno dei riferimenti a funzioni che sono contenute in librerie esterne. Di conseguenza, essi hanno bisogno di queste librerie per funzionare.
Ora è importante comprendere che i programmi dinamically linked non possono chiamare se stessi, poiché senza le librerie necessarie caricate in memoria non possono funzionare. In GNU/Linux ci viene in aiuto un programma molto speciale, chiamato dynamic loader (o dynamic linker, o runtime linker). Il suo nome può variare tra le diverse architetture, ma solitamente viene installato come ld.so o ld-linux.so.2.
Esso è il runtime linker, ovvero un piccolo programma che si occupa di rintracciare le librerie condivise usate da un'applicazione. Il processo funziona più o meno così:
-> Il programma invocato passa il controllo al dynamic linker con una lista di librerie da caricare in memoria;
-> Il dynamic linker cerca e carica le librerie richieste;
-> Il dynamic linker restituisce il controllo all'applicazione chiamante, insieme agli indirizzi di memoria in cui le librerie sono state caricate;
-> L'applicazione viene eseguita e fa riferimento alle zone di memoria comunicate dal loader per le funzioni.
Il dynamic linker può anche essere chiamato "program interpreter" e per vedere questo parametro basta leggere i contenuti di un'applicazione tramite readelf. Provate a digitare
readelf -l /bin/bash
La sezione in cui è nominato il program interpreter è denominata Program Headers:
È possibile anche vedere tutte le librerie di cui un programma ha bisogno con ldd:
ldd /bin/bash
Una delle linee contiene proprio ld-linux.so.2, che è il nostro dynamic linker.
In fase di invocazione dello script configure è possibile aggiungere dei parametri prima del nome dello script, e saranno ritenuti validi solo per quella invocazione. Quelli che io chiamo "parametri aggiuntivi" non sono altro che variabili di ambiente molto particolari, che influiscono sulla configurazione dei sorgenti, e quindi dei Makefile risultanti.
Per ogni software ne potete vedere una lista invocando
./configure --help | less
Scorrendo l'output fino alla fine molti software hanno una lista di variabili che possono essere influenzate con alcuni parametri speciali [24]. Modificare queste variabili può essere molto utile per localizzare applicazioni, librerie e header installati in directory non standard o per fornire a gcc opzioni non standard. I parametri dichiarati verranno ereditati da tutti i Makefile generati da configure.
I parametri sono dichiarati come qualsiasi altra variabile Bash, prima della chiamata completa di configure:
VARIABILE1="valore" VARIABILE2="valore" ... VARIABILEn="valore" ./configure <opzioni>
Il valore può essere incluso tra doppi apici, obbligatoriamente quando contenga degli spazi.
Quando l'ambiente di compilazione sia correttamente installato (ovvero il 99,9% delle volte e in tutte le distribuzioni standard) generalmente le variabili modificabili si limitano a quelle della lista seguente, con alcuni tra i loro valori possibili:
CFLAGS: Fornisce alle invocazioni di gcc opzioni aggiuntive. I più usati riguardano:
LDFLAGS: aggiunge uno o più percorsi in cui risiedono librerie installate in locazioni diverse da quelle standard, in genere /lib, /usr/lib o /usr/local/lib. Per ogni percorso va anteposta l'opzione -L, senza lasciare spazi tra essi.
CPPFLAGS: è molto simile all'uso di LDFLAGS, ma:
Header standard in distribuzioni GNU/Linux sono sempre installati in /usr/include.
Abilito 3 task in parallelo (ho un dual core), attivo tutti i warning standard, ottimizzazione per dimensione e compilazione tramite pipe, in una configurazione in cui stabilisco che la directory base per l'installazione del software è /usr:
CFLAGS="-j3 -Os -Wall -pipe" ./configure --prefix=/usr
Tutte queste cose sono generalmente non fattibili solamente tramite script di configurazione, e CFLAGS è un buon modo per precisare alcune opzioni "non standard".
Una libreria fondamentale del software che sto configurando è stata installata in /opt/lib.
LDFLAGS="-L/opt/lib" ./configure
Librerie fondamentali sono installate in /opt/lib e /tmp/lib.
LDFLAGS="-L/opt/lib -L/tmp/lib" ./configure
Header non standard sono installati in /tmp/include e /opt/include.
CPPFLAGS="-I/tmp/include -I/opt/include" ./configure
Come si può dedurre, più variabili possono essere combinate. Prendiamo gli esempi precedenti e mettiamoli insieme in questo esempio: abbiamo librerie non standard in /tmp/lib e /opt/lib, header non standard in /tmp/include e /opt/include e abilitiamo le opzioni di gcc che abbiamo visto:
CFLAGS="-j3 -Os -pipe -Wall" LDFLAGS="-L/opt/lib -L/tmp/lib" CPPFLAGS="-I/tmp/include -I/opt/include" ./configure
L'importante è ricordare, per non sbagliare, di lasciare almeno uno spazio tra una variabile e l'altra e mettere tutti i valori tra doppi apici.
[1]: Un linguaggio portabile è un tipo di linguaggio di programmazione che può essere compilato per una qualsiasi architettura per cui esista il compilatore relativo, con poche o nessuna modifica.
[2]: L'architettura hardware (o "architettura") è la struttura fisica di una CPU, comprendente il set di istruzioni, che ne caratterizza il funzionamento e la gestione delle istruzioni macchina.
[3]: Esistono anche le "ottimizzazioni generiche", che sono metodi di compilazione che renda compatibile un programma con processori diversi, seppur della stessa famiglia come x86, che comprende AMD e Intel. Il loro set di istruzioni non è identico, e tra loro differiscono per alcune istruzioni macchina caratteristiche.
[4]: Tra i primi, il compilatore per FORTRAN nel 1957 ad opera di IBM, usato ancora oggi, e quello per COBOL nel 1960.
[5]: Alcuni compilatori usano uno "stadio zero", chiamato ricostruzione di linea, che serve a correggere i separatori (spazio, tab, newline) in separatori "standard", quando ad esempio vengano usati molti spazi per rappresentarne uno solo.
[6]: Gli "header" sono file particolari che contengono funzioni o definizioni altrimenti non incluse nel codice sorgente appena scritto, e che possono essere riutilizzati più volte da molti file. Durante il preprocessing l'intero contenuto degli header viene copiato all'interno del file sorgente in cui sono dichiarati.
[7]: Differenze tra preprocessing e linking
Probabilmente vi starete chiedendo "Ma il preprocessing e il linking sono la stessa cosa? "
La risposta è no. Il preprocessing sostituisce dei riferimenti a header (prendendo come esempio il linguaggio C) con l'intero contenuti dell'header, nello stesso linguaggio, pronto per essere compilato.
Il linking, invece, rende disponibili le funzioni utilizzate nel codice sorgente, memorizzate in librerie precompilate (i file *.so in Linux e le famose *.dll in Windows) i cui contenuti sono condivisi ed accessibili ai programmi che ne fanno richiesta.
Inoltre nel preprocessing è facile capire che le istruzioni degli header vengono incorporate direttamente nell'eseguibile finale, e ne occuperanno lo stesso spazio in memoria; viceversa, per applicazioni collegate dinamicamente, il linker carica le librerie solo quando servono, e occupano porzioni di memoria separate dal codice del programma compilato.
[7a]: In linguaggi come il C ci possono essere fasi ulteriori, come il linking al termine dell'assembling, che collega le funzioni richiamate dal programma con le librerie condivise installate nel sistema. Ecco perché ogni tanto, quando installate un software da un reopsitory Ubuntu, non vi installa solo quel software, ma anche un insieme di librerie che contengono delle funzioni senza le quali il programma non può girare.
[8]: Le opzioni abilitate o disabilitate da riga di comando prevalgono sulle funzioni o librerie sondate.
[9]: In alcuni casi forzare un'opzione quando non ne esista la libreria nel sistema equivale a far fallire la configurazione. Vedere Troubleshooting.
[10]: Per rimediare a quest'ultimo problema basta rimuovere le opzioni forzate, o installare le librerie o i file di funzione rispettivi. Vedere Troubleshooting.
[11]: Generalmente è una buona idea evocare ./configure senza alcuna opzione per fargli sondare il sistema senza influenzarlo. Aggiungete le opzioni solo se sapete a che cosa vi servono e che cosa state facendo.
[12]: In fase di configurazione è possibile modificare i valori di alcuni parametri fornendoli all'inizio della riga di comando. Tali parametri verranno poi presi da configure e aggiunti ai valori di default nei Makefile. Fate riferimento all'appendice Parametri aggiuntivi per ulteriori informazioni.
[13]: Le locazioni di default, come quasi tutte le altre locazioni, compresi i percorsi di installazione dei file di configurazione (/etc) e delle librerie (/lib) sono modificabili in fase di configurazione, ma nel caso di piccoli software come yasm questi aspetti sono irrilevanti.
[14]: Esistono poi delle tipologie di errore di compilazione e che possono dipendere solamente dal compilatore, ma sono generalmente molto rare, e se possono risolvere aggiornando il compilatore o le librerie di sistema, il che solitamente corrisponde a fare un aggiornamento dell'intero sistema operativo. Possono anche capitare quando vi sia un divario di versione eccessivo tra il software da compilare ed il compilatore, come nel caso in cui si tenti di compilare un software del 2010 con una versione di gcc del 2005, o viceversa.
[15]: L'errore di assenza di file potrebbe derivare anche dal fatto che le librerie sono state installate nel posto sbagliato; di conseguenza il processo di compilazione non riesce a localizzare i file necessari nelle cartelle di default. Per ovviare a questo problema può venirvi in aiuto l'appendice Parametri aggiuntivi, oppure reinstallare le librerie al posto giusto. Ciò vale anche per la configurazione.
[16]: Le righe di comando qui esposte potranno cambiare di progetto in progetto, e le invocazioni corrette sono documentate nei siti ufficiali dei software che volete compilare.
[17]: Molte volte i progetti scaricati tramite SCM non hanno script configure, ed è necessario partire dalla configurazione automatica con autoconf, come descritto in questa pagina.
[18]: Per reperire uno degli SCM potete invocare le seguenti linee di apt-get, per Ubuntu o Debian:
sudo apt-get install subversion
sudo apt-get install cvs
sudo apt-get install git
[19]: I software scaricati via SCM vengono aggiornati più frequentemente di quelli scaricati via browser dai siti ufficiali, e mano a mano che il codice "cresce" potrebbero spuntare nuovi bug, o la compilazione potrebbe avere dei problemi. È importante che ricordiate che ogni nuova funzionalità è da considerare sperimentale fino a che gli sviluppatori non dicono il contrario.
[20]: Gli aggiornamenti via SCM non vengono incorporati nelle tarball o nelle nuove versioni rilasciate finchè gli sviluppatori non le giudicano stabili ed adatte all'utilizzo quotidiano.
[21]: Quando possibile, usate sempre le tarball scaricate via browser sul sito ufficiale degli sviluppatori, anziché un controllo di versione. Usate gli SCM solo se sapete quello che fate, o se volete controllare che una versione SCM sia stata corretta da un eventuale bug presente nell'ultima versione ufficiale, o se cercate una funzionalità che ancora non è presente nella versione ufficiale.
[22]: Non è un buon script perché così rimuove la stringa intera, dovunque sia sul percorso e nel nome del file, anche se non è parte dell'estensione. Per fare un buon lavoro bisogna usare dirname abbinato a basename per estrarre il nome senza suffisso o percorso.
Così facendo vengono estratti solo i suffissi, senza pericolo che sed cancelli stringhe dove non ne deve cancellare.
[22a]: Se volete partire direttamente dalla patch compressa e e l'archivio è gzip potete usare questo comando:
patch -Np1 < zcat file
Oppure
patch -Np1 < gzip -cd file
[23]: make clean e make distclean non riportano l'albero dei sorgenti come prima dell'applicazione delle patch. Se volete eliminare le modifiche fatte con patch dovete prima fare un backup dei sorgenti modificati e poi usare uno degli script riportati.
[24]: less è un tipo di software chiamato pager. Usate le frecce o PgUp e PgDown per navigare nel testo paginato, usate Q o Ctrl+C per uscire da less.
MegaLab.it rispetta la tua privacy. Per esercitare i tuoi diritti scrivi a: privacy@megalab.it .
Copyright 2008 MegaLab.it - Tutti i diritti sono riservati