Difference between sourcepath and just specifying the source file? - java

What is the point of the javac -sourcepath option?
Say you have a file Ball.java, and file Eight.java, there seems to be no difference between running:
javac Eight.java lib/pool/Ball.java
and
javac Eight.java -sourcepath lib
The classpath makes sense to me as it means you just need to distribute class files to developers who won't need to mess with the source, but I would think you're only likely to have/need the source if it's your own/an internal project so can't understand why it's needed.
Do people have large repositories of source code instead of classes?
From further testing I've confirmed that -sourcepath doesn't even take the last modified date of the source into account like the classpath, so couldn't even conceivably save time during building. As when using -classpath, this will build the .class file if it doesn't exist and the source does, or if the .java file is newer than the .class file, whereas any source files on the sourcepath will be built again irregardless of how new they are. And when -sourcepath and -classpath are both specified then it takes the action of the classpath to only rebuild if the source files are newer, so specifying the sourcepath and classpath appears to be completely pointless.
I've also tested to make sure that -sourcepath and -classpath both only build the necessary source files needed for resolution.
The only upside from -sourcepath over specifying the specific .java files I can find is that sourcepath just has to have a directory specified, and any jar or zip files are automatically extracted.
So is the point of -sourcepath due to laziness (not wanting to extract & specify all the source files)?
And is there any upside to using -sourcepath over -classpath? Since they appear to do the same thing but classpath performs it better by saving time when the source doesn't need rebuilding.

The sourcepath is used to locate .java files for classes referenced in the files you asked to compile (Eight.java in your example) for which no .class file exists. The compiler will automatically compile those too. I'm not sure if it compares the modified-time of the .java and corresponding .class file and recompiles the .java if it is newer, but I think it does.

I've found from further colleagues in development that they would generally use this during development if they want to build part of a project or subproject. Instead of building everything from scratch (which could take hours), they instead simply want to build a few projects, then they can specify other directories of the whole project in -sourcepath so that this can resolve classes. This means it can automatically find what dependencies it needs and build these as and when they are needed.
I've also been informed that this can be helpful during maven builds since the order of dependency gathering can be very unorganised. Instead of having missing dependencies because the jars aren't there at time of building, you can specify the entire code tree as the sourcepath. Although I imagine this isn't the best advice, it's helpful when somebody is having problems building.

Related

Decompile JAR, Modify it, Recompile again?

I have an older Jar file which I have to modify in order to get it to work.
I have already decompiled it with jd-gui and changed the parts that needed change. I would now like to know what I have to do in order to get a jar back?
I guess that I have to add the libraries, but there is no extra lib directory.
How do I put everything together?
#Stephen C
"Therefore, when you make your changes and recompile, the resulting ".class" file could have unexpected "
How exactly is this relevant? I only want the function of the Jar file and not a 1:1 copy of it.
The Jar file is unsigned. What exactly would be the reason to sign a Jar file anyway? If I start it from another program?
If you are missing dependencies after decompiling the entire jar, that could mean the jar did not include them. When you create a jar, you can choose not to include dependencies in it, but this assumes that they will be available on the classpath at runtime. Check if there are any relationships with other jar files on the classpath when you run the original jar. See this answer for details on how to achieve this. Once you have identified all the dependencies, you can easily compile the jar from an IDE such as IntelliJ/Eclipse or from command line.
As a side note, if the changes you would like to make to the jar are minor or isolated I recommend editing the bytecode (much easier for small edits). I prefer this bytecode editor.
If decompilation fails on some parts of the code, the functionality of the jar will not be restored upon recompilation. In this case, instead of going through all available decompilers and hoping that you can decompile that code, I suggest you identify the set of classes which you are trying to edit, modify the rest of the classes such that they compile and do not have any side effects on the classes which are of interest to you (do not delete anything that is referenced by these) and compile the jar (even if this isn't the jar you want). You can then extract only the class files which you wanted to modify from the jar, and overwrite them in the original jar. This will only work if your changes didn't have any side effects.
If you are asking how to create a JAR:
You can do it using a build tool such as Maven, Gradle, Ant and so on.
You can do it using a Java IDE
You can do it using the command line jar tool; see How to create a JAR file from the official Oracle Java tutorials.
But it there is no guarantee that what you are doing will actually work. Here are a couple of the problems.
If the original JAR file was signed, you won't be able to re-sign the new JAR ... unless you have the private key that was used when signing.
Decompilation is often inaccurate. There is no guarantee that the Java code that it produces is a correct representation of the original class. Therefore, when you make your changes and recompile, the resulting ".class" file could have unexpected / unwanted differences relative to the original.
I guess that I have to add the libraries, but there is no extra lib directory.
I'm not sure what you mean by that. The (new) dependencies of the JAR don't necessarily need to be in the JAR itself. It depends on what kind of JAR it is; e.g. is it a so-called "executable" JAR that you launch using java -jar my.jar ....
Use jar plugins for gradle or maven for example, or build it with intelliJ, here's an answer in another post: How to build jars from IntelliJ properly?
Since I had trouble getting the .jar file completely recompiled and working with my application, I wanted to share here the steps that were missing in the previous answers.
In my specific case, to make things as easy as possible, I wanted to only modify one .java file in the result of the decompilation and update it directly in the .jar file.
I started from the answer from Paulo Ebermann on a similar question, and I ended up with:
javac -source 8 -target 8 -d classdir -cp \
"\
path/to/dep1.jar; \
path/to/dep2.jar; \
path/to/myoriginal.jar \
" path/to/my/java/class.java
cd classdir
jar -uf path/to/myoriginal.jar path/to/my/java/class.class
Notes:
the path/to/myoriginal.jar which is in the path of the dependencies, this forces the java compiler to look for missing classses in the original .jar file rather than in the local decompiled code (which would require recompilation and more dependencies) and therefore I have intentionally left out the . path in the list of class paths
the -source 8 -target 8 which were required to force the class to compile for the appropriate version of the runtime machine.
the -uf which indicates that the jar tool should only update the file with the provided class

Compiling Java package throws errors for external Jars

Pretty basic problem here. So I have a Java package that I have created that has three classes (one has the main method). I am trying to use a few Apache Jars, and have added these to my build path in Eclipse. However Eclipse wont let me build and run it properly, so I am trying the command line. I have added the env var CLASSPATH and pointed it to my lib directory which hold the Apache Jars. However, when I try to use javac I get a bunch of errors:
package org.apache.xmlrpc does not exist
import org.apache.xmlrpc.client.XmlRpcClient;
I was reading the man page for javac and it said that:
If neither CLASSPATH, -cp nor -classpath is specified, the user class path consists of the current directory.
So I tried copying the Jars to the same location as my three source files, but no change.
Can someone please tell me what I'm doing wrong?
Thanks.
Classpath variable (or command line option of javac) must contain all jars explicitly. It cannot go through jar files stored in specified directory.
You can compile this by specifying the option -cp on the command line:
javac -cp foo.jar:bar.jar foo/bar/Baz.java
You then run it with the same option:
java -cp foo.jar:bar.jar foo.bar.Baz
It sounds like you've just set the classpath to the directory containing the jar files. You need to set it to the individual jar files, or use java.ext.dirs to set an "extension" directory containing jar files. I'd recommend using the specific jar files. Something like:
// Assuming Windows...
CLASSPATH = c:\libs\foo.jar;c:\libs\bar.jar
I'd also personally recommend specifying the classpath on the command line instead of using an environment variable - the latter will work, but it ends up being a bit more fiddly if you want to compile different projects against different libraries.
However, I'd actually recommend getting Eclipse working first, rather than retreating to the command line. It should be fine - if you could give us more information about what's failing in Eclipse, we may be able to help you with that instead.
The jar files in the current directory are not automatically included; that only refers to .class files in normal package/directory hierarchy. Jar files must be added either explicitly, or via a wildcard like javac -cp ./* (Assuming JDK6+)
(Some OSes may require an escape of the * to avoid globbing; OSX does not.)
I agree with previous answers, but I would also recommend to use proper java build tool - like ant (perceived easier to use, but not necessary) or maven ( perceived more difficult to use, but really worth learning )

prog.java and prog.class in Prog.jar file - How can I easily update this code?

Back story:
Long ago, before dinosaurs were around, there was a contractor that wrote a small Java program, that does some stuff, and prints some stuff to the screen.
Current: No one here knows much about Java but me - and I am not fluent.
The program works, but we kind of need just a bit more information on the screen. From the documentation available, I think I know how to go about that, and I definitely know how to print stuff, as it's simply using System.out.println() for this.
I have a .jar file, which I'm 99% certain is being loaded/used, which contains .java files matching every .class file within.
Is there an easy way to (and how might I) slightly modify one of the .java files, and "compile" the new version to replace the current matching .class file within the .jar?
I'm able to open and read the java source file, of course, but don't remember to procedures to turn java into "class" files, and especially not sure if I can just drop the resulting .class file into the .jar file as a replacement.
at the basic level, javac is the compiler; that will produce the .class files. There is also a "jar" command that will regenerate your jar file. Depending on the number of dependencies, that can get complicated quick. You can just type "javac" and "jar" (w/o quotes) to get the args to run it. Another option is to setup an ant build script...
so you could do something like (assuming windows OS):
javac -cp %MY_CLS_PTH% *.java
where MY_CLS_PTH is where any dependent classes are. If you have a package structure, this gets a little more complicated.
jar -cvf .\my_class_dir MyJarFile.jar
where my_class_dir is the directory that contains the .class files.
I think that is right (didn't run it myself) but that should be about the basics...
EDIT: There is a way to just add/re-add a single .class file to an existing jar file. Check out the "jar" command's usage, looks like "-u" will update it (of course you have to pass in the .class file). Also remember that any package structure you have in these classes, you need that directory structure and you reference the class via that dir structure. (Hope that is clear...)
Add myclass.class to the top level directory in myjar.jar:
jar uf myjar.jar myclass.class
Add myclass.class to the subdir directory in myjar.jar:
jar uf myjar.jar -C subdir myclass.class
Add a Version attribute to the manifest of myjar.jar:
First create a file somefile containing the line Version: "X.Y.Z", then
jar umf somefile myjar.jar

Ant: How to compile jar that includes source attachment

How do I create a jar with Ant that includes the source attachment?
Maven does this, but I'm living maven-free since '03 (well, 2009, but it doesn't rhyme).
I can't see any options in the javac task which does this.
I tried including the source .java files in the fileset passed to the javac task, but this didn't work. When I try to view the source through something like Eclipse, I still get the 'no source attachment' display.
Any suggestions?
You need two things to make it work in Eclipse:
Include the source files in the file set, so that each .java file exists in the same folder in the JAR as the corresponding .class file;
In the <javac> task, add the following parameter: debug="on". That way, you encode information into the .class files about the corresponding .java files that tools like Eclipse need in order to connect the two together.

Easy command line Java compile

So I have to send a java project to someone outside our company who has no experience with java and they need to compile it. Is there an easy way to do it on the windows command line that does not require writing out lists of the files?
Personally I think javac should be smart enough to handle
javac *
when in the folder just under the root of the package hierarchy. Is there anything in this ballpark?
Edit: The source folder structure is complex and the is no single entry class so some of the ideas mentioned so far won't work. Thanks though! Think 9 levels deep with code on many levels.
From the folder that represents the base of your package hierarchy, assuming your entry point class is called Main, in a package called app,
javac -classpath . app/Main.java
should generate the correct class definitions. The compiler will ferret out the dependencies and compile whatever other classes are needed. The class files will appear in the same directory as their source files.
If, as you say, you have 'more than one entry' class, you will have to at least identify all those top level classes from the dependency hierarchy, which can be listed as further params to javac, specifying the packages as they occur. Like so, assuming you also need to start with other.Entry
javac -classpath . app/Main.java other/Entry.java
Note that you will still have to figure out which of your classes are tops of independent dependency hierarchies, whether you are creating an ant script or doing it this way.
javac BaseProgram.java will compile BaseProgram.java from the current directory, and all classes it references that are available in source code, in the same directory tree.
If BaseProgram references Class1 and Class2, and they are available in Class1.java and Class2.java in the same directory, then they too will get compiled. Likewise if they are in a package, and the package directory is available, they will be compiled.
Provide them with an Ant script that does the build with the correct libraries on the classpath, etc. The script can also do other tasks such as building JARs, etc.
This requires that that person downloads and installs Ant, but that is not hard. (And there is nothing to stop you from providing them with an appropriate Ant distro to install. Or even sending them a distro ZIP file that has a copy of Ant "preinstalled" in the tree.)
Providing an Ant script means that you avoid them falling into Java newbie traps such as forgetting to set the classpath, being in the wrong directory, forgetting to recompile dependent files and so on. Plus, it is more "professional".
You can build a file listing all the classes you want to compile (extracted from the javac man page) -
Example - Two Arg Files
You can create two argument files -- one for the javac options and
the other for the source file-
names: (Notice the following lists have no line-continuation
characters.)
Create a file named options containing:
-d classes
-g
-sourcepath /java/pubs/ws/1.3/src/share/classes
Create a file named classes containing:
MyClass1.java
MyClass2.java
MyClass3.java
You would then run javac with:
% javac #options #classes
Or you can use *.java on the command line e.g.
javac greetings/*.java
(Again from man javac)
Or why don't you just compile the source into a jar that your customer can run using their JRE - especially considering they are not Java savvy?
A simple way would be by using:
javac *.java.

Categories