Java Makefile always rebuilds, even when no changes were made - java

My makefile always rebuilds the project, even if no changes were made.
How can I fix it?
My project structure follows the usual bin/, src/, Makefile pattern.
Makefile:
# output directory
BIN = bin/
# input directory
SRC = src/
# java compiler
JC = javac
# compile flags
JFLAGS = -d $(BIN) -cp $(SRC)
sourcefiles = $(addprefix $(SRC), \
A.java \
B.java \
C.java)
classfiles = $(sourcefiles:.java=.class)
all: $(classfiles)
%.class: %.java
$(JC) $(JFLAGS) $<
clean:
$(RM) $(BIN)*.class
I made this makefile from examples I found online, but I'm not sure I understand everything being done, so if I could also get an explanation, that would be great :3

In general, make is not a good fit for Java. Make works best with tools that behave similarly to traditional compilers: they take an input file foo.X (and maybe some other input files as well) and they generate a single output file foo.Y. For a C compiler for example X is c and Y is o (foo.c compiles to foo.o).
Make is hard to use in situations where a single invocation of the compiler generates more than one output file, and it's not simple to use when the name of the output file doesn't relate directly to the name of the input file (in this case you have to write all explicit rules, not pattern rules).
For Java compilers a single .java input file can generate multiple different .class files, and the names of the .class files are not necessarily related to the name of the .java file.
In your situation, I'll bet if you look at the output files that javac is generating for your A.java file you'll see it's not generating A.class. Because A.class doesn't exist, make will always try to rebuild it.
Oh. Also. You're putting files in different directories. So even if you DO restrict yourself to situations where the names are identical, you have to write your pattern like this:
# ... Keep the first part as in your example
classfiles = $(patsubst $(SRC)%.java,$(BIN)%.class,$(sourcefiles))
all: $(classfiles)
$(BIN)%.class : $(SRC)%.java
$(JC) $(JFLAGS) $<
# ... Keep the clean rule as in your example
The patterns % must be identical; if you put things in different directories they're not identical.

Related

How do I specify the path to any class in the makefile of a project?

I have a java project, with different directorys containing different classes. And I'm trying to create a makefile in the upper directory that links all the the classes and compiles de project.
So to reach class a, it should follow the path ./src/project/packageA/a.java, and to reach b, ./src/project/packageB/b.java. I have the following makefile:
JFLAGS = -g
JC = javac
JVM = javac
.SUFFIXES: .java .class
.java.class:
$(JC)$(JFLAGS)$*.java
CLASSPATH = .:./src/project
CLASSES = \
a.java \
b.java \
...
MAIN = Main
default: classes
classes: $(CLASSES:.java=.class)
run: $(MAIN).class
$(JVM)$(MAIN)
clean:
$(RM)*.class
Where CLASSES is a macro containing all the classes in the project. I've tried redefining the variable PATH for every directory, assigning vpath and directly specifying the path in every's class name. But none seems to work, I always receive the error message "there is no rule for building the object a.class, needed for 'classes'." This is my first time using makefile and I don't know how to proceed.
Thank you everyone.
Few minor fixes to move the project forward.
Fix the rule for compile - need spaces around JFLAGS
Use the input file for the build rule - so that it will include the full path name
Add vpath to tell the compiler where source files can be
.java.class:
$(JC) $(JFLAGS) $^
CLASSPATH = .:./src/project
vpath %.java src/project
It's not clear from the question where you want to place the object. Consider adding -d to specify target folder, and setting adding cp -d ... to specify the location for the run target. You will need to fix the run time to add spaces.

Java makefile that creates a folder for the classes

I'm trying without any success to create a java makefile that compiles the java source codes to classes which go in a specific folder. So far, I managed to get the classes but I am really having trouble understanding how to create a folder and place the classes in there.
Here's my code so far:
JC = javac
JVM = java
.SUFFIXES: .java .class
.java.class:
$(JC) *.java
default: .java.class
clean:
$(RM) *.class
I followed a lot of tutorials and still can't figure it out. Basically I have my .java files in my folder. When I run make, I would like the classes to go in /bin folder and if it doesn't exist it gets created
You cannot do what you want to do with suffix rules. You'll have to use pattern rules if you want the output to be placed in a different directory than the source. Pattern rules are a feature of GNU make so hopefully you're using that (you don't say).
Plus, the way you're using suffix rules is not right: you don't declare the suffix rule itself as a prerequisite. You declare the actual files you want to build as prerequisites.
Also, make cannot do this by itself: you have to tell your compiler where to put the output. I'm not a Java person so I can't help you with that. Check your manual.
Your recipe builds all the .java files with a single invocation, which is not really how make works: make wants to translate a single source file (plus possibly other header files etc.) into a single output file.
Replace your suffix rule:
.SUFFIXES: .java .class
.java.class:
$(JC) *.java
with a pattern rule:
$(OUT)/%.class : %.java
mkdir -p $(#D)
$(JC) -o $# $<
(I have no idea if -o is right for javac: as I said you'll have to consult your Java manual). You don't need to declare .SUFFIXES when you use pattern rules.
Then, declare your default target to depend on the output files you want to be generated:
classfiles := $(wildcard *.class)
default: $(classfiles:%.class=$(OUT)/%.java)

I try to write my own makefile but it doesn't work

#define the suffixes
.SUFFIXES: .java .class
#compile all java files to .class
.java.class:
javac -g *.java
#CounterStatTester is a tester class for CounterStat
CounterStatTester.class: CounterStat.class
test: CounterStatTester.class CounterStat.class
java CounterStatTester
When I try to "make test", the shell says"make: *** No rule to make target `test'. Stop."
But I did wrote "java CounterStatTester", why it still cannot find a rule?
There is a LOT of instances of poor style here. Instead of just answering the question, I will try to list them all, in order of appearance. If you do all the things I advise, your original problem will disappear for sure.
It is OK to use "poor style" if you know what you are doing and have a good reason to do so, and have already learnt good style. In your case, it is none of these things.
.SUFFIXES is obsolete and should not be used for new code
Likewise, "suffix rules" like .java.class: are obsolete and should not be used.
In any case, your .java.class: rule should take one .java file and produce one .class file. Good style in makefiles, is for each rule recipe to produce one target at a time, not multiple targets.
So, you should have something like this:
%.class: %.java Makefile
javac -g $<
There is no reason why CounterStatTester.class should "depend" on CounterStat.class so you should not have that rule. "Dependence" in this case would mean, that the recipe for CounterStatTester.class read CounterStat.class. CounterStatTester.class really depends on CounterStatTester.java (and Makefile, which is less obvious), which is already captured by the rule I showed you.
As I mentioned above, each rule recipe should write one target. Since test is not a file, but an abstract notion, so it cannot be written, and therefore, it should not have a rule with a recipe.
The point of writing a makefile, is so you don't have to execute recipes unless necessary, saving time. In your case, test does not exist at all, so the recipe would be always executed, which is pointless for a makefile.
Instead, you should have a file with a name like CounterStat_tested and a rule like this:
CounterStat_tested: CounterStatTester.class CounterStat.class Makefile
java CounterStatTester && touch $#
Finally, a makefile should have an all target at the top:
.PHONY: all
all: CounterStatTester.class CounterStat.class CounterStat_tested

Java Makefile Error

I'm having this error when trying to compile my Java project with a Makefile I created.
Error:
make: No rule to make target src/edu/osu/lispinterpreter/output/*.class', needed byclasses'. Stop.*
Makefile content:
JFLAGS = -g
JC = javac
.SUFFIXES: .java .class
.java.class:
$(JC) $(JFLAGS) $*.java
CLASSES = \
src/edu/osu/lispinterpreter/tokenizer/*.java \
src/edu/osu/lispinterpreter/core/*.java \
src/edu/osu/lispinterpreter/output/*.java \
src/edu/osu/lispinterpreter/exceptions/*.java \
src/edu/osu/lispinterpreter/input/InputParser.java \
src/edu/osu/lispinterpreter/input/ReadInputString.java
default: classes
classes: $(CLASSES:.java=.class)
clean:
$(RM) *.class
Can someone give me a hand with the Makefile please?
I'm using Eclipse btw.
From http://www.gnu.org/software/make/manual/html_node/Wildcard-Pitfall.html#Wildcard-Pitfall
When a wildcard matches no files, it is left as it is
It seems src/edu/osu/lispinterpreter/output is empty, so src/edu/osu/lispinterpreter/output/*.java does not match any file and is left as it is, which is not what is intended. Using the wildcard function, this can be avoided.
The solution would be to replace
src/edu/osu/lispinterpreter/output/*.java
by
$(wildcard src/edu/osu/lispinterpreter/output/*.java)
and so on for all the other lines, I think
You either list all the source files individually or use the wildcard directive in Makefile to automatically match the files instead.
Also from your comments it looks like the package names for the java files are edu.osu.lispinterpreter.*.
So I would suggest to move the Makefile to the src directory and make these changes in the Makefile.
CLASSES := $(wildcard edu/osu/lispinterpreter/tokenizer/*.java)
CLASSES += $(wildcard edu/osu/lispinterpreter/core/*.java)
CLASSES += $(wildcard edu/osu/lispinterpreter/output/*.java)
CLASSES += $(wildcard edu/osu/lispinterpreter/exceptions/*.java)
CLASSES += edu/osu/lispinterpreter/input/InputParser.java
CLASSES += edu/osu/lispinterpreter/input/ReadInputString.java
The java compiler should be able to pick up definitions of classes from other packages as long as the package name corresponds to the directory structure without writing any explicit dependencies in the Makefile.
I think that the problem is that your use of * in the CLASSES variable. The way you have written that variable, it is being populated with a list of "file names" that have * characters in them ... which will propagate through the rest of the processing.
You either need to list the classes individually, or do something to tell Make to "glob" the list. If you are using GNU Make then the wildcard function should do the trick.
But note that won't work on other versions of Make, so you've got a Makefile portability problem. (Which brings me back to my comment that Ant is better.)
And once you've gotten past that, you have the problem that if you are compile Java classes one at a time:
your build will be really slow ... 'cos each javac command incurs a JVM startup (assuming you are using the Hotspot or OpenJDK tool chains),
you have to build the classes in the right order ... according to the dependencies inherent in the source code,
you have to add those dependencies to the Makefile (!!!), and
if you have dependency cycles, you've got problems!
With enough patience, you could build a Makefile that coped with this, but it is really tricky, and the resulting Makefile will be fragile. Realistic alternatives are:
just build all of the /.java files in one javac command, irrespective of dependencies,
add a "make depend" rule that uses something to analyse the source code or bytecode files and generate the dependencies in the Makefile.
Or just use Ant.
References:
Why is no one using make for Java?
A Java / Make dependency generator - http://ptolemy.eecs.berkeley.edu/~cxh/java/javadepend/
Another Makefile generator for Java - http://www.tildeslash.com/mmake/mmake.html

JDBC in makefile

I'm trying to write a makefile for a java project as below. I have a database connected (installed and tested working when I compile with another IDE instead of javac). I am not sure how to write the driver into makefile. For the following makefile, after typing make, I got message: package com.mysql.jdbc does not exist. But I have mysql-connector-java-5.1.16-bin.jar in the same folder as my makefile.
JFLAGS = -g
JC = javac\
.SUFFIXES: .java .class
.java.class:
$(JC) $(JFLAGS) $*.java
JavaLibraries = \
mysql-connector-java-5.1.16-bin.jar
CLASSES = \
DBMain.java\
Update.java\
server.java\
Client.java
default: classes
classes: $(CLASSES:.java=.class)
clean:
$(RM) *.class
Thanks for any input.
I cannot emphasize enough that Java and make doesn't fit well together. It's likely you will run into serious problems by building a Java project with make.
However, if you really want to use make, despite all warnings, then you have to adjust the classpath settings for the Java compiler:
JFLAGS = -g
JC = javac
CLASSPATH=mysql-connector-java-5.1.16-bin.jar:.
.SUFFIXES: .java .class
.java.class:
$(JC) $(JFLAGS) -cp $(CLASSPATH) $*.java
...
So the CLASSPATH consists of all used JARs and the package root directory of your *.java files (I assumed that is the current directory), separated by a colon on Unix/Linux systems or semicolon on Windows. Then in the .java.class rule you have to call the Java compiler with the -cp flag to pass the classpath.
This Database Schema Definition Language project contains an exemplary build.xml that shows how to initialize and test a database via JDBC. Note that ant targets are perfectly fine as make commands.
Addendum:
I need is write a makefile for others to use.
This other answer shows a good example of invoking javac directly from a makefile. It shows how to include the classpath, which may solve your immediate problem; but the approach rapidly becomes unwieldy for more elaborate builds, such as those including packages. This can be somewhat mitigated by using the subst function:
PKG = com.name.util
PKG_PATH = $(subst .,/,$(PKG))
A far simpler scheme is to write a minimal ant target, such as <target name="compile"…>, as shown here; then the corresponding make command is simple:
.SUFFIXES: .java .class
.java .class:
ant compile
Certainly, the makefile now depends on ant, but ant is fairly ubiquitous.

Categories

Resources