I have a build.xml which imports other ant xml files. I'd like to get all javac tasks from it so I can see what classpath is set to for these tasks (javac is used at multiple targets). I came up with the following code (simplified a bit):
public static void main(String[] args) throws Exception {
Project project = new Project();
project.init();
String build = "build.xml";
File buildFile = new File(build);
ProjectHelper.configureProject(project, buildFile);
Hashtable<String,Object>ht = project.getTargets();
for (String key : ht.keySet()) {
try {
Target target = (Target)ht.get(key);
Task[] tasks = target.getTasks();
for (Task task : tasks) {
if (task instanceof UnknownElement) {
((UnknownElement)task).maybeConfigure();
task = ((UnknownElement)task).getTask();
if (task == null) {
return;
}
}
if (task instanceof Javac) {
// here we go
}
}
} catch(Exception ignore) {}
}
}
However, there are tasks like MacroDef
which may have nested other tasks. The TaskContainer
interface only specifies addTask(task)
, I see no way to retrieve nested tasks.
How can I retrieve all javac tasks? It's okay to have a solution where the ant library is not used, but XML parsing seems to be cumbersome since ant uses properties, references, buildfiles can import other files etc.
Ok, so to clarify your issue, I'll restate the question.
How do I gather all of the class paths for all of the <javac> tasks in a build.xml file, even when some of those might be nested in <macrodef> definitions?
The simple answer is that a general answer for every case is impossible. The reasons why are deeply tied in computer science theory, covering the issues of computability, and the limitations of the Turing machine. In your case, we know that the input can be infinite (as the ANT language can reference timestamps), and that the output can be a reflection of the input, so we know that it would take an infinite amount of time to collect every possible classpath.
That said, odds are excellent that you can easily deduce some classpaths, and even possibly deduce all classpaths in a reasonable (ie non-infinite) amount of time. The key is that ANT does a lot of configuration up-front, but eventually must do some configuration on-the-fly. A <javac> classpath in a target is configured up-front; because ANT's decision to make all variables immutable. This means for the "typical" javac tag you only need some code that looks like this:
if (task instanceof Javac) {
Javac javac = (Javac)task;
Path path = javac.getClasspath();
if (path == null) {
System.out.println("javac: Path is null");
} else {
System.out.println("javac: Path is " + path.toString());
}
}
But the nested in a macrodef javac tag will need a simulation of the run. The actual configuration will not be held directly within the task, it will be held within a RuntimeConfigurable
wrapper around the class. The Task
nodes will then be created as the project executes, which means that their configuration will be computed on-the-fly.
I'll list a bit of code on how to navigate to the macrodef Wrapper "child" elements:
if (task instanceof MacroDef) {
MacroDef macroDef = (MacroDef)task;
// a Sequence element, but not really, as `unkSeq.getRealThing()` will return null
UnknownElement unkSeq = macroDef.getNestedTask();
// Make sure we are dealing with the wrapper, or we won't get very far
RuntimeConfigurable wrapper = unkSeq.getWrapper();
// Wrappers can be configured too.
wrapper.maybeConfigure(project, true);
Enumeration enumeration = wrapper.getChildren();
while (enumeration.hasMoreElements()) {
// children of the wrapper
RuntimeConfigurable child = (RuntimeConfigurable) enumeration.nextElement();
UnknownElement unkchild = (UnknownElement)child.getProxy();
// you can use this to print the name
System.out.println("child(wrapper): " + unkchild.getTaskName());
// this will be null, as the macro hasn't "executed"
System.out.println("child(real): " + unkchild.getRealThing());
}
}
Since the actual called javac
task doesn't exist within a macrodef pre-runtime, it is not possible to really "know" it's classpath in all cases prior to execution. Yes, you can do some fancy code to simulate a run, tracking the transient ANT properties, but you can never "prove" that such code is the same as the run unless you are dealing with a subset of properties (namely those which are guaranteed to always have the same value between all runs).
I hope I didn't miss the mark on answering your question, and if i did, hopefully something in this will be useful in your solution to your problem. Rest assured though, there is no solution to printing out the class path for every possible build.xml file where javac tasks are embedded within macrodef calls.
--- original post follows --- To really capture the contents of the macrodef, you need to capture the task that refers to the macrodef, and then capture the macrodef, and then look at the elements within the macrodef (keeping in mind that they might be macros too).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With