Workspace Structure

Last revised 11:15 Saturday September 8, 2001

Original work item: "Make the build path less sensitive with regard to whether a project is a source or a binary project. This is also related to patching (i.e., adding a source folder to a binary project)."

Related issue: support for extension directories containing many jars.

There was much discussion. The underlying issue is how to structure workspaces for significant Java development efforts. The Eclipse Project is an example of such an effort, so the question is not merely of passing interest. Since much of what is being developed in the Eclipse Project are plug-ins, the question also touches on PDE's domain. However, some aspects of the problem affect all significant Java development efforts and not just ones for developing plug-ins. So we begin to investigating those.

Problem: Close Collaboration between Adjacent Teams

The following is an attempt to capture a familiar problem in a general way that does not presume exactly how developers' workspaces are to be structured. Consider the case of two components, with PUI dependent on Core (imagine that Core is the core component, and UI is the corresponding UI component built atop it). Now imagine that Core and UI are under active development by two teams, with ownership split along component lines. For maximum convenience to both teams, the teams would like to be able to do the following:

Generalize from 2 to N the number of teams and components to get the full extent of the problem.

Problem: Large Numbers of Libraries

The following Eclipse Corner posting (Jon Skeet <skeet@pobox.com> on 20.8.2001) describes one concern well:
Referencing a project's libraries in another project: The project I'm working on uses a fairly large number of libraries (about 20 jar files, IIRC). I had hoped I'd be able to create one project which used these libraries, and make all other projects have that project on their build-paths to bring in the libraries - that way I wouldn't need to change *every* project every time I added or removed a library. (It's not a common operation, but even so...) Unfortunately, I can't get this to work - putting project A on the classpath of project B only seems to put project A's individual source/class files on the classpath for project B. I've tried adding as an external jar and importing the jar file into the project, and had no joy with either :(
Here is the arrangement:
project P1
    build classpath = source /P1/src; library lib1.jar; library lib2.jar; ...; library lib20.jar
    output /P1/bin
project P2
    build classpath = source /P2/src; library lib1.jar; library lib2.jar; ...; library lib20.jar
    output /P2/bin
It is clear that the customer is looking for a way to deal with a set of libraries that would be required by several projects, and was hoping that required projects would give him that.

Review: Required Projects

In Eclipse 1.0, the build classpath for a project P can contain an entry for another project R, called a required project. Required projects work as follows:

Review: Classpath Entries

In Eclipse 1.0, a classpath is expressed as an array of classpath entries (IClasspathEntry). There are four kinds of classpath entries:

Investigation: Make Classpath Variables More Powerful

Would making classpath variables more powerful address some the problems facing large scale Java development efforts?

One idea would be to allow variables to be bound to a list of paths instead of a single path. This would allow, for example, a single classpath variable "BASE_LIBS" to be bound to the list of the paths of the library jars lib1.jar; lib2.jar; ...; lib20.jar. Each project referencing this classpath variable would thereby gain access to all the libraries:

project P1
    build classpath = source /P1/src; BASE_LIBS
    output /P1/bin
project P2
    build classpath = source /P2/src; BASE_LIBS
    output /P2/bin
Changing the workspace binding of the classpath variable affects all projects that reference the variable. This makes it easy to change the set of libraries without having to change the projects individually.

Since classpath variable bindings are local to the workspace, there is no obvious automatic way by which these classpath variables would get their bindings. The bindings would have to be configured for each workspace; loading bindings from a file, or initializing them via a script, are feasible options..

The classpath variables currently bind to paths, and classpath entries can contains paths that begin in a variable. Changing variables to bind to a list of paths would be a major change, and would likely require reworking much of existing API. We generally agreed that we would not pursue this approach since it seems somewhat unlikely to solve much of the problem.

Proposal: Explicitly Export Libraries from Required Projects

The proposal is to extend the required projects mechanism to allow a required project to contribute more than just its binary output folder. Rather, a project would be able to indicate that any of its libraries are to be exported. Exported libraries also become available to other projects in the workspace that list this project as a required project.

In the above example, the customer could instead have an arrangement like:

project P1
    build classpath = source /P1/source; project PLib
    output /P1/bin
project P2
    build classpath = source /P2/source; project PLib
    output /P2/bin
project PLib
    build classpath = source /PLib/source; export library lib1.jar; ...; export library lib20.jar
    output /PLib/bin
What this means is that when P1 (similarly, P2) is built, the libraries on its build classpath consists of /PLib/bin, lib1.jar, ..., lib20.jar.

The modified semantics of a project P with a required project R are as follows:

Example:
project P
    build classpath = source /P/source; project R; library plib.jar
    output /P/bin
project R
    build classpath = source /R/source; export library rlib1.jar; library rlib2.jar; export project Q; export library rlib3.jar
    output /R/bin
project Q
    build classpath = source /Q/source
    output /Q/bin
Effective build classpath of P:
    build classpath = source /P/source; library /R/bin; library rlib1.jar; library /Q/bin; library rlib3.jar; library plib.jar

Exporting External Libraries

Relative to a given project, a library is internal iff the path to the jar or folder lies inside the project's resource tree. Other libraries are considered external to the project.

Should projects be allowed to export arbitrary libraries, or should exported libraries always be internal to the project?

Restricting exports to internal libraries ensures that a project that is to be used by other projects is somewhat self contained. If you load such a project from a repository, you are guaranteed all the libraries it exports will be contained therein.

On the other hand, unrestricted exports are more flexible, and allow a project to export a collection of libraries that are not necessarily contained within the project's resource tree. (However, it is unclear whether this additional flexibility would be useful.)

We opted to allow unrestricted imports, but decided to simplify the error processing by only reporting problems against the project with the missing library (or required project) explicitly on its classpath. Any missing entries would simply be omitted from the effective classpath calculation.for dependent projects.

Classpath Variables

A project's build classpath may include references to classpath variables that get bound to libraries (or other projects). How do exports and variables interact?

The export flag could be associated either with the variable reference or with the variable binding. If the export flag is associated with the variable reference, it would indicate exporting whatever library the classpath variable happened to be bound to on a given occasion. If the export flag is associated with the variable binding itself, the library the classpath variable happened to be bound to on a given occasion would be exported conditionally on the flag in the binding. The proposal is to go with the former (export flag with variable reference) since its semantics are somewhat simpler and allow the export flag to be shared with other team members (the bindings of classpath variables are not shareable).

Exporting Auxillary Libraries

Note that explicit exports also address another problem that arises when a project needs to export a pre-built library in addition to the results of compiling its source files (the Eclipse debugger's jdi.jar is a fine instance of this). In Eclipse 1.0, only the class files in the project's binary output folder are exported to dependent projects. This proposal allows a project to export any number of additional libraries as required.

Exporting Projects

By exporting other projects, a project can consolidate and concentrate the outputs from several other projects:..

project P1
    build classpath = source /P/source; export library /P1/lib1.jar
    output /P1/bin
project P2
   build classpath = source /P2/source; export library /P2/lib2.jar
   output /P2/bin
project P1andP2
   build classpath = source /P1andP2/source; export project P1; export project P2
   output /P1andP2/bin
Effective build classpath of P1andP2 includes everything exported from both P1 and P2:
build classpath = library /P1andP2/source; library /P1/bin; library /P1/lib1.jar; library /P2/bin; library /P2/lib2.jar
output /P1andP2/bin

Proposal: Admit Library Projects which Contain No Source Code

One objection to this whole approach is that the Java project is being hijacked. In the original design, a Java project is a buildable container of Java source code. The addition of exported libraries starts to turn the project into something more general. If we are comfortable with this general trend, there are ways that the design can embrace it more whole-heartedly.

The above proposal to expand the semantics of required project creates a new role for a Java project as a container of libraries. This notion of library project is rounded out by allowing Java projects without source or output folders. The build classpath of a library project orders the list of exported libraries and projects (and provides the additional context required for browsing the project itself).

In our example, the library project could be expressed more directly without having to postulate source and output folders which are completely unmotivated in this case:

project PLib
    build classpath = export library lib1.jar; ...; export library lib20.jar

A library project is recognizable simply by the absence of source folders on the build classpath. (We considered adding a new flag to the project to explicitly mark library projects, but decided this was not necessary and would be confusing.)

Note that library projects are not "buildable" in any meaningful sense since they lack source code to compile (and without source code there is no pressing need for a binary output folder). However, there still needs to be a mechanism for detecting and reporting errors in the build classpath for library projects. Except for such checks, the Java incremental project builder should ignore library projects.

The following kinds of entries on the build classpath of a library project would serve these purposes:

However, the API (and UI?) should allow the information in all fields to be maintained even for library projects, possibly to facilitate switching a project between a regular and a library project.

The workspace must compute an input delta even for incremental project builders that ignore the delta they are handed. We considered whether we should go one step further and remove the Java builder from a library project's list of incremental project builders. This would make it crystal clear that there is no Java building going on, and it would reduce workspace memory footprint because the workspace would not need to remember the shape of the resource tree at the time of the last build. However, if the builder was removed for library projects, there would need to be mechanism to add it if the classpath was changed to include a source project. We decided that we should simply avoid the additional hassles and leave the Java builder installed for all Java projects.

Background

From Philippe Mulet 08/31/2001 12:13 PM

I also think this has to be a basic mechanism at the JavaCore level, which the PDE can then surface to plugin writers.
I would like to see this one addressed asap, so that it is possible to share projects transparently at least amongst us.

=======

From Erich Gamma on 08/31/2001 12:43 PM

Here is some more input on our favorite topic "make the build class path less sensitive
with regard to whether a project is a source or binary project".

Our conclusion in SNZ was that this boils down to a PDE project layout issue (binary
projects etc). However, the EC discussion from below illustrates a use case for allowing to export
JARs in addition to the output folder from a project, that is independent of PDE.
The scenario is similar to the WSAD scenario with extension dirs. If we support to
contribute contained JARs from a project then this problem would be addressed.

We should therefore reconsider the solution independent of the PDE issue.
If I do that then I come to the conclusion that the proposed mechanism is valuable
and I suggest to go ahead with specifying it in detail.

Thoughts?

=======
From Jon Skeet <skeet@pobox.com> on Eclipse Corner 20.8.2001

Referencing a project's libraries in another project:
The project I'm working on uses a fairly large number of libraries
(about 20 jar files, IIRC). I had hoped I'd be able to create one
project which used these libraries, and make all other projects have
that project on their build-paths to bring in the libraries - that way I
wouldn't need to change *every* project every time I added or removed a
library. (It's not a common operation, but even so...) Unfortunately, I
can't get this to work - putting project A on the classpath of project B
only seems to put project A's individual source/class files on the
classpath for project B. I've tried adding as an external jar and
importing the jar file into the project, and had no joy with either :(

Document History

[...]
Revised 10:40 Wednesday September 5, 2001 - Library projects not explicitly marked.
Revised 11:15 Saturday September 8, 2001 - Allow exporting external libraries and projects.