BuildSystemHowTo

Contents

NetBeans 4.x Project & Build System How-To

Version:
June 21, 2006
Author:
Jesse Glick

Overview

This guide should give developers of NetBeans modules (extensions) a basic idea of how to write new project types for NetBeans 4.x, as well as use certain importants parts of the project and build system functionality from other kinds of modules.

The reader is assumed to be familiar with the basics of developing NetBeans modules: how to compile Java sources against API-providing modules, make the JAR, write the manifest and XML layer, register services in global lookup, etc.

For background, the reader is encouraged to first look at the [design.html general design document] which explains how the different pieces of the architecture work together to provide the user functionality.

Quick architectural summary:

  1. The NetBeans IDE (starting with version 4.0) organizes user work into projects. Each project corresponds to exactly one project folder on disk. (A given disk folder may be a project folder or not; you cannot have two projects in one folder.)
  2. A project owns all files inside its project folder or subfolders (except when a subfolder is itself another project folder, which is allowed); it might also own files located elsewhere on disk, to permit a user to keep IDE-specific configuration separate from existing “pristine” sources.
  3. There are different types of projects available, according to what modules you have installed. Different project types may behave very differently, or look almost alike, depending on their intent.
  4. A project defines what appears beneath its node in the Projects tab—sometimes called its logical view. It can also define a customizer action, usually opening a properties dialog.
  5. A project can be opened or closed via the GUI. It can also be loaded though not open; an open project is however always loaded. A project can be garbage collected if it is closed; attempting to refer to it again (via its project directory) will automatically load it again.
  6. A project may list subprojects, other projects it somehow depends on. This is used mainly when opening a project using the Open Project action.
  7. The IDE defines certain generic actions such as Build which can be invoked on a project, which have corresponding commands, simple strings like build. Some actions can be sensitive to the selected file. Often Apache Ant is used as a build tool to produce a final program or other delierable from sources, but this is not required.
  8. Interaction between project implementations and other functionality in the IDE is normally done using queries, a pattern whereby a client can ask a concrete question (e.g. ClassPath.getClassPath(someJavaFile, ClassPath.COMPILE)) and possibly receive an answer; query implementations are registered (using Lookup.default) by any modules, and asked in turn. For queries mentioned here, usually the query implementation may be registered in the project (using Project.lookup) rather than globally, and the project associated with the file mentioned in the question (e.g. FileOwnerQuery.getOwner(someJavaFile) in the last example) will be asked to provide an answer.
  9. Though project type providers are allowed a great deal of leeway in how they implement the project SPIs, there is a fairly extensive SPI support suite which assumes that the project uses Ant as a build tool, and defines a number of convenience implementations optimized towards this system.

Note: all API classes and interfaces are referred to by simple name. You can look up the Javadoc for any of these classes quickly using the master class index for the NetBeans APIs.

How to Write a Project Type (Generally)

This section discusses general things you need to do in order to write a new project type. Later on we will discuss more concrete suggestions for Ant-based projects using the standard infrastructure.

Generally a project type should be contained in a module dedicated to implementing that project type. Do not expose any APIs from this module. If other modules need special information about your project’s structure not available through the existing APIs, you can define new APIs in separate API modules that both the clients and your project type depend on. This rule helps enforce a clean architecture on the system and ensures that other project types could mimic some of the behavior of your project type if they needed to.

An interesting tutorial on writing a (non-Ant-based) project type from scratch is available: Tim Boudreau’s POV-Ray tutorial

Decide on a Project Layout

The first step for any kind of project type is to decide what the project will look like! You should have a clear idea of what files will reside in the project directory (or perhaps externally), using what file names, what the contents should be, what files will be updated by the IDE’s GUI, what by text editing, how future updates will affect file formats, etc.

If you use the standard Ant-based infrastructure (below), some of these decisions are made for you.

Most project types will define an AntBasedProjectType and keep basic project metadata in $projdir/nbproject/project.xml (below). If you do not do this, e.g. if you need to support a third-party project layout (such as Apache Maven uses), then you will need to implement ProjectFactory yourself to recognize and load projects using your format. A project factory should be designed to be able to quickly reject candidate directories which are not in your layout. (Positive identification of your projects can be a little slower.)

Write the Project Object

Every project is represented by a Project object, which is created by your factory. Its only mandatory behavior is to be able to report the project directory it is associated with. Other than that, all of its behavior is controlled by its Lookup, basically a bag of optional capabilities. Usually org.openide.util.lookup.Lookups can be used to make a Lookup with a fixed list of entries.

All outside code should interact with your project via interfaces found in its lookup. Outside code can check to see if a given project has a capability, and if so, use it. Never make your Project implementation class publicly accessible. Whatever capabilities you wish to expose, do so from the lookup—you can define additional interfaces to expose to clients (public or semi-public, in API-exposing modules) and add implementations to your lookup (in non-public classes in your project type module) if you need to. Beware that the project infrastructure reserves the right to hide the original Project your factory creates and expose only a wrapper to outside callers, so it would never be correct to try to cast a Project object to an implementation class, even if you could access that implementation class.

What to Put in Lookup

Since all the project’s behavior is controlled by its lookup, the question becomes: what interfaces should I implement? There is a suggested list in the Javadoc for Project.getLookup(). Here is an overview of major kinds of interfaces and why you might need them.

General Appearance

Almost all project types will want to define their general appearance and behavior in the IDE’s GUI.

ProjectInformation
Lets you control the display name and icon of the project. Typically all projects of a given type will share an icon, but there may be badging etc. applied as well, and it is possible to have basically different icons depending on project metadata.
LogicalViewProvider
Controls the display in the Projects tab. You can show whatever subnodes you like, according to the project’s semantics. Typically you will show important source roots (try PackageView in the case of Java package roots), or important files. You can also show nodes which do not directly correspond to individual files—e.g. an EJB project shows EJB and web services nodes which are derived from combinations of source files and deployment descriptor information.The root node for the project should usually have a name and icon matching that given in ProjectInformation. The precise context menu will vary by project type, so look at existing project types to keep consistency. Many of these items can be created easily using CommonProjectActions and ProjectSensitiveActions.Remember that you need to include the Project in the lookup of the root node, or project-sensitive actions will not generally work.Note: the Files tab is not under a project’s direct control. It always shows top-level “generic” source groups (acc. to Sources—see below) from the project as top-level nodes, beneath which there is a plain directory tree (filtered according to VisibilityQuery). In most cases there is one node per project—the project directory—but projects using external source roots may display additional nodes.
CustomizerProvider
Implements the Project Properties action in the File menu (also CommonProjectAction.customizeProjectAction). Usually this action should open a dialog containing general GUI configuration for the project, according to its needs. Of course the project may expose additional UI for customization, if appropriate, using context menu items on the project node, subnodes in the logical view, etc.

File Structure and Templates

Most project types will wish to define some aspects of how their source directories are laid out, what they contain, and what may be added to them.

Sources
Basic information about what directories are contained in the project. Technically optional—the default assumption is that the project contains just an untyped project directory—but recommended.Generic source roots refer to top-level directories containing project files. The contents of the Files tab is determined by these.Typed source roots refer to particular directories (which should be inside, or equal to, some generic source root) used for particular purposes. For example, roots of type JavaProjectConstants.SOURCES_TYPE_JAVA refer to Java package roots. Some templates need to be placed in source roots of a certain type; for example, the wizard for adding a Java source file requires a source root of type SOURCES_TYPE_JAVA.
SharabilityQueryImplementation
Optionally lets the project declare that certain folders (or, perhaps, files) are not intended for sharing with other users, typically in a version control system. If your project type defines a build folder, or a folder containing private data (such as file paths on the developer’s local disk), marked in unsharable. The IDE’s VCS integration can use this information to avoid trying to commit such folders to VCS. Other IDE features may use this information too, for example to avoid searching in build folders.
RecommendedTemplates
Optionally define categories of file templates that this project type should allow to be added. For example, a J2ME-oriented project type would probably want to exclude Swing forms and servlets, but permit MIDlets.
PrivilegedTemplates
Defines a set of specific templates that are likely to be important to users of the project type. Used to create the default New submenu in the project’s context menu.

Building and Other Actions

Most project types will have some kind of actions which can be performed on the project: build it, run some program it represents, etc.

ActionProvider
A simple interface used to specify how certain “standard” actions like Build should behave when applied to your project (e.g. from the IDE’s toolbar). Such actions might run an Ant target, for example.Note that you do not need to include mappings in ActionProvider which will be used only from GUI your module provides itself. For example, you can add context menu items to your project’s node that perform additional actions without going through ActionProvider. This interface exists to permit GUI coöperation between your project and the rest of the IDE.
FileBuiltQueryImplementation
If some files have a source representation and can be somehow processed individually into “built” or “compiled” versions, you want to add a FileBuiltQueryImplementation to represent this fact. Currently only *.java files make use of FileBuiltQuery, to show an out-of-date badge, though nodes for other file types could be extended to do so in the future as well.

Java-Specific Behavior

Several queries are used to permit integration of various Java editing and browsing features in the IDE with the project system. Any projects which deal with Java sources should try to implement these queries.

ClassPathProvider
Important query used to specify the class path used for a Java file or source root. Without this query, much important functionality will be broken, e.g. completion in the source editor and refactoring. See its Javadoc for details on usage.
SourceLevelQueryImplementation
Also important—instructs the editor, parser, and other IDE components what Java source level to use for a file. For example, assertions will only be recognized if the level is at least 1.4, and generics only if at least 1.5.
SourceForBinaryQueryImplementation
Also an important query, as it is needed for source stepping when debugging, interproject dependencies, and other purposes. If your project’s Java sources are ever compiled to some build directory, and perhaps packed into JARs after that, you must implement this query in order for other parts of the IDE to understand where to find sources corresponding to the build product.
JavadocForBinaryQueryImplementation
Important if you ever produce or bundle Javadoc in your project. This query enables Javadoc search to work correctly when someone is depending on classes from your project.
UnitTestForSourceQueryImplementation
Helpful to implement in case you have unit tests in your project (typically in JUnit format). The JUnit support module will then be able to properly configure some wizards and actions.

Project Dependencies

SubprojectProvider
If you have a formal way of representing “subprojects” of your project—which might be projects physically packaged into your project, or located inside it on disk, or just used by it at build time, etc.—you can enumerate them with this interface. This is optional and is currently only used for the subproject list in the Open Project dialog (subprojects may be opened automatically) and for CommonProjectActions.openSubprojectsAction.

Miscellaneous

ProjectOpenedHook
You can perform various kinds of special actions when your project is being opened or closed in the GUI. Remember that your project can be loaded in memory without being open, and is expected to function reasonably anyway. The Javadoc mentions various typical actions you might perform here.
AuxiliaryConfiguration
Strongly recommended to implement if possible. Permits foreign code to store extra metadata inside your project, in XML format. Used for example to store files open in the editor from a project, and editor bookmarks.
CacheDirectoryProvider
Also recommended to implement though not yet in use. Permits foreign code to store cache files associated with your project.

How to Write an Ant-Based Project Type

While a project can be written directly to the bare SPIs such as ProjectFactory and various interfaces (such as SubprojectsProvider) placed into project lookup, you may wish to reuse the basic Ant-based project infrastructure used by most IDE project types. This support SPI conveys several major benefits:

  • You do not need your own ProjectFactory; any folder containing a file nbproject/project.xml containing a project type identifier you choose will be recognized as yours. The project load and save cycle is managed for you.
  • There is support for storing project metadata in structured ways, such as in nbproject/project.xml or nbproject/project.properties. For properties-based storage it is possible to load and evaluate multiple properties files in a rich way, and listen to dynamic changes in properties-based configuration.
  • There is direct support for managing Ant builds (of course). You can have build script(s) generated based on project.xml and an XSLT stylesheet you provide. This is usually used to make an nbproject/build-impl.xml file containing default build steps and imported from an editable build.xml.
  • There are default implementations of various queries and other interfaces needed for your lookup, such as Sources, loading configuration from properties files (shared by the Ant script) where appropriate.
  • It is possible to manage references to files (such as libraries) or other projects in a structured way, with a predefined storage format, automatic synchronization to properties files, enumeration of subprojects, and a default GUI for resolving broken references.

The following sections describe what steps you need to take in order to write an Ant-based project type, in addition to or instead of steps taken for general project types.

For a complete example of an Ant-based project type you may wish to look at the implementation of the “general Java project” type, located in netbeans.org CVS under java/j2seproject/ (browse online).

Deciding on a Source Layout

Ant-based projects always have an nbproject subdirectory in the project directory with a file nbproject/project.xml which identifies the project and can contain some metadata. Typically there are several other files in standard locations. See the [design.html#project-layout design document] for an overview of the general Ant-based project layout if you are not yet familiar with it, and study some actual project in the IDE such as a plain Java library project.

Now think about layout details specific to your project type. Perhaps you want to add another properties file for some unforeseen reason; this is up to you (almost all of the Ant-based project infrastructure classes will work happily with such a setup, except perhaps for ReferenceHelper, described below). Certainly you will want to decide what kinds of source files reside where and in what structure. For example, for a plain Java project, the specifics of the structure are:

  1. main Java sources in ${src.dir}, default ${basedir}/src or an external root; may be more than one such source directory
  2. (optional) unit test sources in ${test.src.dir}, default ${basedir}/test or an external root; again, may be more than one such directory
  3. (optional) JAR manifest in ${manifest.file}, default ${basedir}/manifest.mf
  4. ${build.dir} (default ${basedir}/build) holds various transient build products (e.g. compiled classes not yet packed into a JAR)
  5. ${dist.dir} (default ${basedir}/dist) holds the finished JAR as well as any generated Javadoc

Furthermore, you need to decide what project metadata you will store. This includes the structure of project.xml (and perhaps private.xml if you need to use it for anything), as well as a list of recognized keys and their semantics for project.properties and/or private.properties. For example, for a plain Java project, project.xml can specify:

  1. the project name
  2. a list of source roots (giving in each case the name of the Ant property specifying its actual location)
  3. the minimum Ant version needed to build (probably 1.6)
  4. (optional) an explicit platform marker indicating that the build should refer to a particular JDK

These decisions are codified in an XML schema for the project.xml file (example). Currently the schema is not used for runtime validation, but that is expected to change; in the meantime, you are strongly recommended to define a schema to make sure you have clearly defined what can and cannot be stored in project.xml.

Your project type does not directly control the whole project.xml file. Rather, the Ant-based infrastructure will manage loading, parsing, and saving it, using APIs to be described below; and you only control one section of it, called the primary configuration data. You need to select an XML element name and namespace that will identify this block. For example, general Java projects use an element <data xmlns="http://www.netbeans.org/ns/j2se-project/2">. The target namespace for your XML schema should be this namespace: your schema will validate this block only, not the complete file.

You also need to define a primary configuration data block name for private.xml in the nbproject/private/ folder, whether or not you plan to write anything to this file. For example, general Java projects use <data xmlns="http://www.netbeans.org/ns/j2se-project-private/1">. You can just define an empty schema for this block that allows no content (example), or you can store real information here—some information about the project that should not be shared with other users and is not easily kept in properties files.

Also related to project.xml, you need to pick a project type identifier. This is just a short string—it could be the code name base of your module—which uniquely identifies your project type. This will be stored in the <type> element at the top of project.xml.

A Java project can likewise use a number of different Ant properties, such as src.dir, main.class, javac.classpath, run.jvmargs, etc. (XXX link to spec when available) You will need to decide what properties your project type will recognize and what the values should mean, while working on the build script (below).

Writing a Prototypical Build Script

Make a prototype of a real project—it does not need to be loadable by the IDE as a project yet, just have realistic source files and be buildable by Ant (either from the command line or through the IDE using e.g. the Favorites node). Write an empty build.xml:


<project name="x" default="choose-something" basedir=".">
    <import file="nbproject/build-impl.xml"/>
</project>

And write an nbproject/build-impl.xml that does the various build steps you would like the project to do. Generally it should load some properties files first, e.g.


<project name="x-impl" basedir=".."> <!-- note basedir is project directory -->
    <target name="-pre-init"><!-- placeholder --></target>
    <target name="-init-private" depends="-pre-init">
        <property file="nbproject/private/private.properties"/>
    </target>
    <target name="-init-user" depends="-init-private">
        <property file="${user.properties.file}"/>
    </target>
    <target name="-init-project" depends="-init-user">
        <property file="nbproject/project.properties"/>
    </target>
    <target name="-init" depends="-init-project">
        <!-- maybe some other stuff... -->
    </target>
    <!-- now normal targets... -->
</project>

Note that it is conventional to begin the names of “internal” targets that should not be run directly (only as dependencies) with a hyphen (-).

Think about which targets the user should override in build.xml for what purpose. It is nice to put in “placeholder” targets which by default do nothing but which can easily be overridden to insert some custom steps at a certain point in the build.

Selecting Ant Tasks

Which Ant tasks are available to you? Naturally you are free to use any standard Ant task which comes with the Ant distribution and does not require a special library to run. (Make sure you decide which version of Ant your scripts will require at a minimum—generally this will be the version currently shipped with the IDE. Later versions should work as well.) However some other tasks may require a bit of special setup. In particular:

Bundled optional tasks requiring special libraries
Some tasks come with Ant but require a special library in order to run. In the current design of Ant, these can only be run if the IDE includes the library directly in Ant’s main classpath. For example, the <junit> task runs inside the IDE without any user setup because the org.netbeans.modules.junit module requests that junit.jar be added to Ant’s classpath. Other modules may request such classpath additions by implementing AutomaticExtraClasspathProvider.
Non-bundled custom tasks
You may wish to have your project’s build script run some Ant tasks which do not ship with Ant. (Do so only when really required, because it is annoying to users to have their build infrastructure depend on special things.) Some module (perhaps your project type module, perhaps not) must supply the task definition JAR(s):
  1. Make sure the task JAR, as well as any special libraries it may need, is installed in the IDE distribution by including it in the module’s NBM file.
  2. Define a project library of type j2se and place it in the org-netbeans-api-project-libraries/Libraries/ folder of your module XML layer. Example definition (see the Project Libraries API for more details):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE library PUBLIC
          "-//NetBeans//DTD Library Declaration 1.0//EN"
          "http://www.netbeans.org/dtds/library-declaration-1_0.dtd">
<library version="1.0">
    <name>mytasks</name>
    <type>j2se</type>
    <localizing-bundle>org.netbeans.modules.thismodule.Bundle</localizing-bundle>
    <volume>
        <type>classpath</type>
        <resource>jar:nbinst://org.my.module.name/ant/extra/mytasks-1.2.3.jar!/</resource>
    </volume>
</library>
  1. Now build.properties in the user directory will be populated with the actual path to the library, so you can use it in your build script:
<target name="-init-taskdefs" depends="-init">
    <!-- Assumes 1.6-style antlib is present: -->
    <taskdef resource="org/netbeans/mytasks/antlib.xml" uri="antlib:org.netbeans.mytasks">
        <classpath>
            <pathelement path="${libs.mytasks.classpath}"/>
        </classpath>
    </taskdef>
</target>
<target name="use-taskdefs" depends="-init-taskdefs">
    <mytask xmlns="antlib:org.netbeans.mytasks" someattr="true"/>
</target>

You could also manually update build.properties using PropertyUtils to define some other property name not using the libs.name.classpath format, e.g. when your project type module is restored or a project of your type is opened. Using the library manager is easier because build.properties is managed for you.If you wrote the task(s) for this purpose, you are strongly recommended to make task source code available as open source (e.g. under the Sun Public License), so that users retain full control over all software actually used to perform their builds (besides the JDK).

In-VM tasks present in existing modules
NetBeans has the ability to run special Ant tasks which interact with other parts of the IDE (and cannot be run outside the IDE). A few commonly required tasks ship with the IDE; if you want to use them, declare a dependency on the module which defines them. You can refer to the tasks by simple name, but when using Ant 1.6+ it is preferable to use the correct “antlib” namespace. (Note that when prototyping a build script you can use the IDE’s code completion for attributes and subelements of these tasks.)
Web browser integration (antlib:org.netbeans.modules.browsetask)
<nbbrowse> lets you open the IDE’s configured web browser on a given URL (or file).
JPDA debugger integration (antlib:org.netbeans.modules.debugger.jpda.ant)
<nbjpdastart> asks the IDE’s debugger to start listening on a new JPDA port, and define an Ant property with the port so you can launch a Java process which will connect to that port as a client. <nbjpdaconnect> connects to an existing port; more useful for server applications. <nbjpdareload> reloads Java classes using “fix & continue” technology.
Custom in-VM tasks
You can also define your own in-VM tasks and use them the same way as the predefined ones. See the Ant SPI for details.

Parametrizing Build Scripts

Some project types have several variants for build-impl.xml, parametrized somehow. For example, plain Java projects behave a little differently depending on whether you are building and running against the “default platform” (the IDE’s own JDK) or an explicit JDK. The former case might look like (excluding irrelevant details):


<target name="compile">
    <javac srcdir="..." destdir="..." classpath="..."/>
</target>

whereas the latter case might look like:


<target name="compile">
    <javac srcdir="..." destdir="..." classpath="..." fork="true" executable="..."/>
</target>

The rule of thumb here is simple. If some aspect of the build can be parametrized using Ant properties in a straightforward way using the Ant tasks you have available, do so. For example, there is no need to create a different build-impl.xml just to change the build directory; this can be done using a property:


<target name="compile">
    <javac srcdir="..." destdir="${build.classes.dir}" classpath="..."/>
</target>

But in other cases, this is not possible. For example, Ant’s <junit> task can take a jvm attribute to specify an explicit JDK to run against. If you include this attribute, you have to set the JDK. When using the default platform, this attribute must not be there; when using an explicit platform, it must be there. Therefore build-impl.xml needs to be a bit different in these two cases (unless you included both versions in different targets and switched between them at runtime, though this can cause bloat in the build script). Build prototype scripts using all the variants you expect to encounter and verify that they all work the way you want.

Now to go back to project.xml for a moment: whatever variations in build-impl.xml you wish to support must be codified as metadata in project.xml. For example, a plain Java project can include an <explicit-platform> element or not; the presence or absence of this element determines which build-impl.xml variant is produced.

Writing Stylesheets

When you are satisfied with the build-impl.xml you have drafted (perhaps in multiple variants), it is time to write an XSLT stylesheet which produces it. The input to the stylesheet is the project.xml file and the output is the build script. Typically you will just copy most of the prototype build script verbatim into the stylesheet as the default content. You will also want to examine the project.xml input at least for a project name, and optionally also for any other information you need to construct different build script variants. For example, with a project.xml looking like this:


<project xmlns="http://www.netbeans.org/ns/project/1">
    <type>org.netbeans.modules.myprojecttype</type>
    <configuration>
        <data xmlns="http://www.netbeans.org/ns/my-project-type/1">
            <name>Test Project</name>
            <style>first</style> <!-- whatever this means to you -->
        </data>
    </configuration>
</project>

You might have a stylesheet like this:


<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:p="http://www.netbeans.org/ns/project/1"
                xmlns:xalan="http://xml.apache.org/xslt"
                xmlns:myproj="http://www.netbeans.org/ns/my-project-type/1"
                exclude-result-prefixes="xalan p myproj">
    <xsl:output method="xml" indent="yes" encoding="UTF-8" xalan:indent-amount="4"/>
    <xsl:template match="/">
        <xsl:comment><![CDATA[
*** GENERATED FROM project.xml - DO NOT EDIT  ***
***         EDIT ../build.xml INSTEAD         ***
]]></xsl:comment>
        <xsl:variable name="name" select="/p:project/p:configuration/myproj:data/myproj:name"/>
        <xsl:variable name="codename" select="translate($name, ' ', '_')"/>
        <project name="{$codename}-impl" basedir="..">
            <!-- ... -->
            <target name="shows-variants">
                <xsl:variable name="style" select="/p:project/p:configuration/myproj:data/myproj:style"/>
                <xsl:choose>
                    <xsl:when test="$style = 'first'">
                        <do-one-thing-in-ant/>
                    </xsl:when>
                    <xsl:when test="$style = 'second'">
                        <do-another-thing-in-ant/>
                    </xsl:when>
                    <xsl:otherwise>
                        <!-- error -->
                    </xsl:otherwise>
                </xsl:choose>
            </target>
        </project>
    </xsl:template>
</xsl:stylesheet>

Remember that { and } in attribute values have a special meaning in XSLT: if you want to use braces literally, e.g. for Ant property references, double them, e.g.


<!-- this is in XSLT: -->
<target name="something">
    <mkdir dir="${{build.classes.dir}}"/>
    <!-- ... -->
</target>

You will also want a stylesheet to generate build.xml, though typically this is quite simple:


<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:project="http://www.netbeans.org/ns/project/1"
                xmlns:myproj="http://www.netbeans.org/ns/my-project-type/1"
                xmlns:xalan="http://xml.apache.org/xslt"
                exclude-result-prefixes="xalan project myproj">
    <xsl:output method="xml" indent="yes" encoding="UTF-8" xalan:indent-amount="4"/>
    <xsl:template match="/">
        <xsl:comment> You may freely edit this file. See commented blocks below for </xsl:comment>
        <xsl:comment> some examples of how to customize the build. </xsl:comment>
        <xsl:comment> (If you delete it and reopen the project it will be recreated.) </xsl:comment>
        <xsl:variable name="name" select="/project:project/project:configuration/myproj:data/myproj:name"/>
        <xsl:variable name="codename" select="translate($name, ' ', '_')"/>
        <project name="{$codename}">
            <xsl:attribute name="default">default</xsl:attribute>
            <xsl:attribute name="basedir">.</xsl:attribute>
            <description>Builds, tests, and runs the project <xsl:value-of select="$name"/>.</description>
            <import file="nbproject/build-impl.xml"/>
            <xsl:comment><![CDATA[
    You could add descriptions of overridable targets here, or just link to online help...
    ]]></xsl:comment>
        </project>
    </xsl:template>
</xsl:stylesheet>

You can fine-tune the XSLT for your build scripts, as well as the project.xml format and the list of Ant properties you want to recognize, without writing one line of NetBeans module source code. (Just run project.xml through your stylesheets to see the output, using any XSLT tool, such as that built into the NetBeans IDE.) All you are doing at this stage is defining some metadata for the project and verifying that Ant scripts generated from it do indeed build and run an example project the way you want.

Writing the Project Type Skeleton

Now it is time to begin writing the project type provider module proper. First you will need an implementation of AntBasedProjectType which registers your project type in the system. (Place this implementation in default lookup, e.g. using the META-INF/services/ section of your module JAR.) The project type class does not do much except report the project type identifier, and the local name and namespace used for the shared and private primary configuration data blocks (in project.xml and private.xml). The createProject method must create a particular project object; it is passed an AntProjectHelper object which gives you access to a variety of different Ant-based project functionality in a convenient way. You can immediately throw an IOException in case there is something badly wrong with the project on disk; a future version of NetBeans should also let you perform XML validation on project.xml at this time (cf. #42686).

You will need a separate class for the Project implementation, which will usually hold onto the instance of AntProjectHelper and use it to service requests.

Another common thing to set up in your project’s constructor is a property evaluator, which can load the current values of various properties from project.properties, private.properties, and build.properties, using Ant’s property evaluation semantics, and notify you of changes even in specific properties. A PropertyEvaluator instance is also a required parameter for many convenience factory methods and constructors in the Ant-based project support. Many projects can just call AntProjectHelper.getStandardPropertyEvaluator() to load properties from these three files in the usual way. If you have other property files your build script loads, or default values set in the build script, you can make a custom evaluator using factory methods in PropertyUtils—but beware that ReferenceHelper expects the standard semantics (so that it can store relative paths in project.properties and absolute paths in private.properties) and so may not work appropriately if you have a very different property loading model.

The project should create a Lookup containing its particular abilities and return this from the getLookup() method. Normally Lookups.fixed is adequate for this purpose. In principle a project’s lookup could change dynamically, but this is not normally required. More information on what to put in the lookup can be found below.

Handling Build Script (Re-)generation

The normal way that build.xml and build-impl.xml are created is that these files are automatically generated whenever they are missing; and regenerated when they are out of date relative to the current XSLT stylesheet and project.xml, but not modified by the user. (private.xml is not considered: since it is per-user, the shared build script cannot be changed according to its contents.) Note that build-impl.xml is not supposed to be modified by the user, but at least if it is, those modifications will never be clobbered. build.xml can be modified, so if it is, it will not be regenerated; however it is not likely to need regeneration often or at all.

To configure the normal (re-)generation semantics, make sure your project’s lookup contains:

  1. A ProjectXmlSavedHook, used when project.xml is modified and saved.
  2. A ProjectOpenedHook, used when the project is opened.

The Javadoc for GeneratedFilesHelper.refreshBuildScript describes the recommended parameters that should be passed to it from these two hooks. Remember that your XSLT stylesheets should be packaged in the module JAR so they can be passed to this method.

Other kinds of behavior are possible; check the GeneratedFilesHelper Javadoc for more information. You could also produce Ant build scripts using some method other than XSLT transformations (e.g. manual DOM manipulation), but GeneratedFilesHelper will not currently (#42735) help you determine whether the scripts are modified or out of date if you do this, so you would need to write this logic yourself.

Populating the Project Lookup

PENDING

Creating a Properties Dialog

PENDING

Handling Upgrades

PENDING

How to Use Project-Related APIs from Other Modules

PENDING

Some Interesting Things which are Impossible

There is no general API for accessing project settings from the outside, and anyway it comes in various forms depending on the particular project type. For example, J2SE projects currently let you configure a main class, a working directory, etc. These things make no sense for web applications. Conversely, context root is critical for a web application but senseless for a J2SE project. There are no plans to ever have a general API for accessing this kind of project configuration from the outside.

Someday there may be an SPI for plugging in natures (behaviors for a project), in response to accumulated feedback and experience from people trying to do this sort of thing (e.g. the JFluid project); currently there are no plans for it. The project type is currently expected to directly handle all of the significant build/run scenarios which it could support, such as running, debugging, and unit testing.

Currently the JFluid module accomplishes its profiler integration for a fixed list of known project types by relying on knowledge of the disk layout and file formats of those project types, and either running the app directly based on settings read in this way, or generating auxiliary Ant scripts in nbproject/ which can launch the app with specialized parameters in addition to the normal properties-file-based settings.

Note that you can use e.g. ClassPath.getClassPath(FileObject) to find various classpaths which the project claims it uses to build or run the app. How the project actually builds or runs the app is its own business; the API-exposed information is intended for use in code completion, refactoring, and similar development-time-only features, and is intentionally the bare minimum information required for these features. For example, getting the classpath for a particular source root contained in the project is exposed, since the editor and refactoring features need this. Getting the main class of the project (if there is such a thing) is not exposed, since they do not.

Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo