/******************************************************************************* * Copyright (c) 2000, 2013 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.core.builder; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.compiler.*; import org.eclipse.jdt.internal.compiler.ClassFile; import org.eclipse.jdt.internal.compiler.impl.CompilerStats; import org.eclipse.jdt.internal.core.util.Messages; import org.eclipse.jdt.internal.core.util.Util; import java.util.*; @SuppressWarnings({ "rawtypes", "unchecked" }) public class BatchImageBuilder extends AbstractImageBuilder { IncrementalImageBuilder incrementalBuilder; // if annotations or secondary types have to be processed after the compile loop ArrayList secondaryTypes; // qualified names for all secondary types found during batch compile StringSet typeLocatorsWithUndefinedTypes; // type locators for all source files with errors that may be caused by 'not found' secondary types protected BatchImageBuilder(JavaBuilder javaBuilder, boolean buildStarting) { super(javaBuilder, buildStarting, null); this.nameEnvironment.isIncrementalBuild = false; this.incrementalBuilder = null; this.secondaryTypes = null; this.typeLocatorsWithUndefinedTypes = null; } public void build() { if (JavaBuilder.DEBUG) System.out.println("FULL build"); //$NON-NLS-1$ try { this.notifier.subTask(Messages.bind(Messages.build_cleaningOutput, this.javaBuilder.currentProject.getName())); JavaBuilder.removeProblemsAndTasksFor(this.javaBuilder.currentProject); cleanOutputFolders(true); this.notifier.updateProgressDelta(0.05f); this.notifier.subTask(Messages.build_analyzingSources); ArrayList sourceFiles = new ArrayList(33); addAllSourceFiles(sourceFiles); this.notifier.updateProgressDelta(0.10f); if (sourceFiles.size() > 0) { SourceFile[] allSourceFiles = new SourceFile[sourceFiles.size()]; sourceFiles.toArray(allSourceFiles); this.notifier.setProgressPerCompilationUnit(0.75f / allSourceFiles.length); this.workQueue.addAll(allSourceFiles); compile(allSourceFiles); if (this.typeLocatorsWithUndefinedTypes != null) if (this.secondaryTypes != null && !this.secondaryTypes.isEmpty()) rebuildTypesAffectedBySecondaryTypes(); if (this.incrementalBuilder != null) this.incrementalBuilder.buildAfterBatchBuild(); } if (this.javaBuilder.javaProject.hasCycleMarker()) this.javaBuilder.mustPropagateStructuralChanges(); } catch (CoreException e) { throw internalException(e); } finally { if (JavaBuilder.SHOW_STATS) printStats(); cleanUp(); } } protected void acceptSecondaryType(ClassFile classFile) { if (this.secondaryTypes != null) this.secondaryTypes.add(classFile.fileName()); } protected void cleanOutputFolders(boolean copyBack) throws CoreException { boolean deleteAll = JavaCore.CLEAN.equals( this.javaBuilder.javaProject.getOption(JavaCore.CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER, true)); if (deleteAll) { if (this.javaBuilder.participants != null) for (int i = 0, l = this.javaBuilder.participants.length; i < l; i++) this.javaBuilder.participants[i].cleanStarting(this.javaBuilder.javaProject); ArrayList visited = new ArrayList(this.sourceLocations.length); for (int i = 0, l = this.sourceLocations.length; i < l; i++) { this.notifier.subTask(Messages.bind(Messages.build_cleaningOutput, this.javaBuilder.currentProject.getName())); ClasspathMultiDirectory sourceLocation = this.sourceLocations[i]; if (sourceLocation.hasIndependentOutputFolder) { IContainer outputFolder = sourceLocation.binaryFolder; if (!visited.contains(outputFolder)) { visited.add(outputFolder); IResource[] members = outputFolder.members(); for (int j = 0, m = members.length; j < m; j++) { IResource member = members[j]; if (!member.isDerived()) { member.accept( new IResourceVisitor() { public boolean visit(IResource resource) throws CoreException { resource.setDerived(true, null); return resource.getType() != IResource.FILE; } } ); } member.delete(IResource.FORCE, null); } } this.notifier.checkCancel(); if (copyBack) copyExtraResourcesBack(sourceLocation, true); } else { boolean isOutputFolder = sourceLocation.sourceFolder.equals(sourceLocation.binaryFolder); final char[][] exclusionPatterns = isOutputFolder ? sourceLocation.exclusionPatterns : null; // ignore exclusionPatterns if output folder == another source folder... not this one final char[][] inclusionPatterns = isOutputFolder ? sourceLocation.inclusionPatterns : null; // ignore inclusionPatterns if output folder == another source folder... not this one sourceLocation.binaryFolder.accept( new IResourceProxyVisitor() { public boolean visit(IResourceProxy proxy) throws CoreException { if (proxy.getType() == IResource.FILE) { if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(proxy.getName())) { IResource resource = proxy.requestResource(); if (exclusionPatterns != null || inclusionPatterns != null) if (Util.isExcluded(resource.getFullPath(), inclusionPatterns, exclusionPatterns, false)) return false; if (!resource.isDerived()) resource.setDerived(true, null); resource.delete(IResource.FORCE, null); } return false; } if (exclusionPatterns != null && inclusionPatterns == null) // must walk children if inclusionPatterns != null if (Util.isExcluded(proxy.requestFullPath(), null, exclusionPatterns, true)) return false; BatchImageBuilder.this.notifier.checkCancel(); return true; } }, IResource.NONE ); this.notifier.checkCancel(); } this.notifier.checkCancel(); } } else if (copyBack) { for (int i = 0, l = this.sourceLocations.length; i < l; i++) { ClasspathMultiDirectory sourceLocation = this.sourceLocations[i]; if (sourceLocation.hasIndependentOutputFolder) copyExtraResourcesBack(sourceLocation, false); this.notifier.checkCancel(); } } } protected void cleanUp() { this.incrementalBuilder = null; this.secondaryTypes = null; this.typeLocatorsWithUndefinedTypes = null; super.cleanUp(); } protected void compile(SourceFile[] units, SourceFile[] additionalUnits, boolean compilingFirstGroup) { if (additionalUnits != null && this.secondaryTypes == null) this.secondaryTypes = new ArrayList(7); super.compile(units, additionalUnits, compilingFirstGroup); } protected void copyExtraResourcesBack(ClasspathMultiDirectory sourceLocation, final boolean deletedAll) throws CoreException { // When, if ever, does a builder need to copy resources files (not .java or .class) into the output folder? // If we wipe the output folder at the beginning of the build then all 'extra' resources must be copied to the output folder. this.notifier.subTask(Messages.build_copyingResources); final int segmentCount = sourceLocation.sourceFolder.getFullPath().segmentCount(); final char[][] exclusionPatterns = sourceLocation.exclusionPatterns; final char[][] inclusionPatterns = sourceLocation.inclusionPatterns; final IContainer outputFolder = sourceLocation.binaryFolder; final boolean isAlsoProject = sourceLocation.sourceFolder.equals(this.javaBuilder.currentProject); sourceLocation.sourceFolder.accept( new IResourceProxyVisitor() { public boolean visit(IResourceProxy proxy) throws CoreException { IResource resource = null; switch(proxy.getType()) { case IResource.FILE : if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(proxy.getName()) || org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(proxy.getName())) return false; resource = proxy.requestResource(); if (BatchImageBuilder.this.javaBuilder.filterExtraResource(resource)) return false; if (exclusionPatterns != null || inclusionPatterns != null) if (Util.isExcluded(resource.getFullPath(), inclusionPatterns, exclusionPatterns, false)) return false; IPath partialPath = resource.getFullPath().removeFirstSegments(segmentCount); IResource copiedResource = outputFolder.getFile(partialPath); if (copiedResource.exists()) { if (deletedAll) { IResource originalResource = findOriginalResource(partialPath); String id = originalResource.getFullPath().removeFirstSegments(1).toString(); createProblemFor( resource, null, Messages.bind(Messages.build_duplicateResource, id), BatchImageBuilder.this.javaBuilder.javaProject.getOption(JavaCore.CORE_JAVA_BUILD_DUPLICATE_RESOURCE, true)); return false; } copiedResource.delete(IResource.FORCE, null); // last one wins } createFolder(partialPath.removeLastSegments(1), outputFolder); // ensure package folder exists copyResource(resource, copiedResource); return false; case IResource.FOLDER : resource = proxy.requestResource(); if (BatchImageBuilder.this.javaBuilder.filterExtraResource(resource)) return false; if (isAlsoProject && isExcludedFromProject(resource.getFullPath())) return false; // the sourceFolder == project if (exclusionPatterns != null && inclusionPatterns == null) // must walk children if inclusionPatterns != null if (Util.isExcluded(resource.getFullPath(), null, exclusionPatterns, true)) return false; } return true; } }, IResource.NONE ); } protected IResource findOriginalResource(IPath partialPath) { for (int i = 0, l = this.sourceLocations.length; i < l; i++) { ClasspathMultiDirectory sourceLocation = this.sourceLocations[i]; if (sourceLocation.hasIndependentOutputFolder) { IResource originalResource = sourceLocation.sourceFolder.getFile(partialPath); if (originalResource.exists()) return originalResource; } } return null; } private void printStats() { if (this.compiler == null) return; CompilerStats compilerStats = this.compiler.stats; long time = compilerStats.elapsedTime(); long lineCount = compilerStats.lineCount; double speed = ((int) (lineCount * 10000.0 / time)) / 10.0; System.out.println(">FULL BUILD STATS for: "+this.javaBuilder.javaProject.getElementName()); //$NON-NLS-1$ System.out.println("> compiled " + lineCount + " lines in " + time + "ms:" + speed + "lines/s"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ System.out.print("> parse: " + compilerStats.parseTime + " ms (" + ((int) (compilerStats.parseTime * 1000.0 / time)) / 10.0 + "%)"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ System.out.print(", resolve: " + compilerStats.resolveTime + " ms (" + ((int) (compilerStats.resolveTime * 1000.0 / time)) / 10.0 + "%)"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ System.out.print(", analyze: " + compilerStats.analyzeTime + " ms (" + ((int) (compilerStats.analyzeTime * 1000.0 / time)) / 10.0 + "%)"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ System.out.println(", generate: " + compilerStats.generateTime + " ms (" + ((int) (compilerStats.generateTime * 1000.0 / time)) / 10.0 + "%)"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } protected void processAnnotationResults(CompilationParticipantResult[] results) { // to compile the compilation participant results, we need to incrementally recompile all affected types // whenever the generated types are initially added or structurally changed if (this.incrementalBuilder == null) this.incrementalBuilder = new IncrementalImageBuilder(this); this.incrementalBuilder.processAnnotationResults(results); } protected void rebuildTypesAffectedBySecondaryTypes() { // to compile types that could not find 'missing' secondary types because of multiple // compile groups, we need to incrementally recompile all affected types as if the missing // secondary types have just been added, see bug 146324 if (this.incrementalBuilder == null) this.incrementalBuilder = new IncrementalImageBuilder(this); int count = this.secondaryTypes.size(); StringSet qualifiedNames = new StringSet(count * 2); StringSet simpleNames = new StringSet(count); StringSet rootNames = new StringSet(3); while (--count >=0) { char[] secondaryTypeName = (char[]) this.secondaryTypes.get(count); IPath path = new Path(null, new String(secondaryTypeName)); this.incrementalBuilder.addDependentsOf(path, false, qualifiedNames, simpleNames, rootNames); } this.incrementalBuilder.addAffectedSourceFiles( qualifiedNames, simpleNames, rootNames, this.typeLocatorsWithUndefinedTypes); } protected void storeProblemsFor(SourceFile sourceFile, CategorizedProblem[] problems) throws CoreException { if (sourceFile == null || problems == null || problems.length == 0) return; for (int i = problems.length; --i >= 0;) { CategorizedProblem problem = problems[i]; if (problem != null && problem.getID() == IProblem.UndefinedType) { if (this.typeLocatorsWithUndefinedTypes == null) this.typeLocatorsWithUndefinedTypes = new StringSet(3); this.typeLocatorsWithUndefinedTypes.add(sourceFile.typeLocator()); break; } } super.storeProblemsFor(sourceFile, problems); } public String toString() { return "batch image builder for:\n\tnew state: " + this.newState; //$NON-NLS-1$ } }