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