help-smalltalk
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Help-smalltalk] [PATCH] Add startup and shutdown scripts to packages


From: Paolo Bonzini
Subject: [Help-smalltalk] [PATCH] Add startup and shutdown scripts to packages
Date: Mon, 25 Feb 2008 18:45:54 +0100
User-agent: Thunderbird 2.0.0.9 (Macintosh/20071031)

With this patch, packages can specify start/stop scripts. Start scripts can be activated with gst-load, while both start and stop scripts are supported by gst-remote.

I contemplated adding the startup/shutdown to namespaces (e.g. with a specially named class), but then I reasoned that users know about packages, not namespaces. It is a little ugly to have %1 in the startup/shutdown file, but that's what we have for now. It can be changed anytime before 3.1 (which will take a while even though development releases will start soon).

I added documentation for gst-load; gst-remote is completely undocumented so far. Any volunteer to do it in the next week? :-)

Startup/shutdown scripts are provided for Swazoo (which is also undocumented; part of this message will be added to the documentation when time comes). One more user of startup/shutdown scripts will be added soon...

Startup/shutdown arguments for Swazoo look like the following:
- if no argument is given, all sites are started/stopped
- if a number, all sites on the given port are started/stopped
- if a file (startup only), configuration is loaded from the given file
- any other argument is interpreted as a site name (which is started/stopped)

The special site 'swazoodemo' starts an Hello World application on port 8888. So, after

   $ gst-load --start=swazoodemo Swazoo

you can point your browser to http://localhost:8888/ and get a friendly message.

Paolo
2008-02-25  Paolo Bonzini  <address@hidden>

        * kernel/PkgLoader.st: Add start/stop script support.
        * scripts/Load.st: Add start/stop script support.
        * scripts/Remote.st: Add start/stop script support.
        * gst-load.c: Keep options synchronized.

diff --git a/NEWS b/NEWS
index 2221fce..5dcc7ea 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,10 @@ o   The semantics of #on:do: were changed: executing off the 
end of an
 o   New tool gst-remote allows remote control of a GNU Smalltalk VM
     via a TCP socket.
 
+o   Packages can specify start/stop scripts.  Start scripts can be activated
+    with gst-load, while both start and stop scripts are supported by
+    gst-remote.
+
 o   Unbuffered sockets available from class TCP.StreamSocket.
 
 
diff --git a/doc/gst.texi b/doc/gst.texi
index c776a80..bcb9e6b 100644
--- a/doc/gst.texi
+++ b/doc/gst.texi
@@ -1932,6 +1932,12 @@ together with the package, but this may change in future 
versions.
 @item -n
 @item --dry-run
 Do not save the image after loading.
+
address@hidden --start[=ARG]
+Start the services identified by the package.  If an argument is
+given, only one package can be specified on the command-line.  If
+at least one package specifies a startup script, @code{gst-load}
+won't exit.
 @end table
 
 To provide support for this system, you have to give away with your @gst{}
@@ -1996,6 +2002,18 @@ Specifies a testing script that @file{gst-sunit} 
(@pxref{SUnit}) will
 run in order to test the package.  If this is specified, the package
 should list @code{SUnit} among the prerequisites.
 
address@hidden start
+Specifies a Smalltalk script that @file{gst-load} and @file{gst-remote}
+will execute in order to start the execution of the service implemented
+in the package.  Before executing the script, @code{%1} is replaced 
+with either @code{nil} or a String literal.
+
address@hidden stop
+Specifies a Smalltalk script that @file{gst-remote}
+will execute in order to shut down the service implemented
+in the package.  Before executing the script, @code{%1} is replaced 
+with either @code{nil} or a String literal.
+
 @item test
 Specifies a subpackage that is only loaded by @file{gst-sunit} in order
 to test the package.  The subpackage may include arbitrary tags (including
diff --git a/gst-tool.c b/gst-tool.c
index 0084cb5..519ae4d 100644
--- a/gst-tool.c
+++ b/gst-tool.c
@@ -92,13 +92,13 @@ struct tool tools[] = {
   {
     "gst-load", "scripts/Load.st",
     "-h|--help --version -q|--quiet -v|-V|--verbose -n|--dry-run -f|--force \
-       -t|--test -I|--image-file: --kernel-directory:",
+       --start:: -t|--test -I|--image-file: --kernel-directory:",
     NULL
   },
   {
     "gst-reload", "scripts/Load.st",
     "-h|--help --version -q|--quiet -v|-V|--verbose -n|--dry-run -f|--force \
-       -t|--test -I|--image-file: --kernel-directory:",
+       --start:: -t|--test -I|--image-file: --kernel-directory:",
     "--force"
   },
   {
@@ -129,7 +129,8 @@ struct tool tools[] = {
   {
     "gst-remote", "scripts/Remote.st",
     "-h|--help --version --daemon --server -p|--port -f|--file: -e|--eval: \
-        --pid --kill --snapshot:: -I|--image-file: --kernel-directory:",
+       -package: --start: --stop: --pid --kill --snapshot:: -I|--image-file: \
+       --kernel-directory:",
     NULL
   },
   { NULL, NULL, NULL, NULL }
diff --git a/kernel/PkgLoader.st b/kernel/PkgLoader.st
index 82c3b8c..6ed4b84 100644
--- a/kernel/PkgLoader.st
+++ b/kernel/PkgLoader.st
@@ -591,6 +591,22 @@ XML.'>
            collection: self builtFiles
            tag: 'built-file'
            indent: indent.
+       self startScript isNil 
+           ifFalse: 
+               [aStream
+                   nextPutAll: '  <start>';
+                   nextPutAll: self startScript;
+                   nextPutAll: '</start>';
+                   nl;
+                   space: indent].
+       self stopScript isNil 
+           ifFalse: 
+               [aStream
+                   nextPutAll: '  <stop>';
+                   nextPutAll: self stopScript;
+                   nextPutAll: '</stop>';
+                   nl;
+                   space: indent].
        aStream
            nextPutAll: '</';
            nextPutAll: tag;
@@ -716,6 +732,20 @@ XML.'>
        self subclassResponsibility
     ]
 
+    startScript [
+       "Answer the start script for the package."
+
+       <category: 'accessing'>
+       self subclassResponsibility
+    ]
+
+    stopScript [
+       "Answer the stop script for the package."
+
+       <category: 'accessing'>
+       self subclassResponsibility
+    ]
+
     callouts [
        "Answer a (modifiable) Set of call-outs that are required to load
         the package.  Their presence is checked after the libraries and
@@ -745,6 +775,46 @@ XML.'>
        ^self name notNil and: [Smalltalk hasFeatures: self name]
     ]
 
+    start [
+       "File in the receiver and evaluate its start script, passing nil
+        as the argument."
+
+       <category: 'accessing'>
+       self fileIn.
+       self startScript isNil ifTrue: [ ^self ].
+       (self startScript % { 'nil' }) readStream fileIn.
+    ]
+
+    start: anObject [
+       "File in the receiver and evaluate its start script, passing anObject's
+        displayString as the argument."
+
+       <category: 'accessing'>
+       self fileIn.
+       self startScript isNil ifTrue: [ ^self ].
+       (self startScript % { anObject displayString storeString }) readStream 
fileIn.
+    ]
+
+    stop [
+       "Evaluate the stop script of the receiver, passing nil as the
+        argument."
+
+       <category: 'accessing'>
+       self loaded ifFalse: [ ^self ].
+       self stopScript isNil ifTrue: [ ^self ].
+       (self stopScript % { 'nil' }) readStream fileIn.
+    ]
+
+    stop: anObject [
+       "Evaluate the stop script of the receiver, passing anObject's
+        displayString as the argument."
+
+       <category: 'accessing'>
+       self loaded ifFalse: [ ^self ].
+       self stopScript isNil ifTrue: [ ^self ].
+       (self stopScript % { anObject displayString storeString }) readStream 
fileIn.
+    ]
+
     allFiles: prefix [
        <category: 'private - subpackages'>
        prefix isNil ifTrue: [^self allFiles].
@@ -857,6 +927,20 @@ PackageInfo subclass: StarPackage [
        ^self loadedPackage modules
     ]
 
+    startScript [
+       "Answer the start script for the package."
+
+       <category: 'accessing'>
+       ^self loadedPackage startScript
+    ]
+
+    stopScript [
+       "Answer the stop script for the package."
+
+       <category: 'accessing'>
+       ^self loadedPackage stopScript
+    ]
+
     sunitScripts [
        "Answer a (modifiable) OrderedCollection of SUnit scripts that
         compose the package's test suite."
@@ -925,7 +1009,9 @@ PackageInfo subclass: StarPackage [
 
 
 Kernel.PackageInfo subclass: Package [
-    | features prerequisites builtFiles files fileIns relativeDirectory 
baseDirectories libraries modules callouts namespace sunitScripts test |
+    | features prerequisites builtFiles files fileIns relativeDirectory
+       baseDirectories libraries modules callouts namespace sunitScripts
+       startScript stopScript test |
     
     <category: 'Language-Packaging'>
     <comment: 'I am not part of a standard Smalltalk system. I store 
internally the
@@ -979,6 +1065,34 @@ XML.'>
        test := aPackage
     ]
 
+    startScript [
+       "Answer the start script for the package."
+
+       <category: 'accessing'>
+       ^startScript
+    ]
+
+    startScript: aString [
+       "Set the start script for the package to aString."
+
+       <category: 'accessing'>
+       startScript := aString
+    ]
+
+    stopScript [
+       "Answer the start script for the package."
+
+       <category: 'accessing'>
+       ^stopScript
+    ]
+
+    stopScript: aString [
+       "Set the stop script for the package to aString."
+
+       <category: 'accessing'>
+       stopScript := aString
+    ]
+
     namespace [
        "Answer the namespace in which the package is loaded."
 
@@ -1204,45 +1318,22 @@ XML.'>
                                ifFalse: [^self error: 'error in packages file: 
unmatched end tag ' , tag].
 
                            "I tried to put these from the most common to the 
least common"
-                           tag = 'file' 
-                               ifTrue: [self files add: cdata]
-                               ifFalse: 
-                                   [tag = 'filein' 
-                                       ifTrue: [self fileIns add: cdata]
-                                       ifFalse: 
-                                           [tag = 'prereq' 
-                                               ifTrue: [self prerequisites 
add: cdata]
-                                               ifFalse: 
-                                                   [tag = 'provides' 
-                                                       ifTrue: [self features 
add: cdata]
-                                                       ifFalse: 
-                                                           [tag = 'module' 
-                                                               ifTrue: [self 
modules add: cdata]
-                                                               ifFalse: 
-                                                                   [tag = 
'directory' 
-                                                                       ifTrue: 
[self relativeDirectory: cdata]
-                                                                       
ifFalse: 
-                                                                           
[tag = 'name' 
-                                                                               
ifTrue: [self name: cdata]
-                                                                               
ifFalse: 
-                                                                               
    [tag = 'namespace' 
-                                                                               
        ifTrue: [self namespace: cdata]
-                                                                               
        ifFalse: 
-                                                                               
            [tag = 'library' 
-                                                                               
                ifTrue: [self libraries add: cdata]
-                                                                               
                ifFalse: 
-                                                                               
                    [tag = 'built-file' 
-                                                                               
                        ifTrue: [self builtFiles add: cdata]
-                                                                               
                        ifFalse: 
-                                                                               
                            [tag = 'sunit' 
-                                                                               
                                ifTrue: [self sunitScripts add: cdata]
-                                                                               
                                ifFalse: 
-                                                                               
                                    [tag = 'callout' 
-                                                                               
                                        ifTrue: [self callouts add: cdata]
-                                                                               
                                        ifFalse: 
-                                                                               
                                            [tag = openingTag 
-                                                                               
                                                ifTrue: [^self]
-                                                                               
                                                ifFalse: [self error: 'invalid 
tag ' , tag]]]]]]]]]]]]].
+                           tag = 'file' ifTrue: [self files add: cdata] 
ifFalse: [
+                           tag = 'filein' ifTrue: [self fileIns add: cdata] 
ifFalse: [
+                           tag = 'prereq' ifTrue: [self prerequisites add: 
cdata] ifFalse: [
+                           tag = 'provides' ifTrue: [self features add: cdata] 
ifFalse: [
+                           tag = 'module' ifTrue: [self modules add: cdata] 
ifFalse: [
+                           tag = 'directory' ifTrue: [self relativeDirectory: 
cdata] ifFalse: [
+                           tag = 'name' ifTrue: [self name: cdata] ifFalse: [
+                           tag = 'namespace' ifTrue: [self namespace: cdata] 
ifFalse: [
+                           tag = 'library' ifTrue: [self libraries add: cdata] 
ifFalse: [
+                           tag = 'built-file' ifTrue: [self builtFiles add: 
cdata] ifFalse: [
+                           tag = 'sunit' ifTrue: [self sunitScripts add: 
cdata] ifFalse: [
+                           tag = 'start' ifTrue: [self startScript: cdata] 
ifFalse: [
+                           tag = 'stop' ifTrue: [self stopScript: cdata] 
ifFalse: [
+                           tag = 'callout' ifTrue: [self callouts add: cdata] 
ifFalse: [
+                           tag = openingTag ifTrue: [^self] ifFalse: [
+                               self error: 'invalid tag ' , tag]]]]]]]]]]]]]]].
                            cdata := nil].
                    ch isAlphaNumeric 
                        ifTrue: 
diff --git a/packages/swazoo-httpd/package.xml 
b/packages/swazoo-httpd/package.xml
index 31c8996..93601b3 100644
--- a/packages/swazoo-httpd/package.xml
+++ b/packages/swazoo-httpd/package.xml
@@ -3,6 +3,20 @@
   <prereq>Sport</prereq>
   <namespace>Swazoo</namespace>
 
+  <start>
+    %1 isNil ifTrue: [ ^Swazoo.SwazooServer start ].
+    %1 ~ '^[0-9]+$' ifTrue: [ ^Swazoo.SwazooServer startOn: %1 asNumber ].
+    (File name: %1) exists ifTrue: [ ^Swazoo.SwazooServer configureFrom: %1 ].
+    %1 = 'swazoodemo' ifTrue: [ ^Swazoo.SwazooServer demoStart ].
+    Swazoo.SwazooServer startSite: %1
+  </start>
+
+  <stop>
+    %1 isNil ifTrue: [ ^Swazoo.SwazooServer stop ].
+    %1 ~ '^[0-9]+$' ifTrue: [ ^Swazoo.SwazooServer stopOn: %1 asNumber ].
+    Swazoo.SwazooServer stopSite: %1
+  </stop>
+
   <test>
     <sunit>
       Swazoo.CompositeResourceTest
diff --git a/scripts/Load.st b/scripts/Load.st
index 2e0df95..9e4fa5c 100644
--- a/scripts/Load.st
+++ b/scripts/Load.st
@@ -30,12 +30,14 @@
 
 Smalltalk arguments isEmpty ifTrue: [ ObjectMemory quit ]!
 
-| helpString quiet verbose wasVerbose snapshot force test sunit packages |
+| helpString quiet verbose wasVerbose snapshot force test sunit packages
+  startMessage |
 snapshot := true.
 quiet := false.
 verbose := false.
 force := false.
 test := false.
+startMessage := Message selector: #fileIn arguments: #().
 wasVerbose := FileStream verbose: false.
 packages := OrderedCollection new.
 sunit := ''.
@@ -50,6 +52,7 @@ Options:
     -f --force            reload package if already loaded
     -n --dry-run          don''t save the image after loading
     -t --test             run SUnit tests if available
+       --start[=ARG]      start the package and keep running the image
     -I --image-file=FILE  load into the specified image
        --kernel-dir=PATH  use the specified kernel directory
     -h --help             show this message
@@ -59,7 +62,8 @@ Options:
 "Parse the command-line arguments."
 Smalltalk
     arguments: '-h|--help --version -q|--quiet -v|-V|--verbose -n|--dry-run
-               -f|--force -t|--test -I|--image-file: --kernel-directory:'
+               -f|--force -t|--test -I|--image-file: --kernel-directory:
+               --start::'
     do: [ :opt :arg |
 
     opt = 'help' ifTrue: [
@@ -88,6 +92,11 @@ Smalltalk
     opt = 'test' ifTrue: [
        test := true ].
 
+    opt = 'start' ifTrue: [
+       startMessage := Message
+           selector: (arg isNil ifTrue: [ #start ] ifFalse: [ #start: ])
+           arguments: (arg isNil ifTrue: [ #() ] ifFalse: [ { arg } ]) ].
+
     opt = 'dry-run' ifTrue: [
        snapshot := false ].
 
@@ -99,10 +108,16 @@ Smalltalk
 
 force ifTrue: [
     packages do: [ :each | Smalltalk removeFeature: each asSymbol ] ].
+
 [
+    (packages size > 1 and: [ startMessage selector == #start: ]) ifTrue: [
+       stderr nextPutAll: 'gst-load: Cannot pass start argument to multiple 
packages
+'.
+       ^self ].
+
     packages := packages collect: [ :each | PackageLoader packageAt: each ].
     packages do: [ :each |
-       each fileIn.
+       each perform: startMessage.
         sunit := sunit, ' ', each sunitScript ] ]
     ifCurtailed: [ ObjectMemory quit: 1 ].
 
@@ -134,5 +149,9 @@ test
            (File name: tmpFileName) remove ] ]
 
     ifFalse: [
-       snapshot ifTrue: [ ObjectMemory snapshot ] ]!
+       snapshot ifTrue: [ ObjectMemory snapshot ] ].
+
+(startMessage selector ~= #fileIn and: [ 
+    packages anySatisfy: [ :pkg | pkg startScript notNil ]])
+       ifTrue: [ Processor activeProcess suspend ]!
 
diff --git a/scripts/Remote.st b/scripts/Remote.st
index 96fbaba..4e05efb 100644
--- a/scripts/Remote.st
+++ b/scripts/Remote.st
@@ -75,26 +75,29 @@ host := nil.
 
 helpString :=
 'Usage:
-    gst-load [ flag ... ] package ...
+    gst-remote [ flag ... ] host
 
 Options:
-       --daemon           start background server
-       --server           start daemon
-    -p --port=PORT        connect/listen on given port (default 5432)
-    -f --file=FILE        file in FILE
-    -e --eval=CODE        evaluate CODE
-       --kill             kill daemon
-       --snapshot[=FILE]  save image
-       --pid              print daemon pid
-    -h --help             show this message
-       --version          print version information and exit
+       --daemon               start background server
+       --server               start daemon
+    -p --port=PORT            connect/listen on given port (default 5432)
+    -f --file=FILE            file in FILE
+    -e --eval=CODE            evaluate CODE
+       --kill                 kill daemon
+       --snapshot[=FILE]      save image
+       --package=PACKAGE      load package
+       --start=PACKAGE[:DATA] load package and start it (defined in 
package.xml)
+       --stop=PACKAGE[:DATA]  load package and start it (defined in 
package.xml)
+       --pid                  print daemon pid
+    -h --help                 show this message
+       --version              print version information and exit
 '.
 
 "Parse the command-line arguments."
 Smalltalk
     arguments: '-h|--help --version --daemon --server -p|--port -f|--file:
-               -e|--eval: --pid --kill --snapshot::
-               -I|--image: --kernel-directory:'
+               -e|--eval: --pid --kill --snapshot:: --start: --stop:
+               --package: -I|--image: --kernel-directory:'
     do: [ :opt :arg |
 
     opt = 'help' ifTrue: [
@@ -114,9 +117,34 @@ Smalltalk
     opt = 'port' ifTrue: [
        port := arg asInteger ].
 
+    opt = 'start' ifTrue: [
+       | package data |
+       package := arg copyUpTo: $:.
+       package = arg
+           ifTrue: [
+               commands add: '(PackageLoader packageAt: %1) start'
+                               % {package storeString} ]
+           ifFalse: [
+               commands add: '(PackageLoader packageAt: %1) start: %2'
+                               % {package storeString. (arg copyAfter: $:) 
storeString } ] ].
+
+    opt = 'stop' ifTrue: [
+       | package data |
+       package := arg copyUpTo: $:.
+       package = arg
+           ifTrue: [
+               commands add: '(PackageLoader packageAt: %1) stop'
+                               % {package storeString} ]
+           ifFalse: [
+               commands add: '(PackageLoader packageAt: %1) stop: %2'
+                               % {package storeString. (arg copyAfter: $:) 
storeString } ] ].
+
     opt = 'file' ifTrue: [
        commands add: (File name: arg) ].
 
+    opt = 'package' ifTrue: [
+       commands add: 'PackageLoader fileInPackage: %1' % {arg storeString} ].
+
     opt = 'eval' ifTrue: [
        commands add: arg ].
 
@@ -144,7 +172,6 @@ Smalltalk
 
 server ifTrue: [
     PackageLoader fileInPackage: 'Compiler'.
-    "PackageLoader fileInPackage: 'Swazoo'."
     Transcript := MultiplexingTextCollector message: Transcript message.
     [
        | queue |
@@ -190,7 +217,7 @@ server ifTrue: [
            ifFalse: [ TCP.Socket remote: host port: port ].
 
        commands do: [ :each |
-           "Using #readStream" makes it work both for Strings and Files."
+           "Using #readStream makes it work both for Strings and Files."
            s nextPutAll: each readStream; nextPut: $<0>; flush.
 
            [ s peekFor: $<0> ] whileFalse: [

reply via email to

[Prev in Thread] Current Thread [Next in Thread]