[Openmcl-devel] Setting a toplevel function for build-application

Gary Byers gb at clozure.com
Tue Sep 17 16:31:52 UTC 2013

A lot of this stuff is documented somewhat murkily (I -think- that that's a word);
that's probably because much of it is implemented somewhat murkily.  Adding to
the murk is the fact that I've never used BUILD-APPLICATION (and the fact that
I'm in the middle of something else at the moment and am not using a Mac.)

The command-line CCL is "an instance of the class
CCL::LISP-DEVELOPMENT-SYSTEM", which is a subclass of the class
CCL::APPLICATION.  These classes and some methods on them are indeed
defined (mostly) in ccl:level-1;l1-application,lisp.  The phrase "a
CCL application is an instance of a class" means "in the application's
image file, the variable CCL::*APPLICATION* is an instance of a
class"; SAVE-APPLICATON takes some (tersely documented) arguments that
allow the saved image's application class to be specified.

At a fairly small number of points in its execution, a saved image
will decide what to do by invoking some method on the object that's
the value of CCL::*APPLICATION*.  One of these points is when a saved
image is first mapped into memory and some very low-level reinitialization
has occurred; at that point, the startup code does (something very like)

(ccl::toplevel-function ccl::*application*)

If ccl::*application* is an instance CCL::LISP-DEVELOPMENT-SYSTEM, that
method will create a thread and arrange to run the REPL in it, and cause
the initial thread to reset itself and spend most of its life doing
periodic "housekeeping" tasks.  (These tasks include ^C handling, GC
finalization, forcing output on some interactive streams, etc.)

argument is provided to SAVE-APPLICATION, very similar things happen except
for the fact that the provided function is run instead of the CCL REPL.

If the application isn't a LISP-DEVELOPMENT-SYSTEM, it's not clear whether
the :TOPLEVEL-FUNCTION argument has a useful role.  (It may, I suppose; I'm
just not sure of what that role would be.)

If the application is a Cocoa application - an instance of the class 
GUI::COCOA-IDE, which is new since the last time that I was paying attention
to any of this - its CCL::TOPLEVEL-FUNCTION method creates another thread
to do the periodic housekeeping stuff, then initializes Cocoa and runs the
Cocoa event loop.  You might be able to subclass GUI::COCOA-IDE and override
this method and do this very slightly differently, but there aren't that many
ways of doing this differently.

Cocoa also uses an application class (NSApplication) with a single global
instance of that class; like many other Cocoa objects, this instance can
have another NSObject associated with it as the instance's "delegate" object.
Most interesting events (with a small #\e) in an NSApplication's lifetime
result in methods being invoked (if they're implemented) on the NSApplication
object's delegate.

The CCL IDE uses an instance of the class LispApplicationDelegate as
the NSApplication's delegate; this class's methods are defined in
"ccl:cocoa-ide;app-delegate.lisp".   The application delegate is also
the target for a bunch of menu and other UI items. You might find it easiest to
subclass this class and override a few of its methods:

(defclass beeping-application-delegate (gui::lisp-appication-delegate)
  (:metaclass ns:+ns-object))

(objc:defmethod (#/applicationDidFinishLaunching: :void)
     ((self lisp-application-delegate) notification)
   (call-next-method notication))

If you can figure out how to persuade BUILD-APPLICATION to use
BEEPING-APPLICATION-DELEGATE as you application's delegate's
class name, you're only minutes away from having an application
the beeps when it starts up.  (The thrill may wear off fairly

On Sat, 14 Sep 2013, Ron Garret wrote:

> I can't figure out how to set a toplevel function for build-application.  The docs say:
> "The best source of information about writing your own toplevel is the Clozure CL source code, especially the implementations of TOPLEVEL-FUNCTION in "ccl/level-1/l1-application.lisp"
> In that file there is no example of calling build-application, but there is a (generic) function called toplevel-function.
> If I do this:
> (build-application :name "testapp" :directory #P"~/Desktop/" :copy-ide-resources t)
> the resulting application (mostly) works fine [1], but if I do this:
> (build-application :name "testapp" :directory #P"~/Desktop/" :copy-ide-resources t
>                   :toplevel-function 'toplevel-function)
> The resulting application crashes with hundreds of console message like this:
> 9/14/13 12:45:08 PM	[0x0-0x3ae9ae6].com.clozure.store.ccl-x8664[90745]	> While executing: #<CCL::STANDARD-KERNEL-METHOD NO-APPLICABLE-METHOD (T)>, in process toplevel(8).
> 9/14/13 12:45:08 PM	[0x0-0x3ae9ae6].com.clozure.store.ccl-x8664[90745]	> Error: There is no applicable method for the generic function:
> 9/14/13 12:45:08 PM	[0x0-0x3ae9ae6].com.clozure.store.ccl-x8664[90745]	>          #<STANDARD-GENERIC-FUNCTION CCL::UI-OBJECT-DO-OPERATION #x3020000BC73F>
> 9/14/13 12:45:08 PM	[0x0-0x3ae9ae6].com.clozure.store.ccl-x8664[90745]	>        when called with arguments:
> 9/14/13 12:45:08 PM	[0x0-0x3ae9ae6].com.clozure.store.ccl-x8664[90745]	>          (#<A Dead Mac Pointer> :BREAK-OPTIONS-STRING T)
> Clues appreciated.
> What I'm actually trying to do is build a standalone application that just opens a webkit window and does nothing else (i.e. no listener).  But I want to do it programmatically, without a nib file.
> rg
> [1] Actually, the resulting application complains about not being able to find the header directory, but that's easy to fix.
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list