Compiling a java project from linux command line - java

I am having trouble understanding how to actually use the command line to run a big java project ( by big I mean with multiple files and folder).
Imagine I have a project containing :
~/src/main/fr/file1.java
~/src/main/fr/file2.java
~/src/test/test1.java
All my life people have done the makefile for me. I just code the java src with vim and compile and run with make. Now there is no makefile ! I compile with maven (that I am still understanding how it works.). After compiling with maven (I just run maven compile). I then have a new folder named target.
~/target/main/fr/file1.class
~/target/main/fr/file2.class
~/target/test/test1.class
Now how can I run test1 ? I tried using java -classpath =... test1 but I always get errors ...
If can someone help me (or just give me some resources so I can finally understand basic project structuring and scripting) it will be amazing. Thank you !

Here is a minimal working example to compile java source files from multiple locations and pack them in a single runnable jar.
$ tree
.
├── makefile
├── src_folder_1
│   └── Main.java
└── src_folder_2
└── Person.java
The contents of java files appear at the end for completeness. Here is the makefile.
BIN_DIR = bin
WITH_DEBUG_INFO = -g
.DEFAULT_GOAL:
app.jar:
compile_phase.done: ${SRC_FILES}
#mkdir -p ${BIN_DIR}
#javac ${WITH_DEBUG_INFO} ${SRC_FILES} -d ${BIN_DIR}
#echo "DONE" >> $#
manifest.mf:
#echo "Class-Path: ${BIN_DIR}/*.class\nMain-Class: Main\n" > $#
app.jar: manifest.mf compile_phase.done
#jar cfm $# $< -C ${BIN_DIR} .
#rm -rf ${BIN_DIR}
#rm $^
See that all java source files are compiled within the same rule, and the corresponding *.class files are put in a dedicated bin directory. To emphasize that everything needed to run the jar is inside it I completely removed the bin directory and the manifest.mf. Now you can run the program with
$ java -jar app.jar oren
Hello oren
Here are the java files for completeness:
$ cat src_folder_1/Main.java
class Main {
static public void main(String[] args) {
Person p = new Person(args[0]);
System.out.format("Hello %s\n",p.toString()); }}
$ cat src_folder_2/Person.java
class Person {
private String name;
public Person(String n) { this.name = n;}
public String toString() { return name; }}

Related

How to handle jars in a makefile for a java project

I need help incorporating jars(specifically mysql-connector-java.jar & javax.persistence_2.1.0.v201304241213.jar) into my makefile. I am aware that Make is not ideal for a java project, but due to requirements, using ant/Maven is not an option. So far my makefile looks like:
# Makefile brutally copypasta'd from previous CSC pracs.
LIB = ../lib
SRCDIR = src
BINDIR = bin
TESTDIR = test
DOCDIR = doc
JAVAC = javac
JFLAGS = -g -d $(BINDIR) -cp $(BINDIR)
vpath %.java $(SRCDIR)
vpath %.class $(BINDIR)
# define general build rule for java sources
.SUFFIXES: .java .class
.java.class:
$(JAVAC) $(JFLAGS) $<
all: client server
client: shared/NetMessage.class client/Command.class client/UploadConnectionThread.class client/DownloadConnectionThread.class client/MainConnectionThread.class client/ClientState.class client/Main.class
server: shared/NetMessage.class server/ConnectionInstance.class server/ServerState.class server/Main.class
# handling the cyclic dependency between ClientState and the connection thread classes:
client/ClientState.class: client/UploadConnectionThread.class client/DownloadConnectionThread.class client/MainConnectionThread.class
client/UploadConnectionThread.class client/DownloadConnectionThread.class client/MainConnectionThread.class: client/ClientState.java client/UploadConnectionThread.java client/DownloadConnectionThread.java client/MainConnectionThread.java
rm -f $(BINDIR)/client/ClientState.class $(BINDIR)/client/DownloadConnectionThread.class $(BINDIR)/client/UploadConnectionThread.class $(BINDIR)/client/MainConnectionThread.class
$(JAVAC) $(JFLAGS) $(SRCDIR)/client/ClientState.java $(SRCDIR)/client/UploadConnectionThread.java $(SRCDIR)/client/DownloadConnectionThread.java $(SRCDIR)/client/MainConnectionThread.java
# handling cyclic dependency between ServerState and ConnectionInstance
server/ServerState.class: server/ConnectionInstance.class
server/ConnectionInstance.class: server/ConnectionInstance.java server/ServerState.java
rm -f $(BINDIR)/server/ServerState.class $(BINDIR)/server/ConnectionInstance.class
$(JAVAC) $(JFLAGS) $(SRCDIR)/server/ServerState.java $(SRCDIR)/server/ConnectionInstance.java
# All the actual program classes
# compile/run commands
run_server: server
java -cp bin server.Main
run_client: client
java -cp bin client.Main 127.0.0.1
clean:
#rm -f $(BINDIR)/*.class
#rm -f $(BINDIR)/*/*.class
I have researched the Javadocs and other questions on SO, but the answers provided, are for simple project structures where the makefile produces classfiles in the same directory as the java files and no packages are used. In this case however the class files are created in bin/server (if they are server files) or the approriate bin/subdirectory. My project structure is as follows:
/bin
/server/
/client/
/shared/
/src
/server/
/client/
/shared/
/lib
/mysql-connector-java.jar
/javax.persistence_2.1.0.v201304241213.jar
/makefile
I have already tried adding the jars to the classpath as follows: CLASSPATH = /lib/mysql-connector-java.jar, but it does not work when I add the class path to the rule: .SUFFIXES: .java .class, as Make picks it up as multiple targets.

Import multiple libraries in makefile within Javac command

The problem
This is a problem I just faced using makefile in java, on Windows.
I wanted to set up my classpath with multiple path (libraries, etc.). The new command work by hands, but not from the makefile which throws me this error :
javac : no source files
Example
Let's say I have this makefile :
JFLAGS = -g
JARFLAGS = -cvfm
CLASSPATH = ./bin
LIBS = C:/java/lib/mylib.jar
SOURCEPATH = ./src/client
compileAll:
javac $(JFLAGS) -d $(CLASSPATH) -cp $(CLASSPATH)\;$(LIBS) $(SOURCEPATH )/*.java
jar $(JARFLAGS) app.jar bin/client/MANIFEST.MF bin/client/*.class
So the command line to compile the project is :
javac -g -d ./bin -cp ./bin;C:/java/libs/lib.jar ./src/client/*.java
It works well.
The class files goes to ./bin directory. It imports classes from ./bin and the lib.jar library. And it compliles all the source files from the ./src/client directory.
This command works perfectly by hands, but no from the makefile which doesn't compile anything.
Thanks to my text editor which colored the ';' character, I understood that I just needed to escape (disable) the ';' character by using a '\' :
javac -g -d ./bin -cp ./bin\;C:/java/libs/lib.jar ./src/client/*.java
Now, it works well from makefile !

What is the purpose of writing a seemingly self-referential "-d . " in the command line interface

When compiling an application in the command line interface, I sometimes see a command written as follows:
javac -d . HelloWorld.java
I understand that:
-d <directory> = Specify where to place generated class files
. = the current folder
My question is: what is the purpose of writing -d .?
It seems self-referential and completely redundant/unnecessary. I would expect simply the following, which to my knowledge has the same effect and is less verbose:
javac HelloWorld.java
Is there something that I am missing?
I have used symbolhound.com to search the web for this specific phrase, but could not find any explanation.
This page on the Oracle Java site does it, for instance:
javac -d . XorInputStream.java
javac -d . XorOutputStream.java
javac -d . XorSocket.java
javac -d . XorServerSocket.java
javac -d . XorServerSocketFactory.java
javac -d . XorClientSocketFactory.java
javac -d . Hello.java
javac -d . HelloClient.java
javac -d . HelloImpl.java
WHEN THE SOURCE DIR IS NOT THE CURRENT DIR
Suppose you have a Hello.java file in /tmp/src
When you are in /tmp, compiling with
javac -d . src/Hello.java
puts the class file in the current directory, so it is /tmp/Hello.class.
Without the option, it goes to the same directory as the source file, that is /tmp/src/Hello.class.
MOREOVER there is a difference when packages are involved. Compiling this code
package Foo;
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, World");
}
from the /tmp/src directory with the -d . option builds a subdirectory for the package
/tmp/src
├── Foo
│   └── Hello.class
└── Hello.java
whereas javac Hello.java leaves the class file in the current directory
$ tree
.
└── Hello.java
0 directories, 1 file
$ javac Hello.java
$ tree
.
├── Hello.class
└── Hello.java
0 directories, 2 files
No animal was hurt during the test with javac 1.8.0_101.

How can I compile and run a Java class in a different directory?

I'm writing a makefile that compiles a .java file in a different directory, and then I want to run it, without changing directories. I want to do something along the lines of:
$(SQM_JAVA_TOOL_DONE) : $(SQM_JAVA_TOOL)
$(shell cd /home_dir)
javac myjavafile.java
java myjavafile
where the Java file is /home/myjavafile.java, and the makefile isn't running from /home.
How can I do this?
I might be misunderstanding the question, but you can compile with
javac /home/MyJavaFile.java
This will create MyJavaFile.class in /home
You can then run it by including /home on the classpath. e.g.
java -cp /home MyJavaFile
If you want to generate the class file in a different directory then you can use the -d option to javac.
Use the -d command line parameter with javac to tell it what directory you'd like to store the compiled class files in. Then, to run the program, simply include this directory in the classpath:
javac -d some/directory myjavafile.java
java -cp some/directory myjavafile
Just to add to the existing answers, you may want the --source-path flag:
--source-path <path>, -sourcepath <path>
Specify where to find input source files
I believe this effectively sets the package root javac will compile from (i.e. <path> will be stripped from the expected package name of the files). It's still necessary to enumerate the files to compile, and this should still be relative to the current working directory, not the path passed to --source-path.
For example, to compile and run from a project's root where source is stored in src/ and you want it build in bin/:
$ javac --source-path src -d bin src/mypackage/*.java
$ java -cp bin mypackage.Main
This works even from directories elsewhere in the filesystem, e.g.:
$ javac --source-path /some/absolute/path/src -d /some/absolute/path/bin /some/absolute/path/
$ java -cp /some/absolute/path/bin mypackage.Main
I am using VS Code and installed java and code runner extensions. When I created new java project using the extension, it was creating the .class file in src instead of bin. To solve the issue I opened settings.json file from File > Preferences > Settings and searched for "settings" (or "code-runner"). Then I added following lines in that file.
"code-runner.executorMap": {
"java": "cd \"$workspaceRoot\\\" && javac --source-path src -d bin src\\$fileName && java -cp bin $fileNameWithoutExt",
}
If you don`t want to see the command that runs before code file then add these lines instead:
"code-runner.clearPreviousOutput": true,
"code-runner.showExecutionMessage": false,
"code-runner.executorMap": {
"java": "there is && clear added in the execution paramater"
"java": "cd \"$workspaceRoot\\\" && javac --source-path src -d bin src\\$fileName && clear && java -cp bin $fileNameWithoutExt",
}
I hope this finds someone with similar issue.

java class not found despite providing a jar file

I'm puzzled by the process of running java programs, maybe you can help.
I have several .java files in ~/working_dir/org/project/ that have main functions, and I want to package them in a jar to run them. I do:
cd ~/working_dir/org/projectname
javac -classpath $CLASSPATH *.java
cd ~/working_dir/
jar cf myjar.jar org/
And then try to run one of the classes in the jar by doing:
java -cp myjar.jar org.project.SomeClass
and get
Exception in thread "main" java.lang.NoClassDefFoundError: org/project/SomeClass
Could not find the main class: org.project.SomeClass
What do I do wrong? The classes compile without any errors, and jar tf myjar.jar shows that they're indeed there. As far as I know I don't need to create a Manifest file because I provide the class from which I want to run the main function at runtime - or am I wrong here?
Help much appreciated!
If the exploded jar org/project/SomeClass is beneath your current working dir:
/ <- you are here
+---/org
|
+-----/project
|
+--------SomeClass.class
try java -cp . org.project.SomeClass instead
First of all, note that if you simply do
javac org/project/SomeClass.java
the class file will end up right beside the .java file which makes it tricky to include only .class-files in the jar. I suggest you use the -d option to specify destination directory:
javac -d bin org/project/SomeClass.java
Have a look at the following bash-session for details to get it working:
A listing of the source directory:
user#host:/working_dir/src$ ls -R
.:
org
./org:
projectname
./org/projectname:
SomeClass.java
The SomeClass.java file:
user#host:/working_dir/src$ cat org/projectname/SomeClass.java
package org.project;
public class SomeClass {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
Compile it (with target directory ../bin)
user#host:/working_dir/src$ javac org/projectname/SomeClass.java -d ../bin
List the result and make sure you got the directories right:
user#host:/working_dir/src$ cd ../bin/
user#host:/working_dir/bin$ ls -R
.:
org
./org:
project
./org/project:
SomeClass.class
Create the jar file:
user#host:/working_dir/bin$ jar cf myjar.jar org
Make sure you got the directories right and didn't accidentally include the "bin" directory:
user#host:/working_dir/bin$ jar tf myjar.jar
META-INF/
META-INF/MANIFEST.MF
org/
org/project/
org/project/SomeClass.class
Launch the main method:
user#host:/working_dir/bin$ java -cp myjar.jar org.project.SomeClass
Hello World
user#host:/working_dir/bin$

Categories

Resources