org.eclipse.jdt.internal.core.builder.BatchImageBuilder Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (c) 2000, 2018 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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.CompilationGroup;
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
Set typeLocatorsWithUndefinedTypes; // type locators for all source files with errors that may be caused by 'not found' secondary types
final CompilationGroup compilationGroup;
protected BatchImageBuilder(JavaBuilder javaBuilder, boolean buildStarting, CompilationGroup compilationGroup) {
super(javaBuilder, buildStarting, null, compilationGroup);
this.compilationGroup = compilationGroup;
this.nameEnvironment.isIncrementalBuild = false;
this.incrementalBuilder = null;
this.secondaryTypes = null;
this.typeLocatorsWithUndefinedTypes = null;
}
protected BatchImageBuilder(BatchImageBuilder batchImageBuilder, boolean buildStarting, CompilationGroup compilationGroup) {
super(batchImageBuilder.javaBuilder, buildStarting, batchImageBuilder.newState, compilationGroup);
this.compilationGroup = compilationGroup;
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()));
if(this.compilationGroup != CompilationGroup.TEST) {
JavaBuilder.removeProblemsAndTasksFor(this.javaBuilder.currentProject);
}
cleanOutputFolders(true);
this.notifier.updateProgressDelta(0.05f);
this.notifier.subTask(Messages.build_analyzingSources);
LinkedHashSet sourceFiles = new LinkedHashSet<>(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();
}
}
@Override
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.compilationGroup != CompilationGroup.TEST) {
// CompilationGroup.MAIN is done first, so this notifies the participants only once
// calling this for CompilationGroup.TEST could cases generated files for CompilationGroup.MAIN to be deleted.
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);
}
Set visited = new LinkedHashSet<>(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() {
@Override
public boolean visit(IResource resource) throws CoreException {
resource.setDerived(true, null);
return resource.getType() != IResource.FILE;
}
}
);
}
try {
member.delete(IResource.FORCE, null);
} catch(CoreException e) {
Util.log(e, "Error occurred while deleting: " + member.getFullPath()); //$NON-NLS-1$
}
}
}
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() {
@Override
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);
try {
resource.delete(IResource.FORCE, null);
} catch(CoreException e) {
Util.log(e, "Error occurred while deleting: " + resource.getFullPath()); //$NON-NLS-1$
}
}
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();
}
}
}
@Override
protected void cleanUp() {
this.incrementalBuilder = null;
this.secondaryTypes = null;
this.typeLocatorsWithUndefinedTypes = null;
super.cleanUp();
}
@Override
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() {
@Override
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 :
BatchImageBuilder.this.notifier.checkCancel();
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("\n>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$
}
@Override
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.compilationGroup);
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, this.compilationGroup);
int count = this.secondaryTypes.size();
Set qualifiedNames = new HashSet<>(count * 2);
Set simpleNames = new HashSet<>(count);
Set rootNames = new HashSet<>(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);
}
@Override
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 HashSet<>(3);
this.typeLocatorsWithUndefinedTypes.add(sourceFile.typeLocator());
break;
}
}
super.storeProblemsFor(sourceFile, problems);
}
@Override
public String toString() {
return "batch image builder for:\n\tnew state: " + this.newState; //$NON-NLS-1$
}
}