rfield@1422: /* katleman@1448: * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. rfield@1422: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. rfield@1422: * rfield@1422: * This code is free software; you can redistribute it and/or modify it rfield@1422: * under the terms of the GNU General Public License version 2 only, as rfield@1422: * published by the Free Software Foundation. Oracle designates this rfield@1422: * particular file as subject to the "Classpath" exception as provided rfield@1422: * by Oracle in the LICENSE file that accompanied this code. rfield@1422: * rfield@1422: * This code is distributed in the hope that it will be useful, but WITHOUT rfield@1422: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or rfield@1422: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License rfield@1422: * version 2 for more details (a copy is included in the LICENSE file that rfield@1422: * accompanied this code). rfield@1422: * rfield@1422: * You should have received a copy of the GNU General Public License version rfield@1422: * 2 along with this work; if not, write to the Free Software Foundation, rfield@1422: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. rfield@1422: * rfield@1422: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA rfield@1422: * or visit www.oracle.com if you need additional information or have any rfield@1422: * questions. rfield@1422: */ rfield@1422: rfield@1422: package org.openjdk.tests.separate; rfield@1422: rfield@1422: import java.util.*; rfield@1422: import java.util.concurrent.atomic.AtomicInteger; rfield@1422: import java.util.concurrent.ConcurrentHashMap; rfield@1422: import java.io.*; rfield@1422: import java.net.URI; rfield@1422: import javax.tools.*; rfield@1422: rfield@1422: import com.sun.source.util.JavacTask; rfield@1422: rfield@1422: import static org.openjdk.tests.separate.SourceModel.Type; rfield@1422: import static org.openjdk.tests.separate.SourceModel.Class; rfield@1422: import static org.openjdk.tests.separate.SourceModel.Extends; rfield@1422: import static org.openjdk.tests.separate.SourceModel.SourceProcessor; rfield@1422: rfield@1422: public class Compiler { rfield@1422: rfield@1422: public enum Flags { rfield@1422: VERBOSE, // Prints out files as they are compiled rfield@1422: USECACHE // Keeps results around for reuse. Only use this is rfield@1422: // you're sure that each compilation name maps to the rfield@1422: // same source code rfield@1422: }; rfield@1422: rfield@1422: private static final AtomicInteger counter = new AtomicInteger(); rfield@1422: private static final String targetDir = "gen-separate"; rfield@1422: private static final File root = new File(targetDir); rfield@1422: private static ConcurrentHashMap cache = rfield@1422: new ConcurrentHashMap<>(); rfield@1422: rfield@1422: Set flags; rfield@1422: rfield@1422: private JavaCompiler systemJavaCompiler; rfield@1422: private StandardJavaFileManager fm; rfield@1422: private List tempDirs; rfield@1422: private List postprocessors; rfield@1422: rfield@1422: private static class SourceFile extends SimpleJavaFileObject { rfield@1422: private final String content; rfield@1422: rfield@1422: public SourceFile(String name, String content) { rfield@1422: super(URI.create("myfo:/" + name + ".java"), Kind.SOURCE); rfield@1422: this.content = content; rfield@1422: } rfield@1422: rfield@1422: public CharSequence getCharContent(boolean ignoreEncodingErrors) { rfield@1422: return toString(); rfield@1422: } rfield@1422: rfield@1422: public String toString() { return this.content; } rfield@1422: } rfield@1422: rfield@1422: public Compiler(Flags ... flags) { rfield@1422: setFlags(flags); rfield@1422: this.tempDirs = new ArrayList<>(); rfield@1422: this.postprocessors = new ArrayList<>(); rfield@1422: this.systemJavaCompiler = ToolProvider.getSystemJavaCompiler(); rfield@1422: this.fm = systemJavaCompiler.getStandardFileManager(null, null, null); rfield@1422: } rfield@1422: rfield@1422: public void setFlags(Flags ... flags) { rfield@1422: this.flags = new HashSet(Arrays.asList(flags)); rfield@1422: } rfield@1422: rfield@1422: public void addPostprocessor(ClassFilePreprocessor cfp) { rfield@1422: this.postprocessors.add(cfp); rfield@1422: } rfield@1422: rfield@1422: /** rfield@1422: * Compile hierarchies starting with each of the 'types' and return rfield@1422: * a ClassLoader that can be used to load the compiled classes. rfield@1422: */ rfield@1422: public ClassLoader compile(Type ... types) { rfield@1422: ClassFilePreprocessor[] cfps = this.postprocessors.toArray( rfield@1422: new ClassFilePreprocessor[0]); rfield@1422: rfield@1422: DirectedClassLoader dcl = new DirectedClassLoader(cfps); rfield@1422: rfield@1422: for (Type t : types) { rfield@1422: for (Map.Entry each : compileHierarchy(t).entrySet()) { rfield@1422: dcl.setLocationFor(each.getKey(), each.getValue()); rfield@1422: } rfield@1422: } rfield@1422: return dcl; rfield@1422: } rfield@1422: rfield@1422: /** rfield@1422: * Compiles and loads a hierarchy, starting at 'type' rfield@1422: */ rfield@1422: public java.lang.Class compileAndLoad(Type type) rfield@1422: throws ClassNotFoundException { rfield@1422: rfield@1422: ClassLoader loader = compile(type); rfield@1422: return java.lang.Class.forName(type.getName(), false, loader); rfield@1422: } rfield@1422: rfield@1422: /** rfield@1422: * Compiles a hierarchy, starting at 'type' and return a mapping of the rfield@1422: * name to the location where the classfile for that type resides. rfield@1422: */ rfield@1422: private Map compileHierarchy(Type type) { rfield@1422: HashMap outputDirs = new HashMap<>(); rfield@1422: rfield@1422: File outDir = compileOne(type); rfield@1422: outputDirs.put(type.getName(), outDir); rfield@1422: rfield@1422: Class superClass = type.getSuperclass(); rfield@1422: if (superClass != null) { rfield@1422: for( Map.Entry each : compileHierarchy(superClass).entrySet()) { rfield@1422: outputDirs.put(each.getKey(), each.getValue()); rfield@1422: } rfield@1422: } rfield@1422: for (Extends ext : type.getSupertypes()) { rfield@1422: Type iface = ext.getType(); rfield@1422: for( Map.Entry each : compileHierarchy(iface).entrySet()) { rfield@1422: outputDirs.put(each.getKey(), each.getValue()); rfield@1422: } rfield@1422: } rfield@1422: rfield@1422: return outputDirs; rfield@1422: } rfield@1422: rfield@1422: private File compileOne(Type type) { rfield@1422: if (this.flags.contains(Flags.USECACHE)) { rfield@1422: File dir = cache.get(type.getName()); rfield@1422: if (dir != null) { rfield@1422: return dir; rfield@1422: } rfield@1422: } rfield@1422: List files = new ArrayList<>(); rfield@1422: SourceProcessor accum = rfield@1422: (name, src) -> { files.add(new SourceFile(name, src)); }; rfield@1422: rfield@1422: for (Type dep : type.typeDependencies()) { rfield@1422: dep.generateAsDependency(accum, type.methodDependencies()); rfield@1422: } rfield@1422: rfield@1422: type.generate(accum); rfield@1422: rfield@1422: JavacTask ct = (JavacTask)this.systemJavaCompiler.getTask( rfield@1422: null, this.fm, null, null, null, files); rfield@1422: File destDir = null; rfield@1422: do { rfield@1422: int value = counter.incrementAndGet(); rfield@1422: destDir = new File(root, Integer.toString(value)); rfield@1422: } while (destDir.exists()); rfield@1422: rfield@1422: if (this.flags.contains(Flags.VERBOSE)) { rfield@1422: System.out.println("Compilation unit for " + type.getName() + rfield@1422: " : compiled into " + destDir); rfield@1422: for (JavaFileObject jfo : files) { rfield@1422: System.out.println(jfo.toString()); rfield@1422: } rfield@1422: } rfield@1422: rfield@1422: try { rfield@1422: destDir.mkdirs(); rfield@1422: this.fm.setLocation( rfield@1422: StandardLocation.CLASS_OUTPUT, Arrays.asList(destDir)); rfield@1422: } catch (IOException e) { rfield@1422: throw new RuntimeException( rfield@1422: "IOException encountered during compilation"); rfield@1422: } rfield@1422: Boolean result = ct.call(); rfield@1422: if (result == Boolean.FALSE) { rfield@1422: throw new RuntimeException( rfield@1422: "Compilation failure in " + type.getName() + " unit"); rfield@1422: } rfield@1422: if (this.flags.contains(Flags.USECACHE)) { rfield@1422: File existing = cache.putIfAbsent(type.getName(), destDir); rfield@1422: if (existing != null) { rfield@1422: deleteDir(destDir); rfield@1422: return existing; rfield@1422: } rfield@1422: } else { rfield@1422: this.tempDirs.add(destDir); rfield@1422: } rfield@1422: return destDir; rfield@1422: } rfield@1422: rfield@1422: private static void deleteDir(File dir) { rfield@1422: for (File f : dir.listFiles()) { rfield@1422: f.delete(); rfield@1422: }; rfield@1422: dir.delete(); rfield@1422: } rfield@1422: rfield@1422: public void cleanup() { rfield@1422: if (!this.flags.contains(Flags.USECACHE)) { rfield@1422: for (File d : tempDirs) { rfield@1422: deleteDir(d); rfield@1422: }; rfield@1422: tempDirs = new ArrayList<>(); rfield@1422: } rfield@1422: } rfield@1422: rfield@1422: // Removes all of the elements in the cache and deletes the associated rfield@1422: // output directories. This may not actually empty the cache if there rfield@1422: // are concurrent users of it. rfield@1422: public static void purgeCache() { rfield@1422: for (Map.Entry entry : cache.entrySet()) { rfield@1422: cache.remove(entry.getKey()); rfield@1422: deleteDir(entry.getValue()); rfield@1422: } rfield@1422: } rfield@1422: }