Delivering Applications

$Revision: 5.0.2.18 $

The document introduction.htm provides an overview of the Allegro CL documentation with links to all major documents. The document index.htm is an index with pointers to every documented object (operators, variables, etc.) The revision number of this document is below the title. These documents may be revised from time to time between releases.

1.0 Delivery introduction
2.0 Definitions
3.0 Developing the application and preparing for delivery
    3.1 A summary of the delivery process
    3.2 Legal and licensing issues
    3.3 Deciding on necessary features
    3.4 Deciding on top-level (user interaction)
    3.5 Packaging the product
    3.6 Including all desired modules
    3.7 Defining the init functionality
    3.8 Specifying the initial value of *package*
    3.9 Setting up logical pathname translations
    3.10 Use of shared libraries (foreign files)
    3.11 CLOS training
          3.11.1 Generic functions, method combination, and discrimination
          3.11.2 Effective methods
          3.11.3 Caches for fast dispatching
          3.11.4 Constructor functions
          3.11.5 How to do CLOS start up optimizations
          3.11.6 make-instance optimization
4.0 Creating the deliverable
    4.1 Resources
    4.2 Defsystem
    4.3 Tuning the application
    4.4 More on the development environment
    4.5 GC parameters and switches
    4.6 GC cursors
    4.7 Allegro Presto
    4.8 Allegro Runtime
    4.9 Windows specific information
    4.10 Installation of your application on Windows using the Install Wizard
5.0 Patching your application after delivery
    5.1 The Allegro CL patch naming scheme
    5.2 Loading Allegro CL patches
    5.3 Patches for your application
    5.4 Creating patch files in general
    5.5 Creating a patch file
    5.6 What to do with patch files
    5.7 Including application patches in an image at build time
    5.8 Superseding a patch
    5.9 Withdrawing a patch
    5.10 Distributing patches
    5.11 Loading patches

1.0 Delivery introduction

This document describes the application generation utility in Allegro CL. The facility differs from the image creation utility (see building_images.htm) in that the end result is not a single image file but a directory of files in theory suitable to be delivered to customers after suitable packaging.

2.0 Definitions

3.0 Developing the application and preparing for delivery

3.1 A summary of the delivery process

excl:generate-application is the functional entry point for developers to deliver an application.  In short, the task of this function is to build and assemble the files for the developer's application into a single directory. This directory has many purposes. As a developer, you might want to:

  1. Test your application under non-development conditions.
  2. Test your application on another machine, to shake out any environmental dependencies in your application.
  3. Package your application for delivery to your customers.

For the purposes of this document, it does not matter what the motivation of the developer is, excl:generate-application is used in the same way.

There are three distinct delivery types, as:

  1. an .exe (ACL is in control even though the user might not be able to tell this),
  2. [MS Windows only] an OLE in-proc server (the application is started via OLE calls in another application), and
  3. a shared library.

(1) is the most common case, by far. (3) is what is commonly called Lisp as a subroutine. (2) requires registration, either implicit or explicit. In the OLE samples there are examples of both.

It is important to understand that excl:generate-application calls excl:build-lisp-image and the latter starts a new (operating system) process to build the requested image.  Any errors that occur during the building of the image will be handled in the other process.  The development environment of the originating process (the one in which the call to excl:generate-application was made) cannot be used to debug the problem in the image creating process.

3.2 Legal and licensing issues

If you want to distribute your applications outside your organization or inside your organization to users with machines not licensed to run Allegro CL, you must be licensed to do so by Franz Inc. One type of license that allows distribution is an Allegro Runtime license. See runtime.htm. Please contact your Franz Inc. sales representative for information on licensing applications.

3.3 Deciding on necessary features

You need all the features necessary to run your application (whatever they may be). Certain items, like the debugger, the inspector, the tracer, will not be present in an image unless explicitly used or called for. If you are preparing a runtime delivery, be sure to check the license to see what modules cannot be included in the image. See

If you are building a standard Allegro Runtime image (see runtime.htm), the value of the runtime keyword argument to generate-application (actually, it is a build-lisp-image argument accepted by generate-application) should be :standard. In that case, the compiler cannot be in the final image. You must either specify include-compiler nil or include-compiler t and discard-compiler t. The latter choice allows the compiler to be present during the build, which is sometimes useful. The compiler may be included in a Dynamic Allegro Runtime image (value of runtime :dynamic).

3.4 Deciding on top-level (user interaction)

Are your users going to interact with your application through the Lisp top-level (so they will enter Lisp forms or at least one Lisp form), through a custom top-level of your own, or will users interact with your application via some graphical user interface? (Of course, some applications may have no top-level -- that is, little or no user interaction is necessary.)

If your application has a custom top-level, you must write its functionality and have it initiated when the application image starts. You do this by having the function that is the value of excl:*restart-app-function* initiate your top-level, if there is one. If your application has no top level then the value of excl:*restart-app-function* should be the function that starts your application running.

If you want to use the standard Lisp top-level, leave the value of excl:*restart-app-function* nil. Any initializations you might want to do can be done by the function that is the value of excl:*restart-init-function*.

A minimal top-level is provided if you build an image with the include-tpl argument to build-lisp-image (or generate-application, which accepts build-lisp-image arguments). See 10.0 Minimal top levels in building_images.htm for more information.

3.5 Packaging the product

In the past, it was possible to deliver a application as a single file (called a standalone application). This is no longer possible. We have, however, created a precise method to help you easily create and identify all the necessary parts of your application for it to be complete. excl:generate-application is the entry point to creating your application. This function creates a directory containing all the files making up your application.

3.6 Including all desired modules

A standard Allegro CL image on startup does not contain all the system modules that a program may invoke. Instead, certain modules are left out (and contained in the bundle file, typically sys:files.bu but the name varies, or in a fasl file in sys:;code;). When a module is called for with cl:require, it is looked for according to the sys:*require-search-list*, which usually looks in sys:;code; and, if it is not there, in the bundle file (which is essentially a collection of fasl files). Further, when an important function associated with the facility is called (e.g. cl:trace for the trace module, ff:def-foreign-call for the foreign module, etc.), the system detects that the module must be loaded and loads it automatically (a process called autoloading).

However, the bundle file, files.bu or whatever it is named, generally cannot be distributed with applications. (It is explicitly forbidden to distribute the bundle file, usually files.bu but the name varies, with a runtime image. If you have a VAR license, you may or may not be allowed to distribute the bundle file, depending on the terms of the license. In this document, we assume you are not permitted to distribute the bundle file. If you have any questions about licensing issues, please contact your Franz Inc. Sales Representative. If you have an Allegro Runtime license, see runtime.htm)

Therefore, if you want functionality in your application, you must ensure that it is loaded into the image.

The file sys:develenv.cl contains a list of cl:require's that load optional functionality for the Lisp development environment. Some of the cl:require's in this file are explicitly forbidden by your Allegro CL Runtime or Allegro CL Dynamic Runtime license agreements, and those are identified by comments. Do not modify that file but rather copy it or the desired parts of it to your own file for use with your own application.

You should cl:require the modules you need for your application to work properly. Check the autoloads.out file generated when the autoload-warning keyword argument to excl:generate-application is specified true. That will tell you what modules might be autoloaded in your application and you can decide whether it is necessary to include the module. (Just because a module might be autoloaded, that does not mean that it will be autoloaded in your application. Modules are typically autoloaded when an important function associated with the module is called. If your application does not call a function that triggers an autoload, the autoload won't occur.)

3.7 Defining the init functionality

Look at the file sys:;code;aclstart.cl. It is the source for the startup routine used by Lisp (the function start-lisp-execution) along with the sources for some ancillary functionality. (Note that the low-level initialization, including mapping of .so/.dll files built with the image -- systems libraries and others -- has already been performed when start-lisp-execution is called. As with any UNIX or Windows program, failure to find a needed shared object or Library file (so/sl/dll) during the low-level startup causes immediate program failure usually accompanied by a terse message identifying the unfound file. ff:list-all-foreign-libraries can be used to identify dependencies on .so, .sl and .dll files.)

To see exactly what Lisp is doing when it starts up, regarding the loading of the Lisp shared library, you can set the environment variable ACL_STARTUP_DEBUG.   This will help diagnose errors in the startup process. Note that on Windows, the messages are displayed in the Console window.

Examining the source for start-lisp-execution will tell you the exact sequence of operations -- when the command-line arguments are processed, when the init files are read, etc., so you can know in what order to do things. The startup sequence is also given in startup.htm.

There are several places where a programmer can intervene in the startup process. One relatively early place is the restart-actions list (the value of excl:*restart-actions*). Please note that this list may be used by Allegro CL or related functionality (such as CLIM). Therefore, you should add to the list but do not remove items from it or destroy it. Even earlier, -e command-line arguments are processed (assuming command-line arguments are not ignored by the image). Slightly later (but with a different interface), the ACL_STARTUP_HOOK environment variable is examined. If it has a value, that value is read by read-from-string and the result is evaluated.

The final two locations for programmer intervention in startup are the functions which are the values of excl:*restart-init-function* and excl:*restart-app-function*. As you may see from aclstart.cl, if excl:*restart-init-function* is non-nil, it is assumed to name a function with no arguments and that function is funcall'ed. The purpose of excl:*restart-init-function* is to perform application initializations of any sort.

After excl:*restart-init-function* completes, either excl:*restart-app-function* is funcall'ed (if it is non-nil) or a standard Lisp listener is started (but not both). If your application has its own top-level, it should be started with excl:*restart-app-function*. (Or if no top-level is needed, excl:*restart-app-function* would perform whatever your application does.) Note that the function that is the value of excl:*restart-app-function* must not return. The consequences are undefined if it does return.

It is entirely your choice whether you use your own top-level or use the Lisp top-level. Note too that if you are using the normal top level, that is the image was built with the include-tpl argument to build-lisp-image true, you can start a Lisp top-level at any time by evaluating the following form:

(tpl:start-interactive-top-level
  *terminal-io* 'tpl:top-level-read-eval-print-loop nil)

3.8 Specifying the initial value of *package*

Programmers who use a Lisp listener as a top-level often want the current package (the value of *package*) to be something other than the common-lisp-user package when the user sees the first prompt. Evaluating the following two forms as part of excl:*restart-init-function* accomplishes this (replace :my-package with the desired name, of course):

(tpl:setq-default *package* (find-package :my-package))
(rplacd (assoc 'tpl::*saved-package*
               tpl:*default-lisp-listener-bindings*)
    'common-lisp:*package*)

The second form suppresses (on startup) the [changing package from ...] warning printed by Allegro CL when the value of *package* is changed by the system in a Lisp listener (usually printed when the debugger is entered).

3.9 Setting up logical pathname translations

All logical pathname translations (except the one for sys: set up in the low-level startup code) are cleared as one of the first actions of start-lisp-execution (indeed, the first form in the function definition):

(defun start-lisp-execution ()
  ...
  (flush-all-logical-pathname-translations)
  ...)

This is often not what an application developer wants. However, it is a conscious choice because the potential for mysterious bugs resulting from bogus translations being present in the image outweighed the cost of re-establishing the translations when needed.

If you want to use logical pathname translations in your application, then you will need to arrange for them to be present. You can do this in one of two ways:

  1. Have sys:hosts.cl contain translations for additional hosts as required. Allegro CL, when it sees a logical host for which no translation is defined, will first look in sys:hosts.cl to see if a translation is defined there. This file may have to be configured at the customer site (since the directory structure is usually different in every system).
  2. Have another file where the translations are defined. Use excl:logical-pathname-translations-database-pathnames prior to creating the image to tell the system about the additional translations file (and the system will look at that file in addition to hosts.cl when it encounters an unknown logical host). This solution is similar to the one just above but does not require modifying hosts.cl which may be inconvenient to modify.

3.10 Use of shared libraries (foreign files)

excl:generate-application causes all .so/.dll/.sl files loaded during image creation to be copied to the directory it creates. It also arranges, upon image restart, for these files to be reloaded from this directory (which is what will become sys:).

3.11 CLOS training

(This section was inadvertently left out of the original version of he 5.0 Allegro CL documentation.)

It is possible to significantly speed up the initialization of CLOS-based applications by gathering information about CLOS usage in the application and including that information in the application image. In this section, we discuss what information is useful for this purpose, how to collect it, and how to include it in an image.

Note about the development environment: Lisp users typically run in developer-environment mode. In that mode, the debugger, the code that runs the Emacs-Lisp interface, Allegro Composer (if ordered) etc. is loaded into Lisp. Often your application will not use those facilities and your application images will not want to include them. However, CLOS training will include information about those utilities (and then require you to have them available when you build your application image) unless you do one of the following:

This is the less-desirable solution. You can create an image without the development environment by specifying

:include-devel-env nil

in the call to build-lisp-image. You can then train with that image.

This is the better choice. You will see forms like

(clos::preload-constructors ([packages])) 

and

(clos::precache-generic-functions ([packages])) 

below. If no packages are listed, the entire system (including development environment features) will be trained. If packages are listed, only things in those packages will be trained. Packages are named by keywords. Typically, packages should include :user, :lisp, and the packages of your application. Thus, if your application packages are :foo and :bar, the preload-constructors form would be

(clos::preload-constructors (:user :lisp :foo :bar)) 

The preload-generic-functions form would similarly list the packages.

3.11.1 Generic functions, method combination, and discrimination

A generic function examines its arguments to determine which method or methods are applicable. This is called discrimination. The symbol-function of a generic-function is a discriminator function. There are various kinds of discriminators and the discriminator for a particular generic function may change during the execution of a program.

When Lisp determines that a generic function needs a different kind of discriminator it checks to see if the one it needs has already been built and, if not, creates one. The creation of a discriminator is relatively expensive since it involves the Lisp compiler.

When a CLOS application is loaded the generic-functions all have a simple discriminator which will select the correct discriminator when the generic function is first called. Therefore when a CLOS application starts it will run very slowly unless the discriminators it needs are already built. The only way to tell which discriminators your program needs is to run your program for a while and then look at the list of discriminators that exist. Allegro CL provides a mechanism for dumping out these discriminators and then loading them in with your program so that when the program starts all the discriminators it will need will already exist.

You can dump discriminator functions to a fasl file by compiling a source file that contains the following two lines after loading and running your application (note that the best optimization is achieved if you combine this with the caching optimization described below):

(in-package :clos) 
(preload-forms) 

3.11.2 Effective methods

With method-combination a call to a generic function can result in a sequence of methods being called. The code that calls the methods and processes the results of each call is called an effective method. In order to make effective methods fast, Lisp compiles them. In order to cut down on the compilation cost, Lisp actually creates effective-method templates which are functions closed over the particular methods to be called.

Thus many effective methods can share the same code. Just as in the case of discriminators above, it is expensive to start a CLOS application running if the effective methods it will need haven't been compiled already. And again Allegro CL provides a way of saving the effective methods that the application has used so that they can be defined before the application starts.

You can dump effective methods to a fasl file by compiling a source file that contains the following two lines after loading and running your application (this is the same as for discriminator functions):

(in-package :clos) 
(preload-forms) 

3.11.3 Caches for fast dispatching

Generic functions use caching to implement fast dispatching. When an application starts the caches are empty so initial performance is degraded by having to handle cache misses. Allegro CL provides a way to fill the caches when an image starts up.

You can dump caches to a fasl file by compiling a source file that contains the following two lines after loading and running your application (see above under the heading Note about the development environment for information on ([packages]) in the following form):

(in-package :clos) 
(clos::precache-generic-functions ([packages]}) 

3.11.4 Constructor functions

As we describe briefly below (under the heading make-instance optimization), calls to make-instance can be replaced with calls to some equivalent (but much faster) constructor functions. Allegro CL provides a way to preload compiled constructor functions.

You can dump constructors to a fasl file by compiling a source file that contains the following two lines after loading and running your application (see above under the heading Note about the development environment for information on ([packages]) in the following form):

(in-package :clos) 
(clos::preload-constructors ([packages])) 

3.11.5 How to do CLOS start up optimizations

The four possible start-up optimizations were just described. Conveniently, two (discrimination and effective methods) are achieved with the same utility. All depend on information being available. Therefore, the following is the first step for all optimizations:

  1. Exercise your application. Load your application into a Lisp image and run it as you expect one of your users will run it. Doing this causes Lisp to gather experience about how CLOS is being used.

Once you have exercised your application sufficiently, you are ready to create the optimizing files. This is a standard fasl file created by compiling a special source file (described below).

  1. Create the clos optimization fasl file. While Lisp is still running, create a Lisp source file (we call it closopt.cl) that contains the following four forms (see above under the heading Note about the development environment for information on ([packages]) in the following forms):
(in-package :clos) 
(preload-forms) 
(clos::preload-constructors ([packages])) 
(clos::precache-generic-functions ([packages])) 

Compile closopt.cl with compile-file. Lisp will put the discriminators, the effective methods, the constructors, and the contents of its CLOS caches into the resulting fasl file (closopt.fasl). If that file is built into the application binary image (it should be specified as one of the :lisp-files in a call to build-lisp-image, as an input-file in a call to generate-application, or required by another file specified in either location), then an application using CLOS will start up significantly faster.

Note that loading this file will not invoke the compiler so this can be loaded into a compilerless Lisp.

3.11.6 make-instance optimization

We have already discussed dumping make-instance constructor functions. Note that calls to make-instance where the class-name is a quoted constant and each of the keywords is a constant are transformed by the compiler into calls to constructor functions. A constructor function is a piece of code that is equivalent to the make-instance call except that it is significantly (10 to 100 times) faster.

The optimization is automatic when the call to make-instance is formed in a particular way.

In order for an optimized constructor function to be used certain restrictions apply:

  1. The set of keywords must be valid for the call.
  2. Only certain methods must be applicable as defined by the following table:
Generic Function: Condition for optimization:
make-instance Only system-supplied methods are applicable
initialize-instance Only system supplied-standard methods and user-supplied :after methods are applicable
shared-initialize Only system supplied-standard methods and user-supplied :after methods are applicable

Conditions for creation of constructor functions: The calls to make-instance are replaced by calls to the constructor regardless of whether an optimized constructor can be used. The first time the constructor function is called, the restrictions are tested and if ok an optimized constructor is generated. When the restrictions are not obeyed the constructor calls make-instance. Redefining a class or one of its superclasses or adding/removing a method to one of the generic functions mentioned above causes the constructor function to be recomputed.

4.0 Creating the deliverable

As stated above, excl:generate-application assembles the files needed to deliver an application. generate-application both builds the application's image file and copies any other files needed to support this image.  The definition of excl:generate-application is:

(generate-application application-name
                      destination-directory
                      input-files
                      &key allow-existing-directory
                           application-administration
                           application-files
			   (application-type :exe)
                           autoload-warning
                           (copy-shared-libraries t)
                           (copy-file-function 'sys:copy-file)
                           debug
                           image-only
                           pure-files
                           purify
                           ...excl:build-lisp-image keyword args...)

The required arguments:

The keyword arguments:

4.1 Resources

Resources are a way of specify default information for an application.  The most common of which is command line arguments.  Resources are handled differently on Windows and UNIX:

UNIX

Resources are stored in a plain text file on UNIX.  This file, sys:lisprc, if it exists can contain resource information for application startup.  Currently, this is just command line arguments. The format of lisprc is:

.command-line: command line args...

or

appname.command-line: command line args...

where appname is the name of the Lisp executable used to start Lisp and command line args... are a list of valid command line arguments. appname should be used when there are multiple applications sharing the same directory and different command line argument resources are needed for each application.

For example, a sys:lisprc of

.command-line: -Q

would cause all applications in the directory this appears to start up quietly.

If there are both command line arguments in the resource file and given on the command line that starts the application, then command line arguments seen by the application are the concatenation of the resource command line and the given command line. This allows the given command line to override the resource command line.

Windows NT only

Resources on Windows do not use sys:lisprc, since executables themselves have resources on Windows.  The relevant resource types for delivery are icons and command lines.

There is a utility, setcmd.exe, in the bin/ subdirectory of the Allegro directory, that works only on Windows NT:

setcmd [-o | -p | -r] app.exe [arguments...]

The -o flag allows you to set the command line for app.exe to the given arguments.  The -p flag prints the command line resource in app.exe.   The -r prints all resources in app.exesetcmd can be used on .dlls as well. A sample call to a command prompt is:

C:\Program Files\acl50\bin\setcmd -o app.exe +cx

When app.exe is then started, the console window will stay hidden (the meaning of the +cx argument, see startup.htm#3.0 Command line arguments).

Windows 95/8 (and also NT)

There is an alternative method of setting resources, one that works on Windows 95/8 and that allows you to change the default icon for a program.  This is how you would set the command line resource:

(require :res "winapi/res")
(windows::set-cmd-line "lisp.exe" "foo.exe" "-I" "lisp.dxl")

would change the default command line of lisp.exe to -I lisp.dxl by creating a new foo.exe.  If you want to change the icon:

(windows::set-exe-icons "lisp.exe" "foo.exe" "ACL5ICON" "foo.ico")

would change the icon of lisp.exe to that in foo.ico by creating a new foo.exe"ACL5ICON" was found from running this command:

setcmd -r lisp.exe
Resource type: Resource id: 3 (icon) Resource id: 1
Resource type: Resource id: 3 (icon) Resource id: 2
Resource type: Resource id: 3 (icon) Resource id: 3
Resource type: Resource id: 14 (group icon) Resource name: ACL5ICON
Resource type: Resource id: 14 (group icon) Resource name: ACLCONICON

4.2 Defsystem

If the application is made up of many source files, then using the defsystem utility (described in defsystem.htm) will help the management (for compilation and loading) of the application. If defsystem is used, then it is easy, for example, to create a single .fasl file representing the compiled application. See defsystem.htm and the function concatenate-system for more information.

4.3 Tuning the application

The application should be optimized. Allegro CL contains a space and time profiler that should be used to find places in the application which can be optimized. See profiling.htm. The optimization of Common Lisp source code has two components, aside from optimizing algorithms used in the application:

Both of the above can be done globally or locally to a particular function. Functions which are known to be used frequently should be optimized by declaring the types of the values bound to symbols, when the types are known and checked. Then, increasing the speed compilation quality and decreasing the safety and debug qualities will allow the compiler to produce smaller and faster code. These issues are discussed in compiling.htm. Note particularly the :explain declaration discussed that document, in 9.3 Help with declarations.

4.4 More on the development environment

If the application will not use the development environment of Allegro CL, then certain features of it can be turned off or compiled out of the application. For example, you might use the following global proclamation:

(proclaim '(optimize (debug 0)))

It will cause the compiler to compile with no consideration for easy debugging (presumably your users will not debug your application). Currently, this means local names of variables and the argument list for functions and macros will not be saved. Although the saving is not great, if these features are not to be used, then there is no reason to have the compiler annotate the fasl files with them.

Another saving can be achieved by evaluating:

(setf (argument-saving) nil)

This will cause the runtime calling sequence to be more efficient on some architectures (RS/6000 currently).

4.5 GC parameters and switches

Setting of GC parameters and switches appropriate to application-specific behavior is important for performance. gc.htm contains a complete discussion of the subject, and the only information included here is a check-list of items to consider.

Switches

:clip-new (default: nil)

If keeping newspace small so that scavenges are short is important, then this feature should be enabled. One negative aspect of this, however, is that garbage collections will be more frequent and this may cause more short-lived objects to be tenured, resulting in faster growth of the Allegro CL memory image. You should schedule more frequent global garbage collections to keep the image smaller if you set :clip-new to t.

:print (default: nil)

The users of many applications will not want to see the gc messages. Keeping this switch nil will prevent them from being printed. On the other hand, the gc message does explain why your application seems to have paused (during a gc). See also the discussion of gc cursors below.

Parameters

:generation-spread (default: 4)

Depending on the behavior of the application, changing the generation spread may cause less garbage to be tenured. The default value has been chosen for development but not the runtime environment of applications.

:free-bytes-new-other (default: 131072)
:free-percent-new (default: 25)
:free-bytes-new-pages (default: 131072)
:expansion-free-percent-new (default: 35)
:quantum (default: 32)

These parameters determine the size of newspace after a scavenge. There must be at least :free-bytes-new-pages + :free-bytes-new-other bytes free, in addition to there being at least :free-percent-new percent of newspace free. :quantum specifies the number of pages (8k each) for newly created newspaces. The initial value is 32 for 256kb newspaces. :expansion-free-percent-new specifies the percent free in newly created newspaces.

:expansion-free-percent-old (default: 35)

This specifies how much must be free in an oldspace after it is created. A new oldspace is created because there is some amount of data that needs to be tenured and there is no current oldspace that can hold it.

See gc.htm for more information on memory layout.

4.6 GC cursors

A gc cursor facility provides some visual clue to the user that a garbage collection is taking place. Application writers find gc cursors useful since their users may think the application has hung while in fact it is just gc'ing. Unfortunately, implementing a gc cursor is difficult. See gc.htm for more information.

In addition to the above parameters and switches, the variable excl:*global-gc-behavior* determines whether or not a global gc is automatically performed when a certain number of bytes have been tenured (moved into oldspace). excl:*tenured-bytes-limit* specifies this limit. It is very important to note that an interactive application would have execution suspended for an indeterminate amount of time if a global gc is performed--scavenges are normally quite short, in comparison.

The initial values for the above parameters are reasonable defaults, but there may be better defaults for individual applications. See gc.htm for more information.

4.7 Allegro Presto

Allegro Presto is designed to limit system code loading to only what is required for the system's execution. See 3.0 The Allegro Presto algorithm in loading.htm for more information about Allegro Presto.

4.8 Allegro Runtime

Allegro Runtime is a Franz Inc. product which licenses distribution of application written in Allegro CL. Please contact your Franz Inc. sales representative if you want more information on Allegro Runtime and its terms. See the document runtime.htm for technical details of Allegro Runtime, including a list of restrictions on runtime images. To produce a runtime image, the runtime keyword argument to excl:generate-application (actually to excl:build-lisp-image and passed to that function by excl:generate-application) must be specified with a non-nil value. See runtime.htm for allowable values.

4.9 Windows specific information

The file msvcrt.dll is needed by all Allegro CL applications generated on Windows (in releaases prior to 5.0.1 final, mfc42.dll was also needed but that is no longer true). If the copy-shared-libraries argument is true, generate-application copies these two files to a subdirectory of the destination-directory called system-dlls (this is a change from 5.0 where they were copied to destination-directory). When your application is installed, these dll's should be copied to the Windows system directory if necessary (i.e. if they are not already there with the same or a later version). In the system-dlls subdirectory, they will not be seen by your application or any other program.

These system dll's have presented a problem for Allegro CL applications. They are needed if the application is to run successfully but having them in more than one location where Windows sees them can create difficulties. In release 5.0, generate-application copied mfc42.dll and msvcrt.dll to destination-directory. However, we discovered there could be problems if they were also in the Windows system directory. But copying them to the Windows system directory blindly is also problematic because of version mismatch (if you overwrite a later version, other programs that depend on the later version may then fail).

Starting in 5.0.1, we have tried to mitigate this problem by providing an installation wizard (described in section 4.10 below) that does the right thing: it finds out if the dll files in the system-dlls subdirectory are in the Windows system directory already. Any that are not are copied to the Windows system directory. The versions of the ones that are present are compared to the versions of the files in system-dlls. Earlier versions are then updated (more precisely, are either then updated or things are arranged so they will be updated when Windows is restarted, so other running programs will not be affected).

Note that dll's loaded with load with :system-library specified as true (see loading.htm#1.0 Using the load function) are not copied to destination-directory or the system-dlls subdirectory. The only files that are copied to the system-dlls subdirectory are mfc42.dll and msvcrt.dll. (This may seem counter-intuitive, but we feel free to copy mfc42.dll and msvcrt.dll because Microsoft explicitly allows it.)

4.10 Installation of your application on Windows using the Install Wizard

The Allegro CL Install Wizard, a new feature in 5.0.1, is a tool that application programmers writing Allegro CL-based applications can use to help deliver applications on the Windows platform.

In accordance with your license agreement, you can distribute Allegro CL-based applications. The files in the directory described in `1. Create the initial application directory' below are typically suitable for distribution, but again, this is controlled by your license. Ask your Franz Inc. sales representative for information on what your license allows if you are unsure. We assume in the remainder of this section those files are licensed for distribution.

Reasons for providing the Install Wizard. In 5.0, you could create a delivery directory, either with generate-application or the IDE's File | Build Project Distribution menu command. It was hoped that this directory was suitable for distribution, but there turned out to be problems getting system DLL's right, as described above in section 4.9 above. The main problem, recall, is doing the right thing with system DLL's like mfc42.dll and msvcrt.dll. They should be installed on an application user's machine if necessary but should not be installed if not necessary. Somehow, it must be determined whether such installation is necessary. For that reason, simply copying the directory that is the output of generate-application or Build Project Distribution is not enough. Further, when installing on Windows, various bookkeeping tasks like updating the registry must be performed. A program, usually named setup.exe, is typically provided to perform such tasks. The Install Wizard generates an appropriate setup.exe. Note: the user installing the application must have administrator privileges on Windows NT.

Note that programs like InstallShield (tm) also perform the tasks described above. The Install Wizard provided by Allegro CL is a simpler and less flexible variant of such a program.

Here are the steps for using the Install Wizard.

1. Create the initial application directory

Use either generate-application or the IDE's File | Build Project Distribution to create a directory, which for example purposes we will call c:/foo/foo/. This will be a directory of files and subdirectories. This directory is named by the destination-directory argument to generate-application and by the Distribution directory dialog when using the Build Project Distribution menu command.

When generate-application or Build Project Distribution complete, you now have the initial application directory.

2. Run the Install Wizard

Run the Install Wizard via a shortcut on the Start | Programs | Allegro CL submenu. This will display the Install Wizard dialog, which has the following fields:

The Install Wizard will create a new directory (called the Output directory) and copy the files from the source directory to it. Further, it will generate a program named setup.exe and put it in the output directory.

The following buttons are on the bottom of the dialog: Build, Quit and Help. After filling in the fields, click on the Build button to create the directory specified in Output directory. Quit will exit without building anything. Clicking on Help displays this section of this document in a browser.

When the Install Wizard completes, the output directory is suitable for distributing to application users. We assume it is placed on a CD. How the CD is organized is up to you, the application writer. We assume the CD contains a single directory distdir/ which contains the contents of the output directory from above. (At the toplevel, it may have a file causing it to autorun when the CD is inserted into the drive. In any case, setup.exe must be in the same location with respect to all other files as it was in the output directory.) Note, this will work equally well if you are distributing on floppies or some other media.

Once you have the CD, the you as application developer have finished creating the distribution.

3. On the application user's machine

The application user (persumably your customer) receives the application CD and puts it into the CD drive. On the application user's machine, there is (presumably) no Allegro CL, and the source and output directories mention in 2 above do not exist either. Recall we assume the CD contains a single directory distdir/ which contains the contents of the output directory from 2 above. That directory includes the installation program setup.exe. To run setup.exe on Windows NT, the user must have administrator priviliges.

When the application user runs setup.exe (in our example, by inserting the CD into the drive and running distdir/setup.exe),  setup.exe will ask the user for an installation directory for the application, which we will call appdir. This directory must not exist. setup.exe will create the directory and install the application. Specifically, setup.exe will do the following:

  1. Create appdir and copy the application specific files to the appdir on the user's machine.
  2. Determine which of the DLLs in the system-dlls/ subdirectory need to be installed. It will either immediately copy any that do need installation to the Windows `system' directory, or will arrange for them to be installed upon the next reboot of Windows.
  3. Update the registry. This includes adding or updating to \\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\SharedDLLs.
  4. Copy the uninstallation program to <appdir>\uninstall\.
  5. Adds uninstall entries to the registry key \\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\uninstall\.
  6. If shared DLLs were to be installed which already existed, then tell the end-user to reboot to complete the installation.

If at any time the installation program gets an error, it will undo any changes made to the disk containing appdir and to the Windows registry. That is, if the application does not install, then appdir will not exist and no application files will be left on the user's machine, and the registry will be left in the pre-installation state.

5.0 Patching your application after delivery

There may be bugs in the version of Allegro CL used to deliver your application and there may be bugs in your application code. Both need to be fixed for your users. Allegro CL bugs are typically fixed by patches supplied by Franz Inc. You, of course, have to decide how to provide fixes for bugs in your application, but you may wish to mimic the patch system used by Allegro CL. That system and the tools associated with it are the subject of this section.

The following two features are useful in a patch system:

The tools provided support both features. The tools are the sys:defpatch macro, the sys:load-patches function, and the excl:featurep predicate function.

5.1 The Allegro CL patch naming scheme

We first describe the Allegro CL patch scheme and then discuss how you can adapt it to your application's needs. Allegro CL patch files are named as follows:

p<m><p><n>.<v>

So the first letter is p, followed by

For example, p0a001.001 is the first version of the first patch file on version 0 (ACL 5.0) for product a (Allegro CL base Lisp). p0a001.002 is the second version of the first patch file. p0a011.002 is the second version of the eleventh patch file.

5.2 Loading Allegro CL patches

All Allegro CL patches are placed in one directory, sys:;update, that is the update subdirectory of the Allegro directory, where Allegro CL was installed. (In earlier release on Unix, there were several patch directories. In version 5.0, there is one but patch files are coded according to product.)

Patches are loaded by sys:load-patches. It takes only keyword arguments and the arguments are:

update-directory The directory in which to look for patch files. Defaults to the Allegro CL patch directory, sys:;update.
product Value should be nil, meaning load all patch files regardless of the product code (the third letter of the filename, <p> above), or a character or list of characters, meaning load only those files whose product code (third letter) match the single character or is in the list of characters.
patch-file-filter A function of three arguments, a product code, a pathname, and a version (<m> above). Returns true if the pathname names a valid patch file (based on parsing the name and location only).
patch-file-sorter A function of three arguments, a product, a list of patch files (validated by the patch-file-filter), and a version (<m> above). Sorts the list into the order in which the files should be loaded.
libfasl A boolean indicating whether the patch files should be loaded in libfasl mode. See 3.0 The Allegro Presto algorithm in loading.htm for more information on libfasl loading.
version Specifies the version (<m> above). Should be a character object naming a decimal digit (#\0 - #\9). This is for use with application patches only. Defaults to #\0.

You should have an update/ subdirectory to your application directory (or wherever sys: translates to in your application). Then you can distribute post-loadable Allegro CL patches file to customers. post-loadable means that the patch can be loaded into an existing image. However, not all Allegro CL patches are post-loadable. You must distribute a new image with patches loaded if you need to deliver a non-post-loadable Allegro CL patch to your customers.

5.3 Patches for your application

The easiest way to provide loadable patches to your own application is to have a separate directory (say sys:;myapp-update) where your patches will go. Then mimic the Allegro CL patch naming scheme and call sys:load-patches, specifying update-directory to be the directory you chose. sys:load-patches should be called when your application starts up. As long as your patch files are created with sys:defpatch, described next, the scheme should work with your application.

If you want to use a different naming scheme, you will have to supply your own patch-file-filter and patch-file-sorter functions. See the description of sys:load-patches for advice on how to do that.

Again, please do not mix your application patch files with Allegro CL patch files in the same directory (unless you use your own naming scheme that cannot be confused with the Allegro CL naming scheme, and even then it is a bad idea). Franz Inc. reserves the right to use any product code at any time and so you cannot guarantee the uniqueness of filenames simply by using an apparently unused product code.

5.4 Creating patch files in general

The following table describes the three attributes of patch files.

Attribute Meaning
post-loadable Can be loaded into a running image (so named because loadable after -- post -- the original image build).
superseded This specific patch file has been superseded but a later version (a patch file with the same p<m><p><n> but a larger <v>, e.g. p0a005.001 might be superseded by p0a005.002). Note that loading a superseded patch file only updates the record of patches loaded but does not otherwise modify the image.
withdrawn This particular patch file has been withdrawn and there is no later version. Note that loading a withdrawn patch file only updates the record of patches loaded but does not otherwise modify the image.

What is the purpose of the superseded and withdrawn attributes? Two problems with patch management are (1) ensuring that users have all necessary patches and (2) ensuring they do not have patches which (usually because of problems with the patch) have been superseded or withdrawn. The superseded and withdrawn attributes address the second problem. The Allegro CL scheme tries to ensure that users do not have inappropriate patch files by actually replacing superseded or withdrawn patches with new patch files with exactly the same name but different attributes. Then the patch-loading mechanism ensures that if two patches with the same id are loaded, the earlier version must be superseded. By providing superseded patches with the same file names as bad patches, users are thus unlikely to have an out-of-date patch file because those files are overwritten by the superseded or withdrawn patch file when the user received the updated set of patch files. This should become clearer as we describe the scheme in more detail.

5.5 Creating a patch file

A patch file is a compiled Lisp file. At the start of the patch file, there should be a sys:defpatch form, followed by the code that implements the patch. Therefore, a skeleton patch file will look like the following:

;; Our application
;; patch for report XXXX

(sys:defpatch "mpnnn" 1 ;; replace mpnnn with the product version (m),
                        ;; product code (p), the patch id number (nnn) and 
                        ;; 1 with the patch version
"MESSAGE"               ;; Brief patch information (should fit on one line)
:type :myapp            ;; Type should be a keyword of your choosing.
;; Other arguments may be specified.
)

;; Put patch code after here ...

(in-package :blah)

The required arguments to sys:defpatch are:

id

A string identifying the patch number or name. This is usually the <m><p><nnn> of the patch file name and typically includes zero-filled numeric characters -- e.g. "0a001", "1j195", "0z234" -- but can include alphabetic characters and need not be exactly five characters long. It is not the patch file prefix. This id is unique to the patch.

version

A fixnum in the range 1 to 999 inclusive. This is the <v> of the patch file name.

desc

A string containing a brief description of the patch. Short strings are better because this string is printed by excl:dribble-bug when it reports information of patches and long strings may mess up the printing (by forcing line wraps). Example "Fixes filename bug" or "Speeds up processing employee info".

The keyword arguments to sys:defpatch are:

type

A keyword specifying the type of the patch. Default is :unknown. Application programmers should decide on a single type or a group of types for their application and classify their patches according to that scheme. When information on patches in an image is printed by excl:dribble-bug, there are organized by type. The following types are reserved by Allegro CL and should not be used by application programmers: :lisp, :clim, :aclwin, :clim, :system, and :allegro* (any keyword starting with :allegro).

defpatch-version

Default is 1. If a new version of sys:defpatch is supplied by Franz Inc., the default will be changed and patches with the old version will be rejected. In general, do not worry about this argument unless a new version of sys:defpatch is distributed (that distribution will include additional instructions).

post-loadable

Default t. When t, the patch file can be loaded into a running image. When nil. the patch file can only be included in an image during image creation with build-lisp-image. The patch load will abort if load-patches tries to load it into a running image.

superseded

Default nil. If true, the patch (when loaded with load-patches) will be marked `superseded patch - more recent version exists' and the patch will not be (further) loaded. The compile-time effect of specifying this argument true is to ignore the remainder of the file after the sys:defpatch form.

withdrawn

Default nil. If true, the patch (when loaded with load-patches) will be marked `withdrawn patch' and the patch will not be (further) loaded. The compile-time effect of specifying this argument true is to ignore the remainder of the file after the sys:defpatch form.

feature

Default nil. When non-nil, value can be any form acceptable as an argument to excl:featurep. If excl:featurep returns nil when applied to the form, the patch loading is aborted. The reason for aborting printed by the system is the form that is the value of this argument (made into a string).

compile-feature

Default nil. When true, value can be any form acceptable as an argument to excl:featurep.

The compile-feature keyword argument is designed to facilitate producing patches for different platforms. For example, suppose a patch is only applicable to versions of Allegro CL that use os-threads for multiprocessing. Specifying :os-threads as the value of compile-feature will cause compilation to proceed when compiled by a platform that uses OS thread (only Windows in release 5.0) but to abort when compiled by a non-os-thread (Unix in 5.0) Allegro CL. Aborting is what you want in that case, since the patch is not needed for such platforms. The aborting of compilation will signal a condition which looks for a sys::abort-patch-compiling restart. If that restart is not present, an error is signaled (and the programmer must intervene to do something). More typically, compilation of patch files are done in a form like the following:

(dolist (x patch-files)
  (restart-case (compile-file x)
    (sys::abort-patch-compiling (patch)
      ;; Actions of your choice, e.g printing a message like:
      (format t "Aborted patch file ~s, featurep returned nil"
              x))))

Compilation of the remaining patch files will continue and all relevant patch fasl files will be present when the dolist form completes.

5.6 What to do with patch files

How you and your application team will manage patch files depends on how you deliver your application and whether or not your customers can build new images with build-lisp-image. Only customers of properly licensed VARs and customers who hold an appropriate license from Franz Inc. for Allegro CL will be able to build new images. Customers (of yours) who receive runtime images (and are not independently licensed by Franz Inc.) cannot make new original images because dumplisp (called by build-lisp-image does not work in runtime images.

This is an issue because (1) patches which are not post-loadable (i.e. cannot be loaded into a running Lisp) can only be included in a new original image; and (2) post-loadable patches can be loaded into a running image but should not be loaded into an image which already contains them. Therefore, if you have runtime customers (who cannot build original images), you can send them post-loadable patches and arrange for those to be loaded automatically, but you may also send them new images from time to time (which include non-post-loadable patches but will usually include all available post-loadable patches as well). You must ensure that such users do not load the post-loadable patches in their possession which are already included in the current image.

Here is a possible scheme which will work for applications which are distributed as runtime images. (This is not the only possible scheme or even the best for your situation. It illustrates how the tools and their features can be used to produce a scheme that works.)

  1. Your application, myapp, is distributed in a directory. That directory contains a subdirectory update-myapp/ and your users are informed to put patches from you in that directory. Patch files are named p0a<nnn>.<vvv>.
  2. As part of the *restart-init-function*, the following sys:load-patches forms are evaluated
        (sys:load-patches :update-directory "sys:;update-myapp;" :product #\a)
              (sys:load-patches);; to load an Allegro CL patches in update/
    Each time the application is started, all patch files named in the update-myapp directory will be loaded automatically along with any in update.
  3. You create a new image for distribution to customers. This image includes all myapp patches, as well as non-post-loadable patches (which you have been testing internally) and perhaps other features and enhancements unrelated to patches. In this image, the first sys:load-patches form in *restart-init-function* is changed to
        (sys:load-patches :update-directory "sys:;update-myapp;" :product #\b)
  4. You distribute the new image to customers, telling them to delete all p0a<nnn>.<vvv> files from update-myapp and remarking that patches associated with this image will be named p0b<nnn>.<vvv>.

Note that even if your customer ignores your instruction to delete the p0a<nnn>.<vvv> files from myapp-update, those (no longer valid) patches will not be loaded because sys:load-patches is looking for product b and those files have product a.

5.7 Including application patches in an image at build time

The discussion under the previous heading concerns distributing patches to users who cannot themselves build an original image with build-lisp-image. You, however, will build original images (and perhaps your customers are licensed to do so as well). How do you include patch files in the image when it is built? You put appropriate sys:load-patches forms in custom.cl and make sure all your patches are in the directories specified in the sys:load-patches forms. Allegro CL patches can be put in the update subdirectory (they will be included automatically by the image build process).

5.8 Superseding a patch

Say you have sent a patch, p0a001.001 to your users. The sys:defpatch form at the top of the patch file is:

(sys:defpatch "0a001" 1 "Fixes whatever" :type :myapp)

A user complains that including the patch fixes the problem reported but seems to cause another problem. You check it out and find that the patch does introduce new problems. So you create a new version. You put it into a file and compile it to p0a001.002. The sys:defpatch form at the head of the file
is:

(sys:defpatch "0a001" 2 "Fixes whatever" :type :myapp)

We recommend that you also remake p0a001.001 with the following sys:defpatch form at its head:

(sys:defpatch "0a001" 1 "Fixes whatever" :type :myapp :superseded t)

When the compiler processes this sys:defpatch form, it stops compiling the file. Therefore, you can leave the original patch source (for later reference) without worrying that the patch fasl file will be larger than necessary or contain bogus compiled code. You can then tell your customer to put both files in the update directory, overwriting the p0a001.001 already present.

Why do we recommend producing a new p0a001.001 along with p0a001.002? Why not a new, corrected p0a001.001, or why not just tell the user to delete p0a001.001?

Well, you can do those things and often things would work just fine. But our experience is based on the fact that you cannot force users to do anything and the observation that users often misunderstand what you tell them to do or just do not do it (but think they have). The system we are describing is designed to avoid certain obvious problems and to catch mistakes or omissions by the user before they result in trouble.

Why not just update p0a001.001? Because you cannot then tell from a directory listing alone whether the user has the new, right patch or the old, bad patch.

Why not just tell the user to delete p0a001.001 (i.e. why remake the file)? This will work and it is not necessary to remake the file. But you really want your users to get rid of the bad patch file, which ensures that it never causes problems. You can tell your users to download all the patch files allowing overwriting of files. If they do that, the bad patch will be overwritten. Further, if (somehow) p0a001.002 is lost but p0a001.001 is marked superseded, it will not corrupt the image (as the bad patch would) and the dribble-bug output would make clear that a superseded patch has been recorded without its update having been loaded.

5.9 Withdrawing a patch

Occasionally, a patch must be simply withdrawn. A speed-enhancement patch which actually slows things down is one example (your idea for a speedup failed and you do not have other ideas). We again recommend replacing the existing patch file with a patch file marked superseded and creating a new patch file (with a later version number) marked withdrawn. Again, this allows you to tell from a directory listing whether the user is up-to-date or not and shows the fact that the patch is withdrawn in the dribble-bug output. So, the sys:defpatch form in the superseded p0a001.001 would be (like above):

(sys:defpatch "0a001" 1 "Fixes whatever" :type :myapp :superseded t)

And the sys:defpatch form in the p0a001.002, the withdrawn patch would be:

(sys:defpatch "0a001" 2 "Fixes nothing" :type :myapp :withdrawn t)

As with a superseded patch, when the compiler processes this sys:defpatch form, it stops compiling the file. Therefore, you can leave the original patch source (for later reference) without worrying that the patch fasl file will be larger than necessary or contain bogus compiled code.

5.10 Distributing patches

In the era of the World Wide Web, ftp, and users around the world, a typical way to distribute patches to users is having them download the patches from an ftp (or www) site directly into the appropriate directory without actual human contact between you and your users. If you use this model, you should tell your users to download every patch, you should use the version and superseded mechanisms we describe above, and you should tell your users to expect files to be overwritten.

You can also, of course, distribute patches on request, one at a time, with instructions (which usually include `delete all earlier versions of this patch!') The more patch distribution has a human-contact element, the less you have to worry about old version and bad patches not being deleted. The more the system is automated, with less handholding, the more replacing bad patch files with superseded patch files and being very careful about version numbers becomes necessary.

5.11 Loading patches

Patches are compiled lisp files, and such files can be loaded in a number of ways. There is no reason a post-loadable patch file cannot be loaded with load. Often that is useful for quick tests. However, Allegro CL provides a patch-loading function carefully integrated with the patch system described in this section. As far as possible we recommend that you load with sys:load-patches.

The function excl:featurep returns true or nil as the features called for in its argument are or are not on *features*. It is thus a functional analog of the #+/#- reader macros. It is used by sys:load-patches to process the feature argument in sys:defpatch forms.

Things to note

Copyright (C) 1998-1999, Franz Inc., Berkeley, CA. All Rights Reserved.