Squeak
  links to this page:    
View this PageEdit this PageUploads to this PageHistory of this PageTop of the SwikiRecent ChangesSearch the SwikiHelp Guide
Sake - A Squeak based make-like tool
Last updated at 3:32 am UTC on 5 December 2008

Original Project Outline


Create a Squeak-based build system as a library and tool.

To Install


Installer install: 'Packages'.

Technical Specification


Squeak can run scripts through OSProcess, but it doesn't have a framework for declarative building of various targets that is customarily exhibited by Make or Ant. Ruby's Rake demonstrates an interesting approach to this problem that could be implemented in Squeak readily.

The implementation goal of this project is to translate the idioms and features of Rake into a library comparable to SUnit in terms of coding style and level of implementation, then a "mini-language" on top of that which makes it convenient to add build systems to other projects for their own purposes, with some usage examples, and finally (optionally) a graphical tool for running build processes with possibly some interaction and logging interfaces. ToolBuilder is suggested but not necessary for the tool phase.

Benefits to the Community


This would provide Squeak with a declarative, idempotent build system for external files and programs as well as other Squeak images, that also integrates well with other Squeak code and libraries. It could even offer new facilities for building Squeak images using the Installer tool.

Introduction

Sake-Core – is an attempt to provide similar functionality to Make and
Rake, only using Smalltalk as the DSL.

Sake-Packages – is a reimplementation of Universes functionality, using
Sake. It allows the metadata, load script, and unload script for a
package to be defined as a Task.

Sake-Scheduler – provides the ability to register tasks to be performed
periodically.

Sake-Bob – is a tool for building and testing images. (wip)

Basics


Tasks are typically defined class side, but can be added to any class in
the image. A typical task definition follows.

 MakeTheTea classSide >> #taskMakeTea
 
 SakeTask define: [ :task |
 
 	task dependsOn: { MakeTheTea taskBoilWater. }.
 		
 	task if: [ Cup isEmpty ].
 	task action: [ Cup add: 'tea'; add: 'water'. ].
 
 ].

To run the task:

 MakeTheTea taskMakeTea run.

Alternatively #runStepping, or #runLogging are available for debugging.

The main structure of a sake task is defined by #dependsOn:
(or #addDependency:) , #if: , #action: (or #addAction:).
These can all take either blocks, or lists of tasks, or lists of blocks.

To see the heirarchy of tasks that will be traversed.

 MakeTheTea taskMakeTea what explore.

Run Order


There are two algorithms available for determining task run order.
(Further algorithms could be defined in order to support parallelism in
processing.)

Given a single top level task, standard Rake ordering, simply runs all
the dependencies first, for every task.

However Sake-Bob, when given a list of things to do, has to analyse the
dependencies to determine which needs to go first. (Universes does this)

Parameters


Unlike Rake, tasks can be defined in terms of parameters. The typical
unparametized tasks found in Rake, #taskCreateMyStartingDirectory, can
be written more generically as: #taskCreateStartingDirectory: name.

e.g.

 task dependsOn: { self taskCreateStartingDirectory: 'wip' }.

Class Tasks


In the following examples #Cup is used where the class Cup, is not
guaranteed to exist.

 task dependsOn:{ (SakeTask class: #Cup) exists }

 "create a needed class"
 task dependsOn:{ (SakeTask class: Object) subclass: #Cup }

 "create a needed class"
 task dependsOn:{ (SakeTask class: Object)
 				subclass: #Cup category:'Demo'} 

 task dependsOn:{ (SakeTask class: #Cup)
 				removeSelector: #tmp }

 task dependsOn:{ (SakeTask class: #Cup)==
 				removeSelectorsMatching: '' }

 task dependsOn:{ (SakeTask class: #Cup)
 				removeSelectorsMatching: '' }

 task dependsOn:{ (SakeTask class: #Cup) ifExistsDo: [ :c | c rename:
  1. Glass ] }

MetaData


Each Sake task contains a dictionary, SakeInfo that can be used simply
to store any arbitrary metadata.

 task info someField: someValue.

Sake-Packages uses metadata extensively, in this case the typical use
pattern is begin with a subclass of SakeTask and to define the generic
task on the class side and the metadata on the instance side.

 SakeTask subclass: MakeADrink.
 
 MakeADrink classSide >> #taskPourADrink: aDrink
 
 self define: [ :task |
 	task perform: aDrink.
 
 	task if: [ Bar containsBottle: task info name ].
 
  	task action: [ Glass add: (task info name). ].
 ].

Documentation Methods


Subclasses of SakeTask, utilise a specialised compiler, which allows raw
text to be stored in instance side methods following six quotes. """"""

 aTaskClass at: selector category: cat putMethod: code doc: content
 
 aTaskClass docAt: selector.

This is used by the Mantis package to pull raw data from mantis, so that
subsequent tasks can process that data.

SakeSignal and trace:


If a task needs to communicate with the outside context, it can sends a
SakeSignal, a utility method is provided for this.

task lookup: #aSelector.

The outside context can return a value, by defining an exception handler
for SakeSignal.

The default implementation of #trace: is used in Sake-Tests for a task
to signal to the outside testing context what it is doing. Tasks send
  1. trace: and the SakeTaskTest runCaseWith: block, defines an exception
handler to collect the signalled values so as to record the actions of
the tasks. For the tests to pass all of the required actions must have
been signalled.

SakeStep and SakeStop


 task step: 'Are you Sure'.

Signals a notifier to request confirmation.

 task stop: 'It Broke'.

Finishes processing.

Task Extensions


In the same manner as magritte. A class side task definition may be
extended by another package.

 #taskDoThis, may be extended by adding the extension method,
 
 #taskDoThisAndAlso: task.
 	task addDependency: [ ... ].
 	task addAction: [...].

Answering Notifications


Tasks are wrapped in notification handlers, to use them:

 task answer: '
password: 
' with: 'secret'.

Sake/Packages


Below is a SakeTask as generated from the universes package definition.
What follows is a "method", in the class PackagesSqueak310U.

So here is a Sake Task Definition for Seaside.... (info is simply a
dictionary for non-essential meta information)

 PackagesSqueak310U -i- Seaside
 
    self name: 'Seaside'.
    info category: 'Web Development'.
    info description:
 'A framework for building sophisticated web applications in Squeak.
 Develop for the web using reusable, embeddable components and unique
 call/return semantics for moving between pages.'.
 
    info maintainer: 'Lukas Renggli '.
    info homepage: 'http://www.seaside.st/'.
    info squeakMapID: ''.
    info url: 'http://www.squeaksource.com/Seaside/Seaside2.8a1-lr.522.mcz'.
    info version: '2.8.522'.
    info provides: #().
 
    self dependsOn: #('KomHttpServer').

The default load/unload action (which can be overridden is)

   self load: [
       Installer
 installUrl:'http://www.squeaksource.com/Seaside/Seaside2.8a1-lr.522.mcz'.
 ].
 
   self unloadDependsOn: { self taskUnloadDependants }.
   self unload: [
       Installer unload: 'Seaside'.
   ].

usage:


Packages current load: 'Seaside'.

Packages/Sake is also supported by Installer in a manner similar to
universes support, e.g.

 Installer sake addPackage: 'PackagesA'; addPackage: 'PackageB'; install.