1

Any idea why this makefile would re-compile (unnessesarily so) each .class file? Also, I think it is relevant, the java files import the files above them. I've tried this 4 times and wasted hours to get this to work, instead of working on the actual code of my project so any help would be really appreciated.

most recent attempt:

#########################################################################
#                                   #
# http://ieng6.ucsd.edu/~cs131f/makenotes.html              #
#                                   #
#########################################################################
JFLAGS      = -g -d
JC      = javac
.SUFFIXES   : .java .class
SRCDIR      = simpella
OUTDIR      = simpella/out

.java.class:
    $(JC) $(JFLAGS) $(OUTDIR) $*.java

#########################################################################
#
# example run: javac -g -d simpella/out simpella/simpella.java
#
#########################################################################

CLASSES = \
    $(SRCDIR)/Util.java \
    $(SRCDIR)/Converters.java \
    $(SRCDIR)/Connection.java \
    $(SRCDIR)/Simpella.java

default: classes

classes: $(CLASSES:.java=.class)

#########################################################################
clean:
    $(RM) -v simpella/out/simpella/*.class
    @- echo "Cleaned"

Attempt 3...:

#########################################################################
#                                   #
# http://ieng6.ucsd.edu/~cs131f/makenotes.html              #
#                                   #
#########################################################################
JFLAGS      = -g -d
JC      = javac
.SUFFIXES   : .java .class
SRCDIR      = simpella
OUTDIR      = simpella/out

.java.class:
    $(JC) $(JFLAGS) $(OUTDIR) $*.java

#########################################################################
#
# example run: javac -g -d simpella/out simpella/simpella.java
#
#########################################################################

all: Simpella.java


Util.java:
    Util.java=Util.java
Converters.java:
    Converters.java=Converters.class
Connection.java: Converters.java Util.java
    Connection.java=Connection.class
Simpella.java: Connection.java Converters.java Util.java
    Simpella.java=Simpella.class
    @- echo "Done Compiling!!"



#########################################################################
clean:
    $(RM) -v simpella/out/simpella/*.class
    @- echo "Cleaned"

Attempt 2...:

#########################################################################
#                                   #
# http://ieng6.ucsd.edu/~cs131f/makenotes.html              #
#                                   #
#########################################################################
JFLAGS      = -g -d
JC      = javac
.SUFFIXES   : .java .class
SRCDIR      = simpella
OUTDIR      = simpella/out

.java.class:
    $(JC) $(JFLAGS) $*.java

#########################################################################

#CLASSES = Simpella.java Connection.java Converters.java Util.java

all: Simpella.java


#example run: javac -g -d simpella/out simpella/simpella.java

Util.java:
    $(JC) $(JFLAGS) $(OUTDIR) $(SRCDIR)/Util.java
Converters.java:
    $(JC) $(JFLAGS) $(OUTDIR) $(SRCDIR)/Converters.java
Connection.java: 
    $(JC) $(JFLAGS) $(OUTDIR) $(SRCDIR)/Connection.java
Simpella.java: Util.java Connection.java Converters.java
    $(JC) $(JFLAGS) $(OUTDIR) $(SRCDIR)/Simpella.java
    @- echo "Done Compiling!!"

#########################################################################
clean:
    $(RM) -v simpella/out/simpella/*.class
    @- echo "Cleaned"

Attempt 1...:

JFLAGS = -g -d
JC = javac
.SUFFIXES: .java .class
.java.class:
    $(JC) $(JFLAGS) $*.java

CLASSES = \
    simpella\Util.java \
    simpella\Converters.java \
    simpella\Connection.java \
    simpella\simpella.java 


default: classes

classes: $(CLASSES:.java=.class)
    @- echo "Done Compiling!!"

clean:
    $(RM) *.class
Gagan Singh
  • 988
  • 12
  • 22

2 Answers2

2

Creating well defined makefile for java is a complex task because a) source and class files are in different directories, b) those directories are nested, and c) there is no one-to-one mapping from source and class files. Then, even well-done makefile would not achieve good performance because make utility spawns separate process to compile each java file. On the other hand, java compiler itself works fast, has embedded dependence checking, and is able to compile hundreds of java files in single process run. As a result, make utility is not used to compile java files. For small and medium projects, use java compiler and pass all source files to it. For large projects, use Ant, Gradle or other java-aware build tool.

EDIT: to employ javac dependency checking, run it this way:

  javac -d ${OUTDIR} -cp ${OUTDIR} -sourcepath ${SRCDIR} ${MAIN_JAVA_FILE_NAME}

That is, pass only the name of the main java file, point where other source files reside, point where class files reside twice: where to store and where to check for existence. The ${SRCDIR} should contatin java files according to their package structure.

Alexei Kaigorodov
  • 13,189
  • 1
  • 21
  • 38
0

The problem is that you should not use "*.java" as a target, but should use it as a dependency for each related "*.class". Otherwise, like your "Attempt 2", when the target is a file with no dependency, it will not be rebuild, and will always shout "up to date".

The main idea is to tell your make to understand the dependencies clearly. Below is a test case to make it simpler to build class files, it will be ok for a small project (class Test will use method from Ref, therefore it relies on Ref.class and we should manually add this). I hope better answers on this issue!!! But for large project, hmm, why not try ant instead?

Makefile:

OBJ = ./obj/
SRC = ./src/
JARS = $(wildcard lib/*.jar)
LIB = .:$(OBJ):$(shell echo '$(JARS)' | sed 's/jar /jar:/g')

define make-target
    @echo + cc $<
    @javac -classpath $(LIB) $< -d $(OBJ) $*
endef

all: always ./obj/Ref.class ./obj/Test.class

./obj/Ref.class: ./src/Ref.java
    $(make-target)
./obj/Test.class:./src/Test.java ./obj/Ref.class
    $(make-target)

run:
    @java -classpath $(LIB) Test
always:
    @mkdir -p $(OBJ)

Test.java:

public class Test {
  public static void main(String argv[]) {
    Ref ref = new Ref();
    ref.run();
  }
}

Ref.java:

public class Ref {
  public void run() {
    System.out.println("hhhh");
  }
}

and I would like to share my 'simpler' template here(you just need to add file name and dependency):

OBJ = obj
SRC = src
JARS = $(wildcard lib/*.jar)
LIB = .:$(OBJ):$(shell echo '$(JARS)' | sed 's/jar /jar:/g')

targets := $(wildcard */*.java)
targets := $(patsubst %.java,%.class,$(targets))
targets := $(notdir $(targets))
targets := $(addprefix $(OBJ)/,$(targets))

define build
    @echo + cc $<
    @javac -classpath $(LIB) $< -d $(OBJ) $*
endef
define extend 
    $(shell echo $1|sed 's/.class/.java/'|sed 's/$(OBJ)/$(SRC)/')
endef

all: always $(targets)

.SECONDEXPANSION:
$(OBJ)/Ref.class: $$(call extend, $$@)
    $(call build)
$(OBJ)/Test.class:$$(call extend, $$@) $(OBJ)/Ref.class
    $(call build)

run:
    @java -classpath $(LIB) Test


.PHONY: clean always
clean:
    @rm -rf $(OBJ)
always:
    @mkdir -p $(OBJ)
sleepsort
  • 1,321
  • 15
  • 28