/******************************************************************************* * 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; } } } }