package xapi.javac.dev.api; import xapi.fu.In2.In2Unsafe; import xapi.fu.Out2; import xapi.javac.dev.model.CompilerSettings; import xapi.log.X_Log; import xapi.reflect.X_Reflect; import xapi.util.X_Debug; import static xapi.util.X_String.classToSourceFiles; import javax.annotation.processing.Processor; import java.io.File; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * Created by James X. Nelson (james @wetheinter.net) on 6/30/16. */ public class PendingCompile { private final CompilerSettings settings; private final CompilerService compiler; private final Map<String, Class<?>> javaFiles = new LinkedHashMap<>(); private final boolean test; public PendingCompile(Class<?> cls, CompilerService compiler) { this.settings = compiler.settingsForClass(cls); this.compiler = compiler; String loc = X_Reflect.getFileLoc(cls); test = loc.contains("test-classes"); final String canonicalFile = normalizeLocation(loc, test) + classToSourceFiles(cls); javaFiles.put(canonicalFile, cls); } protected String normalizeLocation(String loc, boolean test) { if (test) { loc = loc.replace("target/test-classes", "src/test/java"); } else { loc = loc.replace("target/classes", "src/main/java"); } return loc; } public PendingCompile addProcessor(Class<? extends Processor> processor) { String existing = settings.getProcessorPath(); final String loc = X_Reflect.getFileLoc(processor); if (existing.contains(loc)) { existing = existing + File.pathSeparator + loc; settings.setProcessorPath(existing); } return this; } public Out2<Integer,URL> compile(){ return compiler.compileFiles(settings, javaFiles.keySet().toArray(new String[javaFiles.size()])); } public Out2<Integer, URL> compileAndRun(In2Unsafe<ClassLoader, List<Class<?>>> callback) { return compileAndRun(callback, true); } @SuppressWarnings("varargs") public Out2<Integer, URL> compileAndRun(In2Unsafe<ClassLoader, List<Class<?>>> callback, boolean synchronous) { final Out2<Integer, URL> result = compile(); assert result.out1() == 0 : "Annotation processing failed"; final URLClassLoader cl = new URLClassLoader(new URL[]{result.out2()}, Thread.currentThread().getContextClassLoader()); Thread runIn = new Thread(()->{ try { List<Class<?>> loaded = new ArrayList<>(); for (Class<?> cls : javaFiles.values()) { loaded.add(cl.loadClass(cls.getCanonicalName())); } callback.in(cl, loaded); } catch (ClassNotFoundException e) { X_Log.error(getClass(), "Compilation did not succeed for classes", javaFiles, e ); throw X_Debug.rethrow(e); } }); runIn.setContextClassLoader(cl); runIn.start(); if (synchronous) { try { runIn.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw X_Debug.rethrow(e); } } return result; } }