My dynamic compilation in Java 6 is working perfectly. However, I would like to change the output path. I have tried tons of things (I'll spare you) to no avail. Anyway, here's the working code
String[] filesToCompile = { "testFiles/Something.java" };
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(filesToCompile);
CompilationTask task = compiler.getTask(null, fileManager, null,null, null, compilationUnits);
System.out.println("Good? " + task.call());
But the output goes to the source directory, which is not what I want.
I suspect that the answer may lie in the compiler.getTask
but the API is not very explicit as to what some of the parameters might mean. Or perhaps something with the fileManager. I've tried
fileManager.setLocation(StandardLocation.locationFor("testFiles2"), null);
but again, guessing is probably not a good idea.
Thanks!
Edit: I've tried using options, too, like this (sorry if there's a more compact way):
final List<String> optionsList = new ArrayList<String>();
optionsList.add("-d what");
Iterable<String> options = new Iterable<String>() {
public Iterator<String> iterator() {
return optionsList.iterator();
}
};
and then passing the options to getTask, but error message is "Invalid Flag."
I was facing this same problem today.
The answer ( using the regular getTask
method instead of `run ) is to specify the output dir in the FileManager:
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(outputDir));
And that's it!! :)
The documentation is a bit misleading, I mean, a sample could come very handy. But eventually it took me there.
EDIT
Here's a running sample:
// write the test class
File sourceFile = new File("First.java");
FileWriter writer = new FileWriter(sourceFile);
writer.write(
"package load.test;\n" +
"public class First{}"
);
writer.close();
// Get the java compiler for this platform
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(
null,
null,
null);
//-- H E R E --//
// Specify where to put the genereted .class files
fileManager.setLocation(StandardLocation.CLASS_OUTPUT,
Arrays.asList(new File("/tmp")));
// Compile the file
compiler
.getTask(null,
fileManager,
null,
null,
null,
fileManager.getJavaFileObjectsFromFiles(Arrays.asList(sourceFile)))
.call();
fileManager.close();
// delete the file
sourceFile.deleteOnExit();
Code in the first post would work, but the following error get's thrown:
java.lang.IllegalArgumentException: invalid flag: -d folder
This is because by passing "-d folder"
makes the parser think it's parsing one option. The options must be separated like "-d", "folder"
.
Working example follows:
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager sjfm = javaCompiler.getStandardFileManager(null, null, null);
String[] options = new String[] { "-d", "output" };
File[] javaFiles = new File[] { new File("src/gima/apps/flip/TestClass.java") };
CompilationTask compilationTask = javaCompiler.getTask(null, null, null,
Arrays.asList(options),
null,
sjfm.getJavaFileObjects(javaFiles)
);
compilationTask.call();
I have 0 experience with the Java 6 dynamic compiler tools. But nobody else has answered :)
The compilation task gets a FileManager
object. If you use the standard one, then classes are generated in the source directory tree. What you could do is provide your own FileManager subclass with an overridden getFileForOutput
method. The API description of getFileForOutput indicates that this will influence where your output (= class) files will go.
Update
How to hook up file managers
ForwardingJavaFileManager, ForwardingFileObject, and ForwardingJavaFileObject Subclassing is not available for overriding the behavior of a standard file manager as it is created by calling a method on a compiler, not by invoking a constructor. Instead forwarding (or delegation) should be used. These classes makes it easy to forward most calls to a given file manager or file object while allowing customizing behavior. For example, consider how to log all calls to JavaFileManager.flush():
final Logger logger = ...;
Iterable<? extends JavaFileObject> compilationUnits = ...;
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager stdFileManager = compiler.getStandardFileManager(null, null, null);
JavaFileManager fileManager = new ForwardingJavaFileManager(stdFileManager) {
public void flush() {
logger.entering(StandardJavaFileManager.class.getName(), "flush");
super.flush();
logger.exiting(StandardJavaFileManager.class.getName(), "flush");
}
};
compiler.getTask(null, fileManager, null, null, null, compilationUnits).call();
Update 2
I read up on dynamic compilation and built my own app to do this. This code contains a bit too much ceremony (i.e. it could be simplified) but it works!
package yar;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
public class DynamicCompiler {
JavaCompiler compiler;
public DynamicCompiler() {
this.compiler = ToolProvider.getSystemJavaCompiler();
if (this.compiler == null) {
throw new NullPointerException("Cannot provide system compiler.");
}
}
public void compile() {
this.compiler.run(null, System.out, System.err,
"-d", "testFiles2",
"testFiles/Hello1.java", "testFiles/Hello2.java");
}
/**
* @param args
*/
public static void main(String[] args) {
try {
DynamicCompiler dc = new DynamicCompiler();
dc.compile();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
I'm not sure how to get this code to work with a dynamically generated list of Java files; I'd probably just do compiler.run
separately for each source file.
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