example

This example uses the development version of the Xilize plugin, Xilize 3.0, which as of 7 July 2006 is not yet released. v2.0 is on the web. v0.9 is available through jEdit Plugin Central (i.e. use Plugin Manager in the editor).

On this page:

introduction

Xilize is an interesting example because it depends on another plugin (ErrorList) but otherwise is free of dependencies. The ErrorList plugin dependency is handled overriding <path id="project.class.path"> in the build file. The Xilize build.xml file also contains one task override <target name="build.post"> which handles the special requirements described in the next section.

On the other hand, the Xilize plugin for jEdit is part of a larger project which includes other components — a translation engine and plugins for NetBeans and OpenOffice. So its development directory structure is more complex than a typical plugin.

One reason the Xilize build file is so simple (hopefully a PC packager's dream) is I rarely use it directly. Instead, it is usually called from a higher-level build file.

special requirements

docbook not used

By default, plugin-build.xml will translate docbook input to a set of HTML files for a plugin's help system documentation. However, if your plugin help doesn't require docbook's extensive capabilities, you can provide the HTML files themselves. You just need a way to create them and build them into your jar.

The Xilize plugin provides a Textile-like markup language for the creation of any plain-text output format and by default produces XHTML. So as the author of Xilize I did not see docbook as an option. Also, since the Xilize documentation is both extensive and requires features not available in the jEdit help system, the help documentation provided directly by the plugin consists of minimal information and points to a website for the rest.

Note: The jEdit help system, org.gjt.sp.jedit.help.HelpViewer, uses a javax.swing.JEditorPane with a javax.swing.text.html.HTMLEditorKit to display HTML. As the Java API docs say, "Because HTML is a very popular format of content, some support is provided by default. The default support is provided by this class, which supports HTML version 3.2 (with some extensions), and is migrating toward version 4.0." Keep that in mind when creating your help text.

Xilize generates XHTMLHTML 4.0.1. So a little care was required to ensure the output was "dumbed-down" to HTML 3.2.

special jar directory

Xilize can automatically create several files for its user by copying them from sources stored in its jar. Thus the sources for these files must be in the plugin's jar in a location known at compile time.

custom manifest file

plugin-build.xml will add a simple manifest.mf file to a plugin jar file — the one ant creates by default when a manifest is not specified in the <jar> task. It will contain a "Built-By" attribute set to the value of ${user.name} in the packagers environment. (Note: this is user name as set on the machine used to do the packaging — not your name.)

My preference for including additional information in the manifest, require a custom manifest in the jar.

build.xml

Here is the build.xml used by the Xilize plugin. See notes following the listing.

<?xml version="1.0"?>
<project name="xilize-jEdit" default="build" basedir=".">

    <description>
    builds the Xilize2 plugin using the required build-support plugin-build.xml
    </description>

    <!-- set for the following import task -->
    <property name="build.support" value="/home/andy/wdev/xlib/jEditBuildSupport"/>
    
    <import file="${build.support}/plugin-build.xml"/>

    <!-- override due to dependency on another plugin  -->
    <path id="project.class.path">
        <pathelement path="${jedit.user.home}/jars/ErrorList.jar"/>
    </path> 

    <!-- build.post, add help files, resources, and custom manifest to the jar {{{ -->
    <target name="build.post" >
        <tstamp>
            <format 
                property="xil.build.time" 
                pattern="EEEE, MMMM d, yyyy HH:mm z" 
                locale="en"
            />
        </tstamp>
        <jar 
            destfile="${install.dir}/${jar.file}" 
            basedir="." 
            includes="*.html, browser.actions.xml, xilize/resource/**" 
            update="yes" 
            
        >
            <manifest>
                <attribute name="Built-By" value="${user.name}"/>
                <attribute name="Built-On" value="${xil.build.time}"/>
                <attribute name="Main-Class" value= "com.centeredwork.xilize.Main"/>
                <attribute name="X-Author" value="Andy Streich"/>
                <attribute name="X-Description" value= "plugin for jEdit"/>
                <attribute name="X-Home-Page" value="http://xilize.sourceforge.net/"/>
            </manifest>
        </jar>
        <echo>${xil.build.time}</echo>
    </target>   
    <!-- }}} -->
    
</project>

<!--  :tabSize=4:indentSize=4:noTabs=true:folding=explicit:collapseFolds=1:  -->

notes

The classpath override is necessary because Xilize uses the ErrorList plugin.

Overriding build.post takes care of the Xilize plugin's other special requirements. Notice the jar task's includes attribute is used to pick up the HTML help file and also adds to the xilize directory inside the jar (these are the file Xilize provides the user on request).

Other things you might do in your plugin include these:

build.prepare could be overridden to put the resource files in the right place and get the HTML files in the jar — although the build directories would not yet be created so you would have to do that.

<selector id="packageFiles"> could accomplish this too. It's used after the build directories are created and by default does nothing.

<selector id="extraFiles"> is another candidate, however, by default it selects everthing else you might want. If you use it, you likely want to copy/paste and then extend it.

   <selector id="extraFiles">
        <and>
            <or>
                <filename name="**/actions.xml" />
                <filename name="**/dockables.xml" />
                <filename name="**/services.xml" />
                <filename name="**/*.props" />
                <filename name="**/LICENSE" />
                <filename name="**/README" />
            </or>
            <not>
                <filename name="${build.dir}/*" />
            </not>
        </and>
    </selector>

In any case, we do need the jar task with its update attribute turned on, because that is the only way to get the custom manisfest into the jar with the current plugin-build.xml. So, this build.xml does all necessary work in build.post after plugin-build.xml has constructed the jar file.

changing plugin-build.xml to allow for manifests would be a nice enchancement.

build.properties

The build.properties file for the Xilize plugin. See notes following the listing.

# Xilize plugin for jEdit build properties
#
# these settings override the defaults in plugin-build.xml

src.dir=src

# destination for xilize.jar 
install.dir=../../dist
# install.dir=..

# jEdit install directory 
jedit.install.dir=/home/andy/wdev/xlib/jedit4.3pre9source

# User settings directory
jedit.user.home=/home/andy/wdev/jedit/latest

# Plugin dependencies
plugin.dependencies=ErrorList

# compiler switches, others are set in ../../build.xml
compiler.target=1.5
compiler.source=1.5

# a comma separated list of packages for javadoc
# Note: not in the build.properties.sample, perhaps it should be 
javadoc.packagenames=xilize,com.centeredwork.xilize

Notes:

reviewers note:
# a comma separated list of packages for javadoc
# Note: not in the build.properties.sample, perhaps it should be
javadoc.packagenames=xilize,com.centeredwork.xilize

wrapper build

Because the Xilize plugin for jEdit is part of a larger project to deliver Xilize plugins for a variety of environments, the build.xml above is normally called by a higher-level build two directories up the tree.

development directory structure

wrapper build.xml

For completeness here is the higher-level build file used during development to call the Xilize plugin's build.xml. Long lines are wrapped in the listing.

<?xml version="1.0"?>
<project name="XilizeJars" default="" basedir="." >
    <description>
    builds Xilize related jar files, 
    set "release" property for release distribution, 
    see release.properties file
    </description>

    <property file="release.properties" />
    <property file="build.number" />
    <property name="lib.external" location="../../xlib" />
    
    <property name="xil.jar.dist" location="dist" />
    <property name="docs.dir" location="docs" />
    <property name="engine.dir" location="comp/engine" />
    <property name="xpje.dir" location="comp/xilize" />
    
    <property name="xpje.rel.label" 
        value="${xpje.jar.name}-v${xpje.version.major}.${xpje.version.minor}.${xpje.version.mm}" />
    <property name="xpje.id"  value="${xpje.rel.label}_${build.number}" />
    <property name="xpje.sources.zip.file" value="${xpje.id}_sources.zip" />
    <property name="xpje.javadoc.zip.file" value="${xpje.id}_javadoc.zip" />
    <property name="xpje.jar" value="${xpje.jar.name}.jar" />
    <property name="xpje.engine.copy" value="${xpje.dir}/src/com" />
    <property name="xpje.jedit.settings" location="../../jedit/latest" />

    <property name="engine.rel.label" 
       
value="${engine.jar.name}-v${engine.version.major}.${engine.version.minor}.${engine.version.mm}" />
    <property name="engine.id"  value="${engine.rel.label}_${build.number}" />
    <property name="engine.sources.zip.file" value="${engine.id}_sources.zip" />
    <property name="engine.javadoc.zip.file" value="${engine.id}_javadoc.zip" />
    <property name="engine.jar" value="${engine.jar.name}.jar" />
    
    <property name="docs.id"  value="xilize-website_${build.number}" />
    <property name="docs.sources.zip.file" value="${docs.id}_sources.zip" />
    
    <!-- build engine -->
    <target name="engine" depends="init" description="create engine-only jar and javadocs" >
        <ant antfile="engine.build.xml" dir="${engine.dir}" target="dist"/>
    </target>
    
    <!-- build jedit plugin  {{{ -->    
    <property name="install.dir" value="${xil.jar.dist}" />

    <target name="-xpje.init" depends="init" >
        <property file="${xpje.dir}/build.properties" />
        <propertyfile file="${xpje.dir}/version.props">
            <entry 
                key="plugin.xilize.XilizePlugin.version" 
               
value="${xpje.version.major}.${xpje.version.minor}.${xpje.version.mm}.${build.number}"/>
        </propertyfile>
    </target>

    <target name="-xpje.debug" unless="release">
        <property name="compiler.debug" value="on" />
        <echo>xilize debug jar: ${install.dir}</echo>
    </target>
    <target name="-xpje.release" if="release">
        <property name="compiler.debug" value="off" />
        <echo>xilize release jar: ${install.dir}</echo>
    </target>
    
    <!-- uses jEdit-specific build file -->
    <target name="xpje.build" depends="xpje.version.create, -xpje.init, -xpje.debug, -xpje.release"
            description="builds jEdit plugin " >
        <sync todir="${xpje.dir}/src/com">
            <fileset dir="${engine.dir}/src/com"/>
        </sync>
        <ant dir="${xpje.dir}" />
        <copy file="${xil.jar.dist}/${xpje.jar}" todir="${xpje.jedit.settings}/jars" />
    </target>
    <!-- }}} -->
    
    <target name="build" depends="engine, xpje.build" description="builds all components" >
        <buildnumber/>
    </target>

    <target name="javadoc" description="create javadocs" >
        <ant antfile="engine.build.xml" dir="${engine.dir}" target="javadoc"/>
        <ant dir="${xpje.dir}" target="javadoc" /> 
    </target>
    
    <target name="dist-prepare">
        <copy file="${engine.dir}/dist/${engine.jar}" todir="${xil.jar.dist}" /> 
        
        <delete file="dist/${engine.javadoc.zip.file}" quiet="true" />
        <delete file="dist/${xpje.javadoc.zip.file}" quiet="true" />        
        <delete file="dist/${engine.sources.zip.file}" quiet="true" />
        <delete file="dist/${xpje.sources.zip.file}" quiet="true" />
        
        <zip basedir="comp/engine/dist" destfile="dist/${engine.javadoc.zip.file}"
includes="javadoc/**"/>
        <zip basedir="comp/xilize/build/docs" destfile="dist/${xpje.javadoc.zip.file}"
includes="javadoc/**"/>
        <zip basedir="comp" destfile="dist/${engine.sources.zip.file}"
excludes="**/build/**,**/dist/**" includes="engine/**"/>
        <zip basedir="comp" destfile="dist/${xpje.sources.zip.file}" excludes="**/build/**"
includes="xilize/**"/>
        <zip basedir="." destfile="dist/${docs.sources.zip.file}" includes="docs/**"/>        
    </target> 
    
    <target name="dist" depends="build, javadoc, dist-prepare" 
            description="builds components, creates javadocs, archives sources and javadocs" />
    
    <!-- initialization and clean up {{{ -->
    <target name="init">
        <tstamp>
            <format 
                property="build.time" 
                pattern="EEEE, MMMM d, yyyy HH:mm z" 
                locale="en"
            />
        </tstamp>    
        <mkdir dir="${xil.jar.dist}"/>
        <condition property="compiler.debug" value="off">
            <isset property="release"/>
        </condition>
    </target>
    
    <target name="-clean">
        <delete dir="${xil.jar.dist}" />
    </target>

    <target name="clean" depends="-clean" description="clean all components">
        <ant dir="${docs.dir}" target="clean" />
        <ant antfile="engine.build.xml" dir="${engine.dir}" target="clean"/>
        <ant dir="${xpje.dir}" target="clean" />
        <!-- delete the engine sources from the plugin source tree, 
            can't do this in the plugin build file,
            don't want jEdit plugin packagers to deal with this detail -->
        <delete dir="${xpje.engine.copy}" />        
    </target>
    <!-- }}} -->
    
    <!-- Version.java generation {{{ -->
    <target name="xpje.version.create" depends="init">
        <echo append="false" file="${xpje.dir}/src/xilize/Version.java">
package xilize;

// this file is auto-generated at release time

public class Version {
    public static final String ID = "${xpje.id}";
    public static final String RELEASE_STATUS = "${xpje.release.status}";
    public static final String COMPONENT = "${xpje.component}";
    public static final String HOSTAPP_NAME = "${xpje.hostapp.name}";
    public static final String PLUGIN_NAME = "${xpje.visible.name}";
    public static final String JAR_FILENAME = "${xpje.jar}";
    public static final int MAJOR_VERSION = ${xpje.version.major};
    public static final int MINOR_VERSION = ${xpje.version.minor};
    public static final int MM_VERSION = ${xpje.version.mm};
    public static final int BUILD_NUM = ${build.number};
    public static final String BUILD_TIME = "${build.time}";
    private Version() {}
}        
       </echo>
       <echo>created xilize.Version.java</echo>
    </target>
    <!-- }}} -->    

</project>

<!--  :tabSize=4:indentSize=4:noTabs=true:folding=explicit:collapseFolds=1:  -->

shameless plugin plug

The released beta version of the Xilize plugin for jEdit is being used to create documentation for some opensource projects. See, for example, the About this Document section at the bottom of this page. There's a nice plug for jEdit there as well. It is also used for general website development.

See the here for information about the syntax and capabilities of the currently released Xilize_v2.0beta and the current development version of v3.0 — soon to be submitted to Plugin Central — used to create this development set.

If I may say so myself, it is

The new version, a nearly complete re-write, incorporates BeanShell capabilities so you can extend Xilize markup to do whatever you like. (jEdit, as you know, has BeanShell built-in for macro writing. The Console plugin also provides a BeanShell shell.)