Unix e' un ottimo ambiente per la produzione di programmi. Presenta infatti un numero notevole di strumenti rivolti ai programmatori.
La stessa filosofia di Unix, rispetto a quella di altri sistemi operativi o ambienti, e' rivolta all'apertura ed alla documentazione.
In questo breve documento vengono riportati, quale "assaggio" i principali strumenti utili per la programmazione in ambiente Unix.
Un ambiente applicativo complesso e' composto da diversi moduli. La separazione in moduli consente la creazione di gruppi di lavoro, lo sviluppo/test/manutenzione di moduli separati, ...
Per compilare un programma e' necessario che le tutte le versioni di ogni singolo modulo siano aggiornate.
Questo puo' essere ottenuto compilando ogni volta tutti i file sorgente nell'ordine corretto. E' sufficiente scrivere uno schell script che contiente tutti i comandi di compilazione. Tuttavia questo porta ad un dispendio di tempo notevole.
E' opportuno ricompilare invece i soli moduli che hanno subito modifiche. Naturalmente questo deve tenere conto delle date di modifica di ogni singolo file e delle relazioni presenti tra i singoli moduli.
A tale scopo si utilizza il programma make (in realta' il make e' utilizzabile anche per altri scopi ma l'utilizzo principale e' quale supporto alla compilazione di programmi).
Facciamo un semplice esempio commentando i diversi costrutti del make.
# Esempio di file di make
all: figlio1 figlio2
figlio1: familia
genera familia > figlio1
figlio2: familia
genera familia > figlio2
familia: padre madre
sposa padre madre > familia
La prima riga contiene un commento.
La seconda riga (non vuota) contiene la dichiarazione una dipendenza. Indica che "all" dipende da "figlio1" e da "figlio". Poiche si tratta della prima dipendenza presente nel file, se il make viene lanciato senza parametri sara' questo l'obiettivo del make. Quindi il make cerchera' di aggiornare "all" basandosi su tutte le dipendenze presenti.
Le altre righe sono analoghe.
La terza riga contiene una ulteriore dipendenza. "figlio1" dipende da "famiglia"; per effettuare l'allineamento deve essere lanciato il programma "genera familia -o figlio1" (notare che la sintassi richiede un tab come prefisso).
Se sono presenti "padre" e "madre" il lancio di make generera' dapprima "familia" e quindi i due "figli".
Se si modifica un prerequisto (eg. touch padre) al lancio di make verra' generata una nuova "familia" e nuovi "figli".
Se si uccide un figlio (eg. rm figlio1) al lancio di make verra' ricreato.
Se si lancia make familia verranno eseguiti i soli passi necessari per ricreare la familia.
Il make aggiorna quindi i vari componenti a seconda del livello di aggiornamento presente.
La sintassi del make permette inoltre la definizione di variabili e l'indicazione delle dipendenze tra suffissi di file.
E' tutto qui. Semplice vero?
Nel seguente esempio (piu' serio del precedente) vengono specificate in modo esplicito le dipendenze tra i moduli di un programma e viene richiamata una libreria:
pgm: a.o b.o $(LINK.c) -o $@ a.o b.o -lddd a.o: incl.h a.c cc -c a.c b.o: incl.h b.c cc -c b.c
Nel seguente esempio vengono utilizzate le regole implicite:
pgm: a.o b.o
cc a.o b.o -o pgm -lddd
a.o b.o: incl.h
Per il controllo e la gestione delle release software sono disponibili diversi strumenti su Unix. Tra quelli di base il piu' diffuso e' l'SCCS (Source Code Control System).
Il make e' completamente integrato con l'SCCS, ne riconosce infatti i diversi comandi ed e' in grado di estrarre i moduli corretti per la compilazione.
Lavori di medie/grandi dimensioni svolti da gruppi di programmatori DEBBONO utilizzare un software per la gestione delle revisioni.
Perdere un poco di tempo (un paio d'ore) per impostare l'utilizzo dell'SCCS e' spesso opportuno ma dimenticato.
Il programma che effettua la compilazione e' il cc. Per effettuare verifiche sintattiche e semantiche dei programmi e' utilizzabile il programma lint. Il linking degli oggetti e' effettuato dal programma ld. Generalmente viene richiamato in automatico da cc.
Per la creazione delle librerie si utilizzano i comandi di ar (statiche) o cc -g (dinamiche).
Per controllare i moduli presenti in un eseguibile e' utilizzabile il comando nm. Le informazioni sui moduli componenti un eseguibile possono essere eliminate (per diminuire l'occupazione di un eseguibile o per ragioni di sicurezza) con il comando strip.
Per controllare le dipendenze dinamiche da librerie shared e' utilizzabile il comando ldd. Il percorso di ricerca e' guidato dal settaggio della variabile LD_LIBRARY_PATH (LIBPATH su AIX).
Spesso viene utilizzato il comando strings per trovare tutte le stringhe di caratteri significative contenute in un eseguibile.
Il programma adb consente il debug (e' un assembler debugger) dei programmi; spesso e' disponibile anche l'sdb che e' la versione simbolica. L'utilizzo non e' certo immediato, e' quindi utilizzato soprattutto:
Il comando truss, disponibile su alcuni sistemi, consente di seguire tutti i richiami a system call effettuati da un programma. E' quindi potentissimo per determinare il comportamento di programma di cui non si conosce il funzionamento.