1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.javac; 18 19 import com.google.common.collect.Lists; 20 import com.google.common.io.Files; 21 22 import java.util.stream.Collectors; 23 import org.apache.bcel.classfile.ClassParser; 24 import org.apache.bcel.classfile.JavaClass; 25 26 import java.io.File; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.net.URI; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.List; 33 import java.util.Locale; 34 35 import javax.annotation.processing.Processor; 36 import javax.tools.DiagnosticCollector; 37 import javax.tools.JavaCompiler; 38 import javax.tools.JavaFileObject; 39 import javax.tools.SimpleJavaFileObject; 40 import javax.tools.StandardJavaFileManager; 41 import javax.tools.StandardLocation; 42 import javax.tools.ToolProvider; 43 44 /** 45 * Helper class for compiling snippets of Java source and providing access to the resulting class 46 * files. 47 */ 48 public class Javac { 49 50 private final JavaCompiler mJavac; 51 private final StandardJavaFileManager mFileMan; 52 private final List<JavaFileObject> mCompilationUnits; 53 private final File mClassOutDir; 54 Javac()55 public Javac() throws IOException { 56 mJavac = ToolProvider.getSystemJavaCompiler(); 57 mFileMan = mJavac.getStandardFileManager(null, Locale.US, null); 58 mClassOutDir = Files.createTempDir(); 59 mFileMan.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(mClassOutDir)); 60 mFileMan.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(mClassOutDir)); 61 mCompilationUnits = new ArrayList<>(); 62 } 63 classToFileName(String classname)64 private String classToFileName(String classname) { 65 return classname.replace('.', '/'); 66 } 67 addSource(String classname, String contents)68 public Javac addSource(String classname, String contents) { 69 JavaFileObject java = new SimpleJavaFileObject(URI.create( 70 String.format("string:///%s.java", classToFileName(classname))), 71 JavaFileObject.Kind.SOURCE 72 ){ 73 @Override 74 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { 75 return contents; 76 } 77 }; 78 mCompilationUnits.add(java); 79 return this; 80 } 81 compile()82 public void compile() { 83 compileWithAnnotationProcessor(null); 84 } 85 compileWithAnnotationProcessor(Processor processor)86 public void compileWithAnnotationProcessor(Processor processor) { 87 DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>(); 88 JavaCompiler.CompilationTask task = mJavac.getTask( 89 null, 90 mFileMan, 91 diagnosticCollector, 92 null, 93 null, 94 mCompilationUnits); 95 if (processor != null) { 96 task.setProcessors(Lists.newArrayList(processor)); 97 } 98 boolean result = task.call(); 99 if (!result) { 100 throw new IllegalStateException( 101 "Compilation failed:" + 102 diagnosticCollector.getDiagnostics() 103 .stream() 104 .map(Object::toString) 105 .collect(Collectors.joining("\n"))); 106 } 107 } 108 getOutputFile(String filename)109 public InputStream getOutputFile(String filename) throws IOException { 110 Iterable<? extends JavaFileObject> objs = mFileMan.getJavaFileObjects( 111 new File(mClassOutDir, filename)); 112 if (!objs.iterator().hasNext()) { 113 return null; 114 } 115 return objs.iterator().next().openInputStream(); 116 } 117 getClassFile(String classname)118 public InputStream getClassFile(String classname) throws IOException { 119 return getOutputFile(String.format("%s.class", classToFileName(classname))); 120 } 121 getCompiledClass(String classname)122 public JavaClass getCompiledClass(String classname) throws IOException { 123 return new ClassParser(getClassFile(classname), 124 String.format("%s.class", classToFileName(classname))).parse(); 125 } 126 } 127