CC's logo



CC's Build System

Introduction
Basic individual project build and deployment
More advanced project build and deployment
Extracting commonality
Repository building
Automatic Repository building
Adding a Project to the Process
CC's specific Maven goals
Auto-versioning deployment system
Automatic deployment interface
Adding unit testing


Other Information

Maven Definitions
Jelly Scripting Hints



CC's Specific Maven Goals

The following describes Maven goals created specifically for CC.

ccdist
ccmanifest
ccplugins
ccscm
ccjnlp
main parent maven.xml

Get the source code.


See Jelly Scripting Hints for coding help.

Note: Default goals are goals that are iinvoked by just using the plugin name and not referencing a specific goal.


ccdist:

The default goal for this plugin is to create a deployable zipped application. The output zip file will be named "${pom.name}.zip" by default and will be created in the 'target' directory. Other goals that are available from this are steps of the default goal for the plugin:

ccdist:zip-dist
Runs 'ccdist:create-dist' then zips up the distribution.

ccdist:create-dist
Runs 'ccdist:copy-jars' and 'ccdist:make-run-script', creating the distribution without zipping it up in the 'target/dist' directory.

ccdist:copy-jars
Runs 'jar:jar' and puts the file in 'target/dist/jar'.
 
ccdist:make-run-script
This goal will make OS-specific batch/script files to make the application runnable after it is unzipped on the client machine. The files created are 'target/dist/run.bat' and 'target/dist/run.sh'.

ccdist:signjar
This goal is a modified version of the jnlp plugin jar signer, but it uses the same properties:

maven.jnlp.signjar.store
maven.jnlp.signjar.alias
maven.jnlp.signjar.storetype
maven.jnlp.signjar.storepass

The jar file that is signed is the default output file: ${maven.build.dir}/${maven.final.name}.jar and the ${maven.jnlp.signjar.store} property must be defined for the goal to execute.


With this plugin, if a user needs to add any files to the distribution, they would need to create a post goal for ccdist:create-dist in their project's maven.xml file:

<!-- Make this part a post goal for create-dist. -->
<!-- User will have to do this part -->
<postGoal name="ccdist:create-dist">
      <ant:copy todir="target/dist" file="teemss-project.xml"/>
      <ant:mkdir dir="target/dist/nativelib"/>
      <ant:copy todir="target/dist/nativelib">
          <ant:fileset file="jug-native/libLinux_x86_EtherAddr.so"/>
          <ant:fileset file="jug-native/Win_x86_EtherAddr.dll"/>
          <ant:fileset file="../SensorNative/GoIO_DLL.dll"/>
          <ant:fileset file="../SensorNative/bin/*.dll"/>
      </ant:copy>
</postGoal>




ccmanifest:

This plugin only has one goal, called as the default or with 'ccmanifest:dependency'.  By default, this goal is called for CC projects as a pregoal for 'jar:jar' if using the main CC project.xml file and is used to create a manifest with lib dependencies as an entry that is similiar to something like the following:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.5.3
Created-By: 1.4.2_03-b02 (Sun Microsystems Inc.)
Class-Path:  Framework/lib/framework-SNAPSHOT.jar Data/lib/data-SNAPSH
 OT.jar Graph/lib/graph-1.0.0.jar Swing/lib/swing-SNAPSHOT.jar

All of the information required to create the Class-Path entry is taken from the project's project.xml file.


Requirements

Note: This requirement has changed since we have moved to the Maven repository structure of 'groupId/jars/artifactId-version.jars'. cc.path is still used for multiproject goals and as a flag for this plugin, so it should still be defined. The manifest classpath is now hardcoded to each ${pom.artifacts} ${externJar.dependency.artifactDirectory}/jars/${externJar.name} where externJar represents one pom.artifact.

Each dependency of a project must set the cc.path property for the dependency to appear in the project's project.xml file. The setting is the relative path to the main class of an application. Since CC's file system has a parallel structure, we can define this and be relatively sure that all projects can use the same path.

Example:

<dependency>
    <groupId>data</groupId>
    <artifactId>data</artifactId>
    <version>SNAPSHOT</version>
    <type>jar</type>
    <properties>
        <cc.path>Data/lib</cc.path>
    </properties>
</dependency>


Another requirement is to have the maven.jar.manifest property to be set It is set to a default value when using the jar plugins, so a jar goal can be called that will automatically set it to the default value of 'conf/Manifest.mf'. Obviously, to have the ccManifest specific manifest included
in the jar manifest, ccManifest would need to be called first. This requirement is satisfied normally for CC projects since the main 'Projects/Build/maven.xml' file has a pregoal for 'jar:jar' that calls 'ccmanifest'.



ccplugins:

This plugin only has one goal, called as the default or with 'ccplugins:delete'. The goal of this plugin is to delete all plugins that are listed as a dependency of a project so the next time any Maven goals are run on the project, Maven will download and install them again. Currently, all CC plugins are defined as dependencies in the 'Projects/Builds/project.xml' file.

The idea behind this plugin was to make the plugin installation transparent for an end user. There were two reasons for this:
  1. Normally, to install a plugin, a user would need to download the source code and run 'plugin:install' and 'plugin:uninstall' to uninstall the plugin. In both cases, the source code is needed.
  2. If a plugin is updated without reversioning, a user will not know of the update and will retain the old version on their system unless told to reinstall it. Likewise, if the update is versioned, the user must be informed of the version change and update the version information in their project.xml file.
There may be misunderstanding on my part about plugin management, but I didn't find any information that would lead me to believe anything different from the two problems listed above. Even a discussion with the Maven user group didn't change my mind. The assumption at the time of this plugin's creation was that non-developer users might need to download a set of files and run Maven commands on them as a type of installation, so we needed as little user-intervention as possible.

With this plugin:
  1. New users will automatically get the latest version of the dependency plugins
  2. Old users can periodically run ccplugins to refresh their dependency plugins
  3. Project maintainers do not have to version changes for minor fixes to a dependency plugin
  4. Developers and non-developers don't need to learn about plugins or download and build the code
An entry for a dependency plugin looks like this in a project.xml file:

<dependency>
    <groupId>concord</groupId>
    <artifactId>maven-ccPlugin-plugin</artifactId>
    <version>1.0.0</version>
    <type>plugin</type>
</dependency>



ccscm:

This is a really big plugin that has been evolving as the requirements have been changing. Basically, most goals are CC specific operations on the source code management system that the basic Maven scm plugin doesn't provide. At this writing, everything is CVS specific, but there probably will need to be Subversion specific goals as well.

Not all goals are listed here, since a good many of them are internal goals to the plugin and would require more work or property settings than it would be worth to use them.

ccscm:get-classpaths

This goal is used to determine the dependencies of a built jar file. The idea is to unjar the manifest or classes/classes.properties files to create a list of dependents. The classpath in the manifest is created by the ccmanifest:dependency goal. This is necessary for old unversioned jar files or activity jar files that do not have a way of recreation. This is mostly for 'bridging the gap' until all projects and activity jar files are in CVS and  rebuildable.

Three properties are required, but they all have defaults:

ccScm.jar.file=${basedir}/lib/${maven.final.name}.jar
ccScm.search.path=${basedir}/../.
ccScm.dependency.file=${basedir}/depend_catalog.xml


ccscm:writeprojectfile


This goal is for writing out the project.xml with different parameters for dependency versions and the project version. The versions can be obtained from property settings or from a version mapping file. There are three sets of properties to determine the method of setting versions that can be specified together, but have the following descending order of priority:

1. Use dependency version list properties
2. Use the version mapping file
3. Use the project version property

An example:

maven -DccScm.project.version=5.5.5.5 -DccScm.dep.list=framework,domain -DccScm.dep.ver.framework=1.0-beta -DccScm.dep.ver.domain=3.2 ccscm:writeprojectfile

In the above example, there is a combination of properties defined. The list form defines a version for the 'framework' and 'domain' projects, but for the project itself and any other undefined dependency versions, the version is defined to be '5.5.5.5'. If we had also used the mapping file properties, framework and domain would have the versions of '1.0-beta' and '3.2' respectively, then any dependencies defined in the mapping file would get their versions from it, and lastly, anything not defined would get the version '5.5.5.5'.

To set any of these parameters, use the following properties:

ccScm.dep.list - sets the comma-separated list of dependencies that you will provide the ccScm.dep.ver.<dependency> property for.
ccScm.dep.ver.<dependency> - the version property for the dependency in the ccScm.dep.list property.

ccScm.use.map - indicates the versions for the dependencies should be obtained from the ccScm.map.file property.
ccScm.map.file - an xml file with version mapping information. All dependency versions and the main project version will be obtained from the last build version entry for that project. By default, CC's projects have this property to be set to 'Projects/Builds/version_map.xml'.

ccScm.project.version
- sets project's currentVersion property and any dependency versions not determined by the map file or from the dependency list versions.

ccscm:catalog

This goal is used to create a self-referencing catalog of all files in a project and their CVS versions. It also will maintain md5 hash and modified timestamps of the files. Finally, it will contain a tag name for the project to allow file retrieval by tagname of version.

This goal is a wrapper around the ccscm:createfilecatalog goal, creating a file of the cvs status of the files that is passed and parsed by the ccscm:createfilecatalog goal. The catalog created by this goal can be used by the ccscm:checkout goal to retrieve the project in the state it was in when the catalog was created.

ccscm:version-project

This goal is used to version a project by completing all the steps necessary for the build to be repeatable and retrievable from CVS. It wraps the ccscm:writeprojectfile and ccscm:catalog goals along with the updating of the file catalog and project.xml file in CVS.

ccscm:createmap

This goal is used to determine the dependencies of a project and create an xml mapping file. The mapping for a project will include:
              Name
              Version
              ArtifactId
              CatalogVersion
              Dependencies Names(artifactId)
              Dependencies Versions
              Most Current Project Version
Since the key to a project is it's name and version, this scheme makes it possible to recursively determine a dependent's information.
Note: Existing maps for a project will be replaced if the new one matches the name and version.

ccScm.catalog.file - An xml file listing the location and name of the project files. Defaults to ${basedir}/file_catalog.xml.
ccScm.catalog.version - If this property is set, the catalog version will not be determined from cvs and therefore will not be retrievable later. This is only useful for offline testing.

ccscm:checkout

This goal is used to retrieve a project from a previously created file catalog from the ccscm:catalog goal, given the catalog's version or tag name.



ccjnlp:

This plugin is used for deploying a date-versioned jnlp distribution to the ${pom.siteDirectory}/jnlp on the remote server. The reasoning behind this is so there can be multiple jnlp distributions that are known to work at a particular time. Currently at CC, developers create a jnlp distribution that has no version information and are referenced from a web page that may be written by non-developers. These jnlp distributions may also have activities that are createded by non-developers, but are packaged as jar files. If the developer or an activity writer has a change, they want to deploy it without having to change web pages or other places that the jnlp file is referenced from as well as change the jnlp file, so they don't want to version the jar files. To maintain versions, this plugin can create timestamped jars that will not be overwritten on the server by a full or partial jnlp deployment. If necessary, a previously known-good deployment can be referenced by the timestamped deployment, or recreated from source. Now that the automatic deployment system is in place, the versioning for a jnlp build is redundant since we can reverse to a known good jnlp from the version mapping that the system does.

Currently, this only works with ssh and scp defined by the ${maven.ssh.executable} and ${maven.scp.executable} variables. I have sent a patch to the jnlp plugin developers to add deployment to the jnlp plugin, but until they do, I need this customized version. The jnlp plugin itself handles creating the jnlp file and the ${maven.jnlp.dir} with the required jars when they have a dependency property set like:

<jnlp.jar>true</jnlp.jar>

in the project.xml file. There are three goals in this plugin; the default goal, ccjnlp, ccjnlp:deploy and ccjnlp:create. The default goal is the same as the create goal. The create goal is called from within the deploy goal. What it does is:

  1. Runs jnlp:generate-jnlp to do the default jnlp creation stuff
  2. Runs ccscm:catalog to create a file catalog of the current cvs versions of the projects files
  3. Adds a date stamp to the file name of all the required jars
  4. Copy the file catalog to the jnlp directory with the date stamp in the file name
  5. Copy the jnlp file to a file with a file with the date stamp in the file name
  6. Rewrite the jnlp files to reference the jars with date stamps in their file names
The deploy goal runs the above, then zips up the files and unzips them on the server.

Multiple jnlp files from a single source tree

Another useful thing about the ccJnlp plugin is the ability to create multiple jnlp files from one project. This was implemented for different arguments passed to the main jar file, but could be extended for other parameters that would make the jnlp files unique. Borrowing a technique from the artifact plugin, a list property can be set with a comma-seperated list of jnlp file names and the arguments that are unique to the main jar file:

ccJnlp.jnlp.list=linear_regression,polynomial_regression,exponential_regression
ccJnlp.jnlp.argument.linear_regression=0
ccJnlp.jnlp.argument.polynomial_regression=1
ccJnlp.jnlp.argument.exponential_regression=2

In this example, we have three jnlp files that will be identical other than the argument parameter to the main jar file and the file name. Below is an example of how the argument parameter appears in the jnlp file:

    <application-desc main-class="org.concord.examples.datagraphaddon.RegressionDataGraph">
        <argument>2</argument>
    </application-desc>
 
Jardiff jnlp creation

The ccjnlp:create goal is undergoing changes as developers decide on what format the jnlp file should be in for their deployments and to use technologies such as Sun's Java download servlet. To use versioning with the download servlet, the versioning is removed from the name of the jar files, but is added as a 'version' attribute to the jar resources jnlp element. Also, some variables in the jnlp file are overwritten by the servlet, namely $$name, $$codebase, and $$context. So, the end product is like this example(note the version attribute in the jar elements):

<?xml version="1.0" encoding="UTF-8"?>
<jnlp href="$$name" spec="1.0+" codebase="$$codebase">
    <information>
        <title>Bar Graph Example</title>
        <vendor>The Concord Consortium</vendor>
        <homepage href="index.html" />
        <description />
        <description kind="short" />
        <offline-allowed />
    </information>
    <security />
    <resources>
        <j2se version="1.4+" max-heap-size="128m" initial-heap-size="32m" />
        <jar version="1.0" href="graph.jar" />
        <jar version="1.0" href="swing.jar" />
        <jar main="true" version="1.0" href="bargraph.jar" />
    </resources>
    <application-desc main-class="org.concord.examples.bargraph.BarGraphPanel" />
</jnlp>


The regular jnlp plugin has some properties that help to create a jnlp file with the download servlet:

maven.jnlp.http.codebase can be set to \$\$codebase to have it appear correctly in the jnlp file.
maven.jnlp.jardiff can be set to 'true' to have the version attribute set for jars that have a version other than SNAPSHOT.

So, ccJnlp can use those variables and the following properties as well:

ccJnlp.href.name can be set to \$\$name

There is a naming convention for the jardiff to work. Instead of a jar file named ${artifactId}-${version}.jar, it needs to be named ${artifactId}__V${version}.jar. Strangely, the jnlp plugin doesn't handle the naming unless the jars are being signed as well, but it does change the internal versioning refererences described above.




Parent maven.xml:

This file contains standard pre/post goals used by all CC's Maven projects as well as some goals for managing multiproject builds.

The pre goal for jar runs the default ccmanifest goal.
The post goal for jar runs ccdist:signjar, creates the 'lib' directory, and copies the output jar files to it.
The post goal for clean deletes the 'conf' and 'lib' directories that are created by ccManifest and CC's post goal for jar.


Using multiproject setups:

There is a multiproject plugin for Maven, but the goals in the parent maven.xml file is used for CC specific multiproject setups. Developing this set of multiproject goals facilitates the ability to create automated multiproject builds without knowing any more than the project name of the top level project. It also removes the need for developers to limit their source tree or set up multiproject includes manually, since most developers checkout the entire Projects source tree and if they were to run multiproject goals, all subprojects would be built.

The multiproject goals are used for major projects that have dependencies and the same goals need to be run on all of the dependencies as well as the major project, such as building all the source code, deploying the documentation, etc. The multiproject set goals uses the directory structure that we currently have set up where all projects are parallel to one another, the 'Builds' directory is parallel as well, and major project multiproject goals are run from one directory below the 'Builds' directory (see Introduction):

     Projects
         |..Builds
         |    |..Pedagogica (multiproject build dir)
         |    |      .
         |    |      .
         |    |      .
         |    |..Portfolio (multiproject build dir)
         |
         |..Activity
         |     .
         |     .
         |     .
         |..Pedagogica
         |..Portfolio

If there is not a directory for the project to run multiproject goals against, all that is required is to create a Builds/${project} directory and copy the project.xml file for the major project to that directory. This can be checked in to CVS if it is to be repeated. Then run all multiproject goals in the Builds/${project} dir.

To create the structure for multiproject goals with the source of the major project and it's dependencies:
     Use the parseproject goal. This creates the project.properties file for the major project.
     To prevent downloading of dependent projects, set the property "download=false" (-Ddownload=false).

To check out only the major project:
     Use the projectco goal.

To run multiproject goals:
     Use the maven multiproject goals after running parseproject goal to create the project.properties file for the main project.

The following goals are for multiproject setups:

parseproject

This goal is used to read the project file in the 'Projects/Builds' directory, checkout the source code to 'Projects/${project}' if it isn't checked out already (by calling the projectco goal) and create a project properties file for the project with definitions for maven.multiproject.basedir and maven.ccmultiproject.includes properties (by calling the parsexml goal). Note that the maven.ccmultiprojects.includes property is meant to override the maven.multiproject.includes property used with the Maven multiproject plugin. It will include the major project in the includes list (whereas maven.multiproject.includes can't).

parsexml

This is a recursive goal used to parse the main project.xml file and any project.xml files that it includes to determine all dependencies of the main project and it's dependencies, checking out the dependency projects if the download property is not set to 'false'. This allows a project and all of it's dependencies to be built from the bottom up including the dependencies own dependencies if they are just required for building and not runtime.

projectco

This goal is used from within the 'Projects/Builds/${project}' directory to checkout projects from CVS using the ccScm.cvs.module property to define the project and maven.scm.cvs.root or the CVSROOT environment variable to define the cvs repository. The scm:checkout-project goal is then used to checkout the project in it's proper location in the 'Projects' tree. The maven.scm.checkout.dir property can be used to override the final destination if really necessary and any other scm properties can be set if needed.

init-multiproject-path

This goal is called as a pre goal to multiproject:projects-init, which isn't a published goal but happens at the initialization of any multiproject goal. It can be prevented from running by setting the maven.ccmultiproject.run property to any non-empty value in case a user just wanted to run the standard multiproject goal. This goal uses the maven.ccmultiproject.includes property defined by the parseproject goal and creates a classpath for any included dependency based upon the ccPath property set in the included project.xml file.



Getting the source code


Source code may also be accessed though ViewCVS for the plugins and ViewCVS for the parent project files, anonymous CVS access, or through SSH access for approved developers.

      
   
Anonymous CVS Access
To checkout the files in the CVS repository, you must have cvs software installed and in your path. Use the following comand to login to CVS:

   cvs -d:pserver:anonymous@source.concord.org:/cvs/repo/CodeBank login

Just hit enter when a password prompt appears. Now enter the following command to checkout the source files:

   cvs -z3 -d:pserver:anonymous@source.concord.org:/cvs/repo/CodeBank co -P Projects/MavenPlugins
and
   cvs -z3 -d:pserver:anonymous@source.concord.org:/cvs/repo/CodeBank co -P Projects/Builds

Developer SSH Access
To obtain CVS access rights via SSH, please send a message through our web contact page. To use SSH access, you must have a SSH client installed on your machine as well as CVS. You must set the CVS_RSH environment variable to 'ssh', then use the following command to access the CVS source code:

   cvs -z3 -d:ext:USERNAME@source.concord.org:/cvs/repo/CodeBank co -P Projects/MavenPlugins
and
   cvs -z3 -d:ext:USERNAME@source.concord.org:/cvs/repo/CodeBank co -P Projects/Builds

Replace USERNAME with the appropriate value.





All Contents Copyright © 2005, The Concord Consortium.
All Rights Reserved. Privacy Policy

Last modified: Friday, 17-Jun-2005 17:03:20 EDT