/******************************************************************************* * Copyright (c) 2005, 2015 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.ant.internal.launching.debug; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.Stack; import java.util.Vector; import org.apache.tools.ant.BuildEvent; import org.apache.tools.ant.Location; import org.apache.tools.ant.Project; import org.apache.tools.ant.Target; import org.apache.tools.ant.Task; import org.apache.tools.ant.taskdefs.MacroInstance; import org.eclipse.ant.internal.launching.debug.model.DebugMessageIds; public class AntDebugState { private static final String fgAntTaskName = "ant"; //$NON-NLS-1$ private static final String fgAntCallTaskName = "antcall"; //$NON-NLS-1$ private IDebugBuildLogger fLogger; @SuppressWarnings("unused") private Stack<Task> fTasks = new Stack<Task>(); @SuppressWarnings("unused") private Map<Task, Object> fTaskToProxies = new HashMap<Task, Object>(); private Task fCurrentTask; private Task fStepOverTask; private Task fStepIntoTask; private Task fLastTaskFinished; // properties set before execution private Map<String, Object> fInitialProperties = null; private Map<String, Object> fProperties = null; private Map<Project, Vector<?>> fProjectToTargetNames = null; private Map<Project, Map<Target, Vector<Target>>> fProjectToMapOfTargetToBuildSequence = null; @SuppressWarnings("unused") private Stack<Target> fTargetsToExecute = new Stack<Target>(); @SuppressWarnings("unused") private Stack<Target> fTargetsExecuting = new Stack<Target>(); private boolean fConsiderTargetBreakpoints = false; private boolean fShouldSuspend; private boolean fClientSuspend = false; private boolean fStepIntoSuspend = false; private boolean fIsAfterTaskEvent = false; public AntDebugState(IDebugBuildLogger logger) { fLogger = logger; } @SuppressWarnings("unused") public void buildStarted() { fProjectToTargetNames = new HashMap<Project, Vector<?>>(); fProjectToMapOfTargetToBuildSequence = new HashMap<Project, Map<Target, Vector<Target>>>(); } /** * Call-back from {@link org.eclipse.ant.internal.launching.runtime.logger.AntProcessDebugBuildLogger} when the build has finished * * @since 1.0.1 */ public void buildFinished() { if (fProjectToTargetNames != null) { fProjectToTargetNames.clear(); } if (fProjectToMapOfTargetToBuildSequence != null) { fProjectToMapOfTargetToBuildSequence.clear(); } fTargetsExecuting.clear(); fTargetsToExecute.clear(); if (fInitialProperties != null) { fInitialProperties.clear(); } if (fProperties != null) { fProperties.clear(); } if (fTaskToProxies != null) { fTaskToProxies.clear(); } if (fTasks != null) { fTasks.clear(); } } public void waitIfSuspended() { fLogger.waitIfSuspended(); } public Task getLastTaskFinished() { return fLastTaskFinished; } private void setLastTaskFinished(Task lastTaskFinished) { fLastTaskFinished = lastTaskFinished; } public Task getCurrentTask() { return fCurrentTask; } public void setCurrentTask(Task currentTask) { fCurrentTask = currentTask; } private Map<String, Object> getInitialProperties() { return fInitialProperties; } public Task getStepOverTask() { return fStepOverTask; } public void setStepOverTask(Task stepOverTask) { fStepOverTask = stepOverTask; } private boolean considerTargetBreakpoints() { return fConsiderTargetBreakpoints; } private void setConsiderTargetBreakpoints(boolean considerTargetBreakpoints) { fConsiderTargetBreakpoints = considerTargetBreakpoints; } private Stack<Task> getTasks() { return fTasks; } public void setShouldSuspend(boolean shouldSuspend) { fShouldSuspend = shouldSuspend; } public boolean shouldSuspend() { return fShouldSuspend; } private Map<Target, Vector<Target>> getTargetToBuildSequence(Project project) { return fProjectToMapOfTargetToBuildSequence.get(project); } public void setTargetToExecute(Target target) { if (target == null) { if (!fTargetsToExecute.isEmpty()) { fTargetsToExecute.pop(); } } else { fTargetsToExecute.push(target); } } public void setTargetExecuting(Target target) { if (target == null) { if (!fTargetsExecuting.isEmpty()) { fTargetsExecuting.pop(); } } else { fTargetsExecuting.push(target); } } private Target getTargetToExecute() { if (fTargetsToExecute.isEmpty()) { return null; } return fTargetsToExecute.peek(); } private Target getTargetExecuting() { if (fTargetsExecuting.isEmpty()) { return null; } return fTargetsExecuting.peek(); } public boolean isStepIntoSuspend() { return isAfterTaskEvent() && fStepIntoSuspend; } public void setStepIntoSuspend(boolean stepIntoSuspend) { fStepIntoSuspend = stepIntoSuspend; } public boolean isClientSuspend() { return fClientSuspend; } public void setClientSuspend(boolean clientSuspend) { fClientSuspend = clientSuspend; } public Task getStepIntoTask() { return fStepIntoTask; } public void setStepIntoTask(Task stepIntoTask) { fStepIntoTask = stepIntoTask; } public void resume() { fLogger.notifyAll(); } public Map<String, Object> getProperties() { return fProperties; } public Location getBreakpointLocation() { if (isAfterTaskEvent() && getCurrentTask() != null) { return getCurrentTask().getLocation(); } if (considerTargetBreakpoints()) { Target targetExecuting = getTargetExecuting(); if (targetExecuting != null) { return getLocation(targetExecuting); } } return null; } private boolean isAfterTaskEvent() { return fIsAfterTaskEvent; } private void setAfterTaskEvent(boolean isAfterTaskEvent) { fIsAfterTaskEvent = isAfterTaskEvent; } public void taskStarted(BuildEvent event) { setAfterTaskEvent(true); if (getInitialProperties() == null) {// implicit or top level target does not fire targetStarted() fInitialProperties = event.getProject().getProperties(); } setCurrentTask(event.getTask()); setConsiderTargetBreakpoints(false); Stack<Task> tasks = getTasks(); if (!tasks.isEmpty()) { // cache the parent task proxy as when that task is started or finished the // proxy is not yet available or is nulled out Task parentTask = tasks.peek(); Object proxy = parentTask.getRuntimeConfigurableWrapper().getProxy(); if (proxy != null) { fTaskToProxies.put(parentTask, proxy); } } tasks.push(getCurrentTask()); waitIfSuspended(); } public void taskFinished() { Stack<Task> tasks = getTasks(); if (!tasks.empty()) { Task lastTask = tasks.pop(); setLastTaskFinished(lastTask); setCurrentTask(null); String taskName = lastTask.getTaskName(); if (getStepOverTask() != null) { if ((fgAntCallTaskName.equals(taskName) || fgAntTaskName.equals(taskName)) && (!fgAntCallTaskName.equals(getStepOverTask().getTaskName()) && !fgAntTaskName.equals(getStepOverTask().getTaskName()))) { setShouldSuspend(true); } else if (fTaskToProxies.remove(lastTask) instanceof MacroInstance) { setShouldSuspend(true); } } } waitIfSuspended(); } public void stepOver() { setStepOverTask(getCurrentTask()); if (getCurrentTask() == null) { // stepping over target breakpoint setShouldSuspend(true); } resume(); } public void targetStarted(BuildEvent event) { setAfterTaskEvent(false); Project eventProject = event.getProject(); if (getInitialProperties() == null) { fInitialProperties = eventProject.getProperties(); } if (fProjectToTargetNames.get(eventProject) == null) { Object ref = eventProject.getReference(IAntCoreConstants.TARGET_VECTOR_NAME); if (ref != null) { fProjectToTargetNames.put(eventProject, (Vector<?>) ref); @SuppressWarnings("unused") HashMap<Target, Vector<Target>> targetToBuildSequence = new HashMap<Target, Vector<Target>>(); setTargetToExecute(initializeBuildSequenceInformation(event, targetToBuildSequence)); fProjectToMapOfTargetToBuildSequence.put(eventProject, targetToBuildSequence); } } setTargetExecuting(event.getTarget()); if (event.getTarget().equals(getTargetToExecute())) { // the dependencies of the target to execute have been met // prepare for the next target Vector<?> targets = fProjectToTargetNames.get(eventProject); if (!targets.isEmpty()) { setTargetToExecute(eventProject.getTargets().get(targets.remove(0))); } else { setTargetToExecute(null); } } setConsiderTargetBreakpoints(true); } public int getLineNumber(Location location) { try { // succeeds with Ant newer than 1.6 return location.getLineNumber(); } catch (NoSuchMethodError e) { // Ant before 1.6 String locationString = location.toString(); if (locationString.length() == 0) { return 0; } // filename: lineNumber: ("c:\buildfile.xml: 12: ") int lastIndex = locationString.lastIndexOf(':'); int index = locationString.lastIndexOf(':', lastIndex - 1); if (index != -1) { try { return Integer.parseInt(locationString.substring(index + 1, lastIndex)); } catch (NumberFormatException nfe) { return 0; } } return 0; } } public static Location getLocation(Target target) { try {// succeeds with Ant newer than 1.6.2 return target.getLocation(); } catch (NoSuchMethodError e) { return Location.UNKNOWN_LOCATION; } } public String getFileName(Location location) { try {// succeeds with Ant newer than 1.6 return location.getFileName(); } catch (NoSuchMethodError e) { // Ant before 1.6 String locationString = location.toString(); if (locationString.length() == 0) { return null; } // filename: lineNumber: ("c:\buildfile.xml: 12: ") int lastIndex = locationString.lastIndexOf(':'); int index = locationString.lastIndexOf(':', lastIndex - 1); if (index == -1) { index = lastIndex; // only the filename is known } if (index != -1) { // bug 84403 // if (locationString.startsWith("file:")) { // return FileUtils.newFileUtils().fromURI(locationString); // } // remove file: return locationString.substring(5, index); } return null; } } private void appendToStack(StringBuffer stackRepresentation, String targetName, String taskName, Location location) { stackRepresentation.append(targetName); stackRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); stackRepresentation.append(taskName); stackRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); stackRepresentation.append(getFileName(location)); stackRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); stackRepresentation.append(getLineNumber(location)); stackRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); } public void marshalStack(StringBuffer stackRepresentation) { Stack<Task> tasks = getTasks(); stackRepresentation.append(DebugMessageIds.STACK); stackRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); Target targetToExecute = getTargetToExecute(); Target targetExecuting = getTargetExecuting(); Project projectExecuting = null; if (targetExecuting != null) { projectExecuting = targetExecuting.getProject(); } else if (!tasks.empty()) { // no target...must be a task Task task = tasks.peek(); projectExecuting = task.getProject(); } if (!isAfterTaskEvent()) { appendToStack(stackRepresentation, targetExecuting.getName(), IAntCoreConstants.EMPTY_STRING, getLocation(targetExecuting)); } for (int i = tasks.size() - 1; i >= 0; i--) { Task task = tasks.get(i); if (task.getProject() == projectExecuting) { appendToStack(stackRepresentation, task.getOwningTarget().getName(), task.getTaskName(), task.getLocation()); } else { // sub build target dependencies String targetName = task.getOwningTarget().getName(); if (targetName != null && targetName.length() != 0) { // skip for implicit target Iterator<Target> itr = fTargetsToExecute.iterator(); while (itr.hasNext()) { Target target = itr.next(); if (target.getProject() != projectExecuting) { targetToExecute = target; continue; } marshalTargetDependancyStack(stackRepresentation, target, targetExecuting); } } projectExecuting = task.getProject(); targetExecuting = task.getOwningTarget(); appendToStack(stackRepresentation, targetExecuting.getName(), task.getTaskName(), task.getLocation()); } } // target dependency stack marshalTargetDependancyStack(stackRepresentation, targetToExecute, targetExecuting); } private void marshalTargetDependancyStack(StringBuffer stackRepresentation, Target targetToExecute, Target targetExecuting) { if (targetToExecute != null) { Vector<Target> buildSequence = getTargetToBuildSequence(targetToExecute.getProject()).get(targetToExecute); int startIndex = buildSequence.indexOf(targetExecuting) + 1; int dependancyStackDepth = buildSequence.indexOf(targetToExecute); Target stackTarget; for (int i = startIndex; i <= dependancyStackDepth; i++) { stackTarget = buildSequence.get(i); if (stackTarget.dependsOn(targetExecuting.getName())) { appendToStack(stackRepresentation, stackTarget.getName(), IAntCoreConstants.EMPTY_STRING, getLocation(stackTarget)); } } } } public void marshallProperties(StringBuffer propertiesRepresentation, boolean escapeLineSep) { Stack<Task> tasks = getTasks(); if (tasks.isEmpty()) { return; } propertiesRepresentation.append(DebugMessageIds.PROPERTIES); propertiesRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); Project project = tasks.peek().getProject(); Map<String, Object> lastProperties = getProperties(); Map<String, Object> currentProperties = project.getProperties(); if (lastProperties != null && currentProperties.size() == lastProperties.size()) { // no new properties return; } Map<String, Object> initialProperties = getInitialProperties(); Map<String, Object> currentUserProperties = project.getUserProperties(); String originalPropertyName; String propertyValue; for (String propertyName : currentProperties.keySet()) { originalPropertyName = propertyName; if (lastProperties == null || lastProperties.get(propertyName) == null) { // new property if (escapeLineSep) { propertyName = escapeLineSeparator(propertyName); } propertiesRepresentation.append(propertyName.length()); propertiesRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); propertiesRepresentation.append(propertyName); propertiesRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); propertyValue = (String) currentProperties.get(originalPropertyName); if (escapeLineSep) { propertyValue = escapeLineSeparator(propertyValue); } propertiesRepresentation.append(propertyValue.length()); propertiesRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); propertiesRepresentation.append(propertyValue); propertiesRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); propertiesRepresentation.append(getPropertyType(initialProperties, currentUserProperties, originalPropertyName)); propertiesRepresentation.append(DebugMessageIds.MESSAGE_DELIMITER); } } propertiesRepresentation.deleteCharAt(propertiesRepresentation.length() - 1); fProperties = currentProperties; } private int getPropertyType(Map<String, Object> initialProperties, Map<String, Object> currentUserProperties, String propertyName) { if (initialProperties.get(propertyName) != null) { // properties set before the start of the build if (currentUserProperties.get(propertyName) == null) { return DebugMessageIds.PROPERTY_SYSTEM; } return DebugMessageIds.PROPERTY_USER; } else if (currentUserProperties.get(propertyName) == null) { return DebugMessageIds.PROPERTY_RUNTIME; } else { return DebugMessageIds.PROPERTY_USER; } } private String escapeLineSeparator(String stringToEscape) { if (!(stringToEscape.indexOf('\r') != -1 || stringToEscape.indexOf('\n') != -1 || stringToEscape.indexOf("\\r") != -1 //$NON-NLS-1$ || stringToEscape.indexOf("\\n") != -1)) { //$NON-NLS-1$ return stringToEscape; } StringBuffer escapedValue = new StringBuffer(stringToEscape); for (int i = 0; i < escapedValue.length(); i++) { switch (escapedValue.charAt(i)) { case '\r': escapedValue.replace(i, i + 1, "\\r"); //$NON-NLS-1$ i++; break; case '\n': escapedValue.replace(i, i + 1, "\\n"); //$NON-NLS-1$ i++; break; case '\\': if (escapedValue.charAt(i + 1) == 'r' || escapedValue.charAt(i + 1) == 'n') { escapedValue.replace(i, i + 1, "\\\\"); //$NON-NLS-1$ i++; } break; default: break; } } return escapedValue.toString(); } private Target initializeBuildSequenceInformation(BuildEvent event, Map<Target, Vector<Target>> targetToBuildSequence) { Project antProject = event.getProject(); Vector<String> targets = antProject.getReference(IAntCoreConstants.TARGET_VECTOR_NAME); if (targets == null || targets.size() < 1) { return null; } Hashtable<String, Target> allTargets = antProject.getTargets(); Vector<Target> sortedTargets; for (String targetName : targets) { sortedTargets = antProject.topoSort(targetName, allTargets); targetToBuildSequence.put(allTargets.get(targetName), sortedTargets); } // the target to execute return allTargets.get(targets.remove(0)); } }