juzu.impl.compiler.Compiler Maven / Gradle / Ivy
/*
* Copyright 2013 eXo Platform SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package juzu.impl.compiler;
import juzu.impl.common.Tools;
import juzu.impl.common.FileKey;
import juzu.impl.compiler.file.JavaFileObjectImpl;
import juzu.impl.compiler.file.SimpleFileManager;
import juzu.impl.fs.Filter;
import juzu.impl.fs.Visitor;
import juzu.impl.fs.spi.ReadFileSystem;
import juzu.impl.fs.spi.ReadWriteFileSystem;
import juzu.impl.fs.spi.ram.RAMFileSystem;
import juzu.impl.common.Location;
import juzu.impl.common.Spliterator;
import javax.annotation.processing.Processor;
import javax.inject.Provider;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
/** @author Julien Viet */
public class Compiler {
public static Builder builder() {
return new Builder(null, null, null, new ArrayList>());
}
public static class Builder {
/** . */
private ReadFileSystem> sourcePath;
/** . */
private ReadWriteFileSystem> classOutput;
/** . */
private ReadWriteFileSystem> sourceOutput;
/** . */
private List> classPaths;
/** . */
private Provider extends Processor> processor;
/** . */
private Provider extends JavaCompiler> javaCompiler;
/** . */
private CompilerConfig config;
private Builder(
ReadFileSystem> sourcePath,
ReadWriteFileSystem> sourceOutput,
ReadWriteFileSystem> classOutput,
List> classPaths) {
Provider javaCompiler = new Provider() {
public JavaCompiler get() {
return ToolProvider.getSystemJavaCompiler();
}
};
this.processor = null;
this.javaCompiler = javaCompiler;
this.sourcePath = sourcePath;
this.sourceOutput = sourceOutput;
this.classOutput = classOutput;
this.classPaths = classPaths;
this.config = new CompilerConfig();
}
public Builder javaCompiler(Provider extends JavaCompiler> javaCompiler) {
this.javaCompiler = javaCompiler;
return this;
}
public Builder processor(Provider extends Processor> processor) {
this.processor = processor;
return this;
}
public Builder classOutput(ReadWriteFileSystem> classOutput) {
this.classOutput = classOutput;
return this;
}
public Builder output(ReadWriteFileSystem> output) {
this.classOutput = this.sourceOutput = output;
return this;
}
public Builder sourcePath(ReadFileSystem> sourcePath) {
this.sourcePath = sourcePath;
return this;
}
public Builder sourceOutput(ReadWriteFileSystem> sourceOutput) {
this.sourceOutput = sourceOutput;
return this;
}
public Builder addClassPath(ReadFileSystem> classPath) {
classPaths.add(classPath);
return this;
}
public Builder addClassPath(Iterable> classPaths) {
for (ReadFileSystem> classPath : classPaths) {
addClassPath(classPath);
}
return this;
}
public Builder config(CompilerConfig config) {
this.config = config;
return this;
}
public Compiler build() {
if (sourcePath == null) {
throw new IllegalStateException("No null source path");
}
if (classOutput == null) {
throw new IllegalStateException("No null class output");
}
if (sourceOutput == null) {
throw new IllegalStateException("No null source output");
}
Compiler compiler = new Compiler(
javaCompiler.get(),
sourcePath,
classPaths,
sourceOutput,
classOutput,
config
);
if (processor != null) {
compiler.addAnnotationProcessor(processor.get());
}
return compiler;
}
}
/** . */
private JavaCompiler javaCompiler;
/** . */
private Set processors;
/** . */
private ReadFileSystem> sourcePath;
/** . */
private ReadWriteFileSystem> classOutput;
/** . */
private ReadWriteFileSystem> sourceOutput;
/** . */
private Collection> classPaths;
/** . */
private CompilerConfig config;
public Compiler(
ReadFileSystem> sourcePath,
ReadWriteFileSystem> output,
CompilerConfig config) {
this(sourcePath, output, output, config);
}
public Compiler(
ReadFileSystem> sourcePath,
ReadWriteFileSystem> sourceOutput,
ReadWriteFileSystem> classOutput,
CompilerConfig config) {
this(sourcePath, Collections.>emptyList(), sourceOutput, classOutput, config);
}
public Compiler(
ReadFileSystem> sourcePath,
ReadFileSystem> classPath,
ReadWriteFileSystem> sourceOutput,
ReadWriteFileSystem> classOutput,
CompilerConfig config) {
this(sourcePath, Collections.>singletonList(classPath), sourceOutput, classOutput, config);
}
public Compiler(
ReadFileSystem> sourcePath,
Collection> classPaths,
ReadWriteFileSystem> sourceOutput,
ReadWriteFileSystem> classOutput,
CompilerConfig config) {
this(ToolProvider.getSystemJavaCompiler(), sourcePath, classPaths, sourceOutput, classOutput, config);
}
public Compiler(
JavaCompiler javaCompiler,
ReadFileSystem> sourcePath,
Collection> classPaths,
ReadWriteFileSystem> sourceOutput,
ReadWriteFileSystem> classOutput,
CompilerConfig config) {
this.sourcePath = sourcePath;
this.classPaths = classPaths;
this.sourceOutput = sourceOutput;
this.classOutput = classOutput;
this.javaCompiler = javaCompiler;
this.processors = new HashSet();
this.config = config;
}
public void addAnnotationProcessor(Processor annotationProcessorType) {
if (annotationProcessorType == null) {
throw new NullPointerException("No null processor allowed");
}
processors.add(annotationProcessorType);
}
public void compile(String... compilationUnits) throws IOException, CompilationException {
// Copy anything that is not a java file
RAMFileSystem sourcePath1 = new RAMFileSystem();
sourcePath.copy(new Filter() {
public boolean acceptDir(Object dir, String name) throws IOException {
return true;
}
public boolean acceptFile(Object file, String name) throws IOException {
return !name.endsWith(".java");
}
}, sourcePath1);
//
VirtualFileManager fileManager = new VirtualFileManager(
javaCompiler.getStandardFileManager(null, null, null),
sourcePath1,
classPaths,
sourceOutput,
classOutput
);
//
Collection files = getFromSourcePath(sourcePath, compilationUnits);
//
compile(fileManager, files);
}
private Collection getFromSourcePath(ReadFileSystem fs, String... compilationUnits) throws IOException {
SimpleFileManager
manager = new SimpleFileManager
(StandardLocation.SOURCE_PATH, fs);
ArrayList tmp = new ArrayList();
final ArrayList javaFiles = new ArrayList();
for (String compilationUnit : compilationUnits) {
tmp.clear();
ArrayList names = Spliterator.split(compilationUnit.substring(1), '/', tmp);
String name = tmp.get(tmp.size() - 1);
if (!name.endsWith(".java")) {
throw new IllegalArgumentException("Illegal compilation unit: " + compilationUnit);
}
P file = manager.getFileSystem().getPath(names);
if (file == null) {
throw new IllegalArgumentException("Could not find compilation unit: " + compilationUnit);
}
names.remove(names.size() - 1);
String pkg = Tools.join('.', names);
FileKey key = FileKey.newJavaName(pkg, name);
javaFiles.add(manager.getReadable(key));
}
return javaFiles;
}
public void compile() throws IOException, CompilationException {
VirtualFileManager fileManager = new VirtualFileManager(
javaCompiler.getStandardFileManager(null, null, null),
sourcePath,
classPaths,
sourceOutput,
classOutput
);
compile(fileManager, getFromSourcePath(fileManager.sourcePath));
}
private Collection getFromSourcePath(final SimpleFileManager fileManager) throws IOException {
final ArrayList javaFiles = new ArrayList();
final StringBuilder buffer = new StringBuilder();
fileManager.getFileSystem().traverse(new Visitor.Default() {
@Override
public void enterDir(Object dir, String name) throws IOException {
if (name.length() > 0) {
buffer.append(name).append('.');
}
}
public void file(P file, String name) throws IOException {
if (name.endsWith(".java")) {
FileKey key = FileKey.newJavaName(buffer.substring(0, buffer.length() - 1), name);
JavaFileObject fileObject = fileManager.getReadable(key);
javaFiles.add(fileObject);
}
}
@Override
public void leaveDir(Object dir, String name) throws IOException {
if (name.length() > 0) {
buffer.setLength(buffer.length() - name.length() - 1);
}
}
});
return javaFiles;
}
private void compile(
VirtualFileManager fileManager,
Collection compilationUnits) throws IOException, CompilationException {
if (compilationUnits.isEmpty()) {
if (!config.getForce()) {
return;
}
else {
URI uri = URI.create("/Dumb.java");
compilationUnits = Collections.singleton(new SimpleJavaFileObject(uri, JavaFileObject.Kind.SOURCE) {
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return "public class Dumb {}";
}
});
}
}
//
final List errors = new ArrayList();
DiagnosticListener listener = new DiagnosticListener() {
public void report(Diagnostic extends JavaFileObject> diagnostic) {
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
int columnNumber = (int)diagnostic.getColumnNumber();
int lineNumber = (int)diagnostic.getLineNumber();
Location location = (columnNumber > 0 && lineNumber > 0) ? new Location(columnNumber, lineNumber) : null;
// We pass the default locale instead of null as ECJ has issues with null
String s = diagnostic.getMessage(Locale.getDefault());
//
Message message = Message.parse(s);
// Best effort to get a java.io.File
JavaFileObject obj = diagnostic.getSource();
String source = null;
File resolvedFile = null;
if (obj != null) {
URI uri = obj.toUri();
source = uri.getPath();
if (obj instanceof JavaFileObjectImpl) {
JavaFileObjectImpl foo = (JavaFileObjectImpl)obj;
try {
resolvedFile = foo.getFile();
}
catch (Exception ignore) {
}
} else {
// We are likely in eclipse (with its JavaFileObject wrapper) and we should get the file from the URI
if (uri.getScheme().equals("file")) {
resolvedFile = new File(uri);
}
}
}
//
errors.add(new CompilationError(
message != null ? message.getCode() : null,
message != null ? Arrays.asList(message.getArguments()): Collections.emptyList(),
source, resolvedFile, location, s));
}
}
};
//
List options = new ArrayList();
options.add("-g");
for (String optionName : config.getProcessorOptionNames()) {
options.add("-A" + optionName + "=" + config.getProcessorOptionValue(optionName));
}
//
JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, listener, options, null, compilationUnits);
task.setProcessors(processors);
// We don't use the return value because sometime it says it is failed although
// it is not, need to investigate this at some piont
boolean ok = task.call();
// Clear caches
fileManager.sourceOutput.clearCache();
fileManager.classOutput.clearCache();
fileManager.sourceOutput.clearCache();
if (fileManager.classPath != null) {
fileManager.classPath.clearCache();
}
// Clear processors as we should not reuse them
processors.clear();
//
if (!ok) {
throw new CompilationException(errors);
}
}
}