Are You Being Served
Volume Number: 15 (1999)
Issue Number: 11
Column Tag: Version Control
Are You Being Served?
by Jonathan Guyer, Washington, DC
Version management for the well-appointed developer
There are several version control options for the Mac, but for years my favorite has been VOODOO from UNI SOFTWARE PLUS (UNISOFT). I'm apparently not alone, as VOODOO received the 1997 MacWorld/MacTech Eddy award for the Best Developer Tool. Earlier this year, UNISOFT released a completely redesigned and considerably more powerful implementation: VOODOO Server <http://www.unisoftwareplus.com/products/voodooserver.html>. This latest version control offering for the Macintosh is the subject of this article.
I'll Start Next Week, Honest...
Version management, like backups and flossing, is one of those things that we all know that we should do, but that too many of us never seem to find the time to do properly. Each is tedious and painful. Each consumes time that could be devoted to something more enjoyable. Besides, Byzantine source code, like head crashes and gingivitis, is something that only happens to other people, right?
If only that were true. How often have you pursued a promising trail of "improvements" to your application code, only to discover that you've turned something that worked reasonably well into a convoluted, poorly documented, unworkable mess of #ifdef's? (Assuming you even exercised the limited discipline of #ifdefing your code and didn't just hack away at the only copy of your source files (which, of course, I've never done)). No problem, you think, "I backed up my source code before I started into this." But wait, that was over a month ago, and some of the subsequent code was pretty good stuff. And what about your coworkers, who've been productively writing for the last month, oblivious to the mess you've made of the source tree? (We all know that it's our coworkers who do this to us, never us to them, but you get the idea.)
Enter version control. If you floss regularly and Retrospect dutifully backs up your hard disk every night, you might wonder why you need specialized version management software. Admittedly, many of the tasks are similar (to backup, not flossing). You could probably do an adequate job of administering your source tree with just Retrospect. Some advantages of having a dedicated version manager, however, are:
- Tight integration with one or more development environments. Numerous plugins for numerous source control systems have been developed for CodeWarrior, MPW, BBEdit, and Alpha, at least.
- The ability to tag the entries in the database. Backup software lets you keep track of when you stored the files. Version control software also allows you to make annotations telling why you made a given set of changes.
- Database structure needn't follow your local folder structure. The point of backup software is to restore the state of your hard disk to what it was before disaster struck. The point of version management software is to store a historical record of your project in a structure that's meaningful for your project (this is not necessarily the structure demanded by your compiler).
- Facilities to find out how your local files compare with those checked into the version archive. Backup software just stores any files that have changed since the last backup. Checking files into a version database is a more reasoned process.
You'll usually only want to check-in stable sources and you'll want to take care not to clobber your coworkers' changes.
As UNISOFT stresses, though, version management is no replacement for regular backups. In the unlikely event that your VOODOO Server database gets corrupted, nothing's going to save you but a pristine backup (version management's not much of a substitute for proper dental hygiene, either).
Given the central role that version control plays in the life of any sane programmer, it's not surprising that the subject has been covered in MacTech before. Paul Snively (Snively 1996) and Richard Wesley (Wesley 1998) wrote excellent surveys of version management software on the Macintosh and Christoph Reichenberger (Reichenberger 1996) reviewed VOODOO Server's non-client/server predecessor.
My experience with version control is limited to several years with VOODOO, nearly a year with VOODOO Server, and an exceptionally brief flirtation with a couple of Mac CVS clients. As a result, I won't attempt to draw comparisons between VOODOO Server and other tools, except where those comparisons are unequivocal. I commend anyone interested in the range of version management options on the Mac to read Paul Snively's and Richard Wesley's earlier MacTech articles. There are also at least two web pages devoted to version control on the Mac: Metrowerks' <http://www.metrowerks.com/desktop/version_control/> and Richard Wesley's <http://www.electricfish.com/hawkfish/macvcs/>.
VOODOO Server's Notion of Version Management
If you're already familiar with VOODOO, you may want to skip this section, since VOODOO and VOODOO Server handle version management in largely the same way. Note, however, that VOODOO Server does not yet support variants (see below).
In addition, the treatment of files within a VOODOO Server project is a little different. The original VOODOO organizes sources in a hierarchy of structure nodes, component nodes, version group nodes, and variant nodes. VOODOO Server has a somewhat simpler architecture of parts, folders, files, and (eventually) variants. In VOODOO Server, the structure within each part is a direct reflection of the folder structure that was stored in that part. VOODOO's structure nodes can have an abstract arrangement that bears no relation to the folders on your hard disk.
UNISOFT treats source control a little differently than other tools. The acronym "Versions Of Outdated Documents Organized Orthogonally" actually does convey an important quality of VOODOO: orthogonality. Two dimensions of that orthogonality are common to all version management tools; they're what it's all about: components and revisions. Each file you archive is a component and each instance of archiving is a revision.
The third dimension is variants and is, to my knowledge, unique to VOODOO (unfortunately, it's not even accessible in VOODOO Server, yet, but it's worth talking about anyway, since UNISOFT promises to enable this feature soon). Revisions represent components at different times, reflecting changes in implementation, bug fixes, and the general evolution of an application. Variants, on the other hand, represent different, but related, applications or implementations.
As UNISOFT recommends, "If possible, manage variants by avoiding them." Sometimes this is impossible, though. For example, an application implemented for Mac and Windows will have (hopefully) the bulk of its code in a common variant. Platform-specific sources will need to have separate Mac and Windows variants, though. When the differences are simple, the classic #ifdef (or its equivalent) is the preferable way to support multiple implementations. For very divergent implementations, though, rather than maintaining two separate source trees or cluttering your code with #ifdef macintosh ... #elif defined(__INTEL__) ... #endif, variants allow you to have a single source tree and a single project or makefile that generates the particular application you're interested in.
Want to compile your Windows app? Fetch those sources and build. Time for Mac? Let VOODOO Server fetch the necessary files, overriding the appropriate Windows versions. There is no requirement that components in different variants maintain revision parity, either. Update revisions as appropriate and branch variants as needed. There's no need to worry about a geometrically expanding database. VOODOO Server employs a delta storage mechanism that makes efficient use of space.
This is a good time to highlight another way that VOODOO Server differs from other tools. In RCS- or SCCS-based version management systems, version numbers are an integral part of the storage mechanism. There is no particular problem with this until you start trying to achieve the effect of VOODOO's variants. At that point, all the information has to be encoded in the version number, which quickly gets unreadable. With VOODOO, the user assigns version numbers. If you don't like numbers, it's no problem to assign names to your major code revisions and there's no reason at all to apply specific designations to intermediate revisions. VOODOO emphasizes the different revisions and variants of your project, not your individual source files.
One of the most useful aspects of VOODOO Server is that it understands Macintosh files, resource forks and all. During the long course of my PhD, I certainly archived the various codes that I wrote. I also recorded each revision of my LaTeX journal articles, the development of my Mathematica notebooks, all of my Igor Pro experiment files, and the entire set of LaTeX files and PostScript figures that made up my dissertation. I generally recommend storing the sources needed to generate files, but in the case of some of my figures, generation was sufficiently onerous that I preferred to store the figure along with its Igor or Mathematica source. Many of these files were text-only, but a significant percentage were either binary or had resource forks. Other version control systems have developed ways to deal with binaries and with two-fork Mac files, but none seems to do so as smoothly as VOODOO or VOODOO Server.
All of the preceding was as true for the original VOODOO as it is for VOODOO Server. One might ask, then, what necessitates a completely new application and architecture. VOODOO Server advances on its predecessor in several important ways. The most obvious is enshrined in the name. The distinction of a client/server architecture may seem pedantic if you do all of your work on a single machine. For distributed development, though, the advantage is enormous. This way, local and remote machines can focus on their own respective tasks and network traffic is minimized. With the old VOODOO, the entire database must be mounted to the "client" machine before you can work with it; in contrast, VOODOO clients only transfer what they need. Just as important, only one process, the Server, ever accesses the VOODOO Server database. This significantly reduces the risk of corruption. CVS, which superficially appears to have a client/server architecture, actually allows each client to directly access its database.
Another new feature of VOODOO Server is that storage takes place in two steps: files are first checked in, then, when the Server is idle, it performs the differencing and compression. This is one of the advantages of the new client/server architecture. With the old VOODOO, you had to stay connected to the database while all of this was going on.
In a major advance over the old VOODOO, VOODOO Server fully implements the AppleEvent Object Model. As many of you surely know, integrating the AEOM with an existing application is often more trouble than it's worth. It's not surprising that UNISOFT decided to start from scratch. While the old VOODOO allowed moderate AppleEvent access, VOODOO Server's AppleEvent suite is superb. Later in this article, I'll show how to take advantage of this important new feature.
VOODOO Server and CodeWarrior
Out of the box, VOODOO Server's most convenient client tool is the CodeWarrior IDE. When you first begin working with any source control system, you'll want to do one of two things: (1) enter an existing project and set of sources into version control or (2) fetch the files from an archive so that you can begin working with them. In either case, you'll first need to Edit->Version Control Settings....
VCS Settings
Figure 1.VCS Setup panel of the VCS Settings dialog in the CodeWarrior IDE.
The general Version Control System (VCS) settings are manipulated with the dialog in Figure 1. Most of the items in the VCS Setup panel are self-explanatory. Two idiosyncratic settings are the Database Path and Local Path, which must be set before you can carry out version management operations within the IDE. VOODOO Server doesn't generally care what these paths are, but only pays attention to the Part Paths in Figure 2 (VOODOO Server does use the Local Path as a reference point when you perform a ""Recursive->Fetch To...). Usually, you'll just set these to {Project}: and forget about them.
Having to set these extraneous paths for every project is only a minor inconvenience; what is more vexing is the absolute requirement that all local sources lie within the Local Path. In the course of Alpha development, we've run up against this limitation. Alpha 7.x and Alpha 8.x both run the same Tcl scripts. As we work to develop Alpha 8.x and simultaneously support Alpha 7.x, we'd certainly prefer to keep only a single set of Tcl sources. Alpha 8.x can follow aliases to find its Tcl scripts, but Alpha 7.x cannot. Therefore, we keep the original Tcl folder in the Alpha 7.x directory and an alias to it in the Alpha 8.x directory. So far, so good.
The problem comes when we try to track the Tcl scripts in the same source control project with the Alpha application. For a long time, Alpha 7.x wasn't even on the same drive with my development directories, so there was no way to both share the Tcl scripts and track them within Alpha's VOODOO Server project. For the time being, then, we're forced to maintain separate Tcl directories. Fortunately, VOODOO Server itself doesn't care where your sources lie, as long as appropriate part folders have been defined for them. Most VOODOO clients, such as the one I'll develop later in this article, should happily archive sources from any accessible volume. Hopefully, Metrowerks will one day see the wisdom of really supporting VCS tools other than their own.
Figure 2.VOODOO Server panel of the VCS Settings dialog in the CodeWarrior IDE.
The VOODOO Server panel in Figure 2 is where you'll find some of the more interesting settings. The first thing to choose is the VOODOO Server itself. If you choose a Local server, then the 'VoSe' application on your own computer will be used. If you select a Remote server, then you'll be presented with a PPCBrowser, giving access to any VOODOO Server available via Program Linking.
The next step is to specify the VOODOO Project and Part Folders. As discussed above, the part folders must lie within the Local Path you assigned in the VCS Setup panel. It's neither necessary nor desirable to assign a part folder for every local folder in your source tree. Part folders contain their entire subordinate folder structure, unless a more specific part folder contains the items in question. It would be nice if there was a more automated way to assign the paths. VOODOO Server can't do much until you do this and it's particular about what the folders are named, but you generally only need to do this once. I'll show a way a client can help with this a little bit later.
The Options pane contains elements that require a bit more thought. For the moment, variants are not fully enabled in VOODOO Server, but the Variant Filter still allows you to restrict the components you work with. If a single component is shared between multiple variants, filtering isn't necessary. On the other hand, if a particular component has different implementations in different variants (obviously, some must or you wouldn't bother with variants to begin with), then you'll need to filter sufficiently that each component you work with is unique. Depending on the nature of your project, this is one setting that you may need to change more frequently than the other ones in this pane.
The Locking Method can be one of None, Finder Locking, CKID, or CKID & Finder Locking. Finder Locking simply uses the Finder's lock flag to control access to a file. CKID refers to the Projector 'ckid' resource. This source control resource is respected by most development tools you're likely to use (Alpha, BBEdit, CodeWarrior, ResEdit, Resorcerer,... (look for the words "Projector Aware")), so you'll likely want to use at least this setting. If you've got some tools that don't know about the 'ckid', then you'll probably want to set Finder Locking as well.
Lastly, you'll want to set your Compare Helpers. Before checking components in and out of VOODOO Server's database, it's a good idea to know how your local files differ from those in the archive (if at all). This way, you can set a meaningful comment when you check-in or know whether you're ready to override your own sources with your coworker's maniacal changes.
Text Files can be compared using BBEdit, CodeWarrior, or MS Word (no Alpha yet, I'm afraid). Since we're talking about the IDE to begin with, and because Metrowerks' compare tool is quite nice, I imagine that many users will choose CodeWarrior. Resource Files can be compared with the commercial Resorcerer or freeware ResCompare tool. ResCompare seems to be the superior tool here, since it shows you how your resources differ.
To control an existing project:
Once the project is configured, source control is all handled through the menus in Figure 3. The VCS () menu is built into CodeWarrior. The VOODOO Server submenu in the Scripts () menu is obtained by adding UNISOFT's supplied script folder to CodeWarrior's (Scripts) folder.
Figure 3.VCS menu and VOODOO Server script menu in the CodeWarrior IDE.
Beginning to control a project with VOODOO Server is a pretty simple matter. Select ->Project->Add... to add the CW project file itself and assign an appropriate comment, like "Finally tracking versions!" Next, select all the files in the project (don't worry if they're not all contained by your part paths; VOODOO Server will ignore the ones that don't apply) and select ->Add.... Again assign a comment (the default will be the same one you just entered for the project) and the rest is done for you.
When the Add... is finished, you can see which files were stored by scanning the project window for the VCS status icons. In order, the icons mean:
checked-out (writable)
checked-in (read-only)
Modify Read-Only (MRO) (checked-in but writable)
not under source control, but locked by the Finder
not under source control and unlocked in the Finder
These same icons appear in the IDE's and BBEdit's editor windows (the settings are respected by Alpha, but it gives no visual indication (yet)). This VCS status often gets garbled, especially if you access the source files with multiple tools, so, if you find the indications anomalous, try selecting ->Sync File Status.
To fetch an archived project:
If you're not beginning to apply source control to your own project, then you're likely in the position of joining an existing project. In this case, you'll need to fetch the project file, sources, resources, etc. from a VOODOO Server database. All you need do is run UNISOFT's ->VOODOO Server->Fetch Initial CW Project script. The advantage of this script is that you can run it even if there is no CW Project open. Once the CW project file has been fetched, open it. Now run ->VOODOO Server->Setup Version Control and then - if necessary - manually customize your VCS Settings (these are unique to each project in the CodeWarrior IDE).
Now you are prepared to get the complete project structure and all corresponding files. With just the one command ->Recursive->Fetch Read-Only the VOODOO Server plugin will connect to your specified Server and fetch all of the files in the project.
If you're not beginning to apply source control to your own project, then you're likely in the position of joining an existing project. In this case, you'll need to fetch the project file, sources, resources, etc. from a VOODOO Server database. All you need do is run UNISOFT's ->VOODOO Server->Fetch Initial CW Project script. The advantage of this script is that you can run it even if there is no CodeWarrior project open. Once the project has been fetched, open it. Now run ->VOODOO Server->Setup Version Control and then - if necessary - manually customize your VCS Settings (these are unique to each project in the CodeWarrior IDE). Now you are prepared to get the complete project structure and all corresponding files. With just the one command, ->Recursive->Fetch Read-Only, the VOODOO Server plugin will connect to your specified Server and fetch all of the files in the project.
If your defined part folders don't match those in VOODOO Server's database, the plugin will complain, but unfortunately won't assist you in creating the right ones. Aside from this minor blemish, it's a pretty simple matter to retrieve an entire source hierarchy when you join a development effort (or if you want to start over again after botching up the sources you already have).
If you're administering a project, one way to simplify matters for new team members is to create a minimal project directory for them to start with. You can assign default VCS settings, create all the necessary (empty) folders, and assign part folders that point to them. Your users need only download this simple project folder and customize their settings. Because part paths are not {Project}-relative, they'll need to take particular care to update those paths for their own platform, but at least you've given them guidance on what parts they need.
Until the release of Mac OS 9, establishing an AppleTalk-over-IP connection was a slow and haphazard process. Rather than creating an empty set of directories, my coworkers and I have found it preferable to create a StuffIt archive containing a complete set of project files and sources. We only update this archive at major code revisions (if at all). This archive can be retrieved quickly with ftp or AppleShareIP and then the user only has to fetch the files that have changed since the archive was last stuffed. Happily, Mac OS 9 makes this unnecessary; in addition to "classical" LocalTalk and EtherTalk, this latest OS supports AppleTalk over the Internet, too.
Day-to-day version control
Once you have a set of sources under control by VOODOO Server, you'll need to periodically synchronize your changes with the archive. When you do this is really up to you. It's undesirable to store every single change in the database. On the other hand, you want to have a meaningful history of your project's development. It'll probably take some experimentation to establish a workable routine.
The VCS () and Scripts () menus give access to just about every task you might want to perform. The items in the main VCS menu apply to the top editor window or, if the project window is frontmost, whichever files you have selected in the project window. The Project submenu pertains to the CodeWarrior project file. The Recursive submenu operates on all the files in the project. You can survey the status of your project by performing one of the Log... operations. Mark major revisions to your code with Edit Bookmarks.... At any time, you can Show Differences between a local file and any of its corresponding versions in the database.
To get a broader overview of your project's history, you can Browse History..., as shown in Figure 5. This dialog allows you to Turn Back to earlier revisions and Fetch, View, or scan information about your files at any point during the development of your projects. You can then easily Show Differences between local and archived versions of your files.
Figure 5.Browse History dialog in the CodeWarrior IDE.
We've already seen that to obtain an initial set of sources, you Fetch Read-Only. If you want to make changes to those sources, you have two choices. You can Check Out..., which will prevent any other users from working with that component. Alternatively, you may choose Modify Read-Only (MRO) editing. This second option doesn't have a menu item. When you attempt to edit a read-only file, the IDE will give you the option to Check Out or Make Writable; choose the latter for MRO editing.
Generally, you should use Check Out if your sources are well compartmentalized (aren't they all?) or if your changes are quick. Use MRO if you've got intertwined sources or lengthy changes and other team members who need to tweak those sources while you're working on them. This option involves more work, as you'll need to carefully compare your local sources to those in the archive before you check them in, but it provides more flexibility. Because control is not as positive, MRO is generally to be avoided.
When you're done editing, you'll either want to Check In or Undo Check Out. You'll use the first if you want your coding efforts to be made available to everybody else in the project and the second if you just want to dispose of the evidence. If you've been doing MRO editing, you'll need to Check Out... before you can Check In. If you decide that a particular source file no longer belongs in your project, you should choose Make Obsolete.... This removes the component from the active database, but it's not deleted; you can always retrieve it by rolling back to when the file was still active (Browse History...) and doing a Fetch.
VOODOO Admin
The VOODOO Server plugin in the IDE takes care of creating your project and parts based on the information you supplied in the VCS Settings dialog. To grant access to other users and to make other global changes to your projects and parts, you'll need to run VOODOO Admin. This FaceSpan-based app is a good example of what can be achieved with VOODOO Server's rich AppleEvent suite. Once you log onto the server, you'll have access to three collections: Projects, Parts, and Users, as shown in Figure 6.
Figure 6.VOODOO Admin’s primary windows.
Each of these collections is independent. Parts can be shared between multiple projects and users are global to the database, with potential access to any part or project. Permissions are handled on a part-by-part basis; you don't grant access to a project, but rather to the parts within that project. These part's access rights then apply to all projects that include them.
VOODOO Admin has an elegant Drag and Drop interface. Simply drag items from the Parts panel to the Parts of Project... panel to build up a project. Likewise, drag items from the Users panel to the Users of Part... panel to begin assigning permissions. Unlike the old VOODOO, a single Admin password controls the whole database. Any user can log on with VOODOO Admin to see and manipulate the project structure (assuming they have the necessary permission), but only those with the Admin password can perform certain radical tasks with the database (like changing user permissions). Note: When you create new users, be sure that those users also have permission for Program Linking to VOODOO Server's machine.
Building VOODOO Clients
VOODOO Server comes prepackaged with the two clients we've already encountered: the CodeWarrior IDE plugin and the VOODOO Admin applet. Both are great tools, but they have their limitations. In particular, if you're not using CodeWarrior, you'll have a pretty hard time performing any version management at all. Fortunately, VOODOO Server's AppleEvent suite is extensive enough that users can build their own clients. If you do all of your development in CodeWarrior and you're happy with the VCS plugin, you can skip the rest of this section.
AppleScript
When I first started working on this article, I set out to show some simple examples of AppleScripting VOODOO Server. Much to my dismay, UNISOFT has already provided AppleScripts and droplets to perform just about every simple version management task I could think of. They've gone a step further by incorporating a large collection of common utility functions in the VOODOO Server ScriptLib. If you do find something that you need to write a script for, chances are that you'll be able to get the ScriptLib to do most of the dirty work. Where it doesn't, its handlers should give a good indication of VOODOO Server's AppleScript syntax and the sorts of sanity checking you should be doing. Let's look at a few examples from UNISOFT's library:
Listing 1: VOODOO Server ScriptLib
VerifyFileExistence
check if vFile exists. if vFile does not exists ask user if he want to create it.
returns a reference to the vFile
on VerifyFileExistence(inServerApp, inPartPath)
set thePartName to ExtractPartName(inPartPath)
tell application "VOODOO Server" - using terms of
tell inServerApp
tell first database
tell part named thePartName
if not (exists vFile named inPartPath) then
.
.
.
else
- return a reference to existing vFile
set theVSFile to ¬
a reference to vFile named inPartPath
end if
end tell
end tell
end tell
end tell
return theVSFile
end VerifyFileExistence
Fetch file from the VOODOO Server to a local file performing on-the-fly MacBinary decoding
FetchFile
on FetchFile(inServerApp, inVSFile, inLocalFile, ¬
inComment, inLocking, inUnlocking)
set theFileRef to missing value
set theTask to missing value
try
tell application "VOODOO Server" - using terms of
tell inServerApp
set theVSPart to container of inVSFile
set theLength to size of vVersion of inVSFile
if format of vVersion of inVSFile ¬
is not MacBinary then
error "Restriction: The script only handles ¬
MacBinary encoded versions."
end if
set theTask to create fetch task for inVSFile ¬
locking inLocking with comment inComment ¬
unlocking inUnlocking
end tell
end tell
tell application "Finder"
set locked of file inLocalFile to false
end tell
set theFileRef to ¬
(MacBinary open for access inLocalFile ¬
with write permission)
set theBufSize to 64 * 1024
-stop log
repeat while theLength > 0
if theLength < theBufSize then ¬
set theBufSize to theLength
tell application "VOODOO Server"
tell inServerApp
set theData to fetch theTask for theBufSize
end tell
end tell
MacBinary write theData to theFileRef
set theLength to theLength - theBufSize
end repeat
-start log
MacBinary close access of theFileRef
set theFileRef to missing value
tell application "VOODOO Server"
tell inServerApp
finish theTask
end tell
end tell
set theTask to missing value
if inLocking then
- file is locked in the database; i. e., we have to unlock it locally
tell application "Finder"
set locked of file inLocalFile to false
end tell
else if inUnlocking then
- file is unlocked in the database; i. e., we have to lock it locally
tell application "Finder"
set locked of file inLocalFile to true
end tell
end if
on error errMsg number errNum
.
.
.
end try
end FetchFile
Now, with just these two procedures, your own script can say
- load the ScriptLib into our script
set gVSLib to load script alias ¬
(extract_parent_folder_path_from(path to me) & ¬
"VOODOO Server ScriptLib")
- check if the file exists before trying to fetch it
set theVSFile to ¬
VerifyFileExistence(pServerApp, thePartPath) of gVSLib
tell application "VOODOO Server" - using terms of
tell pServerApp
set theVSFileExists to exists theVSFile
end tell
end tell
if theVSFileExists then
- Perform the desired fetch
FetchFile(pServerApp, theVSFile, theLocalFile, ¬
theComment, true, false) of gVSLib
end if
Obviously, you'll need to provide the code to assign values to pServerApp, thePartPath, theVSFile, theLocalFile, and theComment. Also, you'll want to verify whether the version in the archive is really what you want before you fetch it (look at UNISOFT's VOODOO Server Check Out script to see what you should be checking). Regardless, the ScriptLib does most of the heavy lifting.
Note that, because of VOODOO Server's adherence to the AEOM, rather than issuing all instructions to VOODOO Server, you can specifically tell parts and databases to do things. Another use of tell that might seem a little peculiar is the frequent use of the double tell, e.g., tell application "VOODOO Server" to tell inServerApp.... This syntax is necessary when communicating with a remote application (AppleScript 1.4 introduces using terms from, but the idea is the same). The Script Editor needs to access the 'aete' resource of your target application in order to compile, but you may not even determine where the remote process is until runtime. By doing a double tell, you can compile against a local copy of the application, but eventually communicate with a remote copy.
Another set of commands that's probably unfamiliar is MacBinary.... These routines are obtained from UNISOFT's MacBinary IO OSAX. This scripting addition is included with VOODOO Server and handles all the drudgery involved in packing two-fork Mac files into VOODOO Server's database. The old VOODOO handled Mac files transparently, but VOODOO Server is clearly constructed with an eye toward multi-platform development. Anything you store or fetch is just a data stream to the Server; so, the client handles local file system specifics.
We see from FetchFile and its complement, StoreFile, that files are checked in and out of VOODOO Server in two steps. First, a task is obtained by calling create store [fetch] task.... This is where you specify all of the version information: comments, locking status, etc. Next, using this task, you transfer data in or out of the database in chunks with store [fetch] theTask for theBufSize (you could probably do it all at once, but if the file's large, you'll lock up the client and server machines, as well as the network, while you do).
An aspect of the old VOODOO that I found tedious in the extreme was adding a large group of files to a variant branch (or adding a variant branch to a large group of files, depending on how you look at it). Although VOODOO Server does not yet support variants, all indications are that this could be accomplished with something as simple as:
tell application "VOODOO Server" to ¬
add to part vFiles {path1, path2,...} ¬
in variants {variant1, variant2,...}
Having spent a lot of time trying to script the old VOODOO (and having cajoled UNISOFT into adding a number of AppleEvents), I'm really pleased with how easy it is to control VOODOO Server with AppleScript.
Alpha Tcl
For something a little more exotic, let's look at sending raw AppleEvents to VOODOO Server. As an example near and dear to my heart, we'll assemble a simple client in Alpha. The Alpha programmer's editor <http://alpha.olm.net/> has an embedded Tcl interpreter (that's "Tool Command Language," not "Think Class Library") akin to Lisp in emacs. One of Tcl's features is that it's easily extensible, both with commands written in Tcl and in C. Alpha exploits this capacity, adding its own commands for things like text editing, hypertext, and AppleEvents (much of this could now be implemented in Tk, but Alpha predates that extension to Tcl).
The point of this article is not to teach the Tcl language or the peculiarities of Alpha (although I can claim some expertise in the latter, I don't begin to be qualified to teach the former). If you're interested in Tcl, I commend you to <http://www.scriptics.com/>, John Ousterhout's canonical guide (Ousterhout 1994), and Clif Flynt's recent text (Flynt 1999). Still, a few explanations are in order to make this code clearer to a wider audience. There's no reason that most of the following Alpha/Tcl/AEGizmos code couldn't be applied directly to a C/C++ plugin for a text editor that starts with "B" (I was going to write one, but I think my editor would kill me if I took the time).
Alpha uses Jens Alfke's AEGizmos http://www.mooseyard.com/Jens/Software/ to send and receive AppleEvents. AEGizmos makes it really easy to read and write AppleEvents, but its strings are not the easiest things for a program like a Tcl script to interpret. As a result, in the scripts that follow, AEGizmos is used for input, but the returned AppleEvent descriptors are automagically parsed by my TclAE library into a form that's a little more manageable by the scripts. The specifics of TclAE are not germane here, other than to note that:
- tclAE::build::result builds and sends the specified AppleEvent, parses the result, and returns its direct object ('--'). There are three required arguments: the target process, the event class, and the event ID. These can be followed by an optional list of event parameters. If the event result is an AppleEvent error, this gets thrown.
- tclAE::build::typedResult does the same thing, but leaves the result in a more explicit form.
- tclAE::build::throw does the parsing, but it does not return the direct object; it's only interested in AppleEvent errors. If there is no error, this routine returns quietly.
- AEBuild doesn't do any result parsing at all; it just sends the specified event.
In Tcl syntax, enclosing an item within square [] brackets causes command substitution to be performed on the contained script and its result to be put in its place. As an example, the proc VoSe::checkLoggedOn contains the code "obj {...,[tclAE::build::TEXT [set VoSemodeVars(userName)]],...}". The Tcl interpreter recursively processes this, first ascertaining the value of the element userName in the associative array VoSemodeVars. Its value, say John, is then passed to the proc tclAE::build::TEXT, which returns "John" (there's more to this proc than just applying curly quotes. If the userName had contained nasty characters, the result would have been the less legible, but more robust TEXT(«4a6f686e»)). Thus, AEGizmos is ultimately passed "obj {form:name, want:type(cAcc), seld:"John",...}", which it understands to mean "the VOODOO Server user whose name is 'John'...". Note that the usual AppleEvent constants are not used here; AEGizmos gains considerable speed by passing all identifiers explicitly and, as Jens says, "Luckily, the [4-letter] codes are semi-mnemonic anyway."
The entire VoSeMenu.tcl script file is available with this article, so in Listing 2, I'm going to focus on the details of sending AppleEvents between Alpha and VOODOO Server. I precede every AppleEvent with an equivalent pseudo-AppleScript comment (started with '#'). Besides omitting all of the user interface routines, I've excised most of the error checking to make the scripts a little clearer. The routines that retrieve lists from VOODOO Server are split into two parts: VoSe::listXXX and VoSe::displayXXX. I originally did it this way to allow queuing the event results, but Alpha's queuing code is a little buggy right now, so the list results are just passed to the display proc as a parameter.
It's no accident, by the way, that these Tcl procs closely mirror the structure of their counterparts in UNISOFT's VOODOO Server ScriptLib. This is partly because I'm lazy and partly because AppleEvents are largely independent of the language they're rendered in. An especially handy tool for a slothful programmer like me is Capture AE <http://www.westcodesoft.com/FTPOCTools.html>. This utility spies on AppleEvent traffic (such as sent by UNISOFT's AppleScripts) and dumps it directly in AEGizmos form.
Listing 2: VoSeMenu.tcl
VoSe::connect
Before we can interact with VOODOO Server, we need to log on. This proc logs on and sets the GMT offset of the connection. It's particularly important to set the GMT offset for widely distributed development efforts, else the project history won't be right.
time to GMT is actually a system-level call, but the event needs
to be addressed to somebody; it could just as easily have been
addressed to Alpha as to the Finder.
proc VoSe::connect {} {
.
# set connection to logon as user name userName ¬
# with password userPassword
set connection \
[tclAE::build::typedResult \
[dialog::getFlag VOODOOServer] \
VoDo "Lgn " \
-- [VoSe::database 1] \
Acct [tclAE::build::TEXT \
[dialog::getFlag userName]] \
Pass [tclAE::build::TEXT \
[dialog::getFlag userPassword]] \
]
# set time zone of connection to time to GMT
AEBuild [dialog::getFlag VOODOOServer] \
core setd \
-- [$connection print] \
data [tclAE::build::result 'MACS' syso "GMT "]
.
}
VoSe::updatePartPaths
As I promised while discussing the CodeWarrior IDE plugin, here's a way that a VOODOO client can help the user to assign the right part paths. This proc gets called any time the project changes. It queries VOODOO Server for the parts and then automatically creates the part paths. Unlike the IDE, there is no requirement that the path end in the same name as the part (although that's probably a good idea most of the time). There's also no restriction on where you put your part paths.
A more refined version of this proc would save multiple sets of paths and restore them when the same project is selected again.
proc VoSe::updatePartPaths {project} {
.
.
.
# get every part of project project of first database
set parts [tclAE::build::result \
[dialog::getFlag VOODOOServer] core getd \
-- "obj {
form:prop,
want:type(prop),
seld:type(pnam),
from:obj {
form:indx,
want:type(cPrt),
seld:abso(all ),
from:[VoSe::project $project [VoSe::database 1]]
}
}" \
]
# flush old part paths and create new ones
global VoSe::newPaths
catch {unset VoSe::newPaths}
foreach part $parts {
set VoSe::newPaths([VoSe::partPathLabel $part]) ""
}
# prompt the user to assign paths for the parts
VoSe::setLocalPaths
}
VoSe::listFiles
Getting VOODOO Server to list projects, parts, or files involves largely the same actions. I'll only list the procs for files because they illustrate one of the cooler aspects of an application that adheres to the AEOM: whose clauses (AKA where clauses).
In our simple Alpha client, we use whose to limit our list to 'TEXT' files (there's no point getting binaries like CodeWarrior projects, because Alpha won't understand them). In AEGizmos syntax, the whose clause is accomplished with seld:cmpd{...}. Another good example of whose clauses is UNISOFT's Fetch Initial CW Project AppleScript.
Certainly, we could ask for all the files and then check their file types manually. Using a whose clause gets VOODOO Server to do the hard work. There are several advantages to this:
- VOODOO Server knows its own data structure, so it can likely choose the appropriate files more efficiently than our script can.
- Even given identical efficiency, VOODOO Server makes the selection in fast compiled code, rather than our slow(er) Tcl code.
- Because VOODOO Server only returns the files we're interested in, we don't waste network bandwidth.
proc VoSe::listFiles {partInfo} {
global VoSemodeVars
.
.
.
# set part to name of part partInfo
set part [$partInfo get pnam]
# get every vFile of part part of the first database ¬
# whose (file type of vVersion of it) is "TEXT"
set files [tclAE::build::result \
$VoSemodeVars(VOODOOServer) core getd \
-- "obj {
form:test,
want:type(cFil),
from:obj {
form:name,
want:type(cPrt),
seld:[tclAE::build::TEXT $part],
from:obj {
form:indx,
want:type(cDtb),
seld:1,
from:'null'()
}
},
seld:cmpd{
relo:'= ',
obj1:obj {
form:prop,
want:type(prop),
seld:type(pFTp),
from:obj {
form:prop,
want:type(prop),
seld:type(cPlO),
from:'exmn'()
}
},
obj2:[tclAE::build::TEXT "TEXT"]
}
}" \
]
VoSe::displayFiles $files $part
}
VoSe::newerFile
Compare modification date of archived file to local file
Results:
Difference (in seconds) between modification dates
positive if archived is newer
zero if files are the same
negative if local file is newer
If the local file doesn't exist, returns +1
proc VoSe::newerFile {file} {
# set storedDate to modification date of stored file
set storedDate [[[$file get cPlO 0] get pMDt 0] get from]
# set storedDate to result as integer
# (It's in form 'ldt '(«xxxx»), but we want it as an integer for comparisons)
set storedDate [tclAE::coerce::hexd:long $storedDate]
# get modification date of local file
if {[catch {
getFileInfo [VoSe::localPath $file] fInfo
}]} {
# The local file is either undefined or nonexistant
return 1
} else {
return [expr {$storedDate - [set fInfo(modified)]}]
}
}
VoSe::displayFiles
Parse information from VoSe::listFiles and display it in a hybrid log and browser.
A few steps go beyond simple parsing:
- Using VoSe::newerFile, the creation date is reduced to a simple comparison label.
- A second AppleEvent needs to be sent to retrieve the name of the file's user.
- Each file path is reformatted to highlight the part it's in.
proc VoSe::displayFiles {files part} {
global "VoSePaths VoSe::closeHooks VoSemodeVars
set rows {}
foreach file $files {
set row {}
# get vVersion of file
# (Most of the interesting information is here)
set vVersion [$file get cPlO 0]
# get name of vVersion
lappend row [list [$vVersion get pnam]]
## Newest ##
set newest [VoSe::newerFile $file]
if {$newest > 0} {
lappend row [list " saved "]
} elseif {$newest == 0} {
lappend row [list " "]
} else {
lappend row [list "*local*"]
}
# get lock states of file
lappend row [list [$file get cLkS]]
# set userInfo to part user of vVersion
set userInfo [$vVersion get cUsr 0]
# get name of userInfo
set user [tclAE::build::result \
$VoSemodeVars(VOODOOServer) core getd \
-- "obj {
form:prop,
want:type(prop),
seld:type(pnam),
from:[$userInfo print]
}"
]
lappend row [list $user green \
"VoSe::inspectUser \"$user\""]
# get comment of vVersion
lappend row [list [$vVersion get pCnt]]
# get variant set of vVersion
lappend row [list [$vVersion get pVSt]]
# get path of file
# (as returned from VOODOO Server, paths are hard-coded with ":" separators)
set pPth [split [$file get pPth] ":"]
# Bracket the Part portion of the path
set pPth [lreplace $pPth 0 0 "«[lindex $pPth 0]»:"]
# rejoin path with separators appropriate to platform
# (probably will always be ":" for Mac, but why not do it right?)
lappend row [eval file join $pPth]
## Browser information ##
lappend row $file
lappend rows $row
}
.
}
VoSe::checkOutFile
Fetch the specified file from VOODOO Server.
It's not possible to fetch in Alpha 7.x. Fetched data is MacBinary encoded and, as such, contains invalid string characters (like \x00). The binary facilities of Tcl 8.x are required for any storage operations to work.
This proc requires UNISOFT's MacBinary IO OSAX to be installed.
Checking-in looks largely the same.
proc VoSe::checkOutFile {fileInfo} {
global VoSemodeVars VoSePaths
# Make sure that Alpha can handle binary data
if {![alpha::package vsatisfies 8.0 \
[alpha::package versions Alpha]]} {
error "It is not possible to fetch from \
VOODOO Server with this version of Alpha"
}
set localPath [VoSe::localPath $fileInfo]
set newest [VoSe::newerFile $fileInfo]
# determine whether to fetch, based on whether local file is newer
if {$newest > 0} {
.
.
.
}
# make sure there's a directory to fetch into
file mkdir [file dirname $localPath]
# get vVersion of fileInfo
set vVersion [$fileInfo get cPlO 0]
# get size of vVersion
set size [$vVersion get pSiz]
# get format of vVersion
set format [$vVersion get pFmt]
# get file of vVersion
set file [$vVersion get cFil 0]
# create fetch task for file ¬
# without locking and unlocking
set task [tclAE::build::typedResult \
$VoSemodeVars(VOODOOServer) VoDo Fetc \
-- [$file print] \
DLck 'fals'() \
DUlk 'fals'() \
]
# MacBinary open for access file localPath with write permission
set binRef [tclAE::build::result \
'MACS' MBIO open \
-- "obj {
form:name,
want:type(file),
seld:[tclAE::build::TEXT $localPath],
from:'null'()
}" \
perm 'true'() \
]
# fetch data in 64 KiB blocks
set buffSize [expr 64 * 1024]
try {
while {$size > 0} {
if {$size < $buffSize} {
set buffSize $size
}
# fetch task for buffSize as hexadecimal data
set data [tclAE::build::typedResult \
$VoSemodeVars(VOODOOServer) VoDo FchD \
-- [$task print] \
"Cnt " $buffSize \
]
# MacBinary write data to binRef
set binFile [tclAE::build::result \
'MACS' MBIO writ \
-- [$data print] \
refn $binRef \
]
incr size -$buffSize
}
}
# MacBinary close access of binRef
set binFile [tclAE::build::result \
'MACS' MBIO clos \
-- $binRef \
]
# finish task
set blah [tclAE::build::result \
$VoSemodeVars(VOODOOServer) VoDo Fnsh \
-- [$task print] \
]
# open the file for editing, regardless of whether we fetched it
# or if it's already open.
edit -c $binFile
}
Using these procs, along with the rest in VoSeMenu.tcl, we've got a rudimentary, but fully functional, VOODOO client embedded in Alpha, seen in Figure 7.
Figure 7.Alpha-VOODOO client windows.
A more sophisticated client would allow you to move back in the project's history, do more error checking, and automate more of the settings, but the fundamentals are already here. As simple as this client is, there are aspects that are decidedly superior to the IDE plugin client. For instance, part paths can lie anywhere on any visible volume and the user doesn't have to guess at what part paths need to be created.
Other languages
VOODOO clients are certainly not limited to being written in AppleScript or Tcl. Any language or program that gives access to AppleEvents will do. Many systems that have no direct AppleEvent access do allow for embedded AppleScripts, either in the form of a Scripts menu or as a do script extension to the application's own scripting language. One way or another, you should be able to find a way to make your pet application converse directly with VOODOO Server. The easier the communication is, the more likely you are to be disciplined about version management.
Limitations
Despite being a powerful tool, more than capable of managing a large, multi-developer project (the Alpha project has been using it for months and UNISOFT uses it for the management of VOODOO Server itself), VOODOO Server still has a number of limitations. I've already alluded to several of these weaknesses and none of them should come as any surprise to UNISOFT; in a refreshing measure of candor, each one is discussed in the introduction to the VOODOO Server manual.
Mac only
Ironically, two of VOODOO Server's biggest advantages for Mac developers also constitute its potentially greatest weaknesses. VOODOO Server is a 100% Mac-based tool, but that also means that VOODOO Server is a 100% Mac-based tool. For many projects, this won't matter, but cross-platform developers will either need to wait for UNISOFT's promised TCP/IP tools or use Macs for their actual fetching and storing.
AppleTalk
From the Mac developer's perspective, another strength of VOODOO Server is that everything is AppleScriptable. In fact, VOODOO Server is almost (but not quite) a faceless background app. However, AppleScript means AppleEvents, AppleEvents means Program-To-Program Communications (PPC), and PPC means AppleTalk. If you want to work with a VOODOO Server that's not on the same machine as your client, you must have an AppleTalk connection to it. AppleShare IP doesn't cut it, nor does any other form of IP file sharing. You have to have Program Linking, which means you have to have AppleTalk. I stress this point because it's been a real source of misery for my development team. Fortunately, with the advent of Mac OS 9, Apple has finally provided a mechanism for AppleTalk-over-IP (please don't ask me what the heck took them so long).
Until we got Mac OS 9, the Alpha team used Apple Remote Network Server/IP Remote to achieve AppleTalk-over-IP. This shareware tool
http://www.cs.mu.oz.au/appletalk/doc/IPRemote.userDoc.html (what is this Australian fascination with Mac networking, anyway?) functions for our purposes, but barely. ARNS doesn't like OpenTransport and VOODOO Server requires a minimum of Mac OS 8.1; you do the math. If you can establish a link, this system works perfectly well, although it's considerably slower than Mac OS 9. Unfortunately, links are hard to achieve and hard to maintain. This option is not for the faint of heart.
I've been told that Apple's AppleTalk-over-IP protocol is a repackaging of ShareWay IP from Open Door Networks, Inc. http://www.opendoor.com/shareway/. Interestingly, none of the ShareWay IP literature says anything about Program Linking or PPC and only seems to discuss AppleTalk in the context of AppleShare. When the Alpha 8 project started last year, we looked at this product and concluded, based on their sales literature, that it couldn't do what we needed. If you're not able to upgrade to Mac OS 9 for some reason, you'll probably want to give this product a look.
At least until UNISOFT implements their promised TCP/IP-based protocol stack, Mac OS 9 is unquestionably the superior route to version management over the Internet. In my limited experience with Mac OS 9, I've found it fast and reliable, both when compared with IP Remote and with Mac CVS "clients".
No variants
VOODOO Server already supports variants, but they aren't yet accessible. UNISOFT promises to enable variants as soon as possible, but in the interim, you'll need to consider how important this capability is for your project. Although Alpha development would benefit from variants, we've easily gotten along without them for the last year. A few voodoo-list subscribers with special needs have indicated they're sticking with the old VOODOO until variants are available, but most of us seem to prefer the other advantages of VOODOO Server.
Can't convert existing VOODOO projects
At this time, there is no way to convert a standard VOODOO project into a VOODOO Server project. If you're starting a new project, there's no reason to work with the old VOODOO at all (unless you really need variants). If you've got an existing project, you may want to continue working in standard VOODOO until a converter is available. UNISOFT indicates that it will not be possible to insert a converted project into a VOODOO Server project, so if you convert now, you'll always have to keep the old VOODOO project if you want to have access to the versions stored in that project.
No stand-alone client
For users of the old VOODOO, one of the most noticeable features was its graphical interface. The interface had its peculiarities and it evolved and improved over the years, but, from the beginning, it made the vast matrix of components, revisions, and variants both approachable and manageable. The first thing that VOODOO old-timers will notice is that this interface does not exist in VOODOO Server. In fairness, until variants are implemented, it's not really needed. Further, with VOODOO Server's rich AppleEvent dictionary, anybody (including UNISOFT) could mimic the old interface in a VOODOO client tool (you might want to clear it with UNISOFT's vast legal department before you start work on it, though).
As discussed already, VOODOO Server has a much more complete AppleEvent interface than the old VOODOO, allowing complete access to the database from any application that can make a PPC link. Ironically, the absence of an interface on the Server and of a stand-alone client means that archiving anything besides CodeWarrior sources is much harder than with plain VOODOO right now. The set of AppleScripts that comes with VOODOO Server addresses some of this need and the simple Tcl scripts I've shown in this article hopefully demonstrate that it's not too difficult to teach other applications how to be VOODOO clients. Still, at least for the time being, even UNISOFT recommends the original VOODOO if your version management tasks are focused on anything besides code development. As a case in point, I tracked this manuscript in VOODOO.
Moderately steep system requirements
VOODOO Server requires a PowerPC running Mac OS 8.1 or newer. The CodeWarrior plugin requires CodeWarrior Pro 4 or newer. While these requirements aren't unreasonable, they did necessitate some costly upgrades for our group. In the intervening year, Apple has released Mac OSes 8.5, 8.6, and 9 and Metrowerks has released Pro 5, so it seems likely that more potential users will have the minimum configuration now.
Conclusions
VOODOO Server's considerable advantages are
- True client/server architecture, which maximizes efficient use of your computers and network.
- A rich AppleEvent suite, including a full AppleEvent Object Model.
- Full integration with the CodeWarrior IDE. The only limitations I've encountered are products of Metrowerks' VCS API.
- Complete support for the Mac filesystem, allowing easy storage of text files, binaries, and two-fork Mac files.
- A robust database. I've heard a number of anecdotal complaints about database corruptions from users of other version control systems. Not only have I never encountered such a problem in VOODOO Server, I've never even heard of one.
If you're doing Mac-only development and if your development team is all on the same AppleTalk network, I recommend VOODOO Server without reservation. VOODOO Server's client/server architecture and its awareness of time-zones make it excellently suited to multi-site/multi-national development. Thanks to Mac OS 9, AppleTalk is no longer an impediment to far-flung development efforts.
The only developers that might want to hold off on trying VOODOO Server are those that absolutely need variants and those with cross-platform projects. In my view, the temporary lack of variants is not sufficient cause to revert to the old VOODOO, except for unusually complex projects. Cross-platform efforts will need to wait until UNISOFT implements some form of TCP/IP communications. For the rest of us, VOODOO Server is a welcome tool.
Bibliography and References
- Flynt, Clif. Tcl/Tk for Real Programmers, Academic Press, 1999.
- Ousterhout, John K. Tcl and the Tk Toolkit, Addison-Wesley, 1994.
- Reichenberger, Christoph. "Keeping Things Straight, Orthogonally: Do do that VOODOO?" MacTech Magazine 12:6 (June 1996).
- Snively, Paul. "Revisionist History: Organic tools for an unpleasant task". MacTech Magazine 12:6 (June 1996).
- Wesley, Richard. "Version Control and the Single Developer: How Version Control Can Make Your Life Safer and More Productive". MacTech Magazine 14:6 (June 1998).
Jonathan Guyer, http://www.his.com/jguyer/, has been programming on the Mac since 1984 and is a member of the Alpha Cabal, the core developers of the Alpha programmer's editor. Jon wrote the AlphaVOODOO Tcl script that integrates Alpha with the original VOODOO application. In his spare time, Jon is a materials scientist at the National Institute of Standards and Technology in Gaithersburg, MD.