How do you create a deployment process that automatically creates packages to deliver to your users for all supported platforms with as little fuss as possible?
The content of this guide has been created with version 7.9 of VisualWorks.
This article tries to provide a concise guide for standalone VisualWorks application developers. If you, like me, are building standalone apps that need to deploy on all supported platforms, you will encounter some interesting challenges. I have reached a strategy that works reasonably well, and I would like to share that strategy, hopefully benefiting others.
I invite VisualWorks developers to participate in making this article better. Files used in the article will be provided as downloads in a .zip archive.
I have used some (sparse) material already available on the internet. I try to give due credits:
- James Robertson created several video’s describing parts of the process
- Emiliano Pérez has an article at http://cag.lifia.info.unlp.edu.ar/cag/Wiki/05.+Tutorials/Deploying+with+VisualWorks
- There has been a discussion on the VisualWorks mailing list, instigated by me: http://forum.world.st/Installing-end-user-applications-on-Windows-and-ACL-td1599163.html
- 1 Introduction
- 2 Credits
- 3 VisualWorks image preparation
- 4 Setup
- 5 Summary
- 6 Creating the Windows Installer
- 7 Creating the Mac OS X Installer
- 8 Build Scripts
VisualWorks image preparation
The image you want to deploy has to be ready for deployment. This chapter summarises a few reminders.
If your application has windows (that is, it is not headless) you will want to have those windows show your nicely designed icons. This is not applicable for all platforms (on the Mac your windows do not have icons) but it is on Windows.
For that you use the Assets packages. There are two of them, one (Assets) is loaded by default in the image (7.7 up), and one (Assets-IDE) needs to be loaded.
Read the doc. I usually create an application assets class, LogSimAssets for example. Create a folder containing the artwork in the form of png files you want to use, including the image you want to use as your window icon, and sync your assets class with this folder.
VisualWorks will ask you for the folder you want your assets class to sync with, and it will import all readable image files in that folder. The result is a set of class methods in the protocol category png imports
Now you have all the images from that folder available in your image as Smalltalk objects, that you can reach by sending messages to the class like:
The icon you want to be your application window icon can be installed in two locations:
- the application window
- all windows (the default icon to replace the one from Cincom) — this is handy since you do not need to do this manually for every application window in your application, and also it modifies the icon in any standard windows you want to reuse, such as the directory browser.
The first way is simple. Go to your application class’ postOpenWith: method:
super postOpenWith: aBuilder.
aBuilder window application: self.
"Your own initialisation code."
aBuilder window icon: self class windowIcon
Create the windowIcon method in your application class and let it refer to your pretty icon in the assets you imported.
Building your image
My strategy is rather straightforward: after I have checked-in my sources into Store, with a comment like “Released version LogSim 4.2”, I file-out my sources into one or more special directories.
Let us say that for my LogSim application I need three components:
- General code that I keep in a folder called Reflektis (my company’s name)
- A framework for simulations in a folder called Simulations
- The application-specific code in a folder called LogSim
All folders have the same structure:
- all packages/bundles in .st files
- a fileIn.st file that loads the .st files in the directory in the correct order.
Now if I want to create my runtime image I only need to file in the fileIn.st file in the application-specific directory, which will call all dependent file-ins.
There are a few extra’s:
- I create a method prepareForDeployment in my application class. This method will be sent when deploying to prepare my class for deployment. More on that later.
This chapter describes the use of Runtime Packager to prepare a minimal image for deployment.
- Load Runtime Packager
The Simple Approach
This chapter describes an image preparation strategy that does not use Runtime Packager, and (almost) no stripping of the image. This is useful for simple applications, that do not profit much from stripping.
For successfully deploying your VisualWorks application I am assuming the following:
- You have a deployment image, created by using Runtime Packager or any other means.
- You have a Windows icon for your application, in the form of a .ico file with several icon sizes (for Vista and later make sure you include a 256×256 size)
- You are able to use VisualStudio 2010 Professional, to create the setup project in (probably need to chart a deployment strategy without the need of VisualStudio as well). Our approach here will elaborate on using the standard Windows Installer for your application, and not the VisualWorks Install framework. I have never been able to understand how I could use that.
Preparation involves some one-time actions. Note: to be able to do everything described below you need a Mac with XCode and a Windows PC with VisualStudio. Both OS’s can run inside a virtual machine of course (my setup is a Mac with Windows running in a vm).
- Create a standard folder structure.
The buildscripts folder will contain the .cmd files I talk about later. The messages directory will contain all the message folders/files you need for multilanguage projects. The uploads directory will contain the files you want to deliver to your users. You will notice I used the same naming convention Cincom uses for the platform folders.
Now we will go through the following steps.
- Create your runtime image. I name it specific for the application I am creating: logsim.im.
- Run the ImageWriter from any VisualWorks image to convert the 32-bit image to a 64 bit one. For simplicity in the build scripts this one is now named resource64.im
- Now run the build script for the application, in this case it will be buildLogSim.cmd. It will perform the following tasks:
- compress the image with ImageCompress, and rename the 32-bit image to resource.im
- it will call all platform-specific build command files, including the Windows build command files buildwin and buildwin64. These platform builds all do the following:
- copy resource.im or resource64.im into the platform-specific target platform folder. For example for the *nix targets I place the image together with the executable into a folder system. However for Windows this is different, since the VisualStudio Installer project will place these files in the correct destination. So for Windows all build artefacts will be placed in one target folder (win and win64 in my case). The Windows Installer project in VisualStudio will reference these files later. For Mac OS X this is also different since the image (and some other artefacts) need to be placed inside a hidden .app folder. More on Mac OS X in a separate chapter.
- copy the platform-specific runtime into the target folder
- copy other resources into the correct location in the target folder, such as readme files or anything you want to deploy with your application.
- I will mention separately the messages directory, containing all message files you may want to deploy if you are delivering a multilanguage solution. This messages directory in my case is always placed in the same folder as the image.
- For Mac OS X the builder placed the output artefacts directly into the application package, here LogSim.app. On creating the correct layout for Mac OS X, and how to create a user-friendly installer for Mac users I will add a special chapter later.
- the Windows builder does a few more things:
- it uses ResHacker to add the following files as a resource to the executable:
- the image file (resource.im or resource64.im)
- the icon for your application, which I always call app.ico
- an application name as resource 325
- a version from a Windows resource file version.rc that is compiled with the Windows resource compiler
- the logsim.exe.manifest file which is compiled with the Windows resource compiler as well
- For the 32-bit runtime that is the result of the previous steps I use upx to compress the runtime exe as much as possible. Since upx is not yet able to do this for 64-bit Windows executables this is omitted for the 64-bit Windows target.
- I sign the resulting executable with signtool. For this, I realise, you will need to have a developer certificate. However you can ignore this, and tell your users that the install may warn for the unsigned installer.
- it will package the results from the build phase and place the files you will going to provide to your users in a special directory called uploads. Packaging will do the following:
- For the *nix platforms it will use 7-zip to create a .tar file with the folder you want to provide to your user, and gzip that .tar file.
- For Windows this phase does nothing, since packaging, resulting in a Windows Installer .msi file, will be done by VisualStudio.
- Finally some cleanup, to remove all intermediate files. The result is an uploads directory containing all deployments, except for Windows and Mac OS X. This is described next.
- Go to VisualStudio on your Windows machine and build the installer using the setup I describe below.
- Go to XCode on the Mac and build the application using the setup I describe below.
- Finally for the Mac installer you want to build a nice-looking .dmg file, more on that also below.
Creating the Windows Installer
For this you need Microsoft VisualStudio. The examples in this chapter are created in VisualStudio Professional 10.
Creating the Mac OS X Installer
For Mac OS X you might deliver the .tar.gz file produced by the packaging phase described above, but your users will appreciate the common .dmg installer strategy. The file they download is a .dmg file, which is nothing more than a disc image. They can open the file which results in mounting the image on their desktop as a disk. This will open and create a window which shows what you have seen above for the MijnGeldConverter installer. Installation on a Mac involves nothing else than dragging the application to a folder, usually the Applications folder which is mounted as well but as an alias so that users can drag there instead of the “real” application folder on their desktop.
For building the actual application you will need to lay out a specific folder structure, and I have found it easiest to do this in XCode.
The XCode application project
As I mentioned above you will need a Macintosh, either a real one or one running inside a VM, and XCode, the Apple development environment.
This chapter is work-in-progress.
Building the dmg installer
I use DMG Canvas for that, but there are several ways to create a Macintosh disk image.
Here is a screen grab from the result of that for one of my applications, MijnGeldConverter:
This chapter is work-in-progress.
I am using build scripts. These are .cmd files containing scripts that do the following:
- Compress the image
- Build the various platform deployment structures
- For Windows, this phase uses ResHacker (which is part of the VisualWorks installation, in the folder /packaging/win) to create an .exe that contains the image and possibly some resources like the nice icons you have created for your application). Also it creates a resource for the manifest file you need on modern versions of Windows for the ACL (Access Control List).
- This phase fills a series of output folders for each platform you want to support (i.e. hpux11, etc.).
- Package the the produced output into a folder called uploads in the form of zipped archives or (for the Mac) .dmg disk images. These archives are the ones you deliver to your users, for Windows users this will be a Windows Installer .msi file, or a setup.exe.
You might implement the same functionality in some Smalltalk classes, and I may be doing that in the near future myself, but the build script strategy is the one I am using for the moment, also because I want easy command line access to some command-line utilities such as ResHacker, the Windows resource compiler, etc.
For the application name I am using MyApp.
"%vwhome%packagingwinimageCompress" "%vwhome%imagemyapp.im" ..winmyapp.imcopy "%vwhome%imagemyapp.im" ..winmyapp.im
set sdktools=C:Program Files (x86)Microsoft SDKsWindowsv7.0ABin
"%VWHOME%packagingwinResHackResHacker" -addoverwrite "%VWHOME%binwinvisual.exe", myapp.exe, mgc.im, 332, 332,
"%VWHOME%packagingwinResHackResHacker" -addoverwrite myapp.exe, myapp.exe, "splash.bmp", bitmap, 324,
"%VWHOME%packagingwinResHackResHacker" -addoverwrite myapp.exe, myapp.exe, "myapp.ico", icon, 323,0
"%VWHOME%packagingwinResHackResHacker" -addoverwrite myapp.exe, myapp.exe, myapp.txt, 325,325,
"%VWHOME%packagingwinResHackResHacker" -addoverwrite myapp.exe, myapp.exe, version.RES, ,,
"%VWHOME%packagingwinResHackResHacker" -addoverwrite myapp.exe, myapp.exe, manifest.RES, ,,
"%tools%upx304wupx" --best myapp.exe
"%sdktools%signtool" sign /a myapp.exe
rem Set version number for myapp
rem create compressed files from the source folders
"C:Program Files7-Zip7z" a -ttar linuxPPC-%vn%.tar linuxPPC -r0
"C:Program Files7-Zip7z" a -tgzip uploadslinuxPPC-%vn%.tar.gz linuxPPC-%vn%.tar -aoa
"C:Program Files7-Zip7z" a -ttar linux86-%vn%.tar linux86 -r0
"C:Program Files7-Zip7z" a -tgzip uploadslinux86-%vn%.tar.gz linux86-%vn%.tar -aoa
"C:Program Files7-Zip7z" a -ttar solaris-%vn%.tar solaris -r0
"C:Program Files7-Zip7z" a -tgzip uploadssolaris-%vn%.tar.gz solaris-%vn%.tar -aoa
"C:Program Files7-Zip7z" a -ttar hpux11-%vn%.tar hpux11 -r0
"C:Program Files7-Zip7z" a -tgzip uploadshpux11-%vn%.tar.gz hpux11-%vn%.tar -aoa
"C:Program Files7-Zip7z" a -tzip uploadsmacx-%vn%.zip macx -r0 -aoa
move myapp.exe ..
rem delete copied files from the packaging folders
rem DONE PACKAGING
rem delete copied files from the platform folders
del hpux11help* /s /q
del hpux11messages* /s /q
del hpux11system* /s /q
This article is work-in-progress, I welcome suggestions and input.