【问题标题】:Specify Output Path for Dynamic Compilation指定动态编译的输出路径
【发布时间】:2011-01-02 22:43:12
【问题描述】:

我在 Java 6 中的动态编译运行良好。但是,我想更改输出路径。我已经尝试了很多东西(我会饶过你)无济于事。无论如何,这是工作代码

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());

但是输出到了源目录,这不是我想要的。

我怀疑答案可能在compiler.getTask 中,但API 对某些参数的含义并不是很明确。或者也许与文件管理器有关。我试过了

fileManager.setLocation(StandardLocation.locationFor("testFiles2"), null);

但同样,猜测可能不是一个好主意。

谢谢!

编辑:我也尝试过使用选项,就像这样(抱歉,如果有更紧凑的方式):

    final List<String> optionsList = new ArrayList<String>();
    optionsList.add("-d what");
    Iterable<String> options = new Iterable<String>() {         
        public Iterator<String> iterator() {
            return optionsList.iterator();
        }
    };

然后将选项传递给getTask,但错误消息是“Invalid Flag”。

【问题讨论】:

  • +1 让我意识到现在有动态编译之类的东西!
  • 以前有,现在是内置的!

标签: java java-6 dynamic-compilation


【解决方案1】:

我今天也遇到了同样的问题。

答案(使用常规的 getTask 方法而不是 `run )是在 FileManager 中指定输出目录:

fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(outputDir));

就是这样!! :)

文档有点误导,我的意思是,一个样本可能会非常方便。但最终它把我带到了那里。

编辑

这是一个运行示例:

    // 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();

【讨论】:

  • 我已经有一段时间没有看到这个问题了。所以你是说你的答案实际上解决了整个问题?
  • 效果很好 - 与在 getTask() 选项参数中使用“-d”相同,但更简洁。
【解决方案2】:

第一篇文章中的代码可以运行,但会引发以下错误:

java.lang.IllegalArgumentException: invalid flag: -d folder

这是因为传递"-d folder" 会使解析器认为它正在解析一个选项。选项必须像"-d", "folder" 那样分开。

工作示例如下:

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();

【讨论】:

  • 像 "c:\\foo bar\\foo" 或 "c:/foo bar/foo" 这样的路径呢?可以正确解析空格或斜杠吗?
  • 我不知道,但我猜不需要转义,因为参数现在被正确传递(并且因为原始问题的性质正是未解析的参数的性质分开)。尝试并报告?
【解决方案3】:

我对 Java 6 动态编译器工具的经验为 0。但是没有其他人回答:)

编译任务得到一个FileManager 对象。如果您使用标准的,则在源目录树中生成类。您可以做的是为您自己的 FileManager 子类提供一个重写的 getFileForOutput 方法。 getFileForOutput 的 API 描述表明这将影响您的输出(=类)文件的去向。

更新

如何连接文件管理器

ForwardingJavaFileManager、ForwardingFileObject 和 ForwardingJavaFileObject 子类化不可用于覆盖标准文件管理器的行为,因为它是通过调用编译器上的方法而不是通过调用构造函数创建的。相反,应该使用转发(或委托)。这些类可以轻松地将大多数调用转发到给定的文件管理器或文件对象,同时允许自定义行为。例如,考虑如何记录对 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();

更新 2

我阅读了动态编译并构建了自己的应用程序来执行此操作。这段代码包含了太多的仪式(即它可以被简化)但它有效!

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());
      }
   }

}

我不确定如何让这段代码与动态生成的 Java 文件列表一起工作;我可能只是为每个源文件单独做compiler.run

【讨论】:

  • 这可能是真的,但不幸的是 getJavaFileObjects 仅在 StandardJavaFileManager 上......我会看看能做什么。如果这是 Ruby,您的回答就足以修补并完成:)
  • 在 Java 中,要走的路是子类化...ForwardingJavaFileManager 实现了StandardJavaFileManager,这就是您要使用的。
  • 事实上,它有一个构造函数,你可以将你从compiler 得到的FileManager 包裹起来。当然,您会希望在派生类中公开该构造函数。
  • 感谢 Carl,刚刚尝试过(将 fileManager 包装在 ForwardingJavaFileManager 的子类中,然后将其传递给任务)。问题是,永远不会调用 getFileForOutput。但是,ForwardingJavaFileManager 没有实现 StandardFileManager。
  • Ack,恐怕你是对的。对不起,大雁追逐!
猜你喜欢
  • 2014-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-20
相关资源
最近更新 更多