how to compile only changed source files using ANT - java

I am trying to write ant build for compiling source folder here is my script target for compiling.
<target name="compile" depends="init">
<javac srcdir="${src.dir}" destdir="${classes.dir}" debug="true">
<classpath refid="master-classpath"/>
</javac>
</target>
In my project I have near about 1000 .java files. When ever a single .java file is changed above target tends to compile all .java files. Which make development very slow. I just want to know is there any way or code to change the behavior of task to compile only modified or changed .java file rather than all .java file.
Please help me.

As I understand, compiling only the modified java files is the default behavior of ant javac task. Ant uses the timestamp of a .java file and its corresponding .class file to determine if the a Java file needs to be recompiled. I have used it in many projects and never have issues.
Here are a few things you can check
Is your source tree directory structure matching your Java package
structure? Please see Why does ant always recompile all my Java
files?. This is probably the number 1 reason since it is in the FAQ.
I see your compile target depends on init target? What does the init target do? Does it depends on any clean target or does it cleanup the .class files?
What is your ant version? Can you try a different ant version to see if you still have the same problem?
If none of the above fixes your issue, after the compilation, can you manually check the .class file timestamp and compare it with the timestamp of the corresponding .java file?

So if you are not deleting the classes directory in any dependent task say 'clean' the javac task inherently compiles only the changed java files. It is an out of the box feature that javac task provides. Hope it helps.

Related

Apache ant javah task not working with jdk 10+

We are shifting from jdk 1.8 to jdk13. In our build.xml we have
<target name="generate-native-headers" depends="compile,resolve" description="Java to Native">
<javah class="com.zimbra.znative.IO" outputfile="${build.dir}/IO.h" classpathref="build.class.path"/>
</target>
But java10+ is not supporting javah anymore so I found we can achieve this with javac "nativeheaderdir" here - https://ant.apache.org/manual/Tasks/javac.html#nativeheaderdir
So I tried to convert above javah task to javac as below
<target name="generate-native-headers" depends="compile,resolve" description="Java to Native">
<javac srcdir="src/java/com/zimbra/znative" nativeHeaderDir="${build.dir}" classpathref="build.class.path" includes="src/java/com/zimbra/znative/IO.java" />
</target>
Now the missing javah error gone, but I don't see IO.h file generated in my build directory.
Can anyone help me, how to do this? Your help is really appreciated, thank you.
Note: directory src/java/com/zimbra/znative have around 5-6 .java files. I mentioned an example for 1 file only.
I can't see an obvious way to persuade the javac task to do this. nativeHeaderDir= generates the headers, but won't concatenate as you have found.
A post-javac workaround might look like this - combine the per-class headers into a single file using the Ant <concat> task:
<concat destfile="IO.h">
<header>/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
</header>
<concat destfile="IO.h">
<fileset dir="${build.dir}" includes="*.h" />
<filterchain>
<linecontainsregexp negate="true">
<regexp pattern="(#include .jni.h.)|(DO NOT EDIT THIS FILE - it is machine generated)" />
</linecontainsregexp>
</filterchain>
</concat>
</concat>
The basic idea is simple: find all the header files generated by javac, concatenate them into one header with the name required. Optionally, along the way, strip out the repeated #include and comment lines. The result should look the same as a JDK 8 javah-produced header.
With the below changes, I am able to create header files. But the generated file name is "com_zimbra_znative_IO.h". It should be "IO.h"
<target name="generate-native-headers" depends="compile,resolve" description="Java to Native">
<javac srcdir="src/java/com/zimbra/znative" destdir="${build.dir}" nativeHeaderDir="${build.dir}" classpathref="build.class.path"
includes="IO.java"
excludes="Process.java,ProcessorUsage.java,ResourceUsage.java,Util.java,ProxyInfo.java" />
</target>
Generating header files using javac task from ant should be along with your generated class file. So if the corresponding class file already available and no change in the source file (other words your compiled class modification time is higher than the java source file) then the javac ant task won't do any action on that class file including native header file generation.
You have to perform few check,
Is your srcdir attribute pointing to the right directory which starts your packages. (ex. your class name is com.zimbra.znative.MyClass and the file path is src/java/com/zimbra/znative/MyClass.java then your srcdir should be point to src/java.
Provide destdir where your compiled class files will be generated, otherwise it will generate inside your srcdir.
Normally includes not required if you want to generate all header files which all the class having native method in your srcdir
<target name="generate-native-headers" depends="compile,resolve" description="Java to Native">
<javac srcdir="src/java" nativeHeaderDir="${build.dir}" destdir="${build.classes}"
classpathref="build.class.path" includes="src/java/com/zimbra/znative/IO.java" />
</target>
Before running the ant make sure the IO.class file not exist or the source .java file has recent changes than the existing class file. then see it will generate the class file and header files. Header files should be inside ${build.dir}

Some clarification about how ant copy some files into a folder?

I am pretty new to ant (I came from Maven and Ant is a nightmare for me !!!)
I have this target:
<target name="linuxdriver"
description="Linux Driver">
<copy file="${deps.linuxdriver.dir}/${deps.linuxdriver.name}" tofile="${project.datadir}/${deps.linuxdriver.name}"/>
<copy file="${deps.linuxdriver.dir}/${deps.linuxdriver.name}" tofile="${project.deploy}/data/${deps.linuxdriver.name}"/>
<chmod perm="+x" file="${project.datadir}/${deps.linuxdriver.name}"/>
<chmod perm="+x" file="${project.deploy}/data/${deps.linuxdriver.name}"/>
</target>
and I have also a property file in which there is definied the "variable" (are named variable?) used in the previous ant code, specifically I have:
project.datadir = ${basedir}/data
project.deploy.dir = Release
project.deploy = ${basedir}/../${project.deploy.dir}
And now I have some doubts:
1) What represents ${basedir}? A specific directory? What?
2) Using the previous information what exactly are the two destination folder in which the files are copied (using the "copy file...to file" tag)?
That concept of basedir is described in the ANT manual:
Built-in properties
Writing a simple Buildfile
It's normally set to be the directory from where you run the build (can be overridden).
If you need to see how properties are resolved, consider running your build in debug mode

Ant build using two projects

I have two separate projects inside of eclipse: "project" and "pinclude"
Project includes pinclude, so without somehow include the java files for that project inside of my build.xml, javac will always return errors.
How do I got about including .class files inside of ant/javac? I've tried searching for a solution, but so far I've only came up with ways of adding jar files. Would creating a jar of all the "pinclude" .class files fix my problem?.
Thank you for your help.
NOTE:
I'm sorry for the poor naming convention; These are just projects I made to figure out this problem out.
Also, please ignore srcdir and destdit, they are not important.
Build.xml
<project name="project" basedir="." default="dist" >
<target name="dist" >
<javac destdir="bin"
srcdir="${basedir}\myfileslocation\" >
</javac>
</target>
How do I got about including .class files inside of ant/javac? I've
tried searching for a solution, but so far I've only came up with ways
of adding jar files. Would creating a jar of all the "pinclude" .class
files fix my problem?.
Yes. It helps to separate functionality into smaller modules. If you export one project (pinclude in this case) into a jar and import(via classpath) in another , that is the most correct step.
I'm not 100% sure what you need. Are you saying that you have class files from pininclude, and you need to include them in your <javac> compile task? It would be something like this:
<javac destdir="bin"
srcdir="${basedir}/myfileslocation">
<classpath>
<pathelement path="${path.to.pininclude.javac.files}"/>
</classpath>
</javac>

Javadocs Are Disappearing

I just whipped up a medium-sized utilities JAR to be used by many other Eclipse projects. I spent a good deal of time writing tedious javadocs so that it would be pretty obvious how to use the API.
I used Ant to throw everything in a JAR, and I am now adding that JAR to the build path of a new project. But low and behold...when I mouseover any of my utility classes/methods, the tool tip that pops up says:
Note: this element neither has attached source nor attached Javadoc
and hence no Javadoc can be found.
What is going on here? What's the solution? Here is the dist Ant target that JARs up my utilities project:
<target name="dist" depends="compile">
<jar jarfile="dist/testing-utils.jar">
<fileset dir="bin/main" />
<fileset dir="src/main/config" />
</jar>
</target>
What else do I need to configure?!? Thanks in advance!
The java compile task, which I assume you're using in the "compile" target, generates bytecode from your Java source. This bytecode will not include comments by design and thus your distribution jar will not include comments.
What you are probably looking for is to attach the source in Eclipse to get access to the Javadocs. You can attach source to your library jars by right clicking on the target jar, selecting properties, select Java Source Attachment and provide the location of the source jar. You can also provide a folder location, if you want to reference your project directly.
To generate a Jar that includes javadocs and source, you may use the following in ant:
<target description="bundle sources in a jar" name="package-sources">
<jar basedir="src" destfile="build/release/${ant.project.name}-${project.version}-sources.jar"/>
</target>
Reference
If you do want to ship sources then johncarl's answer is probably correct; it is the first of the two options offered by eclipse -- attached sources. If you don't want to ship sources then you need to generate javadocs, see the ant javadoc task. With that approach, you would have two jars, one containing the compiled classes and another containing documentation. In eclipse, your users will add the class jar to project and then attach to it the javadoc jar.

Java: How to compile a runnable jar from packages?

My Java application has got a package structure similar to this:
src/com/name/app
src/com/name/app/do
src/com/name/utils/db
How would I go about compiling Java files in these directories in to a runnable jar? I need to package required libraries into the generated JAR (jdbc).
I've always done these things in Eclipse but now I need to supply a couple of people with a way to compile the repository without the use of eclipse and I was thinking of making a makefile or a script that invokes the necessary javac pattern.
Take a look at Ant. It's a relatively simple build tool to understand, and provides everything that meets your requirements. Here's a quick skeleton build.xml to get you started:
<project name="my_app_name" default="jar">
<target name="compile">
<javac srcdir="src" destdir="bin">
<classpath>
<fileset dir="lib">
<include name="**/*.jar" />
</fileset>
</classpath>
</javac>
</target>
<target name="jar">
<jar manifest="manifest_file" destfile="dist/my_app_name.jar">
<fileset dir="bin" />
<fileset dir="lib" />
</jar>
</target>
You need to create a manifest file that will tell the java process which class holds the "main" method. Here is a good place to start learning about manifests.
As an alternate that produces really cluttered Ant build files, you can right click on your Eclipse project and choose "Export...", then choose "General > Ant Buildfiles".
Anyway, that should get you started. You can ask more specific questions as you run into them.
First of all, consider using Ant for such a task.
But since you asked for a manual process, you need to first create a manifest file, like so:
Manifest-Version: 1.0
Created-By: 1.6.0 (Sun Microsystems Inc.)
Class-Path: lib/jdbc.jar lib/otherlib.jar
Main-Class: com.name.app.MainClass
Replace the contents of Class-Path with your libs, and Main-Class with the fully qualified name of your main class.
Then, you need to generate the actual .jar, using the following command:
jar cfm app.jar MANIFEST.MF src/com/name/app/*.class src/com/name/app/do/*.class
Where MANIFEST.MF is the previously mentioned manifest file, and the rest is the folders where your .java classes lie in.
Finally, to run your app, you simply execute: java -jar app.jar.
Consider using Ant to do this. http://ant.apache.org/
I recommend that you use Apache Ant to implement your build scripts.
If implemented correctly, Ant is easy to use and the build scripts can be run on any platform that you can install a JDK on. Indeed, with a little bit of work, you can even set up your project so that users don't even need to download / install Ant. (Hint: add the Ant JAR files and a wrapper script to your project distro)

Categories