This reference guide covers how to use the JBossBuild system.
JBossBuild is a declarative build system. Instead of having to define each step in the build process, JBossBuild allows a developer to declare the inputs and outputs of a build. JBossBuild then uses these definitions to dynamically generate the Ant targets needed to implement that definition.
JBossBuild is implemented as a set of Ant types and tasks, and target definitions. The types (components, componentdefs, artifacts, etc.) are declared by the developer in the build.xml. These definitions are then combined with the targetdefs in tasks.xml (under tools/etc/jbossbuild) to produce the generated ant targets.
There are two kinds of build definitions: toplevel, and component. The toplevel builds define the components of a release and where the artifacts of each component should be placed in the release. The component builds define how each artifact is built, the sources of those artifacts, and any dependencies of thoses sources.
A component build is made up of two parts: the component info (component-info.xml) and the component definition (build.xml or jbossbuild.xml). The component info is much like a declaration or manifest of the component. It defines what the expected outputs (artifacts) of the components are. The component definition specifies how these artifacts are built from source code.
Table 4.1. Component
Name: | component |
Purpose: | Declares a project component. |
Attributes: | |
id | The unique identifier for this component. This should be the same as its directory name in the online repository and in the local directory structure. |
module | The CVS module the component source should be checked out from. |
version | The version of the component. This version is used when retreiving artifacts from the repository. Artifacts are stored in the repository under the directory [id]/[version]. |
Table 4.4. Component
Name: | component |
Purpose: | Declares a project component. |
Attributes: | |
id | The unique identifier for this component. This should be the same as its directory name in the online repository and in the local directory structure. |
module | The CVS module the component source should be checked out from. |
version | The version of the component. This version is used when retreiving artifacts from the repository. Artifacts are stored in the repository under the directory [id]/[version]. |
You can now partially build jboss-head from the repository with the new build system.
You probably want this in it's own directory:
mkdir jboss-dir cd jboss-dir
Then, just check out the toplevel build and the tools module:
cvs co jbossas cvs co tools
You will need to set your cvs info in jbossas/local.properties:
cvs.prefix=:ext:rcampbell
Note, you will need ssh-agent setup to run cvs without entering a password for now. Now you are ready to synchronize and build:
ant sychronize ant build output/jboss-5.0.0alpha/bin/run.sh -c all
The synchronize target will checkout the source components from cvs and download thirdparty components from the repository.
In this section, we take a component - JBoss Deployment (jboss-head/deployment) and demonstrate how to incorporate it into the JBossAS release. This document assumes you have checked out the AS as outlined here.
First, we need to add the component to the toplevel build under jbossas/jbossbuild.xml. The ordering of the components is significant; the deployement module must be placed *after* the other source components it depends on (ie, common). The ordering of the components in the file dictates the order the components will be built. So, in this case, we add the component element at the end of the other JBoss components, but before the thirdparty components.
<!-- ============================================================ --> <!-- Deployment --> <!-- ============================================================ --> <component id="deployment" module="jboss-deployment" version="5.0-SNAPSHOT"> </component>
At this point, we know that the deployment module will come from the jboss-deployment module in cvs -- represented by the module attribute. We give it the same version as the other components in jboss-head. With this one definition, we have several new targets in our toplevel build:
bash-2.05b$ ant -projecthelp | grep deployment all.deployment Build All for the component deployment api.deployment Javadoc for the component deployment build.deployment Build for the component deployment clean.deployment Clean for the component deployment commit.deployment Commit for the component deployment doc.deployment Documentation for the component deployment rebuild.deployment Synchronize then build for the component deployment rebuildall.deployment Synchronize then build all for the component deployment runtest.deployment Run tests for the component deployment synchronize.after.deployment After synchronization processing for the component deployment synchronize.deployment Synchronize for the component deployment test.deployment Build and run the tests for the component deployment
These are all dynamically generated by jbossbuild based on the defintion we have provided. At the moment, we are only concerned with the synchronize target since we still don't have the source for this component. So let's see what the synchronize target will do before we try to call it
To see what a target will do before you call it, you can use the "show" target and pass it a property of which target you want to see.
bash-2.05b$ ant show -Dshow=synchronize.deployment Buildfile: build.xml show: <!-- Synchronize for the component deployment --> <target name="synchronize.deployment"> <mkdir dir="C:\projects\newbuild-jboss\thirdparty\deployment"/> <get verbose="true" dest="C:\projects\newbuild-jboss\thirdparty\deployment/component-info.xml" usetimestamp="true" src="http://cruisecontrol.jboss.com/repository/deployment/5.0-SNAPSHOT/component-info.xml"/> </target>
Whoops! Calling this target will download the component to thirdparty, which is not what we want at this point. In order to get the source for this component, we will want to set a property in the jbossas/synchronize.properties file:
checkout.deployment=true
Now, when we show the deployment.synchronize target we see that it intends to pull the source from cvs:
bash-2.05b$ ant show -Dshow=synchronize.deployment Buildfile: build.xml show: <!-- Synchronize for the component deployment --> <target name="synchronize.deployment"> <cvs dest="C:\projects\newbuild-jboss"> <commandline> <argument value="-d"/> <argument value=":ext:rcampbell@cvs.forge.jboss.com:/cvsroot/jboss"/> <argument value="co"/> <argument value="-d"/> <argument value="deployment"/> <argument value="jboss-deployment"/> </commandline> </cvs> </target>
Ok, so let's go ahead and call this target to checkout the module into our tree (../deployment).
bash-2.05b$ ant synchronize.deployment Buildfile: build.xml synchronize.deployment: [cvs] Using cvs passfile: c:\.cvspass [cvs] cvs checkout: Updating deployment [cvs] U deployment/.classpath [cvs] U deployment/.cvsignore ...
We could have also called the toplevel synchronize target if we wanted to update (or checkout) all the other components and thirdparty artifacts.
Ok, now that we have the source, we can get into creating a component-level build. The toplevel build in jbossas/jbossbuild.xml defines all the components, their versions, and the locations of their artifacts. However, the component-level build defines how those artifacts are composed of java classes and other resources.
Let's start out by just creating a minimal definition and see what happens. First, we want to create our component-info.xml under the deployment module. You can think of this file as the interface for this component. It will be uploaded to the repository along with the artifacts of this component so that other components may reference it.
For now, we can copy the entry from jbossas/jbossbuild.xml.
deployment/component-info.xml<project name="deployment-component-info"> <!-- ============================================================ --> <!-- Deployment --> <!-- ============================================================ --> <component id="deployment" module="jboss-deployment" version="5.0-SNAPSHOT"> </component> </project>
Once the component is declared, it needs to be defined. This is the responsibility of the jbossbuild.xml file:
deployment/jbossbuild.xml<?xml version="1.0"?> <!--[snip: license and header comments ]--> <project name="project" default="build" basedir="." > <import file="../tools/etc/jbossbuild/tasks.xml"/> <import file="component-info.xml"/> <componentdef component="deployment" description="JBoss Deployment"> <source id="main"/> </componentdef> <generate generate="deployment"/> </project>
At the top, we see the root project element, which is required for all Ant build files. More interestingly, we see that two files are imported. The tasks.xml is from jbossbuild. This file defines the custom Ant tasks (like componentinfo) and ultimately drives the dynamic creation of Ant targets based on our component definition. The other file is the component-info.xml file we created above.
The second thing we see is the source element. This says that we have a source directory named "main". jbossbuild requires that you put all of your source under the "src" directory, so this resolves to "deployment/src/main".
Finally, we see the generate element. This basically a clue to jbossbuild to tell it we are done defining our component and that it should generate the targets.
Let's see what we've got now:
bash-2.05b$ ant -f jbossbuild.xml -projecthelp Buildfile: jbossbuild.xml Main targets: all Build All api Javadoc build Build build.main Build for the source src/main clean Clean commit Commit doc Documentation rebuild Synchronize then build rebuildall Synchronize then build all runtest Run tests synchronize Synchronize synchronize.after After synchronization processing test Build and run the tests Default target: build
Again, we see that jbossbuild has automatically generated a basic set of targets for us. Additionally, we see that a specific target has been generated for our main source. As we add artifacts and sources to our component definition, jbossbuild will define specific targets for these as well. Let's take a look at how this target is implemented:
bash-2.05b$ ant -f jbossbuild.xml show -Dshow=build.main Buildfile: jbossbuild.xml show: <!-- Build for the source src/main --> <target name="build.main"> <mkdir dir="C:\projects\newbuild-jboss\deployment\output\classes\main"/> <depend destdir="C:\projects\newbuild-jboss\deployment\output\classes\main" srcdir="src/main"> <classpath> <pathelement location="C:\projects\newbuild-jboss\deployment\output\classes\main"/> </classpath> </depend> <javac destdir="C:\projects\newbuild-jboss\deployment\output\classes\main" deprecation="true" srcdir="src/main" debug="true" excludes="${javac.excludes}"> <classpath> <pathelement location="C:\projects\newbuild-jboss\deployment\output\classes\main"/> </classpath> <src path="src/main"/> </javac> </target>
Based on this one <source id="main"> element all of the above is generated by jbossbuild. However, if we were to call this target now, it would fail because of unresolved imports. To fix this, we need to define the buildpath for the main source. The easiest way to do this is to find the library.classpath and dependentmodule.classpath in the deployment/build.xml:
<!-- The combined library classpath --> <path id="library.classpath"> <path refid="dom4j.dom4j.classpath"/> </path> <!-- The combined dependant module classpath --> <path id="dependentmodule.classpath"> <path refid="jboss.common.classpath"/> <path refid="jboss.j2ee.classpath"/> <path refid="jboss.j2se.classpath"/> <path refid="jboss.system.classpath"/> </path>
Based on this we can determine the buildpath for the main source:
<source id="main"> <include component="dom4j-dom4j"/> <include component="common"/> <include component="j2ee"/> <include component="j2se"/> <include component="system"/> </source>
Generally, you should read this as "The main source tree includes these components as input." Concretely, the exported jars from these components are being included in the classpath of the call to javac:
$ ant -f jbossbuild.xml show -Dshow=build.main <javac destdir="C:\projects\newbuild-jboss\deployment\output\classes\main" deprecation="true" srcdir="src/main" debug="true" excludes="${javac.excludes}"> <classpath> <pathelement location="C:\projects\newbuild-jboss\j2ee\output\lib\jboss-saaj.jar"/> <pathelement location="C:\projects\newbuild-jboss\common\output\lib\namespace.jar"/> <pathelement location="C:\projects\newbuild-jboss\system\output\lib\jboss-system.jar"/> <pathelement location="C:\projects\newbuild-jboss\common\output\lib\jboss-common.jar"/> <pathelement location="C:\projects\newbuild-jboss\deployment\output\classes\main"/> <pathelement location="C:\projects\newbuild-jboss\j2se\output\lib\jboss-j2se.jar"/> <pathelement location="C:\projects\newbuild-jboss\thirdparty\dom4j-dom4j\lib\dom4j.jar"/> <pathelement location="C:\projects\newbuild-jboss\j2ee\output\lib\jboss-jaxrpc.jar"/> <pathelement location="C:\projects\newbuild-jboss\j2ee\output\lib\jboss-j2ee.jar"/> </classpath> <src path="src/main"/> </javac>
How are components resolved to jars? jbossbuild searches for the component-info.xml of the included component. First in the root of the project (..) and second in the thirdparty directory (../thirdparty). The component-info.xml includes an export element which specifies which artifacts should be resolved when the component is included by another component. It's probably not a bad analogy to think of this mechanism as replacing buildmagic's modules.ent and libraries.ent
Now we should compile the source to make sure we got it right. We'll just use the build target because we are lazy and don't want to type build.main (rats!).
bash-2.05b$ ant -f jbossbuild.xml build Buildfile: jbossbuild.xml build.etc: [mkdir] Created dir: C:\projects\newbuild-jboss\deployment\output\etc [copy] Copying 1 file to C:\projects\newbuild-jboss\deployment\output\etc build.main: [mkdir] Created dir: C:\projects\newbuild-jboss\deployment\output\classes\main [javac] Compiling 16 source files to C:\projects\newbuild-jboss\deployment\output\classes\main build: BUILD SUCCESSFUL Total time: 7 seconds
Great! Notice that the output for the source (id=main) is being placed in output/classes/main. Now we are ready to add an artifact definition. Looking at the deployment/build.xml, we see there is one artifact named jboss-deployment.jar. First, let's declare the artifact in our component-info.xml:
<component id="deployment" module="jboss-deployment" version="5.0-SNAPSHOT"> <artifact id="jboss-deployment.jar"/> <export> <include input="jboss-deployment.jar"/> </export> </component>
Notice also that we export this jar. When other components import this one, this is the jar they will want on their classpath.
Now, we need to create an artifactdef for this new artifact. The artifacdef defines how the artifact is composed of other inputs:
... </source> <artifactdef artifact="jboss-deployment.jar"> <include input="main"> <include pattern="org/jboss/deployment/**"/> </include> </artifactdef> </componentdef>
This results in the following target being generated:
bash-2.05b$ ant -f jbossbuild.xml show -Dshow=build.jboss-deployment.jar Buildfile: jbossbuild.xml show: <!-- Build for the artifact jboss-deployment.jar --> <target name="build.jboss-deployment.jar"> <mkdir dir="C:\projects\newbuild-jboss\deployment\output\lib"/> <jar destfile="C:\projects\newbuild-jboss\deployment\output\lib\jboss-deployment.jar"> <fileset dir="C:\projects\newbuild-jboss\deployment\output\classes\main"> <include name="org/jboss/deployment/**"/> </fileset> </jar> </target>
Notice that the <includes input="main"/> is resolved to output/classes/main.
Now that we have completed the artifact, we need to define where it should be placed in the overall release structure. This information, as you will recall, is stored in the toplevel build (jbossas/jbossbuild.xml). We define the location in the release using the release tag:
jbossas/jbossbuild.xml:<component id="deployment" module="jboss-deployment" version="5.0-SNAPSHOT"> <artifact id="jboss-deployment.jar" release="client"/> </component>
This will place the artifact in the client directory of the release:
bash-2.05b$ ant show -Dshow=release.jboss-deployment.jar Buildfile: build.xml show: <target name="release.jboss-deployment.jar"> <mkdir dir="C:\projects\newbuild-jboss\jbossas\output\jbossas-5.0.0alpha\client"/> <copy todir="C:\projects\newbuild-jboss\jbossas\output\jbossas-5.0.0alpha\client"> <fileset file="C:\projects\newbuild-jboss\deployment\output\lib\jboss-deployment.jar"/> </copy> </target>
Now, you should be able perform a build of the application server:
$ ant build ...
Congratulations, you've successfully added a new component to jboss AS.
This section describes the steps necessary to add a component to the build repository, currently at http://cruisecontrol.jboss.com/repository
First, you will want to checkout the repository locally.
cvs -d:ext:user@cvs.forge.jboss.com/cvsroot/jboss co repository.jboss.com
You need to decide on a component name. It is best to use something like organization-component so others can quickly tell what the name refers to. The exception is jboss components which are not prefixed with "jboss".
Underneath the directory named after the component is the version number, which contains the component-info.xml. The lib directory below this will hold the jars.
repository.jboss.com + apache-log4j + 1.2.8 + component-info.xml + lib + log4j.jar
In addition to adding the jars, you also need to create a component-info.xml. This file allows other components to reference your jars. We want to make sure that the component-info.xml reflects the version we indicated in the directory structure above.
<project name="apache-log4j-component-info"> <!-- ============================================================ --> <!-- Apache Log4j --> <!-- ============================================================ --> <component id="apache-log4j" licenseType="apache-2.0" version="1.2.8" projectHome="http://logging.apache.org/"> <artifact id="log4j.jar"/> <artifact id="snmpTrapAppender.jar"/> <export> <include input="log4j.jar"/> </export> </component> </project>
You can commit the new version to the repository using cvs commands. There is (will be) a scheduled process which updates the online repository from cvs every 5 minutes. If this fails, please contact qa@jboss.com
Once the component is available in the online build repository, you may configure toplevel (e.g., jbossas/jbossbuild.xml) build to include it:
<component id="apache-log4j" version="1.2.8" > <artifact id="log4j.jar"/> <artifact id="snmpTrapAppender.jar"/> </component>