| /******************************************************************************* |
| * 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. |
| */ |
| 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. |
| */ |
| protected class ProjectMonitors { |
| /** |
| * 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); |
| } |
| |
| @Override |
| public void buildStarted(BuildEvent event) { |
| checkCanceled(); |
| } |
| |
| protected int computeWork(List<Target> targets) { |
| int result = 0; |
| for (Target target : targets) { |
| result = result + countTarget(target, 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 |
| for (Task task : target.getTasks()) { |
| if (task 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; |
| } |
| |
| @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); |
| } |
| } |
| |
| @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; |
| } |
| |
| @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); |
| } |
| } |
| |
| @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()); |
| } |
| } |
| |
| @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; |
| } |
| |
| @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); |
| } |
| } |