make

Si tratta di una utility che permette di compilare automaticamente una serie di files sorgente per creare un eseguibile, puo' anche svolgere compiti ausiliari nello sviluppo e mantenimento di software.

Cosa fa make e' definito da un file di configurazione : Makefile
Una volta preparato il file basta dare il comando make per eseguire tutto quanto indicato nel makefile.
il file Makefile contiene:

Il make analizza le dipendenze fra l'eseguibile ed i sorgenti tenendo conto della data di ultimo accesso dei files, e ricrea un target solo quando e' meno recente dei files da cui dipende.

Targets possono dipendere da altri targets, si viene quindi a creare una catena di dipendenze e di azioni, il make la analizza e decide quali azioni compiere, in modo da rendere tutti i targets piu' recenti delle "dependencies".

Di utility make ne esistono diverse versioni, con piu' o meno piccole differenze

Esempio di makefile



# -----------------------------------------------------------
#  Esempio di Makefile, semplice: compila un
#  programma fortran, il cui source e' in una serie di files
#  questi files hanno istruzioni include, con cui includono
#  files con commons di uso comune
#  
# -----------------------------------------------------------
#
#  

Il carattere: # serve per introdurre commenti, righe bianche vengono ignorate.
La definizione dei target avviene con la sintassi che vediamo sotto; il primo target e' quello di default.
In questo caso e' def , dipende da barre , che e' un altro target, dopo il ; c'e' l'azione da svolgere, che scrive solo un messaggio a terminale
 
#  definisco il default target 

def : barre ; @echo " =======>  OK, tutto compilato. " 

#  alias del target barre 

all :  barre 

Ecco una serie di definizioni di variabili che saranno usate poi, si noti come il valore della variabile possa contenere spazi bianchi.
Qui vengono definiti i flag usati per il compilatore ed il nome del compilatore

 
#          definizione delle variabili  
 
FC = g77 
FFLAGS =  -w -fvxt -finit-local-zero -O3
LDFLAGS = 
#
#                    ------- Flag del compilatore usati : --------
#
# -g -O0 : per il debug , altrimenti con -O3 ottimizzo la compilazione
# -w  : warnings
# -finit-local-zero : inizializza a 0 le variabili del fortran
# -fvxt : interpreta certi costrutti coerentemente col  fortran del vax	
#
# -----------------------------------------
#
#        -------  Variabili che dicono dove sono le librerie che uso----

Definisco quindi una serie di variabili per identificare le librerie che uso.
L'espressione $(variabile) sostituisce alla variabile il suo valore.
cernlib e' una procedura, in /cern/new/bin , che restituisce i nomi delle librerie, col giusto path, separate da spazi, aggiungendo i nomi di librerie ausiliarie.


 CERN = /cern
 CERN_LEVEL = new
 CERN_ROOT = $(CERN)/$(CERN_LEVEL)
 CERN_BIN = $(CERN_ROOT)/bin
 CERN_LIBS =  $(CERN_BIN)/cernlib geant321 pawlib graflib/Motif packlib mathlib 


Le variabili seguenti contengono i nomi dei files col source.
Notare l'uso di \ prima del carriage return, in modo che sia come se tutti i nomi fossero su una stessa linea


#
#    ------------------------------------------------------------
#    ------  files con il source del programma       -------
#    ------------------------------------------------------------
#

INCLUSI = parametri.inc \
          letti.inc  \
          canali.inc  \
	  fisica.inc   
              
FORTRAN = barre.f \
          uginit.f \
          uhinit.f \
	  sorgenti.f \
          ugeom.f \
	  gukine.f\
          gustep.f \
          guout.f \
	  uglast.f \
	  fisica.f 

OUTPUT =  summary.out  \
          phlist.out \
          ehist.out  \
	  fis.out     


OBJECTS =  $(FORTRAN:.f=.o)   # nella variabile FORTRAN sostituisce .f con .o

Seguono regole di dipendenza, con le righe seguenti si specifica che i files fortran dipendono dagli include files, l'azione e' un touch, cioe' si rendono i files fortran piu' recenti degli objects, in modo che il make nel considerare le dipendenze dei files object sia portato a ricompilarli.

Notare come l'azione possa essere preceduta da ; oppure essere nella linea seguente, preceduta da un tab.

 
#     -----------------------------------------------------
#     --------         regole di dipendenza      ----------
#     -----------------------------------------------------
#    
#
#         I files fortran dipendono dagli include files e dal file Makefile
#         l'azione e' un touch, cioe' rendo i files fortran piu'
#         recenti degli objects, il make quindi li ricompilera'
#
$(FORTRAN) : $(INCLUSI)  
	touch $(FORTRAN) 
#

Un approccio migliore, siccome non tutti i file fortran includono tutti i files *.inc, e' di fare una lista esplicita di tutte le dipendenze.
$@ indica i targets

#barre.f :  B_ugeo_cm.txt B_comm_edep.inc common_geant.dat common_geo_cm.dat common_user.dat \
#             B_init.f B_kine.f B_step.f B_out.f B_last.f  
#	touch $@ 
#
#B_init.f    : B_comm_edep.inc  B_comm_tape.inc common_user.dat common_geo_cm.dat B_comm_sorgente.inc B_histset.f B_fits.f ; touch $@ 
#B_histset.f : B_comm_edep.inc                  ; touch $@ 
#B_kine.f    : B_comm_edep.inc  B_comm_tape.inc  B_comm_sorgente.inc; touch $@ 
#B_out.f     : B_comm_edep.inc  B_comm_tape.inc B_fits.f ; touch $@
#


%.o : %.f ; $(FC) $(FFLAGS) -c $< -o $@


Nell'ultima riga si vede l'uso di una sintassi compatta : l'istruzione dice che tutti i files che finiscono con .o dipendono dai corrispondenti , che finiscono in .f, l'azione e' compilarli col compilatore nella variabile FC , utilizzando i flags in FFLAGS.
$< indica le dipendenze , $@ i targets
L'espressione e' equivalente a:
B_kine.o : B_kine.f ; g77 -w -fvxt -finit-local-zero -c B_kine.f -o B_kine.o
ripetuta per tutti i files object

Di seguito si indica come fare il target barre, cioe' l'eseguibile finale.
il file barre dipende dai files objects, la regola e' di usare g77, LDFLAGS sono eventuali flag per il linker, CERN_LIBS contiene i nomi delle librerie
Ci sono poi indicazioni per altri targets, as esempio posso eliminare i files object con:
make clear
eliminare i files di output con :
make outdel


barre : $(OBJECTS)
	$(FC) $(FFLAGS)  $(LDFLAGS)  -o $@ $(OBJECTS) $(CERN_LIBS) 

#  

reset : ; touch   $(INCLUSI)

#  this target deletes object files 

clear : ; rm *.o   

#  this target deletes output files

outdel : ; rm $(OUTPUT)