I've got a jar file that will match a certain pattern, but contains a version number that will change over time (it's placed there using a dependency manager). It is the only jar file that will ever be in that directory. Is there a way I can invoke Ant's java task using the directory and (if necessary) a pattern to find the jar file rather than statically specifying the filename? I'd rather not have to update build.xml every time the version changes.
So given something like this:
/path/to/some/jarfile-3.24.8.jar
... that might later look like this:
/path/to/some/jarfile-4.3.2.jar
Can I achieve the equivalent of this?
<java jar="path/to/some/jarfile-*.jar" fork="true" spawn="true" />
Thoughts
I was thinking maybe I could rig something up using the fileset task, but I couldn't find a way to reference it from the java task (and the java task doesn't support the <include> nested tag like some other tasks do).
... after further research, pathconvert looks like it might help. I'm not familiar enough with the mechanics to see how to piece things together though.
After some trial and error, I found a solution using fileset and pathconvert - here's my simplified solution:
<fileset dir="/path/to/some" id="jarfile-path-contents" includes="jarfile-*.jar" />
<pathconvert property="file.jarfile" refid="jarfile-path-contents" />
<java jar="${file.jarfile}" fork="true" spawn="false" />
I used fileset in order to specify the pattern and location of the jar.
I used pathconvert to place the found jar file reference into a property.
Then I was able to simply reference that property in my java task.
Based on suggestions from my previous question on programmers.SE, I have split my current project into three subdirectories (swing, common, and android) and created separate Ant scripts in each one. Now I easily created NetBeans projects in the swing and common and was able to set the proper dependencies to compile the Swing app. Then when I go to use classes from the "common" project, I realize that I need to have another Ant script for the Android build process.
So now I have four Ant scripts used to build different parts of my project, along with separate src, test, and other supporting directories for each project. The first problem I encountered is that both NetBeans and the ant script for my "android" project want to use "build.xml" in my "common" project for various build tasks, some of which have name clashes. I decided I want to consolidate all of these projects back into one directory with src, test, etc. subdirectories. The source code is already organized into separate packages for each of the projects and I thought this would make it simpler since I can have a single Ant build script with separate tragets for the Swing and Android builds.
Now the problem I am running into is that when I try to build the "android" project, it wants to build all the Swing classes as well. This brings up a lot of compiler errors. I have even modified the <javac> task in an attempt to exclude the bbct.swing package hierarchy from compileing:
<javac encoding="${java.encoding}"
source="${java.source}" target="${java.target}"
debug="true" extdirs="" includeantruntime="false"
destdir="${out.classes.absolute.dir}"
bootclasspathref="project.target.class.path"
verbose="${verbose}"
classpathref="project.javac.classpath"
fork="${need.javac.fork}"
excludes="${source.dir}/bbct/swing/**"> <------- here
<src path="${source.absolute.dir}" />
<src path="${gen.absolute.dir}" />
<compilerarg line="${java.compilerargs}" />
</javac>
Ant still insists on compiling my classes which use Swing, though.
So I have two questions:
What is the best way to organize this project? Would you suggest one of the two that I have tried or something else entirely?
If I stick with my most recent solution, how do I make javac ignore the Swing-based source files in my project?
Between How to exclude a source package using javac in Ant? and How can I exclude sources in a javac task in ant?, I found a solution using an <exclude> elements, rather than the excludes attribute. Here is the relevant <javac> task:
<javac encoding="${java.encoding}"
source="${java.source}" target="${java.target}"
debug="true" extdirs="" includeantruntime="false"
destdir="${out.classes.absolute.dir}"
bootclasspathref="project.target.class.path"
verbose="${verbose}"
classpathref="project.javac.classpath"
fork="${need.javac.fork}">
<src path="${source.absolute.dir}" />
<exclude name="bbct/swing/**" />
<src path="${gen.absolute.dir}" />
<compilerarg line="${java.compilerargs}" />
</javac>
Addendum:
I suspect that the core of this solution lies in using a path relative to the source attribute or the <src> element. This means that an excludes element should work just as well using a relative path. I haven't tested this yet, though.
I want the javac task to use jars from the system classpath, by which I mean the classpath that is set in the shell's environment before ant is started. That classpath is
CLASSPATH=D:\local\lib\java\*;.;C:\lib\java\*;C:\lib\java\db\*
on my system. I have popular jars there that are used by many projects. The basic snippet I use in the build file is
<target name="build">
<mkdir dir="${obj}" />
<javac srcdir="${src}" destdir="${obj}"
includes="**/*.java"
excludes="**/package-info.java **/deprecated/*.java"
includeAntRuntime="no" debug="true" debuglevel="source,lines"
>
<compilerarg value="-Xlint"/>
</javac>
</target>
That way ant only passes the output directory as classpath.
[javac] '-classpath'
[javac] 'D:\dev\tbull-projects\jsonc\obj'
(jsonc is the project I'm working on, and D:\dev\tbull-projects\jsonc is the working directory.)
I browsed the documentation for a while and came up with two attempts. First one was adding the attribute classpath="${java.class.path}" to the javac tag. That would pass a tremendously long classpath to the compiler, listing every single jar from ant's own lib directory and finally tools.jar from the JDK. Not the classpath that I wanted.
The second shot was setting
<property name="build.sysclasspath" value="first" />
before javac was invoked, and that got me in the right direction. Now these lines were among the output:
dropping D:\dev\tbull-projects\jsonc\D:\local\lib\java\* from path as it doesn't exist
dropping D:\dev\tbull-projects\jsonc\C:\lib\java\* from path as it doesn't exist
dropping D:\dev\tbull-projects\jsonc\C:\lib\java\db\* from path as it doesn't exist
dropping D:\dev\tbull-projects\jsonc\C:\Program Files\Java\jdk1.6.0_18\jre\lib\sunrsasign.jar from path as it doesn't exist
dropping D:\dev\tbull-projects\jsonc\C:\Program Files\Java\jdk1.6.0_18\jre\classes from path as it doesn't exist
Well, you can imagine that these paths really don't exist. I just don't get why ant constructed them this way. It would know how to do path arithmetic on Windows, would it?
Maybe my approach is flawed more fundamentally, so I'll let you know what I'm actually after. So I'm developing this project (a library), which uses another library. The project is gonna be open source, so I want other developers to be able to build it after they have downloaded the dependency library and placed it somewhere in their classpath.
From what I saw in other questions about ant+classpath, it appears that it's a custom fashion to distribute the dependency libs with the source code (so the classpath can be just like ./libs). But I surely don't want to have jars in my git repo. So how could that be done?
Set includeJavaRuntime=true in javac task.
<target name="build">
<mkdir dir="${obj}" />
<javac srcdir="${src}" destdir="${obj}"
includes="**/*.java"
excludes="**/package-info.java **/deprecated/*.java"
includeAntRuntime="no" includeJavaRuntime="true"
debug="true" debuglevel="source,lines">
<compilerarg value="-Xlint"/>
</javac>
</target>
Why wouldn't you set CLASSPATH in Ant? It's perfectly suited to do just that. You're making a mistake if you do anything else. Not only will it work, bu your build.xml will document the requirements as well.
When javac compiles the code , it tries to find the files in rt.jar in a symbol file called ct.sym (which is also present in lib directory). some files are missing in this symbol file.
i have to add a compile option to ignore symbol file and look directly in rt.jar.
so i have used this option -XDignore.symbol.file for ant i put this value in javac tag. it works perfectly if you use eclipse or any other ide .
<compilerarg value="-XDignore.symbol.file"/>
So , whenever you get ClassNotFoundException in using classes from rt.jar , and if the class is still present there , just try to add this argument in java compiler
To reference rt.jar from ant you may use:
<fileset dir="${java.home}/lib" includes="rt.jar"/>
Original details were found here: http://www.javaroots.com/2013/01/javac-error-using-classes-from-rtjar.html
If someone is new to java/ANT world, people who suggest maven are idiots
whatever happened to KISS principle?
OP, instead of using javascript abomination try this
<project default="build">
<property name="src" value="src" />
<property name="obj" value="obj" />
<property name="parent.dir" value="/jakarta-tomcat/common/lib" />
<path id="project.class.path">
<pathelement location="lib/" />
<fileset dir="${parent.dir}" includes="**/*.jar" />
</path>
<target name="build">
<delete dir="${obj}" />
<mkdir dir="${obj}" />
<javac srcdir="${src}" destdir="${obj}" includes="**/*.java" excludes="**/package-info.java **/deprecated/*.java" debug="true" debuglevel="source,lines" classpathref="project.class.path" />
</target>
It's pretty clear the folks behind java, and (or at least), ant, really really really don't want to see $CLASSPATH end up as storage for user installed libraries of the sort that 95% of other mainstream languages (C/C++, perl, python, ruby, etc. etc.) use. So this is a tough paradigm to swallow if you are used to general programming in most other mainstream languages.
The disinclination goes so far that it is obvious ant intentionally strips $CLASSPATH out of the environment, but an easy way around this is to just use a different variable.
<property name="classpath" location="${env.JAVALIBPATH}"/>
This will then work, no fuss, with both <javac> and <java> commands (classpath="${classpath}) which is good, because if you try this instead:
<property name="classpath" location="${env.CLASSPATH}"/>
There is no includeAntRuntime="false" option to <java> which would allow this to work. You simply cannot get $CLASSPATH in and someone has gone to lengths to make sure of it (without, apparently, and yikes, adding in a ponderous javascript hack).
Of course that means you need to use a separate env variable and for your distributed/production version stick to the Java "Sorry no user libs!" paradigm. That's not a big problem if you use a variable name that, if it becomes involved, will almost certainly be undefined on the target system.
Alternatively, there are the Maven Ant Tasks. These will allow you to use Maven's dependency mechanism in a way that, IMO, is cleaner than Ivy. But it's still not a great solution.
Soo... seems I have to answer the question myself. Passing the original classpath to the javac task can be achieved with this:
<!-- load environment into the env property -->
<property environment="env" />
<javac srcdir="${src}" destdir="${obj}"
includes="**/*.java"
excludes="**/package-info.java **/deprecated/*.java"
includeAntRuntime="no" includeJavaRuntime="no"
debug="true" debuglevel="source,lines"
>
<!-- add -classpath option manually -->
<compilerarg value="-classpath" />
<compilerarg value="${env.CLASSPATH}" />
<compilerarg value="-Xlint"/>
</javac>
That does the trick at least so far that the javac task now gets passed the correct classpath. Yet it will still not work, javac now spits these complaints:
[javac] warning: [path] bad path element "D:\local\lib\java\*": no such file or directory
[javac] warning: [path] bad path element "C:\lib\java\*": no such file or directory
[javac] warning: [path] bad path element "C:\lib\java\db\*": no such file or directory
This is a straight lie, these paths do very much exist. I use them all the time, and if I manually craft an equivalent javac invocation at the shell, it works like a charm. I suspect ant's javac doesn't resolve the jar files in those directories. I have to examine that.
UPDATE
It is indeed as I suspected, the wildcard is not resolved to the individual present jar files by the javac task. I managed to do the resolving manually, and now it works as it should. And that resolving was in fact a struggle on its own. So I'll leave the solution here for those poor souls fighting the same stupidity, hopefully before they ask people that have nothing else to do than bullshitting around (yes Anon, talking about you).
Turns out, ant lacks the most basic functionality that you would expect from a build tool. Also turns out that I'm not the first one to notice that. While solutions are rare, there is a very good post about Using JavaScript to make Apache Ant less painful, which really saved my day. Yes, ant can indeed be scripted, which seems not to be widely known, although it is not kept secret. You can safely assume, that Javascript is already available without installing additional libraries if you run ant on Java 6.
Soo... down to business. Here is the thing:
<target name="expand_classpath">
<script language="javascript"><![CDATA[
// the original classpath
var ocp = java.lang.System.getenv("CLASSPATH");
// ... split in parts
var ocp_parts = ocp.split(project.getProperty("path.separator"));
// where our individual jar filenames go,
// together with pure directories from ocp_parts
var expanded_parts = [ ];
for each (var part in ocp_parts) {
if (part.endsWith('*')) {
var dir = part.substring(0, part.length() - 1);
var f = new java.io.File(dir);
// don't know how to construct a java.io.FilenameFilter,
// therefore filter the filenames manually
for each (var file in f.listFiles())
if (file.getPath().endsWith('.jar'))
expanded_parts.push(file.getPath());
} else
expanded_parts.push(part);
}
var expanded = expanded_parts.join(project.getProperty("path.separator"));
project.setProperty("classpath.expanded", expanded);
]]></script>
<!-- <echo message="classpath.expanded = ${classpath.expanded}" /> -->
</target>
<target name="build" depends="expand_classpath">
<mkdir dir="${obj}" />
<javac srcdir="${src}" destdir="${obj}"
classpath="${classpath.expanded}"
includes="**/*.java"
excludes="**/package-info.java **/deprecated/*.java"
includeAntRuntime="no" includeJavaRuntime="no"
debug="true" debuglevel="source,lines"
>
<compilerarg value="-Xlint"/>
<compilerarg value="-Xlint:-fallthrough"/>
</javac>
</target>
I will assume that your "popular" JARs are well-known open-source projects. This means that they're available in the Maven central repository.
While I believe that using Maven is the best answer to this question, you can also hack something using Ant's <get> task. For example, to download the JUnit JAR (may have typos):
<property name="dependency.dir" value="${basedir}/dependencies"/>
<property name="junit.jar" value="junit-4.8.2.jar"/>
<property name="junit.url" value="http://search.maven.org/remotecontent?filepath=junit/junit/4.8.2/${junit.jar}"/>
<target name="download.dependencies">
<mkdir dir="${dependency.dir}/>
<get url="${junit.url}" dest="${dependency.dir}/${junit.jar}"/>
</target>
Of course, if you do this then you'll have to carefully configure your build scripts so that you don't do the download with every run. And you'll increase load on the Maven Central repository.
I have have a folder structure like so:
/PROJECT/PROJECT.html
/PROJECT/PROJECT_readme.txt
/PROJECT/PROJECT.css
/PROJECT/PROJECT.js
/PROJECT/abc_PROJECT_def.txt
/PROJECT/something.js
/PROJECT/other.txt
/PROJECT/somefolder/PROJECT_other.txt
I want to use ANT to copy the complete directory and also change the PROJECT string in the files or folder to a specified value e.g. mysuperproject, so the result folder structure is so :
/mysuperproject/mysuperproject.html
/mysuperproject/mysuperproject_readme.txt
/mysuperproject/mysuperproject.css
/mysuperproject/mysuperproject.js
/mysuperproject/abc_mysuperproject_def.txt
/mysuperproject/something.js
/mysuperproject/other.txt
/mysuperproject/somefolder/mysuperproject_other.txt
Is there a easy way to do that in ANT ?
The Move task should suit you. Read the linked docs carefully.
It's a core task, so you can use it without downloading of configuring anything.
I have a similar requirement. This is what I did:
<target name="move-and-rename" description="Move and Rename">
<copy todir="${toDir}" verbose="true" overwrite="true">
<fileset dir="${fromDir}" includes="**" />
<regexpmapper from="(.*)${fromName}(.*)" to="\1${toName}\2"/>
</copy>
</target>
Invoke on command line as
ant move-and-rename -DfromName=project -DtoName=xxxproject -DfromDir=. -DtoDir=proj2
Hope it helps somebody else.
By specifying "-lib foo.jar" to ant I get the behaviour that the classes in foo.jar is added to the ant classloader and are available for various tasks taking a class name argument.
I'd like to be able to specify the same behaviour but only from inside build.xml (so we can do this on a vanilla ant).
For taskdefs we have functioning code looking like:
<taskdef resource="net/sf/antcontrib/antlib.xml" description="for/foreach tasks">
<classpath>
<pathelement location="${active.workspace}/ant-contrib-1.X/lib/ant-contrib.jar" />
</classpath>
</taskdef>
where the definition is completely provided from the ant-contrib.jar listed.
What is the equivalent mechanism for the "global" ant classpath?
(I have thought out that this is the way to get <javac> use ecj-3.5.jar to compile with on a JRE - Specifying the Eclipse compiler completely from _within_ build.xml - in a way compatible with ant 1.7. Better suggestions are welcome :)
EDIT: It appears that the about-to-be-released version 1.0 of ant4eclipse includes ecj. This does not answer the question, but may solve my basic problem.
if you ever decide to move to Ant 1.8 you can use following construction:
<import>
<javaresource name="resource_name.xml">
<classpath location="path_to_jar.jar" />
</javaresource>
</import>
I've posted an answer to the question that you've linked. I'll repeat a general solution here.
Reading Running Ant via Java, I think you can write a simple wrapper that will properly set a classpath and add any 3rd party library to the resulting class path.
Here I'm just cutting and pasting the sample from the above link with addition of the library to the classpath:
<java
classname="org.apache.tools.ant.launch.Launcher"
fork="true"
failonerror="true"
dir="${sub.builddir}"
timeout="4000000"
taskname="startAnt"
>
<classpath>
<pathelement location="${ant.home}/lib/ant-launcher.jar"/>
<pathelement location="/path/to/3rd-party-lib.jar"/>
</classpath>
<arg value="-buildfile"/>
<arg file="${sub.buildfile}"/>
<arg value="-Dthis=this"/>
<arg value="-Dthat=that"/>
<arg value="-Dbasedir=${sub.builddir}"/>
<arg value="-Dthe.other=the.other"/>
<arg value="${sub.target}"/>
</java>
I think you can even reuse the same build file by referencing ${ant.file}, just give a different target as an entry point.
EDIT
I think, this solution should only be used as a last resort measure. If task in general supports a <classpath> tag, then add required libraries locally to that specific task. Your gave a perfect example of this in your question with an ant-contrib taskdef.