Makefile per Java

gnu make Quanti di voi programmatori Java hanno avuto problemi per la compilazione di programmi? librerie fuori da $CLASSPATH, parametri chilometrici da passare al compilatore Java. Anche usando ambienti di sviluppo per Java la situazione non migliora di molto. Spendiamo molto tempo a settare paramteri e ogni volta che lanciamo la compilazione manca sempre qualcosa!

Potete evitare tutti questi problemi semplicemente organizzando i sorgenti Java e usando un Makefile scritto ad hoc.

Cos’è un Makefile

Un makefile è un file che descrive i passi da efftuare per arrivare ai file che vorremmo ottenere e tutto l’albero delle sue dipendenze. È molto usato nelle piattaforme basate su Unix e può essere utilizzato per compilare ogni tipo di programma. L’utility make interpreta i makefile e compila il codice sorgente per ottenere programmi eseguibili e librerie.

Vantaggi nell’uso di make

Molti di voi saranno scettici nell’utilizzo di un makefile per ogni progetto Java per la semplice ragione che il makefile richiede di specificare tutte le dipendenze tra i files, che sicuramente cambia da progetto a progetto. Quindi dovremmo scrivere un makefile diverso per ogni nostro progetto. Questo limite può essere superato scrivendo un makefile generico e in grado di calcolarsi tutte le dipendenze per il progetto da compilare in modo da utilizzare lo stesso script per tutti i progetti senza bisogno di moficare niente all’interno del makefile.

Un vantaggio sicuramente non trascurabile è la capacità di make di compilare solo il necessario. Evita quindi di compiare ciò che è già compilato e non è stato modificato con un risparmio notevole di tempo.

Con un solo makefile si riesce a compilare tutti i sorgenti del progetto.

Come usarlo

Il makefile che ho scritto per compilare progetti Java può può essere utilizzato senza alcuna modifica per progetti che hanno una certa struttura di directory. I sorgenti Java devono avere una directory per i .java, una per i file .class, e una per le librerie jar.
Come ad esempio:

|-- src
|  `-- main
|      |-- java
|      |  `-- com
|      |      `-- example
|      |          |-- ExampleA.java
|      |          |-- ExampleB.java
|      |          `-- ExampleC.java
|      `-- resources
|          `-- Res.java
|-- classes
|-- libs
|   |-- general
|   |   |-- DataBase
|   |   |   |-- DB1.jar
|   |   |   `-- DB2.jar
|   `-- Logging
|       `-- log.jar
|-- jars

Per usarlo quindi basta scrivere il codice quì sotto nel file Makefile nella directory root del progetto, verificare che il nome delle directory sia quello specificato nel file (classes per i .class; src per i .java e libs per i jar) e lanciare il comando
make
questo è tutto. Se vogliamo anche un jar contenente tutto il progetto basta lanciare
make && make jar

Makefile

#
# Makefile
# author: Danilo Abbasciano
#

# set here the directories for classes and sources
CLASS_DIR = classes
SOURCE_DIR = src
JARS_DIR = jars

SUBDIRS := $(shell find $(SOURCE_DIR) -name "*.java" -printf %h\\n | sort | uniq | sed 's/$(SOURCE_DIR)\/\?//')

JAVA_HOME=/usr/local/java/bin

# compiler field
#JDEBUGFLAGS = -g -deprecation
#JDEBUGFLAGS = -O -depend -nowarn
JCC = $(JAVA_HOME)/javac

.PHONY: clean classes compile jar

all:
	@@for p in $(SUBDIRS); do \
		echo 'compile ' $$p; \
		export SUBDIR=$$p; \
		make --no-print-directory compile; \
	done

clean:
	-rm -f *~ *.class *.bak $(foreach dir,$(SUBDIRS),$(CLASS_DIR)/$(dir)/*.class)


LOCAL_CLASS_DIR=$(CLASS_DIR)/$(SUBDIR)
LOCAL_SOURCE_DIR=$(SOURCE_DIR)/$(SUBDIR)

jar:
	cd $(CLASS_DIR); jar -cf ../$(JARS_DIR)/project.jar *

compile: classes

# create target class dir if not present.
$(LOCAL_CLASS_DIR):
	mkdir -p $(LOCAL_CLASS_DIR)


# new rule for java
.SUFFIXES:
.SUFFIXES: .java .class


# magical command that tells make to find class files in another dir
vpath %.class $(LOCAL_CLASS_DIR)
vpath %.java $(LOCAL_SOURCE_DIR)

# classpath; all in ./jars and ./libs
LIBCLASSPATH:=$(CLASS_DIR):$(SOURCE_DIR):$(subst .jar ,.jar:,$(wildcard libs/*.jar libs/*/*.jar libs/*/*/*.jar jars/*.jar))

# compil command
.java.class:
	CLASSPATH=$(LIBCLASSPATH) $(JCC) -nowarn -d $(CLASS_DIR) $(JDEBUGFLAGS) $<

# find out target files
PATHFILES = $(wildcard $(LOCAL_SOURCE_DIR)/*.java)
FILES = $(subst $(LOCAL_SOURCE_DIR)/,,$(PATHFILES))
classes: $(FILES:.java=.class)

Buona compilazione.

Lascia un commento