|
@@ -0,0 +1,449 @@ |
|
|
|
|
|
/* Copyright (c) 2000 The Apache Software Foundation */ |
|
|
|
|
|
|
|
|
|
|
|
package org.apache.tools.ant; |
|
|
|
|
|
|
|
|
|
|
|
import java.util.*; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* The main class in the Ant class hierarchy. A workspace contains |
|
|
|
|
|
* multiple projects, which in turn contain multiple targets, which |
|
|
|
|
|
* in turn contain multiple task proxies. The workspace also handles |
|
|
|
|
|
* the sorting and execution of targets during a build. |
|
|
|
|
|
* |
|
|
|
|
|
* @author <a href="mailto:mpfoemme@thoughtworks.com">Matthew Foemmel</a> |
|
|
|
|
|
*/ |
|
|
|
|
|
public class Workspace { |
|
|
|
|
|
public static final char SCOPE_SEPARATOR = ':'; |
|
|
|
|
|
|
|
|
|
|
|
private Importer importer; |
|
|
|
|
|
private Map projects; |
|
|
|
|
|
private Map tasks; |
|
|
|
|
|
private List listeners; |
|
|
|
|
|
|
|
|
|
|
|
private Task currentTask = null; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Constructs new Ant workspace with no projects. The only |
|
|
|
|
|
* task that will be registered is the "load" task. |
|
|
|
|
|
* |
|
|
|
|
|
* The importer is used to handle the actual reading of build files. |
|
|
|
|
|
* In theory, different importers could be used to read project info from |
|
|
|
|
|
* DOM trees, serialized objects, databases, etc. |
|
|
|
|
|
*/ |
|
|
|
|
|
public Workspace(Importer importer) { |
|
|
|
|
|
this.importer = importer; |
|
|
|
|
|
this.projects = new HashMap(); |
|
|
|
|
|
this.tasks = new HashMap(); |
|
|
|
|
|
this.listeners = new ArrayList(); |
|
|
|
|
|
|
|
|
|
|
|
registerTask("load", Load.class); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Assigns a task class to a name. |
|
|
|
|
|
*/ |
|
|
|
|
|
public void registerTask(String name, Class type) { |
|
|
|
|
|
tasks.put(name, type); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Returns the class for a task with the specified name. |
|
|
|
|
|
*/ |
|
|
|
|
|
public Class getTaskClass(String name) throws BuildException { |
|
|
|
|
|
Class type = (Class) tasks.get(name); |
|
|
|
|
|
if (type == null) { |
|
|
|
|
|
throw new BuildException("No task named \"" + name + "\" has been loaded"); |
|
|
|
|
|
} |
|
|
|
|
|
return type; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Creates a project with the specified name. The project initially |
|
|
|
|
|
* contains no targets. |
|
|
|
|
|
*/ |
|
|
|
|
|
public Project createProject(String name) { |
|
|
|
|
|
Project project = new Project(this, name); |
|
|
|
|
|
projects.put(name, project); |
|
|
|
|
|
return project; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Returns the project with the specified name, or throws |
|
|
|
|
|
* an exception if no project exists with that name. |
|
|
|
|
|
*/ |
|
|
|
|
|
public Project getProject(String name) throws BuildException { |
|
|
|
|
|
Project project = (Project) projects.get(name); |
|
|
|
|
|
if (project == null) { |
|
|
|
|
|
throw new BuildException("Project \"" + name + "\" not found"); |
|
|
|
|
|
} |
|
|
|
|
|
return project; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Builds all of the targets in the list. Target names must |
|
|
|
|
|
* be of the form projectname:targetname. |
|
|
|
|
|
*/ |
|
|
|
|
|
public boolean build(List fullNames) throws BuildException { |
|
|
|
|
|
|
|
|
|
|
|
// This lets the tasks intercept System.exit() calls |
|
|
|
|
|
SecurityManager sm = System.getSecurityManager(); |
|
|
|
|
|
System.setSecurityManager(new AntSecurityManager()); |
|
|
|
|
|
|
|
|
|
|
|
fireBuildStarted(); |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
// Parse the project files... |
|
|
|
|
|
importTargets(fullNames); |
|
|
|
|
|
|
|
|
|
|
|
// ...figure out the build order... |
|
|
|
|
|
List toDoList = sortTargets(fullNames); |
|
|
|
|
|
|
|
|
|
|
|
// ...and build the targets |
|
|
|
|
|
Iterator itr = toDoList.iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
Target target = (Target) itr.next(); |
|
|
|
|
|
buildTarget(target); |
|
|
|
|
|
} |
|
|
|
|
|
fireBuildFinished(null); |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
catch(BuildException exc) { |
|
|
|
|
|
fireBuildFinished(exc); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
finally { |
|
|
|
|
|
System.setSecurityManager(sm); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Adds a listener to the workspace. |
|
|
|
|
|
*/ |
|
|
|
|
|
public void addBuildListener(BuildListener listener) { |
|
|
|
|
|
listeners.add(listener); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Removes a listener to the workspace. |
|
|
|
|
|
*/ |
|
|
|
|
|
public void removeBuildListener(BuildListener listener) { |
|
|
|
|
|
listeners.remove(listener); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Fires a messageLogged event with DEBUG priority |
|
|
|
|
|
*/ |
|
|
|
|
|
public void debug(String message) { |
|
|
|
|
|
fireMessageLogged(message, BuildEvent.DEBUG); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Fires a messageLogged event with INFO priority |
|
|
|
|
|
*/ |
|
|
|
|
|
public void info(String message) { |
|
|
|
|
|
fireMessageLogged(message, BuildEvent.INFO); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Fires a messageLogged event with WARN priority |
|
|
|
|
|
*/ |
|
|
|
|
|
public void warn(String message) { |
|
|
|
|
|
fireMessageLogged(message, BuildEvent.WARN); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Fires a messageLogged event with ERROR priority |
|
|
|
|
|
*/ |
|
|
|
|
|
public void error(String message) { |
|
|
|
|
|
fireMessageLogged(message, BuildEvent.ERROR); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Imports into the workspace all of the projects required to |
|
|
|
|
|
* build a set of targets. |
|
|
|
|
|
*/ |
|
|
|
|
|
private void importTargets(List fullNames) throws BuildException { |
|
|
|
|
|
Iterator itr = fullNames.iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
String fullName = (String) itr.next(); |
|
|
|
|
|
String projectName = getProjectName(fullName); |
|
|
|
|
|
importProject(projectName); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Imports the project into the workspace, as well as any others |
|
|
|
|
|
* that the project depends on. |
|
|
|
|
|
*/ |
|
|
|
|
|
public Project importProject(String projectName) throws BuildException { |
|
|
|
|
|
Project project = (Project) projects.get(projectName); |
|
|
|
|
|
|
|
|
|
|
|
// Don't parse a project file more than once |
|
|
|
|
|
if (project == null) { |
|
|
|
|
|
|
|
|
|
|
|
// Parse the project file |
|
|
|
|
|
project = createProject(projectName); |
|
|
|
|
|
|
|
|
|
|
|
fireImportStarted(project); |
|
|
|
|
|
try { |
|
|
|
|
|
importer.importProject(project); |
|
|
|
|
|
fireImportFinished(project, null); |
|
|
|
|
|
} |
|
|
|
|
|
catch(BuildException exc) { |
|
|
|
|
|
fireImportFinished(project, exc); |
|
|
|
|
|
throw exc; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Parse any imported projects as well |
|
|
|
|
|
Iterator itr = project.getImports().iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
Import imp = (Import) itr.next(); |
|
|
|
|
|
importProject(imp.getName()); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return project; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Builds a specific target. This assumes that the targets it depends |
|
|
|
|
|
* on have already been built. |
|
|
|
|
|
*/ |
|
|
|
|
|
private void buildTarget(Target target) throws BuildException { |
|
|
|
|
|
fireTargetStarted(target); |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
List tasks = target.getTasks(); |
|
|
|
|
|
Iterator itr = tasks.iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
TaskProxy proxy = (TaskProxy) itr.next(); |
|
|
|
|
|
executeTask(target, proxy); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fireTargetFinished(target, null); |
|
|
|
|
|
} |
|
|
|
|
|
catch(BuildException exc) { |
|
|
|
|
|
fireTargetFinished(target, null); |
|
|
|
|
|
throw exc; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Instantiates the task from the proxy and executes. |
|
|
|
|
|
*/ |
|
|
|
|
|
private void executeTask(Target target, TaskProxy proxy) throws BuildException { |
|
|
|
|
|
Task task = proxy.createTask(); |
|
|
|
|
|
task.setWorkspace(this); |
|
|
|
|
|
task.setProject(target.getProject()); |
|
|
|
|
|
task.setTarget(target); |
|
|
|
|
|
|
|
|
|
|
|
fireTaskStarted(task); |
|
|
|
|
|
currentTask = task; |
|
|
|
|
|
try { |
|
|
|
|
|
task.execute(); |
|
|
|
|
|
|
|
|
|
|
|
fireTaskFinished(task, null); |
|
|
|
|
|
} |
|
|
|
|
|
catch(BuildException exc) { |
|
|
|
|
|
exc.setLocation(proxy.getLocation()); |
|
|
|
|
|
fireTaskFinished(task, exc); |
|
|
|
|
|
throw exc; |
|
|
|
|
|
} |
|
|
|
|
|
finally { |
|
|
|
|
|
currentTask = null; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Does a topological sort on a list of target names. Returns |
|
|
|
|
|
* a list of Target objects in the order to be executed. |
|
|
|
|
|
*/ |
|
|
|
|
|
private List sortTargets(List fullNames) throws BuildException { |
|
|
|
|
|
List results = new ArrayList(); |
|
|
|
|
|
sortTargets(results, new Stack(), fullNames); |
|
|
|
|
|
return results; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void sortTargets(List results, Stack visited, List fullNames) throws BuildException { |
|
|
|
|
|
Iterator itr = fullNames.iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
String fullName = (String) itr.next(); |
|
|
|
|
|
|
|
|
|
|
|
// Check for cycles |
|
|
|
|
|
if (visited.contains(fullName)) { |
|
|
|
|
|
throwCyclicDependency(visited, fullName); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check if we're already added this target to the list |
|
|
|
|
|
Target target = getTarget(fullName); |
|
|
|
|
|
if (results.contains(target)) { |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
visited.push(fullName); |
|
|
|
|
|
sortTargets(results, visited, target.getDepends()); |
|
|
|
|
|
results.add(target); |
|
|
|
|
|
visited.pop(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Creates and throws an exception indicating a cyclic dependency. |
|
|
|
|
|
*/ |
|
|
|
|
|
private void throwCyclicDependency(Stack visited, String fullName) throws BuildException { |
|
|
|
|
|
StringBuffer msg = new StringBuffer("Cyclic dependency: "); |
|
|
|
|
|
for (int i = 0; i < visited.size(); i++) { |
|
|
|
|
|
msg.append((String)visited.get(i)); |
|
|
|
|
|
msg.append(" -> "); |
|
|
|
|
|
} |
|
|
|
|
|
msg.append(fullName); |
|
|
|
|
|
throw new BuildException(msg.toString()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Parses the full target name into is project and target components, |
|
|
|
|
|
* then locates the Target object. |
|
|
|
|
|
*/ |
|
|
|
|
|
private Target getTarget(String fullName) throws BuildException { |
|
|
|
|
|
String projectName = getProjectName(fullName); |
|
|
|
|
|
String targetName = getTargetName(fullName); |
|
|
|
|
|
|
|
|
|
|
|
Project project = (Project) projects.get(projectName); |
|
|
|
|
|
if (project == null) { |
|
|
|
|
|
throw new BuildException("Project \"" + projectName + "\" not found"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Target target = project.getTarget(targetName); |
|
|
|
|
|
if (target == null) { |
|
|
|
|
|
throw new BuildException("Target \"" + fullName + "\" not found"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return target; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Returns the project portion of a full target name. |
|
|
|
|
|
*/ |
|
|
|
|
|
public static String getProjectName(String fullName) throws BuildException { |
|
|
|
|
|
int pos = fullName.indexOf(SCOPE_SEPARATOR); |
|
|
|
|
|
if (pos == -1 || pos == 0) { |
|
|
|
|
|
throw new BuildException("\"" + fullName + "\" is not a valid target name"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return fullName.substring(0, pos); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Returns the target portion of a full target name. |
|
|
|
|
|
*/ |
|
|
|
|
|
public static String getTargetName(String fullName) throws BuildException { |
|
|
|
|
|
int pos = fullName.indexOf(SCOPE_SEPARATOR); |
|
|
|
|
|
if (pos == -1 || pos == 0) { |
|
|
|
|
|
throw new BuildException("\"" + fullName + "\" is not a valid target name"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return fullName.substring(pos + 1); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void fireMessageLogged(String message, int priority) { |
|
|
|
|
|
BuildEvent event; |
|
|
|
|
|
if (currentTask == null) { |
|
|
|
|
|
event = new BuildEvent(this); |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
event = new BuildEvent(currentTask); |
|
|
|
|
|
} |
|
|
|
|
|
event.setMessage(message, priority); |
|
|
|
|
|
|
|
|
|
|
|
Iterator itr = listeners.iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
BuildListener listener = (BuildListener) itr.next(); |
|
|
|
|
|
listener.messageLogged(event); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void fireBuildStarted() { |
|
|
|
|
|
Iterator itr = listeners.iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
BuildListener listener = (BuildListener) itr.next(); |
|
|
|
|
|
BuildEvent event = new BuildEvent(this); |
|
|
|
|
|
listener.buildStarted(event); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void fireBuildFinished(BuildException exc) { |
|
|
|
|
|
BuildEvent event = new BuildEvent(this); |
|
|
|
|
|
event.setException(exc); |
|
|
|
|
|
|
|
|
|
|
|
Iterator itr = listeners.iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
BuildListener listener = (BuildListener) itr.next(); |
|
|
|
|
|
listener.buildFinished(event); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void fireImportStarted(Project project) { |
|
|
|
|
|
BuildEvent event = new BuildEvent(project); |
|
|
|
|
|
|
|
|
|
|
|
Iterator itr = listeners.iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
BuildListener listener = (BuildListener) itr.next(); |
|
|
|
|
|
listener.importStarted(event); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void fireImportFinished(Project project, BuildException exc) { |
|
|
|
|
|
BuildEvent event = new BuildEvent(project); |
|
|
|
|
|
event.setException(exc); |
|
|
|
|
|
|
|
|
|
|
|
Iterator itr = listeners.iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
BuildListener listener = (BuildListener) itr.next(); |
|
|
|
|
|
listener.importFinished(event); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void fireTargetStarted(Target target) { |
|
|
|
|
|
BuildEvent event = new BuildEvent(target); |
|
|
|
|
|
|
|
|
|
|
|
Iterator itr = listeners.iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
BuildListener listener = (BuildListener) itr.next(); |
|
|
|
|
|
listener.targetStarted(event); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void fireTargetFinished(Target target, BuildException exc) { |
|
|
|
|
|
BuildEvent event = new BuildEvent(target); |
|
|
|
|
|
event.setException(exc); |
|
|
|
|
|
|
|
|
|
|
|
Iterator itr = listeners.iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
BuildListener listener = (BuildListener) itr.next(); |
|
|
|
|
|
listener.targetFinished(event); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void fireTaskStarted(Task task) { |
|
|
|
|
|
BuildEvent event = new BuildEvent(task); |
|
|
|
|
|
|
|
|
|
|
|
Iterator itr = listeners.iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
BuildListener listener = (BuildListener) itr.next(); |
|
|
|
|
|
listener.taskStarted(event); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void fireTaskFinished(Task task, BuildException exc) { |
|
|
|
|
|
BuildEvent event = new BuildEvent(task); |
|
|
|
|
|
event.setException(exc); |
|
|
|
|
|
|
|
|
|
|
|
Iterator itr = listeners.iterator(); |
|
|
|
|
|
while (itr.hasNext()) { |
|
|
|
|
|
BuildListener listener = (BuildListener) itr.next(); |
|
|
|
|
|
listener.taskFinished(event); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |