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)
Related
I realize the make isn't the best tool to be using with Java, but I just wanted to experiment with it. I have this script but I'm not sure why it isn't working:
JFLAGS = -g
JC = javac
SRC_DIR = $(PWD)
.SUFFIXES: .java .class
.java.class:
$(JC) $(JFLAGS) $*.java
CLASSES = \
$(SRC_DIR)/Fibonacci_Methods.java \
$(SRC_DIR)/Fibonacci_Methods_Test.java \
default: classes
classes:
$(CLASSES:.java=.class)
clean:
$(RM) *.class
I get this error:
/path/to/make_test/Fibonacci_Methods.class
/path/to/make_test/Fibonacci_Methods_Test.class
make: /path/to/make_test/Fibonacci_Methods.class: No such file or directory
make: *** [classes] Error 1
I'm not sure why. My understanding is that this script should define CLASSES, which should call .java.class target because I'm defining files ending with .java. I don't know the purpose of $(CLASSES:.java=.class) because I would have thought the compilation be done already before this step.
I have not yet compiled the java code, by the way (so I'm running make with .java files only, if that makes a difference).
This is wrong:
classes:
$(CLASSES:.java=.class)
Here you've defined a target with no prerequisites and recipe (which is supposed to be a command that is used to rebuild the target) that consists of a list of .class filenames, so you're getting the error you see because you can't "run" a list of .class files.
You want this:
classes: $(CLASSES:.java=.class)
which defines a target classes with a set of prerequisites which are the .class files you want to build, and no recipe because you don't want to create a target named classes.
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.
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
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.
I am joining a competition that requires me to put all my java classes in one single .java file. Does there exist a tool that does this for me (including changing the visibility of classes to be able to do this)?
Addition: thanks for trying to help me to read the site of the competition but I quote:
It is possible to make more than one
class for your program, but you will
have to put the source for all classes
in a single .java file (the compiler
will produce multiple .class files
anyway). When you do this, you should
not declare your classes public, or
the compiler will complain about it.
So, only 1 .java file is allowed (no jar) and in that file I can have multiple non-public classes besides my public main class (and not only static inner classes as suggested).
If you have access to Unix-y shell (for Windows, you can install e.g. Git for a decent Bash implementation, and it gives you a great VC tool):
cat *.java | sed 's/public class/class/g' >AllTehCodez.java
Doesn't have to be more complicated than that (unless you have a lot of strings containing the substring "public class", of course).
Edit: Doesn't work for package and imports. But...
(
egrep -h ^package *.java | head -1
egrep -h ^import *.java | sort -u
egrep -hv '^(import|package)' *.java | sed 's/public class/class/g'
) >AllTehCodez.java
This does of course assume all the classes are in the same package.
If we exclude various "bijou scripting haquettes" along the lines suggested above, I seriously doubt that any serious tool exists for doing this.
Why? Because this kind of nonsense goes against all known Java style rules and conventions!
The people behind that website need to be taught about archive file formats; e.g. TAR, ZIP, JAR.
EDIT
I take that back. They DO understand JAR files. Quoting from one of their documents:
Using your own executable Java jar with Caia
You can also use your own Java jar in
Caia competitions. We have written the
jarwrapper for that. The source of
jarwrapper.c is put in the
caia_install_/jarwrapper/
folder. In the Windows distro this is
put into the src/ folder.
Suppose the name of your class file is
JavaPlayer.class. The only thing you
will have to do is to rename the
executable in the bin/ folder from
jarwrapper to JavaPlayer. The
executable now will perform the
command: java -jar JavaPlayer. The jar
file should contain a manifest which
points to the class with the main
method.
In manager.txt you can use the program
name JavaPlayer which refers to the
executable that starts your Java jar
player.