Component Specification (Buckminster)
< To: Buckminster Project
A Component Specification (CSPEC) is a generic description of a component. It defines its dependencies to other components, what actions that can be performed on it and how those actions affect the dependencies. It also defines what artifacts the component can export to other components.
As an example; to perform an action such as compilation, certain assets must be available (the source of the component certainly, and header/interface files from components your component is using). Or put more formally: the requirements of an action must be fulfilled.
When you perform an action, such as a compilation, this may produce artifacts needed by other actions. Or put more formally: 'An action can produce artifacts that fulfill requirements of other actions.
Dependencies do not exist in a vacuum, the dependency always exists because some aspect of it is needed to perform a specific action. Or put more formally: There is always a purpose behind a component dependency
A cspec defines the following top-level elements:
An action is something that can be executed. It can have zero or many prerequisites where each prerequisite is either a local or external reference to a component attribute. A local reference appoints something that it is declared in the same CSPEC. An external reference appoints a public attribute in a CSPEC of another component via a dependency. The execution of an action will often produce new artifacts.
Purpose qualified dependency
DEPRECATED! A purpose is something that an action or export will use as a qualifier on a dependency to request a specific export in the appointed component. This concept has been replaced by Component Attribute.
The purpose says "I need this component in order to <purpose>". Here's an example:
Assume that we have three Java components, A, B, and C. A use classes present in B. B in turn use classes from C but hides this completely from A. When compiling A, a compiled version of B is needed but there's no need for C. When running A, clearly both B and C will be needed.
Object Oriented methodology tells us that it would be bad if A needs to know about the runtime dependency from B to C. It would be much better if A can say "I need B in order to compile" or "I need B in order to run" and B could answer, "You need me in order to run? Then I need to bring C along as well". The purpose qualified dependencies resolves this elegantly.
Artifact and derived components
A component will always contain at least one artifact. A source component from a CVS repository will always contain the artifact "source". This artifact is typically one single folder. Different actions such as "compile" or "archive" can produce new artifacts. It is often beneficial to store such artifacts in repositories and offer them as alternatives to the source when a component needs to resolve its dependencies. An FTP or HTTP based repository that stores pre-compiled binaries is a good example of this. In Buckminster terminology, such a binary is a derived component. It will contain a cspec similar to the one bundled with the original source pruned from the actions that produced the binary. The pruning will limit the component usage as well of course.
Since pruning removes actions and usages, some dependencies might end up purposeless. Such dependencies will be pruned too.
Buckminster allow artifacts to be variable. Variability is achieved through artifact attributes and by actions that can produce artifacts attributed in different ways. A trivial example of this is a compiler that can compile the same source, either stripped and optimized (distribution), or with symbols and not optimized (for debug purpose). Rather than having two artifacts and two actions (and two of every required artifact, etc.) attributes can be set in the outmost action, and these are propagated all the way.
A attribute can be set in the artifact declaration or stem from the action that produces the artifact. The initial artifact of a cspec is never variable since it does not stem from an action. The more derived an artifact is, the less variable it becomes. A pre-built binary is typically not variable at all.
If an artifact defines that an attribute stems from the producing action, then that action must somehow provide the attribute. It can do this either by declaring that it is the originator (such as a compiler using attributes to control command line options) or by propagating it to at least one of its required artifacts and/or dependencies. It may also add new attributes to its requirements and/or dependencies.
Names of actions and exports
Buckminster does not stipulate the names of artifacts and exports but here are some examples:
|java.sourcepath||no associated action, this is often an initial export.|
|java.compile||exports artifacts such as classes and external dependencies to other components with java.compile purpose.|
|unit.tests||exports artifacts needed when this component is participating in unit tests.|
|deployable.ear||exports a fully packaged ear file.|
|java.classpath||exports artifacts that contain information about where java binaries such as classes can be found. Also includes external dependencies to other components with java.classpath purpose.|
|eclipse.incremental.build||an internal action that maps to the execution of the eclipse incremental build.|
|build.javadocs||create java API documentation. This action will require its local source artifact and in many cases link to a java.source export of external components.|
|run.tests||the prerequisits for this action is that everything is compiled and deployed up to a point where tests can be run. The action will then start the tests.|
|deploy.ears||deploys ear files in a JEE container after all prerequisits have been fulfilled.|