Introduction

In this article we're going to try to give you a concise but complete view of how Krypton works and how it could fit into your development environment.

Krypton is a build tool that is driven from a command-line interface. IDE bindings and other related tools are also planned. Krypton is tested under Windows and Linux and should run just about anywhere that a Java 1.4 compatible JDK exists.

Separating the What from the How

Krypton's approach is to separate the artifacts that are built, from the production processes that build them. The what from the how. This separation turns the build definition into a purely declarative description of the artifacts, while the work of actually producing those artifacts is encapsulated in plugins that are called Producers. So a Krypton build contains absolutely no build logic, just descriptions of artifacts.

A Krypton build is configured by writing a krypton.xml file, which is typically placed in the root of your projekt. This descriptor file contains defintions of:

  • All the artifacts that can be built from your projekt
  • Meta data bundles (containing names, version numbers, environmental settings etc) that should be retrieved and made available to the build
  • Operations that can be performed against completed artifacts ("upload", "publishtomaven", etc)

Example Build Definition

Let's take a look at a small example projekt built by Krypton. Note that Krypton doesn't mandate a particular directory layout, although template projekt layouts are provided. The directory layout for our example projekt looks like this:

All our sources are placed under the "source" directory. We have two sources: coreimportclasses which contains a couple of properties files, and corejava which contains the Java source code for our program. Main.java uses classes from the Log4J and NekoHtml libraries, so when we build our projekt we'll need to link in the JAR's for those libraries.

So, what we want our build to do is:

  • Retrieve log4j and nekohtml JAR's from a Maven repository
  • Compile our Java code into classes
  • Build a JAR file containing our compiled classes, as well as the files in our coreimportclasses source.

Here's the krypton.xml file for our projekt. It defines meta data bundles, artifacts and operations. For clarity, we've simplified this example to exclude default values, but we'll talk about defaults later.

<krypton name="My Projekt">

  <metadatabundles>

    <metadatabundle type="Properties">
      <setting name="filename"
        value="../../environment.properties" />
    <metadatabundle>

    <metadatabundle type="Properties">
      <setting name="filename"
        value="build.properties" />
    <metadatabundle>

  </metadatabundles>

  <artifacts>

    <artifact name="log4j_jar"
      type="SimpleMaven1RepoArtifact">
      <setting name="artifactId" value="log4j" />
      <setting name="version" value="1.2.8" />
      <setting name="type" value="jar" />
    </artifact>

    <artifact name="nekohtml_jar"
      type="SimpleMaven1RepoArtifact">
      <setting name="artifactId" value="nekohtml" />
      <setting name="version" value="0.9.4" />
      <setting name="type" value="jar" />
    </artifact>

    <artifact name="coreclasstree" type="Classtree">

      <dependency name="java">
        <item type="source" name="corejava" />
      </dependency>

      <dependency name="compilejars">
        <item type="artifact" name="log4j_jar" />
        <item type="artifact" name="nekohtml_jar" />
      </dependency>

    </artifact>

    <artifact name="corejar" type="Jar">

      <setting name="jarFilename"
        value="${component.version.name}.jar" />

      <dependency name="classtree">
        <item type="source" name="coreimportclasses" />
        <item type="artifact" name="coreclasstree" />
      </dependency>

      <operation name="publish" type="PublishToMaven">
        <setting name="repository"
          value="${environment.repository.local}" />
        <setting name="artifactId"
          value="${component.name}" />
        <setting name="version"
          value="${component.version}" />
        <setting name="type" value="jar" />
      </operation>

    </artifact>

  </artifacts>

</krypton>

Artifacts and Producers

This krypton.xml file defines four artifacts:

log4j_jar and nekohtml_jar

These are both "built" by the SimpleMaven1RepoArtifact producer. In fact this producer doesn't create the artifacts but retrieves them from a local or remote Maven 1 repository, and makes them available to the build.

Most producers have settings, which enable you to supply details about the artifact being produced. In the case of the SimpleMaven1RepoArtifact producer it accepts settings that describe the JAR to be retrieved.

coreclasstree

This artifact is built by the Classtree producer which takes some Java source code and some JAR files, and compiles the supplied source code against the supplied JAR files. The Classtree producer doesn't have any settings, but it does have dependencies. These are like inputs to the producer through which raw materials are provided for use during production. You can see that in Krypton, dependencies have a particular role. Classtree has the "java" and "compilejars" dependencies.

Note that when specifying items to satisfy a dependency, you can choose to link in sources, artifacts or a mixture of both. This is an important part of Krypton's model – sources are treated as dependencies, just like intermediate artifacts are.

Note also that many items can be supplied for one dependency. In our example, we have wired the two JARs previously retrieved from a Maven repository into the "compilejars" dependency.

corejar

The final artifact in our example is the corejar artifact which is produced by the Jar producer. This producer takes a setting which is used to specify the filename of the JAR that will be produced. It also takes a classtree dependency through which the "classes" that should be JAR'd up are provided. In this case we have wired in our coreimportclasses source, as well as the coreclasstree artifact that we produced earlier.

Running the Build

To build an artifact, we use the kr command-line. We can ask Krypton to build any artifact in our projekt, and it will build all of the intermediate artifacts leading up to the one we asked for, and finally will build our requested artifact. You can also specify multiple artifacts in one command.

For example, any of the following commands would be valid for our projekt:

> kr log4j_jar
> kr log4j_jar nekohtml_jar
> kr coreclasstree
> kr corejar

Krypton creates a "target" output directory with subdirectories for each artifact. The completed artifacts are placed in their own subdirectories.

Note: The current build of Krypton places artifacts in target/artifacts. The final release of Krypton 2 will leave artifacts in the target directory as shown in this diagram.

Operations and Executors

You might have noticed that our corejar artifact also has an operation defined against it called "publish".


    <artifact name="corejar" type="Jar">

      ...

      <operation name="publish" type="PublishToMaven">
        <setting name="repository"
          value="${environment.repository.local}" />
        <setting name="artifactId"
          value="${component.name}" />
        <setting name="version"
          value="${component.version}" />
        <setting name="type" value="jar" />
      </operation>

    </artifact>

This operation uses the PublishToMaven Executor. As with artifacts, operations may have settings that you can use to configure them. Operations can be executed through the command-line interface by appending the operation name to the artifact name, separated by a colon character. So to execute our publish operation, we would issue the command:

> kr corejar:publish.

Krypton will make sure the artifact is built and up to date, and will then execute the publish operation against it. If the artifact production fails, the operation won't be executed. Many operations can be executed against a single artifact in one command. For example:

> kr corejar:publishtomaven corejar:uploadtodistrosite

Krypton will only build corejar once, and will then execute all of the requested operations against that built artifact.

Metadata Bundles and Collectors

Going back to krypton.xml, you'll see that the first things defined in our projekt are Metadata Bundles. These are collections of name-value pairs that can be retrieved from external sources, and then applied to artifacts. There are different types of Collectors that can pull in meta data bundles. In our project we have used the Properties collector to retrieve values from two properties files.

In the corejar artifact, you can see we have applied meta data values in a couple of places:

  • The name of the JAR file produced is based on the ${component.version.name} value, with the ".jar" suffix appended.
        <setting name="jarFilename"
            value="${component.version.name}.jar" />
    
  • The local Maven repository and other settings used by the "publish" operation are set with meta data values.
        <setting name="repository"
            value="${environment.repository.local}" />
    

Using meta data collectors, you can pull in values from descriptor files, or even other sources such as databases. You can also externalize and centralize values that are used many times in your build or are common to multiple projekts into properties files or other formats.

Default Values

We glossed over the use of default values before to make our initial look at a krypton.xml file clearer. Where you need to use the same setting values on many entities, default values allow you to set values in one place that will be applied across many artifacts, operations or metadata bundles.

Krypton supports Java and Ant as implementation languages for plugins. Almost all of the Krypton Standard Library is implemented as Ant plugins. Ant plugins are all implemented on a common Java class which has two settings which we didn't mention before: antHome and javaHome. The unabbreviated version of our coreclasstree artifact defintion would look like this:

<artifact name="coreclasstree" type="Classtree">

  <setting name="javaHome"
    value="${environment.java.home}" />
  <setting name="antHome"
    value="${environment.ant.home}" />

  <dependency name="java">
    <item type="source" name="corejava" />
  </dependency>

  <dependency name="compilejars">
    <item type="artifact" name="log4j_jar" />
    <item type="artifact" name="nekohtml_jar" />
  </dependency>

</artifact>

As all our artifacts are built by Ant-based producers, these settings need to be provided every time. We could plugin values from a metadata bundle, but we'd still have to repeatedly set those settings for every artifact and also for our "publish" operation as it too is based on an Ant plugin.

The default values feature enables you to set values once which will then be automatically applied to many entites. Default values can be specified per producer, executor or collector, so you could for example tell all Classtree artifacts to use the same setting values. Default values can also be specified per plugin class. Krypton will then apply those default settings to all entities based on that plugin class.

The full set of defaults we would have used in our example projekt looks like this. Note that we are using metadata values for the javaHome and antHome settings.

  <defaults>
    <set type="class" name="AntCollector">
      <setting name="javaHome"
        value="${environment.java.home}" />
      <setting name="antHome"
        value="${environment.ant.home}" />
    </set>
    <set type="class" name="AntProducer">
      <setting name="javaHome"
        value="${environment.java.home}" />
      <setting name="antHome"
        value="${environment.ant.home}" />
    </set>
    <set type="class"
      name="com.x1seven.krypton.plugin.AntExecutor">
      <setting name="javaHome"
        value="${environment.java.home}" />
      <setting name="antHome"
        value="${environment.ant.home}" />
    </set>
    <set type="producer" name="SimpleMaven1RepoArtifact">
      <setting name="remoteRepository"
        value="http://www.ibiblio.com/maven/" />
      <setting name="localRepository"
        value="E:/wrk/x17_lib_mavenrepository" />
    </set>
  </defaults>

Relationship to Ant, NAnt and Maven

Krypton can be used on new projekts, in parallel with Ant, Nant or Maven, or as a replacement for those tools. Krypton imposes no restrictions on the directory layout of your projekt, so it can be adapted to fit any existing structure.

Krypton is able to retrieve metadata from external sources, including properties files or XML files you may already be using to drive your Ant / NAnt build, or Maven POM files. This allows Krypton to be used to complement your existing build processes with additional build functionality.

Krypton also has a special relationship to Ant in that all of the plugins in the Krypton Standard Library are implemented as miniature Ant scripts. Krypton plugins can be written in Java, or Ant, or other languages by writing an adapter. We believe that Ant is an excellent tool and scripting language that effectively unifies almost every build-related tool you could want to use. By supporting Ant as a plugin scripting language, Krypton enables you to use your existing Ant knowledge and the plethora of Ant tasks available to write Krypton plugins.

Further Reading

Contents

Other Pages