是否可以将Java编译器嵌入到应用程序中?

时间:2018-06-27 14:55:02

标签: javac xtext xtend

我使用Xtext和Xtend编写了DSL语法及其相关的代码生成器,该生成器创建了一堆构成Java应用程序的Java源文件。我想为我的同事提供一个程序,让他们选择用我们的特定领域语言编写的文件,运行代码生成器以生成实际Java应用程序的Java源文件,编译这些文件并启动该应用程序。因为我已经安装了所有东西,包括JDK,所以该过程在我的开发机器上可以正常工作。但是向我的同事提供此应用程序将迫使他们在使用该应用程序之前先安装JDK。所以我的问题是:是否可以在分发包中嵌入Java编译器?如果没有,您是否看到另一种进行方式?我正在使用Gradle和javapackager + Inno Setup来生成嵌入JRE的分发程序包。

更新#1

 override public void afterGenerate(Resource input, IFileSystemAccess2 fsa, IGeneratorContext context)
 {
  // get the class loader
  val URL[] classLoaderUrls = #{new URL("file:jfxrt.jar")}
  val URLClassLoader cl = new URLClassLoader(classLoaderUrls)

  // set Java compiler options
  var CompilerOptions compilerOptions = new CompilerOptions
  compilerOptions.sourceLevel = ClassFileConstants.JDK1_8
  compilerOptions.complianceLevel = ClassFileConstants.JDK1_8
  val Map<String, String> options = new HashMap
  options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE)
  options.put(CompilerOptions.OPTION_ReportUnusedParameter, CompilerOptions.IGNORE)
  compilerOptions.set(options)

  val InMemoryJavaCompiler compiler = new InMemoryJavaCompiler(cl, compilerOptions)

  // compile all generated Java source files
  val result = compiler.compile(new JavaSource("xxxx/Bit.java", bitGen.javaSource.toString),
   new JavaSource("xxxx/BitNature.java", bitNatureGen.javaSource.toString),
   new JavaSource("xxxx/ImageMaker.java", imgMakerGen.javaSource.toString),
   new JavaSource("xxxx/MyApp.java", myAppGen.javaSource.toString))

  val URLClassLoader runcl = new URLClassLoader(classLoaderUrls, result.classLoader)

  val Class<?> mainClazz = runcl.loadClass("xxxx.MyApp")

  val Method mainMethod = mainClazz.getMethod("main", typeof(String[]))
  val String[] args = #["C:\\temp\\memory_a.yyy"]
  try
  {
   mainMethod.invoke(null, #[args])
  }
  catch (InvocationTargetException e)
  {
   e.getCause().printStackTrace()
  }
  catch (Exception e)
  {
   // generic exception handling
   e.printStackTrace();
  }
 }

更新#2

逐步执行我的代码,可以看到以下内容:

1-该语句执行没有问题(mainClazz似乎是有效的引用)

val Class<?> mainClazz = result.classLoader.loadClass("xxxx.MyApp")

2-执行时mainMethod似乎也是有效的引用

val Method mainMethod = mainClazz.getMethod("main", typeof(String[]))

3-执行时情况变糟

mainMethod.invoke(null, #[args])

问题出现在JavaFX Application类中,以下语句的启动方法(ClassNotFoundException:xxxx.MyApp)

Class theClass = Class.forName(callingClassName, false,
                               Thread.currentThread().getContextClassLoader());

1 个答案:

答案 0 :(得分:1)

package org.xtext.example.mydsl2.tests

import java.lang.reflect.Method
import java.net.URLClassLoader
import org.eclipse.xtext.util.JavaVersion
import org.eclipse.xtext.xbase.testing.InMemoryJavaCompiler
import org.eclipse.xtext.xbase.testing.JavaSource

class SampleXXX {

    def static void main(String[] args) {
        val urls = #[]
        val classLoader = new URLClassLoader(urls)
        val compiler = new InMemoryJavaCompiler(classLoader, JavaVersion.JAVA8)
        val result = compiler.compile(new JavaSource("demo/Demo.java", '''
        package demo;
        public class Demo {
            public static void main(String[] args) throws Exception {
                Class theClass = Class.forName("demo.Demo", false,
                    Thread.currentThread().getContextClassLoader());
            }
        }
        '''))
        val URLClassLoader runcl = new URLClassLoader(#[], result.classLoader)
        (new Thread() {

            override run() {
            val Class<?> mainClazz = runcl.loadClass("demo.Demo")
            val String[] args2 = #["C:\\temp\\memory_a.yyy"]
            val Method mainMethod = mainClazz.getMethod("main", typeof(String[]))
            mainMethod.invoke(null, #[args2])
            }

        }=>[
            contextClassLoader = runcl
        ]).start
    }

}