protected boolean checkForClassFileChanges( IResourceDelta binaryDelta, ClasspathMultiDirectory md, int segmentCount) throws CoreException { IResource resource = binaryDelta.getResource(); // remember that if inclusion & exclusion patterns change then a full build is done boolean isExcluded = (md.exclusionPatterns != null || md.inclusionPatterns != null) && Util.isExcluded(resource, md.inclusionPatterns, md.exclusionPatterns); switch (resource.getType()) { case IResource.FOLDER: if (isExcluded && md.inclusionPatterns == null) return true; // no need to go further with this delta since its children cannot be // included IResourceDelta[] children = binaryDelta.getAffectedChildren(); for (int i = 0, l = children.length; i < l; i++) if (!checkForClassFileChanges(children[i], md, segmentCount)) return false; return true; case IResource.FILE: if (!isExcluded && org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(resource.getName())) { // perform full build if a managed class file has been changed IPath typePath = resource.getFullPath().removeFirstSegments(segmentCount).removeFileExtension(); if (this.newState.isKnownType(typePath.toString())) { if (JavaBuilder.DEBUG) System.out.println( "MUST DO FULL BUILD. Found change to class file " + typePath); // $NON-NLS-1$ return false; } return true; } } return true; }
protected void finishedWith( String sourceLocator, CompilationResult result, char[] mainTypeName, ArrayList definedTypeNames, ArrayList duplicateTypeNames) { char[][] previousTypeNames = this.newState.getDefinedTypeNamesFor(sourceLocator); if (previousTypeNames == null) previousTypeNames = new char[][] {mainTypeName}; IPath packagePath = null; next: for (int i = 0, l = previousTypeNames.length; i < l; i++) { char[] previous = previousTypeNames[i]; for (int j = 0, m = definedTypeNames.size(); j < m; j++) if (CharOperation.equals(previous, (char[]) definedTypeNames.get(j))) continue next; SourceFile sourceFile = (SourceFile) result.getCompilationUnit(); if (packagePath == null) { int count = sourceFile.sourceLocation.sourceFolder.getFullPath().segmentCount(); packagePath = sourceFile.resource.getFullPath().removeFirstSegments(count).removeLastSegments(1); } if (this.secondaryTypesToRemove == null) this.secondaryTypesToRemove = new SimpleLookupTable(); ArrayList types = (ArrayList) this.secondaryTypesToRemove.get(sourceFile.sourceLocation.binaryFolder); if (types == null) types = new ArrayList(definedTypeNames.size()); types.add(packagePath.append(new String(previous))); this.secondaryTypesToRemove.put(sourceFile.sourceLocation.binaryFolder, types); } super.finishedWith(sourceLocator, result, mainTypeName, definedTypeNames, duplicateTypeNames); }
protected void removeClassFile(IPath typePath, IContainer outputFolder) throws CoreException { if (typePath.lastSegment().indexOf('$') == -1) { // is not a nested type this.newState.removeQualifiedTypeName(typePath.toString()); // add dependents even when the type thinks it does not exist to be on the safe side if (JavaBuilder.DEBUG) System.out.println("Found removed type " + typePath); // $NON-NLS-1$ addDependentsOf( typePath, true); // when member types are removed, their enclosing type is structurally changed } IFile classFile = outputFolder.getFile(typePath.addFileExtension(SuffixConstants.EXTENSION_class)); if (classFile.exists()) { if (JavaBuilder.DEBUG) System.out.println("Deleting class file of removed type " + typePath); // $NON-NLS-1$ classFile.delete(IResource.FORCE, null); } }
boolean filterExtraResource(IResource resource) { if (this.extraResourceFileFilters != null) { char[] name = resource.getName().toCharArray(); for (int i = 0, l = this.extraResourceFileFilters.length; i < l; i++) if (CharOperation.match(this.extraResourceFileFilters[i], name, true)) return true; } if (this.extraResourceFolderFilters != null) { IPath path = resource.getProjectRelativePath(); String pathName = path.toString(); int count = path.segmentCount(); if (resource.getType() == IResource.FILE) count--; for (int i = 0, l = this.extraResourceFolderFilters.length; i < l; i++) if (pathName.indexOf(this.extraResourceFolderFilters[i]) != -1) for (int j = 0; j < count; j++) if (this.extraResourceFolderFilters[i].equals(path.segment(j))) return true; } return false; }
protected void deleteGeneratedFiles(IFile[] deletedGeneratedFiles) { // delete generated files and recompile any affected source files try { for (int j = deletedGeneratedFiles.length; --j >= 0; ) { IFile deletedFile = deletedGeneratedFiles[j]; if (deletedFile.exists()) continue; // only delete .class files for source files that were actually deleted SourceFile sourceFile = findSourceFile(deletedFile, false); String typeLocator = sourceFile.typeLocator(); int mdSegmentCount = sourceFile.sourceLocation.sourceFolder.getFullPath().segmentCount(); IPath typePath = sourceFile .resource .getFullPath() .removeFirstSegments(mdSegmentCount) .removeFileExtension(); addDependentsOf(typePath, true); // add dependents of the source file since its now deleted this.previousSourceFiles = null; // existing source files did not see it as deleted since they were compiled before // it was char[][] definedTypeNames = this.newState.getDefinedTypeNamesFor(typeLocator); if (definedTypeNames == null) { // defined a single type matching typePath removeClassFile(typePath, sourceFile.sourceLocation.binaryFolder); } else { if (definedTypeNames.length > 0) { // skip it if it failed to successfully define a type IPath packagePath = typePath.removeLastSegments(1); for (int d = 0, l = definedTypeNames.length; d < l; d++) removeClassFile( packagePath.append(new String(definedTypeNames[d])), sourceFile.sourceLocation.binaryFolder); } } this.newState.removeLocator(typeLocator); } } catch (CoreException e) { // must continue with compile loop so just log the CoreException Util.log( e, "JavaBuilder logging CompilationParticipant's CoreException to help debugging"); //$NON-NLS-1$ } }
/* Return the list of projects for which it requires a resource delta. This builder's project * is implicitly included and need not be specified. Builders must re-specify the list * of interesting projects every time they are run as this is not carried forward * beyond the next build. Missing projects should be specified but will be ignored until * they are added to the workspace. */ private IProject[] getRequiredProjects(boolean includeBinaryPrerequisites) { if (this.javaProject == null || this.workspaceRoot == null) return new IProject[0]; ArrayList projects = new ArrayList(); ExternalFoldersManager externalFoldersManager = JavaModelManager.getExternalManager(); try { IClasspathEntry[] entries = this.javaProject.getExpandedClasspath(); for (int i = 0, l = entries.length; i < l; i++) { IClasspathEntry entry = entries[i]; IPath path = entry.getPath(); IProject p = null; switch (entry.getEntryKind()) { case IClasspathEntry.CPE_PROJECT: p = this.workspaceRoot.getProject( path.lastSegment()); // missing projects are considered too if (((ClasspathEntry) entry).isOptional() && !JavaProject.hasJavaNature(p)) // except if entry is optional p = null; break; case IClasspathEntry.CPE_LIBRARY: if (includeBinaryPrerequisites && path.segmentCount() > 0) { // some binary resources on the class path can come from projects that are not // included in the project references IResource resource = this.workspaceRoot.findMember(path.segment(0)); if (resource instanceof IProject) { p = (IProject) resource; } else { resource = externalFoldersManager.getFolder(path); if (resource != null) p = resource.getProject(); } } } if (p != null && !projects.contains(p)) projects.add(p); } } catch (JavaModelException e) { return new IProject[0]; } IProject[] result = new IProject[projects.size()]; projects.toArray(result); return result; }
/* * Instruct the build manager that this project is involved in a cycle and * needs to propagate structural changes to the other projects in the cycle. */ void mustPropagateStructuralChanges() { LinkedHashSet cycleParticipants = new LinkedHashSet(3); this.javaProject.updateCycleParticipants( new ArrayList(), cycleParticipants, this.workspaceRoot, new HashSet(3), null); IPath currentPath = this.javaProject.getPath(); Iterator i = cycleParticipants.iterator(); while (i.hasNext()) { IPath participantPath = (IPath) i.next(); if (participantPath != currentPath) { IProject project = this.workspaceRoot.getProject(participantPath.segment(0)); if (hasBeenBuilt(project)) { if (DEBUG) System.out.println( "JavaBuilder: Requesting another build iteration since cycle participant " + project.getName() // $NON-NLS-1$ + " has not yet seen some structural changes"); //$NON-NLS-1$ needRebuild(); return; } } } }
protected void addDependentsOf( IPath path, boolean isStructuralChange, StringSet qualifiedNames, StringSet simpleNames, StringSet rootNames) { path = path.setDevice(null); if (isStructuralChange) { String last = path.lastSegment(); if (last.length() == TypeConstants.PACKAGE_INFO_NAME.length) if (CharOperation.equals(last.toCharArray(), TypeConstants.PACKAGE_INFO_NAME)) { path = path.removeLastSegments( 1); // the package-info file has changed so blame the package itself /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=323785, in the case of default package, there is no need to blame the package itself as there can be no annotations or documentation comment tags in the package-info file that can influence the rest of the package. Just bail out so we don't touch null objects below. */ if (path.isEmpty()) return; } } if (isStructuralChange && !this.hasStructuralChanges) { this.newState.tagAsStructurallyChanged(); this.hasStructuralChanges = true; } // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X' rootNames.add(path.segment(0)); String packageName = path.removeLastSegments(1).toString(); boolean wasNew = qualifiedNames.add(packageName); String typeName = path.lastSegment(); int memberIndex = typeName.indexOf('$'); if (memberIndex > 0) typeName = typeName.substring(0, memberIndex); wasNew = simpleNames.add(typeName) | wasNew; if (wasNew && JavaBuilder.DEBUG) System.out.println( " will look for dependents of " //$NON-NLS-1$ + typeName + " in " + packageName); //$NON-NLS-1$ }
/** * @see * org.eclipse.jdt.internal.core.builder.AbstractImageBuilder#writeClassFileContents(org.eclipse.jdt.internal.compiler.ClassFile, * org.eclipse.core.resources.IFile, java.lang.String, boolean, * org.eclipse.jdt.internal.core.builder.SourceFile) */ protected void writeClassFileContents( ClassFile classfile, IFile file, String qualifiedFileName, boolean isTopLevelType, SourceFile compilationUnit) throws CoreException { // Before writing out the class file, compare it to the previous file // If structural changes occurred then add dependent source files byte[] bytes = classfile.getBytes(); if (file.exists()) { if (writeClassFileCheck(file, qualifiedFileName, bytes) || compilationUnit.updateClassFile) { // see 46093 if (JavaBuilder.DEBUG) System.out.println("Writing changed class file " + file.getName()); // $NON-NLS-1$ if (!file.isDerived()) file.setDerived(true, null); file.setContents(new ByteArrayInputStream(bytes), true, false, null); } else if (JavaBuilder.DEBUG) { System.out.println("Skipped over unchanged class file " + file.getName()); // $NON-NLS-1$ } } else { if (isTopLevelType) addDependentsOf(new Path(qualifiedFileName), true); // new type if (JavaBuilder.DEBUG) System.out.println("Writing new class file " + file.getName()); // $NON-NLS-1$ try { file.create(new ByteArrayInputStream(bytes), IResource.FORCE | IResource.DERIVED, null); } catch (CoreException e) { if (e.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) { IStatus status = e.getStatus(); if (status instanceof IResourceStatus) { IPath oldFilePath = ((IResourceStatus) status).getPath(); char[] oldTypeName = oldFilePath.removeFileExtension().lastSegment().toCharArray(); char[][] previousTypeNames = this.newState.getDefinedTypeNamesFor(compilationUnit.typeLocator()); boolean fromSameFile = false; if (previousTypeNames == null) { fromSameFile = CharOperation.equals(compilationUnit.getMainTypeName(), oldTypeName); } else { for (int i = 0, l = previousTypeNames.length; i < l; i++) { if (CharOperation.equals(previousTypeNames[i], oldTypeName)) { fromSameFile = true; break; } } } if (fromSameFile) { // file is defined by the same compilationUnit, but won't be deleted until later so do // it now IFile collision = file.getParent().getFile(new Path(oldFilePath.lastSegment())); collision.delete(true, false, null); boolean success = false; try { file.create( new ByteArrayInputStream(bytes), IResource.FORCE | IResource.DERIVED, null); success = true; } catch (CoreException ignored) { // ignore the second exception } if (success) return; } } // catch the case that a type has been renamed and collides on disk with an // as-yet-to-be-deleted type throw new AbortCompilation(true, new AbortIncrementalBuildException(qualifiedFileName)); } throw e; // rethrow } } }
protected boolean findSourceFiles( IResourceDelta sourceDelta, ClasspathMultiDirectory md, int segmentCount) throws CoreException { // When a package becomes a type or vice versa, expect 2 deltas, // one on the folder & one on the source file IResource resource = sourceDelta.getResource(); // remember that if inclusion & exclusion patterns change then a full build is done boolean isExcluded = (md.exclusionPatterns != null || md.inclusionPatterns != null) && Util.isExcluded(resource, md.inclusionPatterns, md.exclusionPatterns); switch (resource.getType()) { case IResource.FOLDER: if (isExcluded && md.inclusionPatterns == null) return true; // no need to go further with this delta since its children cannot be // included switch (sourceDelta.getKind()) { case IResourceDelta.ADDED: if (!isExcluded) { IPath addedPackagePath = resource.getFullPath().removeFirstSegments(segmentCount); createFolder( addedPackagePath, md.binaryFolder); // ensure package exists in the output folder // see if any known source file is from the same package... classpath already includes // new package if (this.sourceLocations.length > 1 && this.newState.isKnownPackage(addedPackagePath.toString())) { if (JavaBuilder.DEBUG) System.out.println( "Skipped dependents of added package " + addedPackagePath); // $NON-NLS-1$ } else { if (JavaBuilder.DEBUG) System.out.println("Found added package " + addedPackagePath); // $NON-NLS-1$ addDependentsOf(addedPackagePath, true); } } // $FALL-THROUGH$ collect all the source files case IResourceDelta.CHANGED: IResourceDelta[] children = sourceDelta.getAffectedChildren(); for (int i = 0, l = children.length; i < l; i++) if (!findSourceFiles(children[i], md, segmentCount)) return false; return true; case IResourceDelta.REMOVED: if (isExcluded) { // since this folder is excluded then there is nothing to delete (from this md), but // must walk any included subfolders children = sourceDelta.getAffectedChildren(); for (int i = 0, l = children.length; i < l; i++) if (!findSourceFiles(children[i], md, segmentCount)) return false; return true; } IPath removedPackagePath = resource.getFullPath().removeFirstSegments(segmentCount); if (this.sourceLocations.length > 1) { for (int i = 0, l = this.sourceLocations.length; i < l; i++) { if (this.sourceLocations[i].sourceFolder.getFolder(removedPackagePath).exists()) { // only a package fragment was removed, same as removing multiple source files createFolder( removedPackagePath, md.binaryFolder); // ensure package exists in the output folder IResourceDelta[] removedChildren = sourceDelta.getAffectedChildren(); for (int j = 0, m = removedChildren.length; j < m; j++) if (!findSourceFiles(removedChildren[j], md, segmentCount)) return false; return true; } } } if ((sourceDelta.getFlags() & IResourceDelta.MOVED_TO) != 0) { // same idea as moving a source file // see bug 163200 IResource movedFolder = this.javaBuilder.workspaceRoot.getFolder(sourceDelta.getMovedToPath()); JavaBuilder.removeProblemsAndTasksFor(movedFolder); } IFolder removedPackageFolder = md.binaryFolder.getFolder(removedPackagePath); if (removedPackageFolder.exists()) removedPackageFolder.delete(IResource.FORCE, null); // add dependents even when the package thinks it does not exist to be on the safe side if (JavaBuilder.DEBUG) System.out.println("Found removed package " + removedPackagePath); // $NON-NLS-1$ addDependentsOf(removedPackagePath, true); this.newState.removePackage(sourceDelta); } return true; case IResource.FILE: if (isExcluded) return true; String resourceName = resource.getName(); // GROOVY start // determine if this is a Groovy project final boolean isInterestingProject = LanguageSupportFactory.isInterestingProject(this.javaBuilder.getProject()); // GROOVY end // GROOVY start /* old { if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(resourceName)) { } new */ // GRECLIPSE-404 must call 'isJavaLikeFile' directly in order to make the Scala-Eclipse // plugin's weaving happy if ((!isInterestingProject && org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(resourceName) && !LanguageSupportFactory.isInterestingSourceFile(resourceName)) || (isInterestingProject && LanguageSupportFactory.isSourceFile(resourceName, isInterestingProject))) { // GROOVY end IPath typePath = resource.getFullPath().removeFirstSegments(segmentCount).removeFileExtension(); String typeLocator = resource.getProjectRelativePath().toString(); switch (sourceDelta.getKind()) { case IResourceDelta.ADDED: if (JavaBuilder.DEBUG) System.out.println("Compile this added source file " + typeLocator); // $NON-NLS-1$ this.sourceFiles.add(new SourceFile((IFile) resource, md, true)); String typeName = typePath.toString(); if (!this.newState.isDuplicateLocator( typeName, typeLocator)) { // adding dependents results in 2 duplicate errors if (JavaBuilder.DEBUG) System.out.println("Found added source file " + typeName); // $NON-NLS-1$ addDependentsOf(typePath, true); } return true; case IResourceDelta.REMOVED: char[][] definedTypeNames = this.newState.getDefinedTypeNamesFor(typeLocator); if (definedTypeNames == null) { // defined a single type matching typePath removeClassFile(typePath, md.binaryFolder); if ((sourceDelta.getFlags() & IResourceDelta.MOVED_TO) != 0) { // remove problems and tasks for a compilation unit that is being moved (to // another package or renamed) // if the target file is a compilation unit, the new cu will be recompiled // if the target file is a non-java resource, then markers are removed // see bug 2857 IResource movedFile = this.javaBuilder.workspaceRoot.getFile(sourceDelta.getMovedToPath()); JavaBuilder.removeProblemsAndTasksFor(movedFile); } } else { if (JavaBuilder.DEBUG) System.out.println( "Found removed source file " + typePath.toString()); // $NON-NLS-1$ addDependentsOf( typePath, true); // add dependents of the source file since it may be involved in a name // collision if (definedTypeNames.length > 0) { // skip it if it failed to successfully define a type IPath packagePath = typePath.removeLastSegments(1); for (int i = 0, l = definedTypeNames.length; i < l; i++) removeClassFile( packagePath.append(new String(definedTypeNames[i])), md.binaryFolder); } } this.newState.removeLocator(typeLocator); return true; case IResourceDelta.CHANGED: if ((sourceDelta.getFlags() & IResourceDelta.CONTENT) == 0 && (sourceDelta.getFlags() & IResourceDelta.ENCODING) == 0) return true; // skip it since it really isn't changed if (JavaBuilder.DEBUG) System.out.println( "Compile this changed source file " + typeLocator); // $NON-NLS-1$ this.sourceFiles.add(new SourceFile((IFile) resource, md, true)); } return true; } else if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(resourceName)) { // perform full build if a managed class file has been changed if (this.makeOutputFolderConsistent) { IPath typePath = resource.getFullPath().removeFirstSegments(segmentCount).removeFileExtension(); if (this.newState.isKnownType(typePath.toString())) { if (JavaBuilder.DEBUG) System.out.println( "MUST DO FULL BUILD. Found change to class file " + typePath); // $NON-NLS-1$ return false; } } return true; } else if (md.hasIndependentOutputFolder) { if (this.javaBuilder.filterExtraResource(resource)) return true; // copy all other resource deltas to the output folder IPath resourcePath = resource.getFullPath().removeFirstSegments(segmentCount); IResource outputFile = md.binaryFolder.getFile(resourcePath); switch (sourceDelta.getKind()) { case IResourceDelta.ADDED: if (outputFile.exists()) { if (JavaBuilder.DEBUG) System.out.println("Deleting existing file " + resourcePath); // $NON-NLS-1$ outputFile.delete(IResource.FORCE, null); } if (JavaBuilder.DEBUG) System.out.println("Copying added file " + resourcePath); // $NON-NLS-1$ createFolder( resourcePath.removeLastSegments(1), md.binaryFolder); // ensure package exists in the output folder copyResource(resource, outputFile); return true; case IResourceDelta.REMOVED: if (outputFile.exists()) { if (JavaBuilder.DEBUG) System.out.println("Deleting removed file " + resourcePath); // $NON-NLS-1$ outputFile.delete(IResource.FORCE, null); } return true; case IResourceDelta.CHANGED: if ((sourceDelta.getFlags() & IResourceDelta.CONTENT) == 0 && (sourceDelta.getFlags() & IResourceDelta.ENCODING) == 0) return true; // skip it since it really isn't changed if (outputFile.exists()) { if (JavaBuilder.DEBUG) System.out.println("Deleting existing file " + resourcePath); // $NON-NLS-1$ outputFile.delete(IResource.FORCE, null); } if (JavaBuilder.DEBUG) System.out.println("Copying changed file " + resourcePath); // $NON-NLS-1$ createFolder( resourcePath.removeLastSegments(1), md.binaryFolder); // ensure package exists in the output folder copyResource(resource, outputFile); } return true; } } return true; }
protected void findAffectedSourceFiles( IResourceDelta binaryDelta, int segmentCount, StringSet structurallyChangedTypes) { // When a package becomes a type or vice versa, expect 2 deltas, // one on the folder & one on the class file IResource resource = binaryDelta.getResource(); switch (resource.getType()) { case IResource.FOLDER: switch (binaryDelta.getKind()) { case IResourceDelta.ADDED: case IResourceDelta.REMOVED: IPath packagePath = resource.getFullPath().removeFirstSegments(segmentCount); String packageName = packagePath.toString(); if (binaryDelta.getKind() == IResourceDelta.ADDED) { // see if any known source file is from the same package... classpath already includes // new package if (!this.newState.isKnownPackage(packageName)) { if (JavaBuilder.DEBUG) System.out.println("Found added package " + packageName); // $NON-NLS-1$ addDependentsOf(packagePath, false); return; } if (JavaBuilder.DEBUG) System.out.println( "Skipped dependents of added package " + packageName); // $NON-NLS-1$ } else { // see if the package still exists on the classpath if (!this.nameEnvironment.isPackage(packageName)) { if (JavaBuilder.DEBUG) System.out.println("Found removed package " + packageName); // $NON-NLS-1$ addDependentsOf(packagePath, false); return; } if (JavaBuilder.DEBUG) System.out.println( "Skipped dependents of removed package " + packageName); // $NON-NLS-1$ } // $FALL-THROUGH$ traverse the sub-packages and .class files case IResourceDelta.CHANGED: IResourceDelta[] children = binaryDelta.getAffectedChildren(); for (int i = 0, l = children.length; i < l; i++) findAffectedSourceFiles(children[i], segmentCount, structurallyChangedTypes); } return; case IResource.FILE: if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(resource.getName())) { IPath typePath = resource.getFullPath().removeFirstSegments(segmentCount).removeFileExtension(); switch (binaryDelta.getKind()) { case IResourceDelta.ADDED: case IResourceDelta.REMOVED: if (JavaBuilder.DEBUG) System.out.println("Found added/removed class file " + typePath); // $NON-NLS-1$ addDependentsOf(typePath, false); return; case IResourceDelta.CHANGED: if ((binaryDelta.getFlags() & IResourceDelta.CONTENT) == 0) return; // skip it since it really isn't changed if (structurallyChangedTypes != null && !structurallyChangedTypes.includes(typePath.toString())) return; // skip since it wasn't a structural change if (JavaBuilder.DEBUG) System.out.println("Found changed class file " + typePath); // $NON-NLS-1$ addDependentsOf(typePath, false); } return; } } }