Copyright (c) 2000, 2013 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
/******************************************************************************* * Copyright (c) 2000, 2013 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.ant.internal.core.ant; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.tools.ant.BuildEvent; import org.apache.tools.ant.BuildListener; import org.apache.tools.ant.Project; import org.apache.tools.ant.Target; import org.apache.tools.ant.Task; import org.apache.tools.ant.taskdefs.Ant; import org.apache.tools.ant.taskdefs.CallTarget; import org.eclipse.ant.core.AntCorePlugin; import org.eclipse.ant.internal.core.IAntCoreConstants; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.SubMonitor;
Reports progress and checks for cancellation of a script execution.
/** * Reports progress and checks for cancellation of a script execution. */
public class ProgressBuildListener implements BuildListener { protected Map<Project, ProjectMonitors> projects; protected Project mainProject; protected Project parentProject; private Thread currentTaskThread;
Contains the progress monitor instances for the various projects in a chain.
/** * Contains the progress monitor instances for the various projects in a chain. */
protected class ProjectMonitors {
This field is null for the main project
/** * This field is null for the main project */
private Target mainTarget; private IProgressMonitor mainMonitor; private IProgressMonitor targetMonitor; private IProgressMonitor taskMonitor; protected IProgressMonitor getMainMonitor() { return mainMonitor; } protected Target getMainTarget() { return mainTarget; } protected IProgressMonitor getTargetMonitor() { return targetMonitor; } protected IProgressMonitor getTaskMonitor() { return taskMonitor; } protected void setMainMonitor(IProgressMonitor mainMonitor) { this.mainMonitor = mainMonitor; } protected void setMainTarget(Target mainTarget) { this.mainTarget = mainTarget; } protected void setTargetMonitor(IProgressMonitor targetMonitor) { this.targetMonitor = targetMonitor; } protected void setTaskMonitor(IProgressMonitor taskMonitor) { this.taskMonitor = taskMonitor; } } public ProgressBuildListener(Project project, List<String> targetNames, IProgressMonitor monitor) { projects = new HashMap<>(); mainProject = project; ProjectMonitors monitors = new ProjectMonitors(); IProgressMonitor localmonitor = monitor; if (localmonitor == null) { localmonitor = new NullProgressMonitor(); } monitors.setMainMonitor(localmonitor); projects.put(mainProject, monitors); ArrayList<Target> targets = new ArrayList<>(targetNames.size()); for (String targetName : targetNames) { Target target = mainProject.getTargets().get(targetName); if (target != null) { targets.add(target); } } int work = computeWork(targets); monitors.getMainMonitor().beginTask(IAntCoreConstants.EMPTY_STRING, work); } /* * (non-Javadoc) * * @see org.apache.tools.ant.BuildListener#buildStarted(org.apache.tools.ant.BuildEvent) */ @Override public void buildStarted(BuildEvent event) { checkCanceled(); } protected int computeWork(List<Target> targets) { int result = 0; for (int i = 0; i < targets.size(); i++) { result = result + countTarget(targets.get(i), new ArrayList<>()); } return result; } protected int countTarget(Target target, List<String> alreadySeen) { int result = 1; Project project = target.getProject(); Hashtable<String, Target> targets = project.getTargets(); String targetName; Target dependency; for (Enumeration<String> dependencies = target.getDependencies(); dependencies.hasMoreElements();) { targetName = dependencies.nextElement(); if (alreadySeen.contains(targetName)) { // circular dependency or common dependency return result; } alreadySeen.add(targetName); dependency = targets.get(targetName); if (dependency != null) { result = result + countTarget(dependency, alreadySeen); } } // we have to handle antcall tasks as well Task[] tasks = target.getTasks(); for (int i = 0; i < tasks.length; i++) { if (tasks[i] instanceof CallTarget) { // As we do not have access to the information (at least in Ant 1.4.1) // describing what target is executed by this antcall task, we assume // a scenario where it depends on all targets of the project but itself. result = result + (targets.size() - 1); } } return result; } /* * (non-Javadoc) * * @see org.apache.tools.ant.BuildListener#buildFinished(org.apache.tools.ant.BuildEvent) */ @Override public void buildFinished(BuildEvent event) { ProjectMonitors monitors = projects.get(mainProject); monitors.getMainMonitor().done(); Set<Project> keys = projects.keySet(); Iterator<Project> itr = keys.iterator(); while (itr.hasNext()) { Project project = itr.next(); project.removeBuildListener(this); project.getReferences().remove(AntCorePlugin.ECLIPSE_PROGRESS_MONITOR); } } /* * (non-Javadoc) * * @see org.apache.tools.ant.BuildListener#targetStarted(org.apache.tools.ant.BuildEvent) */ @Override public void targetStarted(BuildEvent event) { checkCanceled(); Project currentProject = event.getProject(); if (currentProject == null) { return; } Target target = event.getTarget(); ProjectMonitors monitors = projects.get(currentProject); // if monitors is null we are in a new script if (monitors == null) { monitors = createMonitors(currentProject, target); } monitors.setTargetMonitor(subMonitorFor(monitors.getMainMonitor(), 1)); int work = (target != null) ? target.getTasks().length : 100; monitors.getTargetMonitor().beginTask(IAntCoreConstants.EMPTY_STRING, work); } protected ProjectMonitors createMonitors(Project currentProject, Target target) { ProjectMonitors monitors = new ProjectMonitors(); // remember the target so we can remove this monitors object later monitors.setMainTarget(target); ArrayList<Target> targets = new ArrayList<>(1); targets.add(target); int work = computeWork(targets); ProjectMonitors parentMonitors = null; if (parentProject == null) { parentMonitors = projects.get(mainProject); monitors.setMainMonitor(subMonitorFor(parentMonitors.getMainMonitor(), 1)); } else { parentMonitors = projects.get(parentProject); parentProject = null; monitors.setMainMonitor(subMonitorFor(parentMonitors.getTaskMonitor(), 1)); } monitors.getMainMonitor().beginTask(IAntCoreConstants.EMPTY_STRING, work); projects.put(currentProject, monitors); return monitors; } /* * (non-Javadoc) * * @see org.apache.tools.ant.BuildListener#targetFinished(org.apache.tools.ant.BuildEvent) */ @Override public void targetFinished(BuildEvent event) { checkCanceled(); Project currentProject = event.getProject(); if (currentProject == null) { return; } ProjectMonitors monitors = projects.get(currentProject); if (monitors == null) { return; } monitors.getTargetMonitor().done(); // if this is not the main project test if we are done with this project if ((currentProject != mainProject) && (monitors.getMainTarget() == event.getTarget())) { monitors.getMainMonitor().done(); projects.remove(currentProject); } } /* * (non-Javadoc) * * @see org.apache.tools.ant.BuildListener#taskStarted(org.apache.tools.ant.BuildEvent) */ @Override public void taskStarted(BuildEvent event) { checkCanceled(); Project currentProject = event.getProject(); if (currentProject == null) { return; } currentProject.getReferences().remove(AntCorePlugin.ECLIPSE_PROGRESS_MONITOR); ProjectMonitors monitors = projects.get(currentProject); if (monitors == null) { return; } Task task = event.getTask(); if (task == null) { return; } currentTaskThread = Thread.currentThread(); monitors.setTaskMonitor(subMonitorFor(monitors.getTargetMonitor(), 1)); monitors.getTaskMonitor().beginTask(IAntCoreConstants.EMPTY_STRING, 1); // If this script is calling another one, track the project chain. if (task instanceof Ant) { parentProject = currentProject; } else { currentProject.addReference(AntCorePlugin.ECLIPSE_PROGRESS_MONITOR, monitors.getTaskMonitor()); } } /* * (non-Javadoc) * * @see org.apache.tools.ant.BuildListener#taskFinished(org.apache.tools.ant.BuildEvent) */ @Override public void taskFinished(BuildEvent event) { checkCanceled(); Project project = event.getProject(); if (project == null) { return; } project.getReferences().remove(AntCorePlugin.ECLIPSE_PROGRESS_MONITOR); ProjectMonitors monitors = projects.get(project); if (monitors == null) { return; } monitors.getTaskMonitor().done(); currentTaskThread = null; } /* * (non-Javadoc) * * @see org.apache.tools.ant.BuildListener#messageLogged(org.apache.tools.ant.BuildEvent) */ @Override public void messageLogged(BuildEvent event) { checkCanceled(); } protected void checkCanceled() { // only cancel if the current task thread matches the current thread // do not want to throw an exception in a separate thread or process // see bug 32657 if (currentTaskThread != null && currentTaskThread != Thread.currentThread()) { return; } ProjectMonitors monitors = projects.get(mainProject); if (monitors.getMainMonitor().isCanceled()) { currentTaskThread = null; throw new OperationCanceledException(InternalAntMessages.ProgressBuildListener_Build_cancelled); } } protected IProgressMonitor subMonitorFor(IProgressMonitor monitor, int ticks) { if (monitor == null) { return new NullProgressMonitor(); } if (monitor instanceof NullProgressMonitor) { return monitor; } return SubMonitor.convert(monitor, ticks); } }