I have three programs that need to be compiled at the same time, 2 written in C and 1 in java. I had all three working with the Makefile when they were in C, but then rewrote one of them in java... is there a way to compile all 3 at once with the same makefile?
Here is my current Makefile:
CC=gcc
JC=javac
JFLAGS= -g
CFLAGS= -Wall -g -std=c99
LDFLAGS= -lm
.SUFFIXES: .java .class
.java.class:
$(JC) $(JFLAGS) $*.java
CLASSES = kasiski.java kentry.java
ALL= ic ftable kasiski
all: $(ALL)
ic: ic.o
kasiski: $(CLASSES:.java=.class)
ftable: ftable.o
ic.o: ic.c ic.h
ftable.o: ftable.c ftable.h
.PHONY: clean
clean:
rm -rf core* *.class *.o *.gch $(ALL)
Yes, you can compile them all at once. If your "all" target depends on all three applications, then "make all" should build all of them. You can throw in "-j3" to actually compile using three separate threads and/or processes (it isn't clear what you mean by "at once"). Also, a couple criticisms here:
Do not define "CC", "CFLAGS", or "LDFLAGS". You should never define "CC" as it is automatically defined for you to the default C compiler on the system, and you should merely append to "CFLAGS" and "LDFLAGS" as needed (using +=) instead of clobbering them, as simply assigning to them makes your Makefile inflexible (because they cannot be overrided or augmented externally).
Using CLASSES to refer to ".java" files is confusing as hell... you should probably rename the variable to JAVA_SRCS, and define JAVA_CLASSES=${JAVA_SRCS:.java=.class}.
For more explanation, please see my Makefile tutorial. That said, you may want to consider a more modern build system such as Bazel or Gradle. These systems are designed to be much simpler to use, less error prone, more portable (because they make it easier to do things portably), and faster.
Related
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)
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.
#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
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.