<fileset>'s strange behaviour

Here is an oddity whose solution was discovered by Jan Matèrne. In Ant, what is the simplest way to get a <fileset> that contains only files that do not have an extension in a directory tree.

You might think you could just specify that the included files end in a period, like so:

<fileset includes="**/*." />

but that will only select files which literally end in a period. The actual answer is a bit counterintuitive. You select all files (implicit) and then exclude those that have an extension:

<fileset excludes="*.*" />

This probably only looks odd to people who think of file extensions as being something special. For Unix people, Ant's behavior isn't counterintuitive at all, as the dot in the first includes pattern is a literal dot and nothing else.

Changing default Locale

When I worked with CheckStyle I realized that there are localized message. So far so fine. But now I want to generate an international (English) site on my (German) machine. But how to realize that?

CheckStyle - like many other programs - uses the java.util.ResourceBundle.getBundle() method which returns the appropriate bundle for the default Locale. So I will set the default Locale to the US value. Before that I store the actual one (or the 'key') as property and restore that after invoking CheckStyle. Because I need (simple) access to the Java API, I write that inside <script> tasks:

P.S. For another thing I needed to change the Locale by setting parameters on VM startup. I found a solution on Eclipse-Bug-Database. Kevin Barnes offered the possibility to set two VM args: -Duser.country=EN -Duser.language=US (haven´t found them in the JDK docs). Haven´t tested that for this context - so just for your info.

Using your own classes inside <script>

When you use <script language="javascript"> you are using the Java API. Ok so far. It´s simple to use java.io.File for getting information about a particular file or creating complex strings with java.util.StringBuffer. But there are two problems:

How to import other (non java.*) classes, e.g. Ant´s own classes?

How to import classes which are not on Ant´s classpath?

How to import other (non java.*) classes, e.g. Ant´s own classes?

The answer to this is described on the homepage of the javascript interpreter, but very hidden (I think). Following the links "Documentation" and "Scripting Java" you´ll get some examples. Inside them it is written: "If you wish to load classes from JavaScript that aren't in the java package, you'll need to prefix the package name with "Packages". For example ...". Transfered to you script task that would be:

importClass(Packages.org.apache.tools.ant.types.Path);

I'm confused. I was told that Java (static typing, compiled to bytecodes, etc.) is a totally different language than JavaScript (dynamic typing, interpreted, etc.). Are we using Java for scripting here ? Or are we actually using JavaScript ? Or both ?

AFAICT, the scripting language is EmcaScript (aka JavaScript), but a java binding layer was added so that the java script can create and manuipulate java objects.

How to import classes which are '''not''' on Ant´s classpath?

Ok, now we can use Ant´s classes and the whole javax.*-stuff. But if I need some custom classes? For example I have to create a reverse sorted list of files. Getting the list of files is no problem. Sorting can be done by java.util.TreeSet. But I need a custom Comparator which does the specific comparison. So I implement one and store it in ${basedir}/ext.

And now the trick: Ant´s Project class provides methods for getting classloader. And there is one which includes specified paths. So we

list = new java.lang.StringBuffer(); file = new java.io.File(filename); // get all targets of the current project targets = project.getTargets(); // get the dependencies - including not executing targets sorted = project.topoSort(root, targets); // create the list and break if we have "executed" our root target // this code is adapted from Project.executeTarget(String) for(it=sorted.iterator(); it.hasNext();) {

A potential problem with this solution arises when Windows XP is not installed in the folder "WINDOWS". While it usually resides there, it can be installed in a folder with any name. Also, upgrading from Windows 2000 keeps the default installation directory that 2000 used, "WINNT". Thus, the folder name alone can't guarantee that you're working with XP.

Ryan Stinnett

Implementing a PreProcessor

On Bug-28738 is a question about preprocessor. You can do that without any external tasks (but I think the external tasks are more comfortable than this way

Important is the question mark in the match clause (.*?), so we´ve got minimal pattern matching. Otherwise all between the first debug-start mark and the very last debug-end will be selected. And therefore we will lose important code segments The flag "s" is responsible that we will get the whole file at once and the "g" that we catch all debug-statements.

You might wish to pass to some target or task properties/parameters such as ${component} and ${targetOS} in the form

<do-something-with object="${${component}.${targetOS}}/>

so that if, for example, ${component} were valued as "driver" and ${targetOS} were valued as "os2", the value of ${${component}.${targetOS}} would be the expansion of ${driver.os2}, i.e., "A.Real.Old.Driver". However, Ant expansions of property instantiations are not recursive. So in this instance the expansion of ${${component}.${targetOS}} is not "A.Real.Old.Driver" but instead undefined and possibly varying by Ant version. (Ant 1.6.1 would yield a literal value of ${${component}.${targetOS}} while Ant 1.7 alpha currently yields ${${component}.os2})

Why does <javac> require you to set source="1.4" for Java1.4 features, "1.5" for Java1.5?

The command line javac tool automatically selects the latest version of Java for the platform you build on. So when you build on Java1.4, it is as if -source 1.4 was passed in, while on Java1.5, you get the effect of -source 1.5

Yet on Ant, you have to explicitly say what version of the Java language you want to build against. If you have the assert keyword in source and omit the source="1.4" attribute, the compile will break. If you use fancy Java1.5 stuff and forget source="1.5", you get errors when you hit enums, attributes, generics or the new for stuff.

Q. Why is this the case? Why doesnt Ant "do the right thing?"

A. Because of a deliberate policy decision by the project.

Ant was written, first and foremost, to build open source projects. In such a project, you don't know who builds your project; you just give out your source and rely on it compiling everywhere, regardless of which platform or JVM the person at the far end used. We also assume that a source tarball will still compile, years into the future.

If <javac> automatically selected the latest version of Java, then any Java1.2 code that used "enum" as a variable name, or "assert". To build the old projects, you would have to edit every build file that didnt want to use the latest Java version and force in a source="1.3" attribute. Except if they were other people's projects, you would have to struggle to get that change committed, and things like ApacheGump would never work, because all old-JVM projects would never compile straight from the SCM repository.

That is why, in Ant, if you want the latest and greatest features of the Java language, you have to ask for them. By doing so you are placing a declaration in your build file what language version you want to use, both now and into the future.

An interesting follow-on question is then "why does javac on the command line risk breaking every makefile-driven Java project by automatically updating Java language versions unless told not to?" That is for Sun to answer, not the Ant team.