Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specify Output Path for Dynamic Compilation

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."

like image 478
Dan Rosenstark Avatar asked Jan 08 '10 14:01

Dan Rosenstark


3 Answers

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();
like image 179
OscarRyz Avatar answered Oct 18 '22 16:10

OscarRyz


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();
like image 25
Gima Avatar answered Oct 18 '22 16:10

Gima


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.

like image 4
Carl Smotricz Avatar answered Oct 18 '22 16:10

Carl Smotricz