blob: fc3b4d5c48c04e3cac5a47354ddf33f480fc301d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.tool;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.Processor;
import javax.lang.model.SourceVersion;
import javax.tools.DiagnosticListener;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.internal.compiler.ClassFile;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
import org.eclipse.jdt.internal.compiler.IProblemFactory;
import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
import org.eclipse.jdt.internal.compiler.batch.FileSystem;
import org.eclipse.jdt.internal.compiler.batch.Main;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.Messages;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
/**
* Implementation of a batch compiler that supports the jsr199
*/
public class EclipseCompiler extends Main implements JavaCompiler {
private HashMap<CompilationUnit, JavaFileObject> javaFileObjectMap;
private static Set<SourceVersion> SupportedSourceVersions;
static {
// Eclipse compiler supports all possible versions from version 0 to
// version 6
// we don't care about the order
EnumSet<SourceVersion> enumSet = EnumSet.range(SourceVersion.RELEASE_0, SourceVersion.RELEASE_6);
// we don't want anybody to modify this list
SupportedSourceVersions = Collections.unmodifiableSet(enumSet);
}
Iterable<? extends JavaFileObject> compilationUnits;
public DiagnosticListener<? super JavaFileObject> diagnosticListener;
public JavaFileManager fileManager;
protected Processor[] processors;
public EclipseCompiler(PrintWriter out, PrintWriter err, boolean systemExitWhenFinished) {
super(out, err, systemExitWhenFinished);
}
public EclipseCompiler() {
super(null, null, false);
}
public boolean call() {
try {
if (this.proceed) {
this.globalProblemsCount = 0;
this.globalErrorsCount = 0;
this.globalWarningsCount = 0;
this.globalTasksCount = 0;
this.lineCount = 0;
this.exportedClassFilesCounter = 0;
// request compilation
performCompilation();
}
try {
if (this.fileManager != null) {
this.fileManager.flush();
}
} catch (IOException e) {
// ignore
}
} catch (InvalidInputException e) {
this.logger.logException(e);
if (this.systemExitWhenFinished) {
this.logger.flush();
this.logger.close();
System.exit(-1);
}
return false;
} catch(IllegalArgumentException e) {
throw e;
} catch (RuntimeException e) { // internal compiler failure
this.logger.logException(e);
return false;
} finally {
this.logger.flush();
this.logger.close();
}
if (this.globalErrorsCount == 0)
return true;
return false;
}
public CompilationUnit[] getCompilationUnits() {
ArrayList<CompilationUnit> units = new ArrayList<CompilationUnit>();
for (final JavaFileObject javaFileObject : this.compilationUnits) {
if (javaFileObject.getKind() != JavaFileObject.Kind.SOURCE) {
throw new IllegalArgumentException();
}
String name = javaFileObject.getName();
name = name.replace('\\', '/');
CompilationUnit compilationUnit = new CompilationUnit(null,
name,
null) {
public char[] getContents() {
try {
return javaFileObject.getCharContent(true).toString().toCharArray();
} catch(IOException e) {
e.printStackTrace();
throw new AbortCompilationUnit(null, e, null);
}
}
};
units.add(compilationUnit);
this.javaFileObjectMap.put(compilationUnit, javaFileObject);
}
CompilationUnit[] result = new CompilationUnit[units.size()];
units.toArray(result);
return result;
}
/*
* Low-level API performing the actual compilation
*/
public IErrorHandlingPolicy getHandlingPolicy() {
// passes the initial set of files to the batch oracle (to avoid finding more than once the same units when case insensitive match)
return new IErrorHandlingPolicy() {
public boolean proceedOnErrors() {
return false; // stop if there are some errors
}
public boolean stopOnFirstError() {
return false;
}
};
}
public IProblemFactory getProblemFactory() {
return new DefaultProblemFactory() {
@Override
public CategorizedProblem createProblem(
final char[] originatingFileName,
final int problemId,
final String[] problemArguments,
final String[] messageArguments,
final int severity,
final int startPosition,
final int endPosition,
final int lineNumber,
final int columnNumber) {
DiagnosticListener<? super JavaFileObject> diagnosticListener = EclipseCompiler.this.diagnosticListener;
if (diagnosticListener != null) {
diagnosticListener.report(new Diagnostic<JavaFileObject>() {
public String getCode() {
return Integer.toString(problemId);
}
public long getColumnNumber() {
return columnNumber;
}
public long getEndPosition() {
return endPosition;
}
public Kind getKind() {
if ((severity & ProblemSeverities.Error) != 0) {
return Diagnostic.Kind.ERROR;
}
if ((severity & ProblemSeverities.Optional) != 0) {
return Diagnostic.Kind.WARNING;
}
if ((severity & ProblemSeverities.Warning) != 0) {
return Diagnostic.Kind.MANDATORY_WARNING;
}
return Diagnostic.Kind.OTHER;
}
public long getLineNumber() {
return lineNumber;
}
public String getMessage(Locale locale) {
setLocale(locale);
return getLocalizedMessage(problemId, problemArguments);
}
public long getPosition() {
return startPosition;
}
public JavaFileObject getSource() {
try {
if (EclipseCompiler.this.fileManager.hasLocation(StandardLocation.SOURCE_PATH)) {
return EclipseCompiler.this.fileManager.getJavaFileForInput(
StandardLocation.SOURCE_PATH,
new String(originatingFileName),
JavaFileObject.Kind.SOURCE);
}
} catch (IOException e) {
// ignore
}
return null;
}
public long getStartPosition() {
return startPosition;
}
});
}
return super.createProblem(originatingFileName, problemId, problemArguments, messageArguments, severity, startPosition, endPosition, lineNumber, columnNumber);
}
};
}
/*
* (non-Javadoc)
*
* @see javax.tools.Tool#getSourceVersions()
*/
public Set<SourceVersion> getSourceVersions() {
return SupportedSourceVersions;
}
/*
* (non-Javadoc)
*
* @see javax.tools.JavaCompiler#getStandardFileManager(javax.tools.DiagnosticListener,
* java.util.Locale, java.nio.charset.Charset)
*/
public StandardJavaFileManager getStandardFileManager(DiagnosticListener<? super JavaFileObject> diagnosticListener,
Locale locale,
Charset charset) {
this.diagnosticListener = diagnosticListener;
return new EclipseFileManager(this, locale, charset);
}
/*
* (non-Javadoc)
*
* @see javax.tools.JavaCompiler#getTask(java.io.Writer,
* javax.tools.JavaFileManager, javax.tools.DiagnosticListener,
* java.lang.Iterable, java.lang.Iterable, java.lang.Iterable)
*/
@SuppressWarnings("unchecked")
public CompilationTask getTask(Writer out,
JavaFileManager fileManager,
DiagnosticListener<? super JavaFileObject> diagnosticListener,
Iterable<String> options,
Iterable<String> classes,
Iterable<? extends JavaFileObject> compilationUnits) {
for ()
PrintWriter writerOut = null;
PrintWriter writerErr = null;
if (out == null) {
writerOut = new PrintWriter(System.out);
writerErr = new PrintWriter(System.err);
} else {
writerOut = new PrintWriter(out);
writerErr = new PrintWriter(out);
}
this.compilationUnits = compilationUnits;
this.diagnosticListener = diagnosticListener;
if (fileManager != null) {
this.fileManager = fileManager;
} else {
this.fileManager = this.getStandardFileManager(diagnosticListener, null, null);
}
initialize(writerOut, writerErr, false);
this.options.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_1_6);
this.options.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_6);
this.options.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_1_6);
// TODO FIXME (olivier) REMOVE BEFORE 3.3 once the APT1.6 IS WORKING FINE
for (String option : options ) {
if ("-processorpath".equals(option) //$NON-NLS-1$
|| ("-processor".equals(option))) { //$NON-NLS-1$
this.options.put(CompilerOptions.OPTION_Process_Annotations, CompilerOptions.ENABLED);
}
}
ArrayList<String> allOptions = new ArrayList<String>();
if (options != null) {
for (Iterator<String> iterator = options.iterator(); iterator.hasNext(); ) {
this.fileManager.handleOption(iterator.next(), iterator);
}
for (String option : options) {
allOptions.add(option);
}
}
if (compilationUnits != null) {
for (JavaFileObject javaFileObject : compilationUnits) {
allOptions.add(new File(javaFileObject.toUri()).getAbsolutePath());
}
}
final String[] optionsToProcess = new String[allOptions.size()];
allOptions.toArray(optionsToProcess);
try {
this.configure(optionsToProcess);
} catch (InvalidInputException e) {
throw new RuntimeException(e);
}
if (this.fileManager instanceof StandardJavaFileManager) {
StandardJavaFileManager javaFileManager = (StandardJavaFileManager) this.fileManager;
Iterable<? extends File> location = javaFileManager.getLocation(StandardLocation.CLASS_OUTPUT);
if (location != null) {
this.setDestinationPath(location.iterator().next().getAbsolutePath());
}
}
return new CompilationTask() {
private boolean hasRun = false;
public Boolean call() {
// set up compiler with passed options
if (this.hasRun) {
throw new IllegalStateException("This task has already been run"); //$NON-NLS-1$
}
Boolean value = EclipseCompiler.this.call() ? Boolean.TRUE : Boolean.FALSE;
this.hasRun = true;
return value;
}
public void setLocale(Locale locale) {
EclipseCompiler.this.setLocale(locale);
}
public void setProcessors(Iterable<? extends Processor> processors) {
ArrayList<Processor> temp = new ArrayList<Processor>();
for (Processor processor : processors) {
temp.add(processor);
}
Processor[] processors2 = new Processor[temp.size()];
temp.toArray(processors2);
EclipseCompiler.this.processors = processors2;
}
};
}
@Override
@SuppressWarnings("unchecked")
protected void initialize(PrintWriter outWriter, PrintWriter errWriter, boolean systemExit, Map customDefaultOptions) {
super.initialize(outWriter, errWriter, systemExit, customDefaultOptions);
this.javaFileObjectMap = new HashMap<CompilationUnit, JavaFileObject>();
}
@Override
protected void initializeAnnotationProcessorManager() {
super.initializeAnnotationProcessorManager();
if (this.batchCompiler.annotationProcessorManager != null &&
this.processors != null) {
this.batchCompiler.annotationProcessorManager.setProcessors(this.processors);
} else if (this.processors != null) {
throw new UnsupportedOperationException("Cannot handle annotation processing"); //$NON-NLS-1$
}
}
/*
* (non-Javadoc)
*
* @see javax.tools.OptionChecker#isSupportedOption(java.lang.String)
*/
public int isSupportedOption(String option) {
return Options.processOptions(option);
}
// Dump classfiles onto disk for all compilation units that where successful
// and do not carry a -d none spec, either directly or inherited from Main.
public void outputClassFiles(CompilationResult unitResult) {
if (!((unitResult == null) || (unitResult.hasErrors() && !this.proceedOnError))) {
ClassFile[] classFiles = unitResult.getClassFiles();
boolean generateClasspathStructure = this.fileManager.hasLocation(StandardLocation.CLASS_OUTPUT);
String currentDestinationPath = this.destinationPath;
File outputLocation = null;
if (currentDestinationPath != null) {
outputLocation = new File(currentDestinationPath);
outputLocation.mkdirs();
}
for (int i = 0, fileCount = classFiles.length; i < fileCount; i++) {
// retrieve the key and the corresponding classfile
ClassFile classFile = classFiles[i];
char[] filename = classFile.fileName();
int length = filename.length;
char[] relativeName = new char[length + 6];
System.arraycopy(filename, 0, relativeName, 0, length);
System.arraycopy(SuffixConstants.SUFFIX_class, 0, relativeName, length, 6);
CharOperation.replace(relativeName, '/', File.separatorChar);
String relativeStringName = new String(relativeName);
if (this.compilerOptions.verbose) {
EclipseCompiler.this.out.println(
Messages.bind(
Messages.compilation_write,
new String[] {
String.valueOf(this.exportedClassFilesCounter+1),
relativeStringName
}));
}
try {
JavaFileObject javaFileForOutput =
this.fileManager.getJavaFileForOutput(
StandardLocation.CLASS_OUTPUT,
new String(filename),
JavaFileObject.Kind.CLASS,
this.javaFileObjectMap.get(unitResult.compilationUnit));
if (generateClasspathStructure) {
if (currentDestinationPath != null) {
int index = CharOperation.lastIndexOf(File.separatorChar, relativeName);
if (index != -1) {
File currentFolder = new File(currentDestinationPath, relativeStringName.substring(0, index));
currentFolder.mkdirs();
}
} else {
// create the subfolfers is necessary
// need a way to retrieve the folders to create
String path = javaFileForOutput.toUri().getPath();
int index = path.lastIndexOf('/');
if (index != -1) {
File file = new File(path.substring(0, index));
file.mkdirs();
}
}
}
OutputStream openOutputStream = javaFileForOutput.openOutputStream();
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(openOutputStream);
bufferedOutputStream.write(classFile.header, 0, classFile.headerOffset);
bufferedOutputStream.write(classFile.contents, 0, classFile.contentsOffset);
bufferedOutputStream.flush();
bufferedOutputStream.close();
} catch (IOException e) {
this.logger.logNoClassFileCreated(currentDestinationPath, relativeStringName, e);
}
LookupEnvironment env = EclipseCompiler.this.batchCompiler.lookupEnvironment;
if (classFile.isShared) env.classFilePool.release(classFile);
this.logger.logClassFile(
generateClasspathStructure,
currentDestinationPath,
relativeStringName);
this.exportedClassFilesCounter++;
}
}
}
/*
* (non-Javadoc)
*
* @see javax.tools.Tool#run(java.io.InputStream, java.io.OutputStream,
* java.io.OutputStream, java.lang.String[])
*/
public int run(InputStream in, OutputStream out, OutputStream err, String... arguments) {
boolean succeed = new Main(new PrintWriter(new OutputStreamWriter(out)), new PrintWriter(new OutputStreamWriter(err)), true).compile(arguments);
return succeed ? 0 : -1;
}
@Override
@SuppressWarnings("unchecked")
protected void setPaths(ArrayList bootclasspaths,
String sourcepathClasspathArg,
ArrayList sourcepathClasspaths,
ArrayList classpaths,
ArrayList extdirsClasspaths,
ArrayList endorsedDirClasspaths,
String customEncoding) throws InvalidInputException {
ArrayList<FileSystem.Classpath> fileSystemClasspaths = new ArrayList<FileSystem.Classpath>();
EclipseFileManager javaFileManager = null;
StandardJavaFileManager standardJavaFileManager = null;
if (this.fileManager instanceof EclipseFileManager) {
javaFileManager = (EclipseFileManager) this.fileManager;
}
if (this.fileManager instanceof StandardJavaFileManager) {
standardJavaFileManager = (StandardJavaFileManager) this.fileManager;
}
if (javaFileManager != null) {
if ((javaFileManager.flags & EclipseFileManager.HAS_ENDORSED_DIRS) == 0
&& (javaFileManager.flags & EclipseFileManager.HAS_BOOTCLASSPATH) != 0) {
fileSystemClasspaths.addAll((ArrayList<? extends FileSystem.Classpath>) this.handleEndorseddirs(null));
}
}
Iterable<? extends File> location = null;
if (standardJavaFileManager != null) {
location = standardJavaFileManager.getLocation(StandardLocation.PLATFORM_CLASS_PATH);
}
if (location != null) {
for (File file : location) {
fileSystemClasspaths.add(FileSystem.getClasspath(
file.getAbsolutePath(),
null,
null));
}
}
if (javaFileManager != null) {
if ((javaFileManager.flags & EclipseFileManager.HAS_EXT_DIRS) == 0
&& (javaFileManager.flags & EclipseFileManager.HAS_BOOTCLASSPATH) != 0) {
fileSystemClasspaths.addAll((ArrayList<? extends FileSystem.Classpath>) this.handleExtdirs(null));
}
}
if (standardJavaFileManager != null) {
location = standardJavaFileManager.getLocation(StandardLocation.SOURCE_PATH);
} else {
location = null;
}
if (location != null) {
for (File file : location) {
fileSystemClasspaths.add(FileSystem.getClasspath(
file.getAbsolutePath(),
null,
null));
}
}
if (standardJavaFileManager != null) {
location = standardJavaFileManager.getLocation(StandardLocation.CLASS_PATH);
} else {
location = null;
}
if (location != null) {
for (File file : location) {
fileSystemClasspaths.add(FileSystem.getClasspath(
file.getAbsolutePath(),
null,
null));
}
}
if (this.checkedClasspaths == null) {
fileSystemClasspaths.addAll((ArrayList<? extends FileSystem.Classpath>) this.handleBootclasspath(null, null));
fileSystemClasspaths.addAll((ArrayList<? extends FileSystem.Classpath>) this.handleClasspath(null, null));
}
final int size = fileSystemClasspaths.size();
if (size != 0) {
this.checkedClasspaths = new FileSystem.Classpath[size];
int i = 0;
for (FileSystem.Classpath classpath : fileSystemClasspaths) {
this.checkedClasspaths[i++] = classpath;
}
}
}
}