packages.xml

Delivered as text/xml

[ hide source ] | [ make this the default ]

File Contents

<?xml version='1.0' ?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
               "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
<!ENTITY % myvars SYSTEM "../variables.ent">
%myvars;
]>
<sect1 id="packages" xreflabel="Packages">
  <title>OpenACS Packages</title>

  <authorblurb>
    <para>By Pete Su and Bryan Quinn</para>
  </authorblurb>

  <sect2 id="packages-overview">
    <title>Overview</title>
    <para>
      This document is a guide on how to write a software package for
      OpenACS. OpenACS packages are installed and maintained with the
      OpenACS Package Manager (APM) which is part of the acs-admin
      package.  This document presents reasons for packaging software,
      conventions for the filesystem and naming that must be
      followed, and step by step instructions for creating a new
      package for the "Notes" example package.
    </para>

  </sect2>


  <sect2 id="server-file-layout">
    <title>Server file layout</title>
    <para>
      Here is how an OpenACS &majorversion; server is laid out
      starting from the Server root (ROOT):
    </para>
    <figure>
      <title>Server file layout diagram</title>
      <programlisting>
ROOT/
    bin/
        Various executables and scripts for server maintenance.
    content-repository-content-files/
        content repository content stored in the filesystem.
    etc/
        Installation scripts and configuration files.
    packages/
        acs-admin/
        acs-api-browser/
        ... many many more...
        workflow/
    log/
        Server error and access logs
    tcl/
        bootstrap code
    www/
        Pages not in packages (static content, customized pages)</programlisting>
    </figure>
  </sect2>


  <sect2 id="packages-looks">
    <title>What a Package Looks Like</title>

    <para>
      Each package encapsulates all of its data model, library code,
      logic, administration pages and user pages in a single part of
      the file tree.  This means developers can track down
      <emphasis>everything</emphasis> that is related to a particular
      package without hunting all over the filesystem.  Encapsulating
      everything about a package in one place also makes it much
      easier to distribute packages independently from the OpenACS Core.
    </para>

    <para>
      In order to make this work, we need a system that keeps track of the
      packages that have been installed in the server, where those packages
      have been installed, and a standard way to map URLs that a client
      sends to our server to the right page in the appropriate
      package. While we&#39;re at it, this tool should also automate
      package installation, dependency checking, upgrades, and package
      removal. In OpenACS &majorversion;, this tool is called the <link linkend="packages-apm">APM</link>.
    </para>

    <para>
      <indexterm id="baby"><primary>OpenACS Package</primary></indexterm>
      To illustrate the general structure of a package, let&#39;s see what the
      package for the "notes" application should look like.
    </para>

    <figure>
      <title>Package file layout diagram</title>
      <programlisting>
ROOT/
  +-- packages/    APM Root
        |
        +-- notes/  Package Root 
        |     |
        |     +-- notes.info                              Package Specification File
        |     +-- sql/
        |     |     |
        |     |     +-- oracle/
        |     |     |        |
        |     |     |        +-- notes-create.sql         Data Model Creation Script for Oracle
        |     |     |        +-- notes-drop.sql           Data Model Drop Script
        |     |     |        +-- *.sql                    Data Model Files
        |     |     |        +-- upgrade/
        |     |     |            +-- upgrade-4.1-4.5.sql  Data Model Upgrade Scripts
        |     |     +-- postgresql/
        |     |     |        |
        |     |     |        +-- notes-create.sql         Data Model Creation Script for PostgreSQL      
        |     |     |        +-- notes-drop.sql           Data Model Drop Script
        |     |     |        +-- *.sql                    Data Model Files
        |     |     |        +-- upgrade/
        |     |     |            +-- upgrade-4.1-4.5.sql  Data Model Upgrade Scripts
        |     +-- tcl/
        |     |     |
        |     |     +-- notes-procs.tcl                   Tcl Library
        |     |     +-- notes-procs.xql                   SQL92 Queries for notes-procs.tcl
        |     |     +-- notes-procs-oracle.xql            Oracle-specific queries for notes-procs.tcl
        |     |     +-- notes-procs-postgresql.xql        PostgreSQL-specific Queries for notes-procs.tcl
        |     |     +-- notes-init.tcl                    Tcl Initialization
        |     |     +-- notes-init.xql                    Queries for notes-init.tcl (work in all DBs)      
        |     |     +-- *.tcl                             Tcl Library Files
        |     +-- lib/
        |     |     |
        |     |     +-- *.tcl                             Includable page logic
        |     |     +-- *.adp                             Includable page templates
        |     +-- www/
        |     |     |
        |     |     +-- admin/                            Administration UI
        |     |     |     +-- tests/                      Regression Tests
        |     |     |     |     +-- index.tcl             Regression Test Index Page
        |     |     |     |     +-- ...                   Regression Tests
        |     |     |     +-- index.tcl                   Administration UI Index Page
        |     |     |     +-- ...                         Administration UI Pages
        |     |     |
        |     |     +-- doc/                              Documentation
        |     |     |     +-- index.html                  Documentation Index Page
        |     |     |     +-- ...                         Administration Pages
        |     |     +-- resources/                        Static Content
        |     |     |     +-- ...                         Static Content files
        |     |     +-- index.tcl                         UI Index Page
        |     |     +-- index.adp                         UI Index Template
        |     |     +-- index.xql                         Queries for UI Index page      
        |     |     +-- *.tcl                             UI Logic Scripts
        |     |     +-- *.adp                             UI Templates
        |     |     +-- *-oracle.xql                      Oracle-specific Queries
        |     |     +-- *-postgresql.xql                  PostgreSQL-specific Queries
        +-- Other package directories.</programlisting>
    </figure>
    <para>
      All file locations are relative to the package root, which in this
      case is <computeroutput>ROOT/packages/notes</computeroutput>. The following table
      describes in detail what each of the files up in the diagram contain.
    </para>

    <para> 
      A special note on the
      <computeroutput><replaceable>PACKAGE-KEY</replaceable>/www/resources</computeroutput>
      directory.
      Files in this directory are available at
      <computeroutput>http://<replaceable>yourserver</replaceable>/resources/<replaceable>PACKAGE-KEY</replaceable>/...</computeroutput>
      and are returned without any permissions checking or even checks
      that the package is installed or mounted.  Files are returned
      directly, so .tcl or .adp files are not sourced in these
      directories.  This makes it suitable for storing icons, css
      files, javascript, and other static content which can be treated
      this way.
    </para>
    
    <table tocentry="1" frame="all">
      <title>Package files</title>
      <tgroup cols="3" colsep="5" rowsep="5">
    <thead>
      <row>
        <entry>File Type</entry>
        <entry>Its Use</entry>
        <entry>Naming Convention</entry>
      </row>
        </thead>
        <tbody>
      <row>
        <entry>Package Specification File</entry>
        <entry>The package specification file is an XML file generated and
          maintained by the OpenACS Package Manager (APM).  It specifies
          information about the package including its parameters and its
          files.</entry>
        <entry><computeroutput>notes.info</computeroutput></entry>
      </row>
      <row>
        <entry>Data Model Creation Script</entry>
        <entry>
          Contains the SQL that creates the necessary data model and
          PL/SQL packages (or PL/pgSQL or whatever) to support the
          package. The name must match the convention below or the
          package will not be installed correctly. Notice that
          the script must be under the appropriate directory for
          the database you are developing your package for
          (hopefully all OpenACS-supported databases :-))
        </entry>
        <entry>
          <computeroutput>sql/&lt;database&gt;/notes-create.sql</computeroutput>
        </entry>
      </row>
      <row>
        <entry>Data Model Drop Script</entry>
        <entry>Contains the SQL that removes the data model and PL/SQL
          packages generated by the creation script.  The name must
          match the convention below or the package will not be
          installed correctly.
        </entry>
        <entry>
          <computeroutput>sql/&lt;database&gt;/notes-drop.sql</computeroutput>
        </entry>
      </row>
      <row>
        <entry>Data Model File</entry>
        <entry>Any .sql file that does not match the naming convention above
          is recognized as a data model file.  It is useful to separate
          the SQL in the creation and drop scripts into several
          files and then have the scripts source the other data model
          files. In Oracle this can be done by including 
          <emphasis>@@ filename</emphasis> in the creation or drop
          scripts. See the <ulink
        url="http://www.orafaq.com/wiki/Scripts">
        Oracle FAQ</ulink> for examples. In
          PostgreSQL the same is accomplished by including <emphasis>\i filename</emphasis>.
        </entry>
        <entry>
          <computeroutput>sql/&lt;database&gt;/*.sql</computeroutput>
        </entry>
          </row>
          <row>
            <entry>Data Model Upgrade Scripts</entry> 
            <entry>
              Contain changes to the data model between versions. The APM
              can automatically load the appropriate upgrade scripts when
              upgrading to a new version of a package.
            </entry>
            <entry>
              <computeroutput>sql/&lt;database&gt;/upgrade/upgrade-&lt;old&gt;-&lt;new&gt;.sql</computeroutput>
            </entry>
          </row>
      <row>
        <entry>
          SQL92 Query Files
        </entry>
        <entry>
          Files with queries that are supported by all
          databases. These are usually SQL92 queries. Notice that
          the .xql filename must match the name of the .tcl file
          that uses those queries.
        </entry>
        <entry>
          <computeroutput>
        *.xql
          </computeroutput>
        </entry>
      </row>
      <row>
        <entry>
          Oracle-specific Query Files
        </entry>
        <entry>
          Files with queries that are Oracle-specific. Notice that
          the .xql filename must match the name of the .tcl file
          that uses those queries.
        </entry>
        <entry>
          <computeroutput>
        *-oracle.xql
          </computeroutput>
        </entry>
      </row>      
      <row>
        <entry>
          PostgreSQL-specific Query Files
        </entry>
        <entry>
          Files with queries that are PostgreSQL-specific. Notice that
          the .xql filename must match the name of the .tcl file
          that uses those queries.
        </entry>
        <entry>
          <computeroutput>
        *-postgresql.xql
          </computeroutput>
        </entry>
      </row>    
      <row>
        <entry>Tcl Library Files</entry>
        <entry>
          The Tcl library files include a set of procedures that provide
          an application programming interface (API) for the package to
          utilize.
        </entry>  
        <entry><computeroutput>tcl/notes-procs.tcl</computeroutput></entry>
      </row>
      <row>
        <entry>Tcl Initialization</entry>
        <entry>The initialization files are used to run Tcl procedures that
          should only be sourced once on startup.  Examples of
          statements to put here are registered filters or procedures.
          Tcl initialization files are sourced once on server startup
          after all of the Tcl library files are sourced.
        </entry>
        <entry>
          <computeroutput>tcl/notes-init.tcl</computeroutput>
        </entry>
      </row>
      <row>
        <entry>Administration UI</entry>
        <entry>The administration UI is used to administer the instances of
          the package.  For example, the forums administration UI is
          used to create new forums, moderate postings, and create new
          categories for forums postings.</entry>
        <entry><computeroutput>www/admin/*</computeroutput></entry> 
      </row>
      <row>
        <entry>Administration UI Index Page</entry>
        <entry>Every package administration UI must have an index page.  In
          most cases, this is <computeroutput>index.tcl</computeroutput> but it can be
          any file with the name <computeroutput>index</computeroutput>, such as
          index.html or index.adp.</entry>
        <entry><computeroutput>www/admin/index.tcl</computeroutput></entry>
      </row>
      <row>
        <entry>Regression Tests</entry>
        <entry>Every package should have a set of regression tests that
          verify that it is in working operation.   
          These tests should be able to be run at any time after the package has
          been installed and report helpful error messages when there is
          a fault in the system.</entry>
        <entry><computeroutput>www/admin/tests/</computeroutput></entry>
      </row>
      <row>
        <entry>Regression Test Index Page</entry>
        <entry>The regression test directory must have an index page that
          displays all of the tests available and provides information
          on how to run them.  This file can have any extension, as long
          as its name is <computeroutput>index</computeroutput>.</entry>
        <entry><computeroutput>www/admin/tests/index.html</computeroutput></entry>
      </row>
      <row>
        <entry>Documentation</entry>
        <entry>Every package must include a full set of documentation that
          includes requirements and design documents, and user-level and
          developer-level documentation where appropriate.</entry>
        <entry><computeroutput>www/doc/</computeroutput></entry>
      </row>
      <row>
        <entry>Documentation Index Page</entry>
        <entry>The documentation directory must include a static HTML file with the name
          of <computeroutput>index.html</computeroutput>.</entry>
        <entry><computeroutput>www/doc/index.html</computeroutput></entry>
      </row>
      <row>
        <entry>UI Logic Scripts</entry>
        <entry>Packages provide a UI for users to access the system.  The UI
          is split into Logic and Templates.  The logic scripts
          perform database queries and prepare variables for
          presentation by the associated templates.</entry>
        <entry><computeroutput>www/*.tcl</computeroutput></entry>
      </row>
      <row>
        <entry>UI Templates</entry>
        <entry>Templates are used to control the presentation of the UI.
          Templates receive a set of data sources from the logic scripts
          and prepare them for display to the browser.</entry>
        <entry><computeroutput>www/*.adp</computeroutput></entry>
      </row>
      <row>
        <entry>UI Index Page</entry>
        <entry>The UI must have an index page composed of a logic script
          called <computeroutput>index.tcl</computeroutput> and a template called
          <computeroutput>index.adp</computeroutput>.</entry>
        <entry><computeroutput>www/index.tcl</computeroutput></entry>
      </row>
    </tbody></tgroup>
    </table>

  </sect2>

  <sect2 id="packages-apm">
    <title>The APM</title>

    <para>
      The APM is used to create, maintain, and install packages.  It takes
      care of copying all of the files and registering the package in the
      system.  The APM is responsible for:
    </para>

    <orderedlist>
      <listitem><para>Package registration</para></listitem>
      <listitem><para>Automatic installation of packages: loading data models, code
      libraries, and so on.</para></listitem>
      <listitem><para>Checking what packages depend on what other packages.</para></listitem>
      <listitem><para>Storing information on the package including ownership and a file
      list.</para></listitem>
    </orderedlist>

    <para>
      In addition for packages that are applications, the APM is responsible
      for keeping track of where in the site a user must go in order to use
      the application. To do this, the APM defines a set of objects that we
      call <emphasis>package instances</emphasis>. Once a package is loaded, the
      administrator can create as many instances of the package as she
      likes, and map these instances to any URL in the site that she
      wants. If packages are analogous to executable programs in an
      operating system, then package instances are analogous to multiple
      running copies of a single program. Each instance can be independently
      administered and each instance maintains its own set of application
      parameters and options.
    </para>

    <para>
      The following sections will show you how to make a package for the
      Notes application. In addition, they will discuss some site
      management features in OpenACS &majorversion; that take advantage of the APM&#39;s package
      instance model. The two most important of these are <emphasis>subsites</emphasis>,
      and the <emphasis>site map</emphasis> tool, which can be used to map applications to
      one or more arbitrary URLs in a running site.
    </para>

    <para>
      We will also discuss how to organize your files and queries so
      they work with the OpenACS Query Dispatcher.
    </para>

  </sect2>


  <sect2 id="packages-making-a-package" xreflabel="Making a Package">
    <title>Making a Package</title>


    <para>
      Here is how you make a package.
    </para>

    <orderedlist>

      <listitem><para>Login as a site-wide administrator on your web service.
    </para></listitem>


      <listitem><para>Go to the package manager on your server.  The URL is <ulink url="/acs-admin/apm">/acs-admin/apm</ulink>.
    </para></listitem>


      <listitem><para>Click on the link <ulink url="/acs-admin/apm/package-add">/acs-admin/apm/package-add</ulink>.
    </para></listitem>


      <listitem><para>Fill out the form for adding a new package. The form explains what
      everything means, but we&#39;ll repeat the important bits here for easy
      reference:

      <variablelist>
        <varlistentry>
          <term>Package Key
          </term>
          
          <listitem><para>
          This is a short text string that should uniquely name your package to
          distinguish it from all the others. It is used as a database key to
          keep track of the package and as the name of the directory in the filesystem where all the files related to your package will live. Example
          package keys in the current system include: <computeroutput>forums</computeroutput>,
          <computeroutput>acs-kernel</computeroutput> and so on. For the example application, we
          will use the package key <computeroutput>notes</computeroutput>.
        </para></listitem>
        </varlistentry>
        <varlistentry>
          <term>Package Name
          </term>
          
          <listitem><para>
          This is a short human readable name for your package. For our example,
          we will use the name "Notes".
        </para></listitem>
        </varlistentry>
        <varlistentry>
          <term>Package Plural
          </term>
          
          <listitem><para>
          If your package name is a nice singular noun, this should be the
          plural form of it. I assume the plural form is used when multiple
          instances of the package are used by a single service. We&#39;ll talk more
          about package instances later. Our example application doesn&#39;t really
          have a good plural name. So just make it also be "Notes".
        </para></listitem>
        </varlistentry>
        <varlistentry>
          <term>Package Type
          </term>
          
          <listitem><para>
          Generally we think of packages as either being <emphasis>applications</emphasis>,
          meaning that the package is meant primarily for use by end-users, or
          <emphasis>services</emphasis> meaning that the package is meant to be a reusable
          library of code, to be used by other packages. <computeroutput>forums</computeroutput> is
          a good example of an application, while <computeroutput>acs-templating</computeroutput> is
          a good example of a service. Our example is an application, so pick
          that.
        </para></listitem>
        </varlistentry>
        <varlistentry>
          <term>Package URL
          </term>
          
          <listitem><para>
          The URL from which people will download your package when it is
          done. Just use the default for this, you can change it later.
        </para></listitem>
        </varlistentry>
        <varlistentry>
          <term>Initial Version
          </term>
          
          <listitem><para>
          Just use the default here, which by convention is 0.1d.
        </para></listitem>
        </varlistentry>
        <varlistentry>
          <term>Version URL
          </term>
          
          <listitem><para>
          Just use the default here.
        </para></listitem>
        </varlistentry>
        <varlistentry>
          <term>Summary and Description
          </term>
          
          <listitem><para>
          Enter a short summary and longer description of what the Notes
          application will do. That is, something like "this application keeps
          short textual notes in the database", and so on.
        </para></listitem>
        </varlistentry>
      </variablelist>
    </para></listitem>


      <listitem><para>Click the button &quot;Create Package&quot;.
    </para></listitem>



      <listitem><para>At this point, APM will create a directory called
      <computeroutput>ROOT/packages/notes</computeroutput>.
    </para></listitem>


      <listitem><para>
          The directory that APM created will be empty except for the
          <computeroutput>notes.info</computeroutput> file. Create a file
          called
          <computeroutput>ROOT/packages/notes/sql/oracle/notes-create.sql</computeroutput>. We&#39;ll
          fill this file with our <link linkend="objects">data model</link>
          very soon. Create a file called
          <computeroutput>ROOT/packages/notes/sql/oracle/notes-drop.sql</computeroutput>. This
          will contain the instructions to drop the data model. To be
          complete, you would also create the PostgreSQL versions of these
          files as well in
          <computeroutput>ROOT/packages/notes/sql/postgresql/notes-create.sql</computeroutput>
          and
          <computeroutput>ROOT/packages/notes/sql/postgresql/notes-drop.sql</computeroutput>.
        </para>

        <para>
          After you do this, go back to the main APM page. From there,
          click the link called &quot;notes&quot; to go to the management
          page for the new package. Now click the link called &quot;Manage
          file information&quot;, then the &quot;Scan the
          <computeroutput>packages/notes</computeroutput> directory for
          additional files in this package&quot; link on that page to scan
          the filesystem for new files.  This will bring you to a page
          that lists all the files you just added and lets you add them to
          the <computeroutput>notes</computeroutput> package.
        </para>

        <para>
          Note that while the <computeroutput>.sql</computeroutput> files
          have been added to the package, they <emphasis>have not</emphasis>
          been loaded into the database. For the purposes of development,
          you have to load the data model by hand, because while OpenACS
          has automatic mechanisms for loading and reloading
          <computeroutput>.tcl</computeroutput> files for code, it does not
          do the same thing for data model files.  
        </para></listitem>

      <listitem><para>Now go back to the main management page for the <computeroutput>notes</computeroutput>
          If your package has parameters, create them using the &quot;Manage
          Parameter Information&quot; link.  Define package callbacks via the "Tcl Callbacks (install,
        instantiate, mount)" link.</para></listitem>
      
      <listitem><para>The new package has been created and installed in the server. At
      this point, you should add your package files to your CVS repository.
      I&#39;ll assume that you have set up your development repository according
      to the standards described in 
      <link linkend="cvs-service-import">this appendix</link>. If so, then you just do this:
    </para>
    

    <programlisting>% cd ROOT/packages
% cvs add notes
% cd notes
% cvs add notes.info
% cvs add sql
% cd sql
% cvs add *.sql
% cd ROOT/packages/notes
% cvs commit -m "add new package for notes"
    </programlisting>



      </listitem>


      <listitem><para>
      Now you can start developing the package. In addition to writing code,
      you should also consider the tasks outlined in the <link linkend="tutorial-newpackage">package development tutorial</link>.
    </para></listitem>

    </orderedlist>


  </sect2>

  <sect2 id="packages-subsites">
    <title>The Site Map and Package Instances</title>


    <para>
      At this point, you are probably excited to see your new package in
      action. But, we haven&#39;t added any user visible pages yet. By
      convention, user visible pages go in the
      <computeroutput>ROOT/packages/notes/www</computeroutput> directory. So go there and add a
      file called <computeroutput>hello.html</computeroutput> with some text in it. Now we have
      to make the user pages visible in the site. Since we didn&#39;t put the
      pages underneath <computeroutput>ROOT/www</computeroutput> they will not appear on their
      own.  What we have to do is <emphasis>mount</emphasis> the application into the site
      map. That is, we have to define the URL from which the application
      will serve its pages.
    </para>

    <para>
      In OpenACS &majorversion;, administrators can define an arbitrary mapping between the
      URLs the user types and the actual file in the filesystem that is
      served. This mapping is called the <emphasis>site map</emphasis> and entries in the
      site map are called <emphasis>site nodes</emphasis>. Each site node maps a URL to an
      OpenACS object. Since package instances are objects, the site map allows
      us to easily map package instances to URLs. As we said before, each
      instance of an application has its own set of parameters and
      runs from its own URL within the site.  What this means is that even
      though all the code for the <computeroutput>notes</computeroutput> application lives in
      <computeroutput>ROOT/packages/notes</computeroutput>, the application itself can run from
      any number of locations in the site. This allows developers and
      administrators to build sites that look to the user like a collection
      of many independent applications that actually run on a single shared
      code base. The <link linkend="request-processor">request-processor</link> document shows
      you how OpenACS figures out which instance of your application was
      requested by the user at any given time. The <link linkend="subsites">page development</link> tutorial shows you how to use this
      information in your user interface.
    </para>

    <para>
      In order to make the new <computeroutput>notes</computeroutput> application visible to
      users, we have to mount it in the site map. You do this by going to
      the <ulink url="/admin/site-map">Site Map</ulink> page, which is by
      default available at <computeroutput>/acs-admin/site-map</computeroutput>. Use the
      interface here to add a new sub-folder called <computeroutput>notes</computeroutput> to
      the root of the site, then click &quot;new application&quot; to mount a new
      instance of the <computeroutput>notes</computeroutput> application to the site. Name the
      new instance <computeroutput>notes-1</computeroutput>.
    </para>

    <para>
      Then type this URL into your browser: <computeroutput>http://<replaceable>yourserver</replaceable>/notes/hello.html</computeroutput>
    </para> 

    <para>
      Now you should see the contents of the page that you added. What has
      happened is that all URLs that start with <computeroutput>/notes</computeroutput> have
      been mapped in such a way as to serve content from the directory
      <computeroutput>ROOT/packages/notes/www</computeroutput>. At this point, you can
      experiment with the site map by mounting multiple instances of the not
      yet written Notes application at various places in the site. In a
      later document, we&#39;ll see how to write your application so that the
      code can detect from what URL it was invoked. This is the key
      to supporting <link linkend="subsites">subsites</link>.
    </para>

  </sect2>

  <sect2 id="packages-summary">
    <title>Summary</title>


    <para>
      The APM performs the following tasks in an OpenACS site:
    </para>

    <itemizedlist>
      <listitem><para>
      Manages creation, installation, and removal of packages from the
      server. Also keeps track of what files belong to which packages.
    </para></listitem>
      <listitem><para>
      Manages package upgrades.
    </para></listitem>
      <listitem><para>
      Manages information on all package <emphasis>instances</emphasis> in a site. For
      correctly written application packages, this allows the site
      administrator to map multiple instances of a package to URLs within a
      site.
    </para></listitem>
      <listitem><para>
      Writes out package distribution files for other people to download and
      install. We&#39;ll cover this later.
    </para></listitem>
    </itemizedlist>

  </sect2>

  <sect2 id="packages-add-reading">
    <title>Additional Reading</title>


    <itemizedlist>
      <listitem><para><xref linkend="apm-design"/></para></listitem>
      <listitem><para><xref linkend="apm-requirements"/></para></listitem>
      <listitem><para><link linkend="tutorial-newpackage">package development tutorial</link></para></listitem>
    </itemizedlist>

    <para><phrase role="cvstag">($Id: packages.xml,v 1.13.2.3 2021/09/02 16:56:01 gustafn Exp $)</phrase></para>

  </sect2>

</sect1>