Universe

A universe is a one-to-one mapping between project names and project definitions, and is constructed from a hierarchy of layers. Being able to construct a universe is a prerequiste for running any build or publishing a layer.

Layers from which a universe cannot be constructed are called incoherent.

Conflicts

A conflict occurs if a layer imports from two or more layers, and both contain (somewhere in their hierarchies) projects with the same name, which have different definitions.

Conflicts are problematic because they mean that Fury cannot uniquely resolve a name to a definition of a project. It means there are two different variants of a project accessible in a layer at the same time, and Fury does not know which one to choose.

But even if there are no direct dependencies on the ambiguous project name, there could be indirect dependencies. It would not be unusual to define a project which depends on two others (each defined in its own layer), both of which depend on the same project, but different versions of it.

This is commonly known as “diamond dependencies”.

The problem becomes particularly apparent when constructing a classpath for compiling or running an application. The classpath is a linearization of a tree of classfiles, organized by their package name, and if multiple classes with the same fully-qualified name appear in the same classpath, one class will shadow the others with the same name. At runtime, this can result in fatal linking errors.

Resolving conflicts

There are several possible solutions to resolving incoherent layers. Essentially, the requirement is that modifications are made to the base layer, and its nested layers, to put the hierarchy of layers into a state from which a universe may be built, but there may be several viable states from which a universe can be constructed, and different approaches to finding them.

Shadowing

Conflicts between projects occur in layers which import from more than one other layer defining a project of the same name. However, a project defined in the one layer will shadow a project with the same name defined in any imported layers, and the redefined project will be the one used universally.

This means that conflicts between two different projects with the same name, imported from different layers into the current layer, can be resolved by defining a new project with the same name in the current layer. The redefined project could be a copy of one of the conflicting projects, or could be a completely new definition.

There is no guarantee, of course, that the newly-redefined project will be compatible with other projects in the universe which depend upon it, but it will, at least, be checked by the compiler and, if inculded in the build, by tests.

For the most difficult cases of conflict resolutions, it may be necessary to make changes to code to enable compilation to succeed for all dependents of a project. Fury makes it very easy to fork an existing source repository to make changes.

Shading

Not to be confused with shadowing, shading allows a project to indicate that its classfiles and the classfiles of its direct dependents should be rewritten to prefixed package names so as not to conflict with classfiles from a variant of the same project.

This is not yet fully designed.

Constraints

It is possible for Fury to work with a layer from which a universe cannot be constructed, however Fury cannot run a build without a universe, and will refuse to publish an incoherent layer.