/*******************************************************************************
* Copyright © 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.edt.ide.core.internal.builder;
import java.util.Map;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.edt.compiler.core.IEGLConstants;
import org.eclipse.edt.compiler.internal.EGLIncompleteBuildPathSetting;
import org.eclipse.edt.compiler.internal.core.builder.BuildException;
import org.eclipse.edt.compiler.internal.core.builder.CancelledException;
import org.eclipse.edt.compiler.internal.core.builder.IBuildNotifier;
import org.eclipse.edt.compiler.internal.core.builder.NullBuildNotifier;
import org.eclipse.edt.compiler.internal.core.lookup.IBuildPathEntry;
import org.eclipse.edt.ide.core.EDTCoreIDEPlugin;
import org.eclipse.edt.ide.core.IIDECompiler;
import org.eclipse.edt.ide.core.internal.dependency.DependencyGraphManager;
import org.eclipse.edt.ide.core.internal.lookup.FileInfoManager;
import org.eclipse.edt.ide.core.internal.lookup.ProjectBuildPath;
import org.eclipse.edt.ide.core.internal.lookup.ProjectBuildPathEntryManager;
import org.eclipse.edt.ide.core.internal.lookup.ProjectBuildPathManager;
import org.eclipse.edt.ide.core.internal.lookup.ProjectEnvironmentManager;
import org.eclipse.edt.ide.core.internal.lookup.ProjectInfoManager;
import org.eclipse.edt.ide.core.internal.lookup.ZipFileBuildPathEntryManager;
import org.eclipse.edt.ide.core.internal.lookup.workingcopy.WorkingCopyProjectBuildPathManager;
import org.eclipse.edt.ide.core.utils.ProjectSettingsUtility;
/**
* @author winghong
*
*
*/
public class Builder extends IncrementalProjectBuilder {
protected static final boolean DEBUG = false;
protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {
//get required projects here so we can return to eclipse if exception during build.
ProjectBuildPath projBP = ProjectBuildPathManager.getInstance().getProjectBuildPath(getProject());
IProject[] reqProjects = projBP.getRequiredProjects();
boolean isOK = false;
IBuildNotifier notifier = null;
if (monitor == null){
notifier = NullBuildNotifier.getInstance();
}else {
notifier = new BuildNotifier(monitor);
}
notifier.begin();
try {
notifier.checkCancel();
// Write out a state for each project after building. Delete the state before building. If no state is found when attempting to run
// an incremental build, assume the last build failed and run a batch build of the project.
IResourceDelta delta = getDelta(getProject());
if (isWorthBuilding()) {
//RMERUI
//Clear the readonly flag, because a project may have been replaced with it's ReadOnly/Binary doppelganger
ProjectBuildPathManager.getInstance().getProjectBuildPath(getProject()).clearReadOnly();
if(ProjectBuildPathManager.getInstance().getProjectBuildPath(getProject()).isReadOnly()){
isOK = true;
}else if (kind == IncrementalProjectBuilder.FULL_BUILD) {
doClean();
cleanBuild(delta,notifier);
}else if (!BuildManager.getInstance().getProjectState(getProject())){
doClean();
cleanBuild(null,notifier);
}else if (BuildManager.getInstance().isFullBuildRequired(getProject())){
fullBuild(delta,notifier);
}else if (hasEGLPathChanged()) {
doClean();
cleanBuild(null,notifier);
}else if(!projBP.getOutputLocation().exists()){
doClean();
cleanBuild(null,notifier);
}else {
if (delta == null) {
doClean();
cleanBuild(delta,notifier);
} else{
if (incrementalBuild(delta,notifier)){
fullBuild(null,notifier);
}
}
}
isOK = true;
}
}catch (CancelledException canceledException){
throw new OperationCanceledException();
} catch (BuildException e) {
addUnhandledExceptionMarker();
EDTCoreIDEPlugin.getPlugin().log("EDT Build Failure",e); //$NON-NLS-1$
} finally {
if(!isOK){
BuildManager.getInstance().setProjectState(getProject(), false);
}else{
BuildManager.getInstance().putProject(getProject(), projBP.getRequiredProjects(),projBP.getSourceLocations(),projBP.getOutputLocation(),ProjectBuildPathManager.getInstance().getProjectBuildPath(getProject()).getBuildPathEntries());
}
notifier.done();
}
return reqProjects;
}
private void initializeOutputLocation(IContainer container){
// create the output location if it doesn't exist
if(!container.exists()){
createFolder(container);
}
}
private void createFolder(IContainer folder) {
if (!folder.exists()) {
createFolder(folder.getParent());
try {
((IFolder) folder).create(true, true, null);
} catch (CoreException e) {
throw new BuildException(e);
}
}
}
protected void addUnhandledExceptionMarker(){
try {
deleteAllMarkers();
IMarker marker = getProject().createMarker(AbstractMarkerProblemRequestor.BUILD_PROBLEM);
marker.setAttribute(IMarker.MESSAGE, BuilderResources.buildUnhandledException);
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
} catch (CoreException e) {
throw new BuildException(e);
}
}
protected boolean cleanBuild(IResourceDelta delta, IBuildNotifier notifier) {
BuildManager.getInstance().setProjectState(getProject(), false);
CleanBatchBuilder bb = new CleanBatchBuilder(this, notifier);
return bb.build(delta);
}
protected boolean fullBuild(IResourceDelta delta, IBuildNotifier notifier) {
BuildManager.getInstance().setProjectState(getProject(), false);
FullBatchBuilder bb = new FullBatchBuilder(this, notifier);
return bb.build(delta);
}
protected boolean incrementalBuild(IResourceDelta delta, IBuildNotifier notifier) {
BuildManager.getInstance().setProjectState(getProject(), false);
IncrementalBuilder ib = new IncrementalBuilder(this, notifier);
return ib.build(delta);
}
protected void startupOnInitialize() {
// add builder init logic here
}
protected void doClean(){
// Initialize the output location
initializeOutputLocation(ProjectBuildPathManager.getInstance().getProjectBuildPath(getProject()).getOutputLocation());
ZipFileBuildPathEntryManager.getInstance().clear(getProject());
DuplicatePartManager.getInstance().clear(getProject()); // Duplicate parts list
FileInfoManager.getInstance().clear(getProject()); // Cached FileInfos
ProjectEnvironmentManager.getInstance().clear(getProject()); // Environment
ProjectBuildPathEntryManager.getInstance().clear(getProject(), true);
ProjectBuildPathManager.getInstance().clear(getProject());
ProjectInfoManager.getInstance().clear(getProject()); // ProjectInfo
cleanOutputDir(getProject());
DependencyGraphManager.getInstance().clear(getProject()); // Dependency Graph
BuildManager.getInstance().clear(getProject()); // Build Manager
deleteAllMarkers(); // Markers
}
protected void clean(IProgressMonitor monitor){
try{
doClean();
}catch (Exception e){
EDTCoreIDEPlugin.getPlugin().log("EDT Clean Failure",e); //$NON-NLS-1$
BuildManager.getInstance().clear(getProject());
}
}
protected void deleteAllMarkers(){
try {
getProject().deleteMarkers(AbstractMarkerProblemRequestor.PROBLEM, true, IResource.DEPTH_INFINITE);
} catch (CoreException e) {
throw new BuildException(e);
}
}
private boolean hasEGLPathChanged() {
String[] entries = BuildManager.getInstance().getRequiredProjects(getProject());
IProject[] reqProjects = ProjectBuildPathManager.getInstance().getProjectBuildPath(getProject()).getRequiredProjects();
if (entries.length != reqProjects.length)
return true;
boolean allowIncompleteBuildPath = EGLIncompleteBuildPathSetting.getIncompleteBuildPathSetting() == EGLIncompleteBuildPathSetting._INCOMPLETE_BUILD_PATH_WARNING;
for (int i = 0; i < entries.length;i++){
IProject proj = ResourcesPlugin.getWorkspace().getRoot().getProject(entries[i]);
IProject proj2 = ResourcesPlugin.getWorkspace().getRoot().getProject(reqProjects[i].getName());
// If we are allowing incomplete build paths, ignore missing projects
if (!proj.exists() && !allowIncompleteBuildPath){
return true;
}
if (!proj.equals(proj2)){
return true;
}
}
BuildManager.BuildPathEntry[] oldPaths = BuildManager.getInstance().getPathEntries(getProject());
IBuildPathEntry[] newPaths = ProjectBuildPathManager.getInstance().getProjectBuildPath(getProject()).getBuildPathEntries();
if (oldPaths.length != newPaths.length)
return true;
for (int i = 0; i < oldPaths.length; i++){
BuildManager.BuildPathEntry oldpath = oldPaths[i];
IBuildPathEntry newpath = newPaths[i];
if (oldpath.getType() == BuildManager.BuildPathEntry.ENTRY_TYPE_PROJECT && !newpath.isProject()){
return true;
}else if (oldpath.getType() == BuildManager.BuildPathEntry.ENTRY_TYPE_ZIPFILE && !newpath.isZipFile()){
return true;
}else if (oldpath.getId().compareToIgnoreCase(newpath.getID()) != 0){
return true;
}
}
entries = BuildManager.getInstance().getSourceLocations(getProject());
IContainer[] sourceLocations = ProjectBuildPathManager.getInstance().getProjectBuildPath(getProject()).getSourceLocations();
if (entries.length != sourceLocations.length)
return true;
for (int i = 0; i < entries.length;i++){
IContainer srcLoc = null;
if (sourceLocations[i].getType() == IResource.PROJECT){
srcLoc = ResourcesPlugin.getWorkspace().getRoot().getProject(entries[i]);
}else {
srcLoc = ResourcesPlugin.getWorkspace().getRoot().getFolder(new Path(entries[i]));
}
if (!srcLoc.exists()){
return true;
}
if (!srcLoc.equals(sourceLocations[i])){
return true;
}
}
String savedOutputLocation = BuildManager.getInstance().getOutputLocation(getProject());
if (savedOutputLocation.length()== 0){
return true;
}
IContainer savedOutputLoc = ResourcesPlugin.getWorkspace().getRoot().getFolder(new Path(savedOutputLocation));
IContainer newOutputLocation = ProjectBuildPathManager.getInstance().getProjectBuildPath(getProject()).getOutputLocation();
if (savedOutputLoc == null || newOutputLocation == null){
return true;
}
if (!savedOutputLoc.equals(newOutputLocation)){
return true;
}
IIDECompiler compiler = ProjectSettingsUtility.getCompiler(getProject());
if (compiler != null) { // should never be null, we wouldn't have gotten here if the compiler was null (see isWorthBuilding())
String oldCompilerId = BuildManager.getInstance().getCompilerId(getProject());
if (!oldCompilerId.equals(compiler.getId())) {
return true;
}
}
return false;
}
private boolean isWorthBuilding() {
// If no compiler was specified, don't build anything.
if (ProjectSettingsUtility.getCompiler(getProject()) == null) {
return false;
}
try {
ProjectBuildPathManager.getInstance().getProjectBuildPath(getProject()).updateEGLPath();
// Abort build only if there are eglpath errors
if (ProjectBuildPathManager.getInstance().getProjectBuildPath(getProject()).isEGLPathBroken()) {
getProject().deleteMarkers(AbstractMarkerProblemRequestor.PROBLEM, true, IResource.DEPTH_INFINITE);
IMarker marker = getProject().createMarker(AbstractMarkerProblemRequestor.BUILD_PROBLEM);
marker.setAttribute(IMarker.MESSAGE, BuilderResources.buildAbortDueToEGLpathProblems);
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
marker.setAttribute(IEGLConstants.EGL_Build_aborted, true);
return false;
}
// make sure all prereq projects have valid build states... only when aborting builds since projects in cycles do not have build states
// except for projects involved in a 'warning' cycle (see below)
IProject currentProject = getProject();
IProject[] requiredProjects = ProjectBuildPathManager.getInstance().getProjectBuildPath(getProject()).getRequiredProjects();
for (int i = 0; i < requiredProjects.length; i++) {
IProject p = requiredProjects[i];
// BWS - Since we are now allowing for incomplete .eglpath files (missing dependencies can be a warning)
// We have to confirm that a project exists and is open before we try to get its state.
// We won't get to this point if the EGL path is marked as broken due to the marker check above
if (p.exists() && p.isOpen()){
if(!BuildManager.getInstance().getProjectState(p)) {
// The prereq project has no build state: if this prereq project has a 'warning' cycle marker then allow build (see bug id 23357)
if (DEBUG)
System.out.println("Aborted build because prereq project " + p.getName() //$NON-NLS-1$
+ " was not built or has cycle references"); //$NON-NLS-1$
currentProject.deleteMarkers(AbstractMarkerProblemRequestor.PROBLEM, true, IResource.DEPTH_INFINITE);
IMarker marker = currentProject.createMarker(AbstractMarkerProblemRequestor.PROBLEM);
marker.setAttribute(IMarker.MESSAGE,ProjectBuildPathManager.getInstance().getProjectBuildPath(p).isEGLPathBroken()
? BuilderResources.bind(BuilderResources.buildPrereqProjectHasEGLpathProblems, p.getName())
: BuilderResources.bind(BuilderResources.buildPrereqProjectMustBeRebuilt, p.getName()));
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
return false;
}
}
}
} catch (CoreException e) {
throw new BuildException(e);
}
return true;
}
/*
* 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.
*/
public void mustPropagateStructuralChanges() {
IProject[] projects = ProjectBuildPathManager.getInstance().getProjectBuildPath(getProject()).getCycleParticipants();
for (int i = 0; i < projects.length;i++){
IProject proj = projects[i];
if (!proj.equals(getProject())){
if (hasBeenBuilt(proj)) {
if (DEBUG)
System.out.println("Requesting another build iteration since cycle participant " + proj.getName() //$NON-NLS-1$
+ " has not yet seen some structural changes"); //$NON-NLS-1$
needRebuild();
return;
}
}
}
}
protected void cleanOutputDir(IProject project){
//RMERUI - Do not clean the output dir if this project is 'read only'
if(!WorkingCopyProjectBuildPathManager.getInstance().getProjectBuildPath(getProject()).isReadOnly()){
IContainer container = ProjectBuildPathManager.getInstance().getProjectBuildPath(project).getOutputLocation();
if (container.getType() != IResource.PROJECT) {
try{
IResource[] children = container.members();
for (int i = 0; i < children.length;i++){
IResource child = children[i];
child.delete(true,null);
}
}catch(CoreException e){
throw new BuildException(e);
}
}
}
}
}