| <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> | |
| <html> | |
| <head> | |
| <meta http-equiv="Content-Type" | |
| content="text/html; charset=windows-1252"> | |
| <title>Project Builders and Natures</title> | |
| <meta name="Author" content="John Arthorne"> | |
| <link rel="stylesheet" href="../../default_style.css"> | |
| </head> | |
| <body link="#0000ff" vlink="#800080" bgcolor="#ffffff" > | |
| <div align="right"><font size="-2">Copyright © 2003, 2004 International Business Machines Corp.</font> | |
| <table border="0" cellspacing="0" cellpadding="2" width="100%"> | |
| <tbody> | |
| <tr> | |
| <td align="left" valign="top" colspan="2" bgcolor="#0080c0"><b><font | |
| face="Arial,Helvetica" color="#ffffff"> Eclipse Corner Article</font></b></td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <div align="left"> | |
| <h1><img src="images/Idea.jpg" height="86" width="120" align="middle"> | |
| </h1> | |
| </div> | |
| <p> </p> | |
| <h1 align="center">Project Builders and Natures</h1> | |
| <blockquote> <b>Summary</b> <br> | |
| This article discusses two central mechanisms that are associated with projects | |
| in an Eclipse workspace. The first of these is incremental project builders, | |
| which create some built state based on the project contents, and then keep that | |
| built state synchronized as the project contents change. The second is project | |
| natures, which define and manage the association between a given project and | |
| a particular plug-in or feature. The purpose and uses of builders and natures | |
| will be described in detail, and working examples will be provided to highlight | |
| the finer details of implementing them for your own plug-in. This article is | |
| intended to supplement, rather than replace, the documentation on builders and | |
| natures found in the Eclipse Platform Plug-in Developer Guide. <br> | |
| <p><b> By John Arthorne, IBM OTI Labs</b><br> | |
| <font size="-1">January 27, 2003</font><br> | |
| <font size="-1">Updated November 23, 2004 for Eclipse 3.0</font></p> | |
| </blockquote> | |
| <hr width="100%"> <a name="1"> </a> | |
| <h2><a name="1">Incremental project builders</a></h2> | |
| <div align="right"><a name="1"><i>Castles in the air - they are so easy to | |
| take refuge in. And so easy to build, too.</i> <br> | |
| Henrik Ibsen</a></div> | |
| <p> <a name="1">All of the key points about incremental project builders | |
| are captured in their carefully chosen name. The best way to introduce | |
| them is to begin by describing each of the terms in this name: </a></p> | |
| <ul> | |
| <li><a name="1"><b>Builder - </b> Builders take raw materials and produce | |
| some output based on those materials. In Eclipse, both the raw materials | |
| and the output of builders are resources in the Eclipse workspace.</a></li> | |
| <li><a name="1"><b>Incremental - </b> It would be inefficient if builders | |
| rebuilt their entire output from scratch every time they were invoked. Instead, | |
| builders in Eclipse are incremental. This means that after the first build, | |
| subsequent builds should only rebuild based on what has changed since the | |
| last build.</a></li> | |
| <li><a name="1"><b>Project - </b> A builder operates on the resources in | |
| a single project in the Eclipse workspace. If there are many projects in | |
| the workspace, builders need to be installed on each one separately.</a></li> | |
| </ul> | |
| <p> <a name="1">All of the basic information about builders, including how to | |
| define them, and how to install and run them, is found </a><a | |
| href="http://help.eclipse.org/help30/topic/org.eclipse.platform.doc.isv/guide/resAdv_builders.htm" target="_blank"> | |
| here</a> in the Eclipse Platform Plug-in Developer Guide. This article will | |
| dive into all the gory details about implementing builders that aren't found | |
| in the basic documentation. If you haven't already done so, read through that | |
| documentation first, then come back to this article. </p> | |
| <a name="1a"> </a> | |
| <h3><a name="1a">The kinds of builds</a></h3> | |
| <p><a name="1a">From a user's point of view, builds in Eclipse come in several | |
| different flavors. Under the covers, all of these different varieties are | |
| implemented by the same builder instance, so your builder will need to be | |
| able to understand all of them. Let's take a look at each of the different | |
| varieties in turn. </a></p> | |
| <h4><a name="1a">Incremental build</a></h4> | |
| <p> <a name="1a">Incremental building is the variety of most interest in | |
| Eclipse. This is what happens when <b>Build Project</b> or <b>Build All</b> | |
| is selected from the <b>Project</b> menu in the Eclipse workbench. When an | |
| incremental build is invoked, the builder is supplied with a <i>resource | |
| delta</i>. A resource delta is a hierarchical description of what has changed | |
| between two discrete points in the lifetime of the Eclipse workspace. In | |
| particular, builders are given a delta that describes all changes in that | |
| project since the last time that builder was called. The goal of incremental | |
| building is to analyze this delta, and then rebuild only the builder output | |
| that is affected by the change. </a></p> | |
| <p> <a name="1a">For a detailed description of the structure of resource | |
| deltas, and for tips on processing them, read the article on </a><a | |
| href="http://www.eclipse.org/articles/Article-Resource-deltas/resource-deltas.html"> | |
| resource change listeners</a>. For this article, it is sufficient to point | |
| out the differences between the resource deltas given to listeners, and | |
| the resource deltas given to builders: </p> | |
| <ul> | |
| <li>Builder resource deltas are rooted at a single project. A builder can | |
| request deltas for multiple projects, but each resource delta returned by | |
| <code>IncrementalProjectBuilder.getDelta</code> has a single project | |
| as its root.</li> | |
| <li>Some resource change information that is available to resource change | |
| listeners is omitted from build deltas. In particular, changes to resource | |
| markers (<code>IResourceDelta.MARKERS</code>), and changes to synchronization | |
| information (<code>IResourceDelta.SYNC</code>) are not included in builder | |
| deltas. These change types are omitted for performance reasons, since this | |
| information is not typically of interest to builders. </li> | |
| <li>Builder deltas may be null. If a builder has never been invoked before, | |
| any request for deltas will return null. Also, if a builder is not run for | |
| a long time, the platform reserves the right to return a null delta. This | |
| is done because keeping track of resource changes over an extended period | |
| of time can be very costly, and it's generally more efficient to just rebuild | |
| from scratch if the builder hasn't been run in a long time. When a builder | |
| receives a null delta, it should just discard all existing built state, and | |
| resort to doing a full build. </li> | |
| </ul> | |
| <h4>Auto-build</h4> | |
| <p> From the point of view of the builder implementation, auto-build is exactly | |
| the same as incremental build. The difference with auto-build is that | |
| it is not triggered by an explicit build request, but as an automatic side | |
| effect of resources changes in the workspace. When auto-build is turned | |
| on, all installed builders will be invoked every time resources are added, | |
| removed, or modified in the eclipse workspace. There is no guarantee about | |
| precisely when auto-build will occur in relation to the operation that modified the | |
| workspace. The build generally occurs in a background thread a short time after | |
| all threads have stopped modifying the workspace. If a thread attempts to modify | |
| the workspace while the build is running, the build is halted and the thread is allowed | |
| to proceed with its changes. Auto-build will continue to attempt running until it | |
| has sufficient time to complete the entire workspace build. As with resource change | |
| events, you can avoid frequent auto-builds by wrapping long running operations in | |
| an <tt>IWorkspaceRunnable</tt> passed to <tt>IWorkspace.run</tt>, or using | |
| a <tt>WorkspaceJob</tt>. | |
| <p> | |
| There is a preference in the Eclipse workbench | |
| for turning auto-build on or off (Preferences > Workbench > Build automatically). | |
| Auto-build can also be turned on or off programatically by setting the auto-build flag | |
| in the workspace description, although this is not generally recommended. The | |
| following code snippet turns auto-build on: </p> | |
| <font color="#4444cc"> | |
| <pre> | |
| IWorkspace workspace = ResourcesPlugin.getWorkspace(); | |
| IWorkspaceDescription description = workspace.getDescription(); | |
| if (!description.isAutoBuilding()) { | |
| description.setAutoBuilding(true); | |
| workspace.setDescription(description); | |
| } | |
| </pre> | |
| </font> | |
| <h4>Full build</h4> | |
| <p> When a full build is invoked, it is a request to discard all existing | |
| built state and start over from scratch. There is currently no command | |
| in the Eclipse workbench that allows a user to directly invoke a full build. | |
| The first build after invoking clean will cause a full build in the cleaned projects. | |
| Theoretically, full build should never be needed if your incremental build is | |
| clever enough. However, if your builder is for some reason unable to correctly | |
| build incrementally, it is useful to have an escape route available to rebuild | |
| everything from scratch. | |
| </p> | |
| <h4>Clean build</h4> | |
| <p> | |
| The final variety of build, introduced in Eclipse 3.0, is <i>clean</i>. Clean | |
| is inspired by the Unix <i>make</i> utility convention of using a target called <tt>clean</tt> | |
| to discard all artifacts produced by a build. As with full build, clean should not | |
| be required if all builders correctly perform their incremental builds. Clean is | |
| intended largely as a safety blanket for the end user when things seem to go wrong | |
| with their build process. Builders should implement clean by deleting all output | |
| files produced by the build, and removing any problem markers associated with | |
| that builder. Unlike other build types, the clean build is not implemented by the | |
| <tt>build</tt> method on <tt>IncrementalProjectBuilder</tt>. For backwards | |
| compatibility, there is a new <tt>clean</tt> method with an empty default implementation, | |
| allowing older builders to continue running without modification. | |
| The following is an example of a <tt>clean</tt> | |
| implementation that just deletes all markers of a particular type from the project: | |
| <font color="#4444cc"> | |
| <pre> | |
| protected void clean(IProgressMonitor monitor) throws CoreException { | |
| getProject().deleteMarkers("com.xyz.myproblems", | |
| true, IResource.DEPTH_INFINITE); | |
| } | |
| </pre> | |
| </font> | |
| </p> | |
| <a name="1b"> </a> | |
| <h3><a name="1b">Gory details about the build process</a></h3> | |
| <p> <a name="1b">The target of a build can either be a single project, or | |
| the entire workspace. The entire workspace is built on every auto-build, | |
| and also on the actions <b>Build All</b> and <b>Clean All</b>. Keep in | |
| mind that on incremental builds, "building" the entire workspace may actually | |
| only involve compiling a single file. So what happens when a workspace | |
| build is invoked? Regardless of whether the build is full, incremental, or clean, | |
| the process is more or less the same, and this section will outline exactly | |
| what happens. </a></p> | |
| <h4><a name="1b">Step 1: computing the workspace build order</a></h4> | |
| <p> <a name="1b">First, the platform computes or retrieves the <i>workspace | |
| build order</i>. This is simply an ordered list of projects to build. This | |
| build order can be explicitly set using the API method <code>IWorkspaceDescription.setBuildOrder</code>. | |
| The following example sets the workspace build order to be project "Foo" | |
| followed by project "Bar":</a></p> | |
| <a name="1b"><font color="#4444cc"> | |
| <pre> | |
| IWorkspace workspace = ResourcesPlugin.getWorkspace(); | |
| IWorkspaceDescription description = workspace.getDescription(); | |
| String[] buildOrder = {"Foo", "Bar"}; | |
| description.setBuildOrder(buildOrder); | |
| workspace.setDescription(description); | |
| </pre> | |
| </font> | |
| </a> | |
| <p> <a name="1b">In the Eclipse UI, a user can set the build order from the | |
| "Build Order" preference page. Setting the build order manually, either | |
| via API or on the preference page, is not recommended in most circumstances. | |
| You may want to do this if you have a headless application that builds particular | |
| projects, or if you have complex project inter-dependencies that the default | |
| build order doesn't handle well. If no build order is set manually, the | |
| workspace computes a default build order based on the <i>project references</i>. | |
| To be precise, the default build order is a breadth-first traversal of | |
| the project reference graph. If there are cycles in the project reference | |
| graph, they are left in the build order, but the projects involved in the | |
| cycle will be built in an indeterminate order. </a></p> | |
| <p> <a name="1b">Once the workspace build order is figured out, the build | |
| process proceeds as follows: </a></p> | |
| <ol> | |
| <li><a name="1b">Build each open project in the build order</a></li> | |
| <li><a name="1b">Build each remaining open project in the workspace, in | |
| no particular order</a></li> | |
| </ol> | |
| <p></p> | |
| <p> <a name="1b">Note: Closed projects are ignored by the build process. </a></p> | |
| <h4><a name="1b">Step 2: Build specifications and arguments</a></h4> | |
| <p> <a name="1b">Now that we know the order in which projects are built, | |
| we should take a closer look at what happens when a particular project is | |
| built. There can be any number of incremental builders associated with a | |
| given project. The set of builders to invoke for a project, and the ordering | |
| of those builders, is specified by the project's <i>build spec</i>. The | |
| build spec is found on the <code>IProjectDescription</code>, and is represented | |
| as an array of <code>ICommand</code> objects. Each command is simply the | |
| name of the builder extension to run, and an optional table of builder arguments. | |
| Builder argument keys and values must be strings, to allow for easy serialization. | |
| Thus, building a single project involves invoking each builder in the build | |
| spec, one at a time, in the order specified in the build spec. </a></p> | |
| <h4><a name="1b">Step 3: Decide if the builder needs to be invoked</a></h4> | |
| <p> <a name="1b">There are some circumstances under which a builder might | |
| be skipped during the build process. In particular, a builder will be skipped | |
| if the build is an incremental or auto-build, and no resources have changed | |
| in the project, or in any of the projects that the builder is interested | |
| in. The return value of a builder's <code>build</code> method is an array | |
| of projects. This list of projects represents the projects that the builder | |
| requires resource deltas for (this implicitly includes the project that the | |
| builder is installed on). If none of these projects have changed since the | |
| last time the builder was invoked, the builder is skipped (for incremental and auto-build only). | |
| Without this step, every builder in the workspace would be invoked after every single | |
| resource change. This optimization ensures that only builders that are capable | |
| of processing the changed resources will be invoked. On the downside, this | |
| means builders cannot generally respond to external changes that occur outside | |
| the workspace, because the builder might be skipped if no resources in the | |
| workspace have changed. </a></p> | |
| <p> <a name="1b"><img src="images/tip.gif"> | |
| If there are circumstances where your builder must be invoked regardless | |
| of whether any resources have been changed, there is a way to circumvent | |
| this behavior. If the builder invokes the method <code>IncrementalProjectBuilder.forgetLastBuiltState</code>, | |
| the information required to compute the resource deltas will be discarded. | |
| This means that the build mechanism cannot determine what has changed since | |
| the last build, so the builder will always be invoked. The downside is that | |
| deltas will not be available for such a builder, so the builder must always | |
| perform a full build. </a></p> | |
| <p> <a name="1b">Finally, if there are errors finding or initializing a builder, | |
| it will be temporarily omitted from the build process. The builder is automatically | |
| added back to the build once the problem is fixed. Error conditions that | |
| can cause a builder to be skipped include: </a></p> | |
| <ul> | |
| <li><a name="1b">The plug-in that owns the builder is missing.</a></li> | |
| <li><a name="1b">The plug-in that owns the builder has been marked as disabled. | |
| This is caused either by an error during activation, or by a missing plug-in | |
| prerequisite.</a></li> | |
| <li><a name="1b">There was an error instantiating the builder. This typically | |
| means a runtime exception was thrown either by the builder's constructor | |
| or static initializer code.</a></li> | |
| <li><a name="1b">The builder belongs to a particular project nature, and | |
| that nature is missing or disabled. This will be discussed in more detail | |
| in the section on project natures.</a></li> | |
| </ul> | |
| <p></p> | |
| <a name="1c"> </a> | |
| <h3><a name="1c">Progress, cancelation, and exception handling</a></h3> | |
| <div align="right"><a name="1c"><i>Progress is the mother of all problems. | |
| </i> <br> | |
| G. K. Chesterton</a></div> | |
| <p> <a name="1c">There are a number of features that are available to builders | |
| that are not available to simple resource change listeners. Most importantly, | |
| builders are entities that the user is intended to be aware of and to interact | |
| with, so there are a number of mechanisms available for builders to communicate | |
| back with their caller. </a></p> | |
| <h4><a name="1c">Reporting progress</a></h4> | |
| <p>The <code>build</code> method on <code>IncrementalProjectBuilder</code> | |
| takes as parameter an instance of <code>IProgressMonitor</code>. This object | |
| should be used for reporting feedback and progress information to the user | |
| as the build proceeds. The most important methods on <code>IProgressMonitor</code> | |
| are <code>beginTask</code>, <code>subTask</code>, <code>worked</code>, and | |
| <code>done</code>. The important concepts to remember about reporting progress | |
| are:</p> | |
| <ul> | |
| <li><code>beginTask</a></code><a name="1c"> and <code>done</code> | |
| must be called exactly once each.</a></li> | |
| <li><a name="1c">The increments passed to the <code>worked</code> method | |
| should eventually add up to the amount specified in <code>beginTask</code>. | |
| If the code branches, you need to ensure every branch reports the same progress.</a></li> | |
| <li><a name="1c">If you need to call API from another component during the | |
| build, you should create a new <code>SubProgressMonitor</code> and pass that | |
| to the API method. Never pass your own progress monitor across an API boundary, | |
| because you have already called <code>beginTask</code>, which can only be | |
| called once for a given progress monitor.</a></li> | |
| <li><a name="1c">Some form of progress (either worked increments or a new | |
| sub-task message), should be visible every few seconds. The purpose is to | |
| reassure the user that something is actually happening.</a></li> | |
| <li><a name="1c">The sub-task messages just need to give a general idea | |
| of what is happening. You don't need to report exactly what is happening | |
| at a low level in the code. Progress messages should be in a vocabulary | |
| that the user will understand.</a></li> | |
| <li>The user won't know or care if you are lying through your teeth.</li> | |
| </ul> | |
| <p></p> | |
| <p> <a name="1c">Here is the recommended basic pattern for using progress | |
| monitors: </a></p> | |
| <a name="1c"><font color="#4444cc"> | |
| <pre> | |
| final int TOTAL_WORK = 100; | |
| try { | |
| monitor.beginTask("Name of task", TOTAL_WORK); | |
| for (int i = 0; i < TOTAL_WORK; i++) { | |
| monitor.subTask("Name of sub task"); | |
| //do some work | |
| monitor.worked(1); | |
| } | |
| } finally { | |
| monitor.done(); | |
| } | |
| </pre> | |
| </font> </a> | |
| <p><a name="1c">In some circumstances, it may be too expensive or even impossible | |
| to compute the exact amount of work units before the operation starts. For | |
| example, when loading files from a server, you may not know exactly how many | |
| files will be loaded or the size of the files until they have already arrived. | |
| In these cases, it is necessary to "fake" the progress numbers a bit. For | |
| example, you can start with a best estimate, and then revise the number of | |
| work units as you approach the total work. If you don't even have an initial | |
| estimate, you can use (warning: practical application of Calculus!) an infinite | |
| series that converges at the total work. The goal is to keep the progress | |
| bar moving, and only reach the end of the bar when the work is almost complete, | |
| not always an easy task! </a></p> | |
| <h4><a name="1c">Handling cancelation</a></h4> | |
| <p> <a name="1c">Progress monitoring in Eclipse is always tied to the mechanism | |
| for cancelation. Builder implementors are encouraged to check for cancelation | |
| as often as possible. The important thing to keep in mind when adding cancelation | |
| support, is that you are the one in control of cancelation. When the caller | |
| attempts to cancel, this is really a cancelation request, not a demand. | |
| If the request cannot be granted without leaving a corrupt or invalid state | |
| behind, then it's ok to defer the cancelation until you are in a consistent | |
| state. If the operation is almost finished and is not reversible, it is | |
| sometimes better to ignore the cancelation request completely. When you decide | |
| that safe cancelation is possible, the recommended approach is to throw a | |
| new <code>OperationCanceledException</code> back from the builder's <code>build</code> | |
| method. This special runtime exception will then be caught and handled | |
| by the platform. The pattern for checking and responding to cancelation | |
| is as follows: </a></p> | |
| <a name="1c"> | |
| <font color="#4444cc"> | |
| <pre> | |
| do some work | |
| checkCancel(monitor); | |
| //do some work | |
| checkCancel(monitor); | |
| //... etc ... | |
| protected void checkCancel(IProgressMonitor monitor) { | |
| if (monitor.isCanceled()) { | |
| forgetLastBuiltState();//not always necessary | |
| throw new OperationCanceledException(); | |
| } | |
| } | |
| </pre> | |
| </font> </a> | |
| <p> <a name="1c">It is sometimes necessary to call <code>IncrementalProjectBuilder.forgetLastBuiltState()</code> | |
| in order to correctly recover from the cancelation the next time your builder | |
| is called. If you don't call this method, the next build will be supplied | |
| with the usual resource delta describing the changes since the last build. | |
| Since the last build was canceled part way through, it might not be possible | |
| to incrementally update your built state. Calling this method will | |
| force the next incremental or auto-build to be a full build. An alternative is to extract the | |
| information you need from the resource delta, and continue the incremental | |
| build the next time one is requested. | |
| </a></p> | |
| <h4><a name="1c">Handling interruption</a></h4> | |
| <p> | |
| In Eclipse 3.0, auto-build was moved into a background thread. In a GUI environment, | |
| this allows the user to continue working as the build progresses. However, if | |
| the user attempts to make further resources changes while the build is running, | |
| auto-build needs to interrupt itself. The workspace checks for this kind of interruption | |
| immediately before calling each individual builder. If the contention occurs while | |
| a builder is running, the user's activity will typically be blocked until the currently | |
| executing builder completes. While this is fine for relatively quick builders, it can | |
| become frustrating for users when a single builder takes a very long time to complete. | |
| To alleviate this, long running builders can check for interruption periodically during | |
| their execution. The method <tt>isInterrupted</tt> on <tt>IncrementalProjectBuilder</tt> | |
| will return <tt>true</tt> if another thread is waiting to modify the workspace. When | |
| this happens, a builder can chose to abort the build and allow the other operation to | |
| proceed. Note that interruption should not always be treated the same as cancelation, | |
| since it occurs implicitly, rather than based on an explicit cancelation request by the | |
| user. It is acceptable to respond to cancelation by discarding build state and resorting | |
| to a full build on the next builder invocation, but this is too drastic for interruption. | |
| Interruption should not cause significant additional overhead for subsequent builds. | |
| Builders that cannot gracefully handle interruption should not respond to it at all. | |
| <h4><a name="1c">Handling exceptions and reporting problems</a></h4> | |
| <p> <a name="1c">There are two categories of problems that a builder will | |
| typically need to address. First, there may be internal errors in the code | |
| of your builder, or errors thrown back by API that is called by the builder. | |
| If it is not possible to recover from these errors, your <code>build</code> | |
| method can throw a <code>CoreException</code> that includes a description, | |
| severity, and optional nested exception. The text of this exception will | |
| be reported directly to the user, and the remainder of the build for that | |
| project will be aborted. Builds of other projects will proceed as normal. | |
| </a></p> | |
| <p> <a name="1c">The second category of problems are against the resources | |
| being built. For example, this includes compilation errors in the user code, | |
| or incorrect configuration information required by the user. These types | |
| of problems are generally reported by creating <i>problem markers</i> that are | |
| attached to the most specific resource that is relevant to that problem. | |
| If no particular resource is relevant, you can attach the marker to the project | |
| itself. You are encouraged to create your own custom marker type for your | |
| builder problems, but the type you define should generally be a subtype of | |
| the generic problem type, <code>IMarker.PROBLEM</code>. This ensures that | |
| your problem type will appear in the task list of the Eclipse workbench. | |
| See the article </a><a | |
| href="http://www.eclipse.org/articles/Article-Mark%20My%20Words/Mark%20My%20Words.html"> | |
| Mark my Words</a> for more information on defining and using markers. </p> | |
| <h3><a name="2b">Interacting with background auto-build</a></h3> | |
| <p> | |
| The fact that auto-build occurs in a background thread can sometimes introduce | |
| challenges for code that relies on builder output. In a headless batch environment, | |
| you cannot simply modify resources and assume auto-build will occur | |
| immediately. Headless tools that previously made this assumption might exit from | |
| their main thread of execution while the background build is still running. The best | |
| solution in this situation is to simply turn off auto-build, and maintain complete control | |
| over when builds occur by explicitly invoking <tt>IWorkspace.build</tt> if and when | |
| required. There are other situations where a plug-in wants to allow auto-build to run, | |
| but needs to wait until it completes before proceeding. For example, when the user | |
| attempts to launch a Java program they have just created or modified, the launch | |
| needs to wait until auto-build completes to ensure that the correct class files are | |
| used. In this situation, the background build job can be joined using the following code: | |
| <font color="#4444cc"> | |
| <pre> | |
| Platform.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, null); | |
| </pre> | |
| </font> | |
| The <tt>join</tt> invocation will block until the auto-build job completes, or until | |
| the join is interrupted or canceled. You can report progress while waiting by passing | |
| a progress monitor to the <tt>join</tt> method. Manual incremental build jobs can be joined | |
| in a similar manner using the <tt>ResourcesPlugin.FAMILY_MANUAL_BUILD</tt> job family. | |
| </p> | |
| <table border="1" cellpadding="4" width="90%" bgcolor="FFFFCC"><tr> | |
| <td bgcolor="0080c0"><font size="+1" color="FFFFCC"><b>Sidebar: the Java builder</b></font> </td> | |
| </tr> | |
| <tr><td> | |
| <p> | |
| The concept of automatic incremental compilation is not familiar to many developers. | |
| A very frequent question from Eclipse beginners is, "where is the compile button?" | |
| The answer is that an IDE with automatic compilation doesn't need a compile button. Every | |
| time you make a change to a file, or a group of files, the incremental builder immediately rebuilds | |
| every source file that was affected by the change. In this environment, the idea of compilation | |
| as a task the user is involved in disappears -- the world is just always in a compiled state. | |
| </p> | |
| <p> | |
| So what magic goes on behind the scenes to make this happen? How does the Java™ builder | |
| know which files need to be recompiled when a given source file changes? This is no easy task, | |
| but in broad brush strokes, this is what the Eclipse Java builder does: | |
| <ul> | |
| <li>The builder maintains a <b>built state</b> that includes a list of all types (classes or | |
| interfaces) that are referenced by each type in the workspace. This information is returned | |
| by the compiler each time a source file is compiled. This state is computed from scratch | |
| on a full build, and updated incrementally on each subsequent build.</li> | |
| <li>Whenever files are modified, the builder receives a resource delta that describes | |
| which files were added, removed, or changed.</li> | |
| <li>For deleted Java source files, the corresponding class files are deleted. Added and changed | |
| source files are added to a queue of files that need to be compiled.</li> | |
| <li>The builder then processes this queue as follows: | |
| <ol> | |
| <li>Remove a file from the queue, and compile it.</li> | |
| <li>Compare the resulting type to the old class file, and see if the | |
| type has <b>structural changes</b>. Structural changes are changes that can affect | |
| the compilation of a referencing type: added or removed methods, fields or types, or changed method | |
| signatures.</li> | |
| <li>If the type has structural changes, find all types in the project that reference the changed type, | |
| and add them to the queue.</li> | |
| <li>If the type has changed at all, write it to disk in the builder's output folder.</li> | |
| <li>Update the built state with the new reference information for the compiled type.</li> | |
| <li>Repeat until the queue is empty.</li> | |
| </ol> | |
| </li> | |
| <li>As a final step, the builder generates problem markers for each compiled type that had | |
| compilation problems.</li> | |
| </ul> | |
| </p> | |
| <p> The critical feature of the Java builder is that it is very fast 95% of the | |
| time. Most times you edit a Java file, you don't make any structural changes. | |
| This means the builder just has to compile that single file, and it's | |
| done. When a type does have structural changes, all types that reference | |
| it will be recompiled. However, those secondary types will almost never | |
| have structural changes themselves, so the builder loop finishes with | |
| at most a couple of iterations. On rare occasions, there will be significant | |
| structure changes that cause many files to be recompiled. There is a lesson | |
| here that you can apply when writing your own incremental builder. If | |
| you can make your builder extremely fast for the most common cases, users | |
| will generally tolerate a longer delay for those rare corner cases when | |
| they come along. </p> | |
| </td></tr></table> | |
| <hr width="100%"> <a name="2"> </a> | |
| <h2><a name="2">Project natures</a></h2> | |
| <div align="right"><a name="2"><i>What nature delivers to us is never stale. | |
| Because what nature creates has eternity in it.</i> <br> | |
| Isaac Bashevis Singer</a></div> | |
| <p> <a name="2">The main purpose of project natures is to create an association | |
| between a project and a given tool, plug-in, or feature set. By adding a | |
| nature to a project, you indicate that your plug-in is configured to use | |
| that project. For example, when the Java nature is added to a project, it | |
| indicates that the Java Development Tool (JDT) plug-ins are aware of that project, and have | |
| configured a classpath and a Java builder to work on that project. </a></p> | |
| <p> <a name="2">In addition to defining the association between a tool and | |
| a project, natures also provide a way of handling the lifecycle of a tool's | |
| interaction with a project. When a nature is added to a project, the project | |
| nature's <code>configure</code> method is called. This gives the tool an | |
| opportunity to initialize its state for that project, and install any incremental | |
| project builders that are needed for that project. Similarly, when a nature | |
| is removed from a project, the nature's <code>deconfigure</code> method is | |
| called. This gives the tool an opportunity to remove or clean up any meta-data | |
| it has created for that project, and to remove any listeners and builders | |
| associated with that tool. </a></p> | |
| <p> <a name="2">Project natures also have significance in the eclipse UI. For | |
| example, the icon for a project in the resource navigator is based on the installed | |
| nature (using the org.eclipse.ui.projectNatureImages extension point). Actions | |
| in the various menus can also be filtered to be visible only for resources that | |
| belong to projects with a given nature. Refer to the </a> | |
| <a | |
| href="http://help.eclipse.org/help30/topic/org.eclipse.platform.doc.isv/reference/extension-points/index.html#workbench" target="_blank"> | |
| UI extension point documentation</a> for more details on action filtering. </p> | |
| <a name="2a"> </a> | |
| <h3><a name="2a">Nature constraints</a></h3> | |
| <div align="right"><a name="2a"><i>The laws of nature are but the mathematical | |
| thoughts of God.</i> <br> | |
| Euclid</a></div> | |
| <p><a name="2a">Natures can be defined so that they are only enabled under | |
| certain conditions. The two types of constraints that can currently be set | |
| are <i>one-of</i> constraints and <i>requires</i> constraints. The one-of | |
| constraint ensures that if two or more natures exist on a project that belong | |
| to the same set, then all such natures will be disabled. The "set" in this | |
| case is just an arbitrary string. As a fictitious example, say there are | |
| several different plug-ins that can provide a project nature for managing | |
| XML files in a project. However, since these plug-ins provide conflicting | |
| services, it is not possible to have two of these XML natures installed on | |
| any single project. These natures could specify membership in a common "xml-natures" | |
| nature set. Then the platform would ensure that only one nature belonging | |
| to that set could be installed on a project at any given time. </a></p> | |
| <p> <a name="2a">The <i>requires</i> constraint ensures that a nature is | |
| only enabled on a project if the nature(s) it requires are also enabled | |
| on that project. For example, if your tools are only applicable for projects | |
| managed by JDT, you can add a requires constraint on the Java nature.</a></p> | |
| <p> <a name="2a">The platform will attempt to enforce these constraints whenever | |
| possible. For example if an attempt to add or remove a nature from a project | |
| would cause a constraint to be violated, the operation will fail. Also, | |
| when several natures are added or removed at once, they will be serially | |
| configured or deconfigured by the platform in an order that maintains the | |
| validity of the constraints. For example, if your nature requires the JDT | |
| nature, the platform will ensure that the JDT nature is configured before | |
| your nature, and deconfigured after your nature. </a></p> | |
| <p> <a name="2a">In some circumstances, the platform may not be able to ensure | |
| the constraints are satisfied. For example, a nature may go missing or have | |
| its definition changed between invocations of the platform. A more common | |
| case is when two users are sharing a project in a repository, but have different | |
| tools available, and hence different natures installed on the project. In | |
| these circumstances, the natures whose constraints are not satisfied are | |
| considered to be <i>disabled</i>. Disabled natures are tolerated by the | |
| platform -- there will be no error messages and the nature will remain in | |
| the project description. You can find out if a nature is enabled by calling | |
| <code>IProject.isNatureEnabled()</code> on a project. </a></p> | |
| <a name="2b"> </a> | |
| <h3><a name="2b">Associating natures with builders</a></h3> | |
| <p> <a name="2b">Incremental project builders and project natures often go | |
| together. The nature life cycle methods provide a convenient place for adding | |
| and removing builders from the project build spec, and the builder's association | |
| with the project is often linked with other tools that benefit from the nature-based | |
| presentation in the UI. Also, the ordering created by the nature "requires" | |
| constraint makes it easy to correctly order the build spec. For example, | |
| if you have a builder that must be run <b>after</b> the JDT builder, you | |
| simply make your nature require the JDT nature, thus ensuring that your nature | |
| will be configured after the JDT builder has already been installed. Then | |
| if you add your nature to the end of the build spec, you are guaranteed that | |
| your builder is run after the JDT builder (assuming nobody else reorders | |
| the build spec). </a></p> | |
| <p> <a name="2b">The relationship between a nature and a builder can be made | |
| concrete by adding extra markup in the extension definitions. In the nature | |
| definition, the builder can be specified by adding a builder attribute to | |
| the nature definition. In the builder definition, you just need to add an | |
| attribute specifying that the builder belongs to some nature. </a></p> | |
| <p> <a name="2b"><img src="images/tryit.gif"> | |
| The following is an example of a builder and a nature that are explicitly | |
| related. In this example, the id of the plug-in is "com.xyz.myplugin". </a></p> | |
| <a name="2b"><font color="#4444cc"> | |
| <pre> <extension <br> id="mynature" <br> name="My Nature" <br> point="org.eclipse.core.resources.natures"> <br> <runtime> <br> <run class="com.xyz.natures.MyNature"/> <br> </runtime> <br> <builder id="com.xyz.myplugin.mybuilder"/> <br> </extension> <br> <extension <br> id="mybuilder" <br> name="My Builder" <br> point="org.eclipse.core.resources.builders"> <br> <builder hasNature="true"> <br> <run class="com.xyz.builders.MyBuilder"/><br> </builder> <br> </extension> <br></pre> | |
| </font> </a> | |
| <p> <a name="2b">There are some benefits to be gained by explicitly associating | |
| a nature with a builder. Primarily, this ensures that builders are automatically | |
| omitted from the build spec when their corresponding nature is missing or | |
| disabled. This gives builders the guarantee that, when they are run, they | |
| can be sure that the builders of their prerequisite natures will also be run. | |
| This also ensures smooth operation in the case where two users are sharing | |
| a project, but only one has the plug-ins required for a particular nature. | |
| All builders that belong to that nature, or that belong to natures that | |
| require that nature, will be silently omitted from the build spec for the | |
| user who does not have the necessary plug-ins. </a></p> | |
| <a name="2c"> </a> | |
| <h3><a name="2c">More on nature life cycle</a></h3> | |
| <div align="right"><a name="2c"><i>Man masters nature not by force but by | |
| understanding.</i> <br> | |
| Jacob Brownowski</a></div> | |
| <p> <a name="2c">Since natures are intended to provide support for a project | |
| life cycle, it is important to understand exactly when natures are configured | |
| and deconfigured. Natures are only configured or deconfigured when a nature | |
| is added or removed from the project description, and then committed using | |
| <code>IProject.setDescription</code>. This is the only API method that will | |
| cause natures to be configured or deconfigured. A nature is only ever configured | |
| <b>once</b> for a given project, even if that project is shared using a repository | |
| with multiple team members. The life cycle of a project nature on a shared | |
| project goes like this: </a></p> | |
| <ul> | |
| <li><a name="2c">User 1 creates the project, and releases to the repository</a></li> | |
| <li><a name="2c">Other users load this project from the repository into | |
| their workspace</a></li> | |
| <li><a name="2c">User 1 now adds nature X to the project. <code>configure()</code> | |
| on nature X is called now.</a></li> | |
| <li><a name="2c">User 1 releases this change to the repository</a></li> | |
| <li><a name="2c">Other users load this change, and the nature is added to | |
| their local project description. The <code>configure()</code> method for Nature X | |
| is <b>not</b> called again for these other users.</a></li> | |
| </ul> | |
| <p></p> | |
| <p> <a name="2c">The <code>configure()</code> method is also <b>not</b> called again when | |
| a project is copied or moved, or when a project is created using <code>IProject.create(IProjectDescription, | |
| IProgressMonitor)</code>. </a></p> | |
| <a name="2c"><br> | |
| </a><a name="3"> </a> | |
| <h2><a name="3">Summary</a></h2> | |
| <p> <a name="3">Incremental project builders provide a mechanism for processing | |
| resources in a project and producing some build output. The builder framework | |
| makes it easier to incrementally maintain that built state as the input resources | |
| change. This article described implementation details about writing your | |
| own builder, such as reporting problems, progress indication, and cancelation | |
| support. Project natures create a concrete connection between a tool and | |
| a project in the eclipse workspace, and they provide support for persisting, | |
| sharing, and managing the relationship of the tool with the project. Natures | |
| and builders are often used together, as this provides a way to install and | |
| remove builders at appropriate times, and allows for builder ordering based | |
| on nature dependencies. </a></p> | |
| <p> <a name="3">For more information about builders and natures, consult the </a><a href="http://help.eclipse.org/help30/" target="_blank"> | |
| Eclipse Platform Plug-in Developer Guide</a>. The API Javadoc for the <a | |
| href="http://help.eclipse.org/help30/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/resources/package-summary.html" target="_blank"> | |
| <code>org.eclipse.core.resources</code></a> package is also an important source | |
| of information on these concepts. </p> | |
| <p><small>Java and all Java-based trademarks and logos are trademarks or registered | |
| trademarks of Sun Microsystems, Inc. in the United States, other countries, or | |
| both.</small></p> | |
| </body> | |
| </html> |