How to make a proper Java makefile? - java

I need to make a makefile for a Java project.
My project is basic. A package which contains my main file and some others packages.
Can someone help me to make a proper makefile for that kind of project ?

Sorry I have not tested so it is likely to fail...
Assuming that what you need is to generate a executable jar file the following should work but I have not tested it.
The Makefile below assumes that your sources are located under ./src and that you are happy to use ./build for intermediary files (which is totally obliterated by the clean target so be careful).
Then 'make' or 'make jar' should generate the jar file.
NAME=MyProject
MAIN_CLASS=MyMainClass
SRC_DIR=./src
CLS_DIR=./build/classes
MANIFEST_FILE=./build/META-INF.MF
JAR_FILE=./$(NAME).jar
SRC_FILES=$(shell find $(SRC_DIR) -iname "*.java")
CLS_FILES=$(patsubst $(SRC_DIR)/%,$(CLS_DIR)/%,$(patsubst %.java,%.class,$(SRC_FILES)))
.PHONY: jar run clean mrproper
jar : $(JAR_FILE)
run : $(JAR_FILE)
java -jar $(JAR_FILE)
$(JAR_FILE) : $(MANIFEST_FILE) $(CLS_FILES)
jar cmf $< $# $(CLS_DIR)
$(MANIFEST_FILE) :
mkdir -p $(dir $#)
echo Main-Class: $(MAIN_CLASS) > $#
$(CLS_DIR) :
mkdir -p $(CLS_DIR)
$(CLS_DIR)/%.class : $(SRC_DIR)/%.java $(CLS_DIR)
javac -d $(CLS_DIR) -sourcepath $(SRC_DIR) $<
clean :
rm -Rf ./build
mrproper : clean
rm -f $(JAR_FILE)
Each time you execute Make it performs a find operation to get the list of source files, that might cause some delay depending on how many files and how fast is the file-system.... but you said is is a small project so it should not be an issue.
The double patsubst might well be compressed into a single one but I did it in two steps just in case.
Also notice that this solution compiles each Java class separately. This can be quite costly and it might be advisable to have another target to compile all at once ideally by creating a file that contains the name of all src java files and passing it to javac.

Related

Why I always received invalid flag error while building .jar

Why I always received invalid flag error while I'm building .jar
When I using Build Jar to make a .jar file in java:
rm -rf build-jar && mkdir build-jar && javac -d build-jar /Users/user/Desktop/projects/Swings/source/library/* && jar cvf build-jar/window.jar build-jar *
I always receive a error:
error: invalid flag: /Users/user/Desktop/projects/Swings/source/library/controller
Usage: javac <options> <source files>
If I use path to .class file:
rm -rf build-jar && mkdir build-jar && javac -d build-jar /Users/user/Library/Application Support/Code/User/workspaceStorage/ca7d24d42ba31de4cfb244fc0f239d07/redhat.java/jdt_ws/swings_12c4bbf0/bin/* && jar cvf build-jar/window.jar build-jar *
I also receive an error:
zsh: no matches found: Support/Code/User/workspaceStorage/ca7d24d42ba31de4cfb244fc0f239d07/redhat.java/jdt_ws/swings_12c4bbf0/bin/*
I'm not very familiar with building jar, It make me headache, And I don't know what to do, the other question's solution or solution on internet are all not work for me(like Classpath invalid flag - Java, etc.)
I'm using IDE vscode, and using vscode extension "JAR Builder"
'star' expansion is a thing your shell does. When you type ls *.txt in your shell, that's not what is run. Your shell itself detects that * and will go out and figure out what you really mean. What actually ends up being executed is ls a.txt b.txt c.txt - everything that star matches, separated out by spaces.
The same is happening here. Hence, why you get this error: Your shell is executing:
javac -d build-jar
/Users/user/Desktop/projects/Swings/source/library/controller
/Users/user/Desktop/projects/Swings/source/library/model
/Users/user/Desktop/projects/Swings/source/library/... and all the other dirs...
and here's the clue: javac does not work like this. You cannot specify directories and expect it to know what to do. You need to list each java file individually, which means you need one heck of a long command line.
There is a reason nobody in the java ecosystem builds apps with the command line. Everybody uses maven or gradle instead. So should you. It'll solve this problem; you just stick your sources in the right location and maven / gradle figure it out from there. Have as many packages as you want.

How do I execute a shell command path after creating a new directory in Makefile?

I'm trying to get an updated path of my variable service_SOURCES, after I have generated some files from jaxb with my makefile:
service.jar$(EXEEXT): $(service_SOURCES)
mkdir -p bin
xjc -d src -p gen.files ./src/resources/info.xsd
javac -cp service_SOURCES
service_SOURCES := $(shell find ./src -name "*.java")
I am trying to compile my existing java code with my generated java code Currently, my service_SOURCES variable finds my folder containing my java files, and compiles them all together. I need it to update to include the newly generated java folder, so it compiles my new java files along with my old java files.
I've tried linking the above commands together using && but the shell command still doesn't update. If I run the above commands in terminal it works, but when I run it in my Makefile the path won't update properly.
(moving to answer as this doesn't fit in comment)
You should understand that Make runs in two phases -- The dependency tree is built in the first phase, and the rules are run in the second. This means that the dependency tree cannot be updated by a shell command run after files are generated by a recipe. If you want to do something like this (and I agree with #JohnBollinger, this might not be the best idea), you might need to split your logic between two makefiles, or have a self-recursive makefile. Either way you need to generate the files, then call the second makefile, which can then build a new dependency tree based on a timely shell command.
Alternative:
service.jar$(EXEEXT): _target_to_generate_java_files_
mkdir -p bin
xjc -d src -p gen.files ./src/resources/info.xsd
javac -cp $$(find ./src -name "*.java")

Can I try/catch this error: "An unexpected error occurred while trying to open jar"

I am working on my first non-trivial Java app. It was started by a co-worker, but she didn't have time to finish, so I took it over. I decided to use Buildr as my build tool: https://buildr.apache.org/
First, to start with empty target directories, I do:
buildr clean
Then I:
buildr --verbose compile
which gives me:
(in /Users/cerhov/projects/openz/lofdg/buildr_fdg, development)
Compiling buildr_fdg
mkdir -p /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources
mkdir -p /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources/com/company
cp /Users/cerhov/projects/openz/lofdg/buildr_fdg/src/main/resources/com/company/students.txt /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources/com/company/students.txt
mkdir -p /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources/com/company
mkdir -p /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources/com
mkdir -p /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources/com/company
cp /Users/cerhov/projects/openz/lofdg/buildr_fdg/src/main/resources/com/company/scores.txt /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources/com/company/scores.txt
mkdir -p /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources/com/company
cp /Users/cerhov/projects/openz/lofdg/buildr_fdg/src/main/resources/com/company/behavior.txt /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources/com/company/behavior.txt
mkdir -p /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources/com/company
cp /Users/cerhov/projects/openz/lofdg/buildr_fdg/src/main/resources/com/company/schoolDates.txt /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources/com/company/schoolDates.txt
mkdir -p /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources/com/company
cp /Users/cerhov/projects/openz/lofdg/buildr_fdg/src/main/resources/com/company/assignments.txt /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources/com/company/assignments.txt
touch /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/resources
mkdir -p /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/classes
Compiling buildr_fdg into /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/classes
Note: /Users/cerhov/projects/openz/lofdg/buildr_fdg/src/main/java/com/company/Main.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
touch /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/classes
Completed in 0.926s
This gives me my ".class" files. Now I want to package this up as a jar, so I:
cerhov : 15:26:04 : ~/projects/openz/lofdg/buildr_fdg $ buildr --verbose package
which gives me:
(in /Users/cerhov/projects/openz/lofdg/buildr_fdg, development)
Building buildr_fdg
Packaging buildr_fdg
Packaging buildr_fdg-1.0.0.jar
rm /Users/cerhov/projects/openz/lofdg/buildr_fdg/target/buildr_fdg-1.0.0.jar
mkdir -p /Users/cerhov/projects/openz/lofdg/buildr_fdg/target
Running integration tests...
Completed in 0.212s
Then I do this:
cerhov : 15:26:56 : ~/projects/openz/lofdg/buildr_fdg $ ls target/
and I see a new Jar file has been created:
buildr_fdg-1.0.0.jar classes resources
so I:
java -jar target/buildr_fdg-1.0.0.jar
but I get:
Error: An unexpected error occurred while trying to open file target/buildr_fdg-1.0.0.jar
I'd like to get more info so I wrapped my whole main() function in a big try/catch statement:
public static void main(String[] args) {
try {
// all my real code goes here, but I have deleted it for clarity
} catch (Exception e) {
e.printStackTrace();
}
}
Then I re-compiled and re-packaged, but I still get the same error. I am unclear how I can force the app to give me more info.
I wanted to see if my manifest.txt got into my jar, so I copied the jar to another folder, cd'ed to that folder, and:
jar xf buildr_fdg-1.0.0.jar
and then this:
cat META-INF/MANIFEST.MF
showed me:
Created-By: Buildr
:
Manifest-Version: 1.0
Main-Class: com/company/Main
Which looks right. Maybe I can add in a classpath, though Buildr seems to have found all the files.
How do I force the app to give me more information about the problem?
UPDATE:
I tried adding this line to the manifest:
Class-Path: /Users/cerhov/projects/launchopen/lofdg/buildr_fdg/src/main/java/com/company
Then I re-compiled and re-packaged. But this:
java -cp buildr_fdg-1.0.0.jar com.company.Main
gave me:
Error: Could not find or load main class com.company.Main
So I tried every variation:
/Users/cerhov/projects/launchopen/lofdg/buildr_fdg/src/main/java/com/
/Users/cerhov/projects/launchopen/lofdg/buildr_fdg/src/main/java/
/Users/cerhov/projects/launchopen/lofdg/buildr_fdg/src/main/
/Users/cerhov/projects/launchopen/lofdg/buildr_fdg/src/
/Users/cerhov/projects/launchopen/lofdg/buildr_fdg/
None of which work. Am I suppose to aim the classpath at the source directory or the target directory?
Why is Buildr unable to manage this for me? It does find all of the files and it does bundle them all into the jar, so it knows where everything is.
I recreated the problem and was able to correct it by manually removing the colon (:) that appears by itself in the manifest file. Don't know why buildr is putting it there. This describes a way of possibly overriding the manifest file in buildr: https://buildr.apache.org/rdoc/Buildr/Packaging/Java/JarTask.html My reputation is too low to post a comment so unfortunately I can only post it as an answer. Hopefully this helps.
It looks like there are a couple of problems with the manifest file. One is that you have a stray ':' as mentioned by kharyam above and the other is that the Main-Class attribute is in incorrect format. It should look like the class name (i.e. "Main-Class: com.company.Main"). If you can post the part of the build file that defines the package we may be able to help further.

Solution for error ClassDefNotFound in Java

I have five class files Servant.class, Server.class, Client.class, TransferRequest.class and TransferResponse.class. My Makefile is at the below. I have this error for any of my class file:
Error occurred during initialization of VM
java/lang/NoClassDefFoundError: java/lang/Object
Makefile:11: recipe for target 'TransferRequest.class' failed
How can I cope with this error? I really tried all solutions which was written here such changing path or sth. This makefile is also 5th or 6 th one. The other well known makefiles gave same error too. I am on Windows machine I use cygwin.
Makefile:
JAVAC=javac
sources = $(wildcard *.java)
classes = $(sources:.java=.class)
all: $(classes)
clean :
rm -f *.class
%.class : %.java
$(JAVAC) $<
Add a classpath (with the -cp) option to your javac.
JAVAC=javac -cp "$CLASSPATH"
Or you could change
$(JAVAC) $<
to add the classpath
$(JAVAC) -cp "$CLASSPATH" $<
This is a problem with javac. Either something is missing from your make file or your java installation is broken.
Find out if you can compile a file by hand with javac to narrow it down.

compile files from different directories with javac, referring a depending jar file?

I have the following set up:
I have 4 packages:
root/src/terminal - has some java files
root/src/mail - has some java files
root/src/data - has some java files
root/src/main - has a single java file, Main.java
I also have the following files
root/bin - a folder to store .class files
root/mail.jar - a jar file which has important classes used in my code
Within the root, I would like to enter a terminal command which compiles root/src/main/Main.java and puts the class files in the root/bin location.
Can someone show me the command to do this? I'm on a Mac (running Leopard).
Here's the one liner:
cd /xyz/root
rm -rf bin/*
javac -d bin -classpath mail.jar -sourcepath src main/Main.java
Alternatively, you could use absolute directory names:
rm -rf /xyz/root/bin/*
javac -d /xyz/root/bin -classpath /xyz/root/mail.jar \
-sourcepath /xyz/root/src /xyz/root/ main/Main.java
In reference to Ant you said "I would rather keep it simple.".
In fact in the long term it is simpler to create a simple Ant build.xml file. The alternative is a bunch of non-portable scripts or batch file ... or lots of typing.
To run the application, assuming that you are still in the /xyz/root directory:
java -classpath bin:mail.jar main.Main
Or on Windows:
java -classpath bin;mail.jar main.Main
Or modify the above to use absolute pathnames in the classpath argument; e.g.
java -classpath /xyz/root/bin:/xyz/root/mail.jar main.Main
Without knowing your operating system?
What you should look into is using Apache Ant. It is a build tool that once installed and configured can utilize a build.xml file in your root to compile class files to a folder as well as package a jar file.
http://ant.apache.org/
try this:
javac -cp "/root/mail.jar;/root/src;" -d "/root/bin" Main.java
This is written hoping that you have package declarations in your classes from src folder like package terminal; and package main;.
See this: Options in javac command
Or use Apache Ant as suggested by maple_shaft.
From comment give by #maple_shaft:
In Unix, Linux operating systems the classpath separator is a colon instead of a semicolon.

Categories