Mon 15 Dec 2008
Adaptation of Netbeans Ant Build for Integration – CopyLibs Issue
Posted by admin under Java , ant , build management , netbeans[15] Comments
(Netbeans 6.1, external ant 1.7 )
If you create a standalone Java application project (i.e. one that comes with all used external jars and libs included, so there are copied to dist/lib) in Netbeans, commit the changes to version control, checkout or update the changes to your test and integration server and try to build the same project there, you might run into problems even if you used an external ant for building the same project in Netbeans on your local machine. (Look here for an explanation why jars are not includeable in the distribution jar itself.)
On the integration server the libs will not be copied to the dist/lib folder. The reason is that Netbeans is using an own ant task org.netbeans.modules.java.j2seproject.copylibstask which extends import org.apache.tools.ant.taskdefs.Jar and this jar will just not be found on the integration server except you completly reconstruct your local build environment on the integration server, what will be difficult if you are using a CI system (in my case it’s hudson). So let’s have a closer look at this issue and some possible solutions:
There are two principal ant build files Netbeans is using during the build process: build.xml – user can edit this file, build-impl.xml – user should not edit this one. Also have a look at this post Visualizing Default Ant Task Dependencies for Netbeans build-impl.xml.
Where and when the Libs are copied to dist/lib by the CopyLibs task?
For that we have to look into build-impl.xml. The libs are copied when the jar target ist executed:
<target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
There are several jar related tasks, which are executed when certain conditions are valid, that means certain variables are set. The interesting task here is -do-jar-with-libraries:
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
This task will be executed if property manifest.available+main.class+mkdist.available is set. Especially if the path to CopyLibs is set.
<condition property="manifest.available+main.class+mkdist.available"> <and> <istrue value="${manifest.available+main.class}"/> <isset property="libs.CopyLibs.classpath"/> </and> </condition>
Where does this path comes from. It is read in
<target depends="-pre-init,-init-private,-init-libraries" name="-init-user"> <property file="${user.properties.file}"/>
For example from: .netbeans/6.1/build.properties.
libs.CopyLibs.classpath=/home/user/bin/netbeans/java2/ant/extra/org-netbeans-modules-java-j2seproject-copylibstask.jar
libs.CopyLibs.javadoc=
libs.CopyLibs.maven-pom=
libs.CopyLibs.src=That’s a problem! The build.properties file will not be found on the integration server, the properties libs.CopyLIbs.classpath and manifest.available+main.class+mkdist.available will not be set, the task -do-jar-with-libraries will not be executed.
What do you need CopyLibs task for?
Look at how the task is used (I included some echos for better understanding):
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries"> <property location="${build.classes.dir}" name="build.classes.dir.resolved"/> <echo message="-------------run.classpath------- ${run.classpath}"/><echo message="--------- build.classes.dir.resolved ------ ${build.classes.dir.resolved}"/> <pathconvert property="run.classpath.without.build.classes.dir"> <path path="${run.classpath}"/> <map from="${build.classes.dir.resolved}" to=""/> </pathconvert> <echo message="-------- run.classpath.without.build.classes.dir ---- ${run.classpath.without.build.classes.dir}"/> <pathconvert pathsep=" " property="jar.classpath"> <path path="${run.classpath.without.build.classes.dir}"/> <chainedmapper> <flattenmapper/> <target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries"> <globmapper from="*" to="lib/*"/> </chainedmapper> </pathconvert> <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/> <copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}"> <fileset dir="${build.classes.dir}"/> <manifest> <attribute name="Main-Class" value="${main.class}"/> <attribute name="Class-Path" value="${jar.classpath}"/> </manifest> </copylibs> <echo>To run this application from the command line without Ant, try:</echo> <property location="${dist.jar}" name="dist.jar.resolved"/> <echo>java -jar "${dist.jar.resolved}"</echo> </target>
and then study the java code. So not much problems here. Mainly copying some files and using standard jar functions, also providing some README file. Below I will show how you can workaround using this task.
Solutions?
First, you could copy the CopyLibs Task jar somewhere and set the property libs.CopyLibs.classpath in your build.xml to the appropriate location or use project.properties file which comes with the Netbeans build scripts. There are little chances that this task will change and you will have to update the CopyLibs Task jar.
Second, you could build your standalone application using One-JAR overriding the jar target from build-impl.xml.
Third, don’t change build structure but avoid using CopyLibs task. Here is an example how to change build.xml:
First make macrodefs and presetdefs from build-impl.xml callable by declaring appropriate namespaces in build.xml:
<project name="Test" default="default" basedir="." xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3"> <description>Builds, tests, and runs the project Test.</description>
Then redefine the jar target a little and override it in build.xml (echos only for feedback):
<!-- target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries" --> <target depends="init,compile,-pre-jar,-post-jar" description="Build JAR." name="jar"> <property location="${build.classes.dir}" name="build.classes.dir.resolved"/> <echo message="-------------run.classpath------- ${run.classpath}"/> <echo message="--------- build.classes.dir.resolved ------ ${build.classes.dir.resolved}"/> <pathconvert property="run.classpath.without.build.classes.dir"> <path path="${run.classpath}"/> <map from="${build.classes.dir.resolved}" to=""/> </pathconvert> <echo message="-------- run.classpath.without.build.classes.dir ---- ${run.classpath.without.build.classes.dir}"/> <pathconvert pathsep=" " property="jar.classpath"> <path path="${run.classpath.without.build.classes.dir}"/> <chainedmapper> <flattenmapper/> <globmapper from="*" to="lib/*"/> </chainedmapper> </pathconvert> <echo message="--- build.classes.dir --- ${build.classes.dir}" /> <copy todir="${dist.dir}/lib" flatten="true"> <path> <pathelement path="${run.classpath.without.build.classes.dir}"/> </path> </copy> <j2seproject1:jar manifest="${manifest.file}"> <j2seproject1:manifest> <j2seproject1:attribute name="Main-Class" value="${main.class}"/> <j2seproject1:attribute name="Class-Path" value="${jar.classpath}"/> <!-- if you deal with versions --> <!-- j2seproject1:attribute name="Build-Version" value="${version}" / --> </j2seproject1:manifest> </j2seproject1:jar> <echo>To run this application from the command line without Ant, try:</echo> <property location="${dist.jar}" name="dist.jar.resolved"/> <echo>java -jar "${dist.jar.resolved}"</echo> </target>
… and you’ve got rid of the annoying CopyLibs task thing.
December 30th, 2008 at 1:47 pm
Thanks, I’ve been annoyed by this too.
January 27th, 2009 at 10:35 pm
Thanks for posting this. This is one of the few references on the Internet about this issue.
I think the better fix is to roll back to Netbeans 6.1. It has no issues with the build. I consider this a bug and I intend to file one. This is one of the stupidest things they could have done and it’s the reason I’m not going to use Netbeans 6.5.
Setting up a decent build for a Java project is a real pain and Netbeans made it simple until they broke it unnecessarily.
January 28th, 2009 at 11:05 pm
Well… I filed a bug under bug #157576 at Netbeans.org.
Here’s the URL: http://www.netbeans.org/issues/show_bug.cgi?id=157576
We’ll see if anyone does anything with it. If it continues through the next version, I may have to either implement my own Ant script, which is too bad really because the IDE used to handle this perfectly, or go to Maven *choke*. I resent baby sitting the build script configuration in Maven. I hate the pom.xml file. But Maven is better then the Netbeans 6.5 build.
April 7th, 2009 at 10:14 pm
OMFG what a pain in the a55.
I have my IDE (nb). I have my java. It compiles, runs like a charm. But it is absolutely riddled with nasty version issues such as the above, ant snafus like “else” statements in 1.6.5 that break 1.6.1, annotations in Junit you name it.
So when I try to check in my code with everyone else’s the thing b0rk5 big time. What a tedious business. I have actually gond back to HAND CODING my java in Emacs and using Makefiles, javac and jar because that way everyone knows what going on.
I am severely annoyed with this. Not content with layer upon layer of complex, superfluous XML, config and all the other bollox that utterly removes any vestiges of usefulness from J2EE – I now find even the most basic tasks on Netbeans have become fundamentally unusable to.
Its embarrassing. Ive got Python wankers LAUGHING at me here because between my wonderful OS and my wonderful programming language the propellorheads have inserted so many dependencies and magic happenings that any java development is now more or less impossible.
Thanks. But not thanks. Netbeans is gone.
June 3rd, 2009 at 3:54 am
I will not add to what has allready been told.
All I have to say is THANKS. You´ve just saved my ars.
Anyway, this issue came back on 6.7RC1.
…interesting, ain´t?
August 30th, 2009 at 5:31 am
Thanks, got annoyed by this too. :-S
May 26th, 2010 at 2:19 pm
Many thanks. I’ve just moved from Netbeans 6.5 to 6.8 and they seem to have dropped the CopyLibs task. Net result, my netbeans 6.5 generated build.xml does not work any longer!
I’ve changed it as you suggest and all is back.
Thanks
June 8th, 2010 at 12:29 am
I haven’t read the explanation of the problem, but the solution (3) indeed solved the issue!
Thanks, and I’ll come back later to read about the root cause.
June 10th, 2010 at 11:31 pm
good post! 9/10…. bookmarked this blog
, do you have you many more like this?
May 23rd, 2011 at 4:19 pm
With hudson you can just set the necessary properties in the Build, Invoke Ant, Advanced, Properties section when you configure your job.
For example, with netbeans 6.9.1:
libs.CopyLibs.classpath=/opt/netbeans-6.9.1/java/ant/extra/org-netbeans-modules-java-j2seproject-copylibstask.jar
libs.CopyLibs.javadoc=
libs.CopyLibs.maven-pom=
libs.CopyLibs.src=
May 31st, 2011 at 4:03 am
Here’s an alternative method. The first target is designed to generate the JNLP file, even if Netbeans has it set off (too reside on a headless server without Netbeans installed), the rest are overrides of Netbeans targets. Some of these overrides aren’t necessary I’m sure and this may not do all that’s required for all projects, but here’s the code. Anyone know why the Netbeans team seems to have duplicated so much of the functionality already found in ant?
May 31st, 2011 at 4:05 am
May 31st, 2011 at 4:06 am
Ha. Needed a code tag. Here’s that server only target:
May 19th, 2012 at 8:45 am
What’s up to every one, the contents existing at this web page are truly remarkable for people knowledge, well, keep up the nice work fellows.
August 9th, 2012 at 5:01 pm
Build failure !
Target “-init-macrodef-copylibs” does not exist in the project
I have this error.