How Mountain Lion Changes the Rules for AppleScript
AppleScript has been around for nearly 20 years, and although in that time it has doubtless succeeded in inviting many non-programmers to try their hands at writing scripts to automate applications, it has its oddities. One of its notable peculiarities is that, unlike most scripting languages, an AppleScript script file isn’t just text. In order to be saved, either as a script file or as a standalone application, an AppleScript script must be compiled first. (An AppleScript script that can’t be compiled — because of a syntax error, for example — can be exported as text, but it can’t be saved as a script file.) OS X 10.8 Mountain Lion keeps the same script file format as its predecessors, and yet at the same time
changes the rules completely: scripts and applications can be saved in an uncompiled state. It is a small change, and simultaneously a huge one.
Before looking at the details, it is worth asking why Mountain Lion makes this change. Or — perhaps more pertinently — why does it make this change now? The answer lies within Mac OS X, with a feature actually introduced in 10.7 Lion: Auto Save. Although this wasn’t true in Lion, the Mountain Lion version of AppleScript Editor now supports Auto Save and Versions.
When you think about autosaving, and the idea of saves being triggered by anything other than the user, it is clear that the old model, where you cannot save a script as a script file without first compiling it, simply won’t work. Apple had a few options in dealing with this conundrum: proceed with an autosave only when the document is compiled, come up with a new script storage format, or try to come up with a method that’s backwards-compatible with existing files. Apple’s engineers chose the third option.
AppleScript Editor can save scripts in four formats:
- .applescript, which is just text, and has effectively evolved from MacRoman to UTF-8
- .scpt, which is the compiled script format
- .scptd, which is a .scpt file stored in a file package
- .app, which is a .scpt file stored in an application shell
Let’s look at the .app format first. It is a standard Cocoa application: a folder that looks like a file and contains a series of subfolders and files in a particular arrangement. AppleScript applications save the compiled script in a file called main.scpt, inside Contents/Resources/Scripts/
. When the user launches the application, this file is loaded, its handlers are called, and its contents are saved back to the same file when finished.
Clearly, if the main.scpt file is not compiled code, the application cannot run. To work around this problem, Apple has resorted to a bit of digital sleight of hand: when it is time to save changes while working in AppleScript Editor, if the script can be compiled, it is saved in the main.scpt file as it always has been. If a syntax error or other problem prevents compilation, a single compiled statement is saved in main.scpt instead. This statement is:
error "This script contains uncompiled changes and cannot be run."
If you try to run such a .app-based script, the compiled script containing that error will execute, the error will be generated, and the message will appear in a dialog. So at least the application runs and provides appropriate feedback to the developer.
But what happened to the script’s real code? Because it cannot be compiled, AppleScript Editor instead saves it in another file, in .rtf format. This file is called main.rtf, and it lives in the same folder as main.scpt. It is saved only when the document cannot be compiled. So when AppleScript Editor in Mountain Lion opens a .app file for editing, it first looks to see if there is a main.rtf file. If that file exists, AppleScript Editor loads main.rtf’s contents as the source code, and if not, it decompiles main.scpt as usual.
This all just works — as long as people running scripts understand the meaning of the error message when they see it, and as long as anyone who wants to edit an uncompiled script is also running AppleScript Editor under Mountain Lion, or another script editor that knows about the change. But if you are running any version of Mac OS X before Mountain Lion, or using osadecompile
at the command line, things become a bit more complicated. You might be quite surprised to see a single error statement where you expect to see swathes of code. And if you were to copy a previous version of the source into AppleScript Editor, you might even end up with a .app script file containing what looks like two
versions of code, depending on which script editor and version of Mac OS X it was later opened in.
Fortunately, as long as you know about the problem, you can work around it. For example, you can open a .app package in the Finder by Control-clicking it and choosing Show Package Contents, drilling down to find main.rtf, opening it in a text editor, and copying and pasting the code into AppleScript Editor.
Script packages — .scptd files — use the same process, and the solution is the same: if there’s a problem, look for the main.rtf file to find the real code.
Importantly, if you do open the main.rtf file in a script package directly and copy the source back into a pre-Mountain Lion version of AppleScript Editor, it’s key that you also remove the main.rtf file. Otherwise, you will end up with a file that shows different code when opened in AppleScript Editor in Mountain Lion, which could be catastrophic down the track.
Ordinary script files, with the extension .scpt, are not packages; they are single files, so Apple had to work around the problem a bit differently. In this case, the .scpt file will end up with the same compiled error statement if the script cannot be compiled, and the actual source will be saved in its resource fork, as an RTF resource. (Veteran scripters will remember how AppleScript’s original script file format relied on a resource fork. It was phased out in favor of what was called the data-fork-only format, which to this day is still described in the Finder as “Compiled OSA Script (data-fork),” even though such files can include a resource fork.)
Again, this RTF resource is created only if the script could not be compiled at save time, and AppleScript Editor in Mountain Lion looks for it first when opening .scpt files. Similarly, older versions of AppleScript Editor and other editors will not know to look for this resource, and so will show the single error statement. This time, however, the solution is not as simple as looking for a file in a package — reading resources from resource forks (which are actually separate invisible files in Mac OS X) is more complex. Fortunately, we will come to a simple solution shortly.
Along with older versions of script editors, the other thing to beware of are utilities that strip off resource forks, or foreign-format file servers that do not handle resource forks correctly. For an uncompiled script file, removing the resource fork is as good as throwing the file in the Trash and emptying it.
So what should a scripter do to minimize problems in light of this change? If you are using AppleScript Editor in Mountain Lion, you first need to keep the problem in mind. If you just want to experiment with existing scripts that you worry about modifying, consider working on a copy of the code, saving back to the original only when you’re certain it’s working.
You could also consider splitting storage and deployment completely by saving your source in .applescript files, which can be executed in AppleScript Editor but nowhere else. This approach has long been discouraged, if not heavily, but the objections are largely theoretical. It’s also common practice if you use AppleScriptObjC in Xcode. And it has another advantage: it works well with source-control systems.
The other step is probably to make sure you deploy execute-only versions of your scripts. In Mountain Lion, AppleScript Editor now has an Export command, which is the only way to save a file as execute-only. It also attempts to compile the script first.
Of course if you are doing much scripting, you are better off abandoning AppleScript Editor for a serious tool like Late Night Software’s $199 Script Debugger. Script Debugger 5.0 doesn’t currently support autosaving — and some would argue this is no great loss — but a version that will recognize and open uncompiled files correctly should appear shortly. [Update: Script Debugger has now been updated. -Adam]
Users of my AppleScriptObjC Explorer should also make sure they are running the latest version, which also handles the hidden source correctly.
For those running earlier versions of Mac OS X or using other script editors, and who might receive files from Mountain Lion users, I’ve written a free utility, Read My Scripts, for extracting the code from uncompiled script files. It’s a simple drag-and-drop application that saves a new version of the script with the correct source, commented out, and removes the main.rtf file or RTF resource. It should work on systems going back to 10.5 Leopard.
[Shane Stanley is a long-time AppleScript user, consultant, and trainer. He is also the author of “AppleScriptObjC Explored” and the developer of the AppleScript-related utilities AppleScriptObjC Explorer, ASObjC Runner, and Read My Scripts.]
According to the Script Debugger mailing list, SD is stymied for the time being by changes in TextEdit and Mail in Mountain Lion. Looks like I'll be waiting quite a while to upgrade. I still haven't heard from Typinator or Keyboard Maestro in both of which I have AppleScripts I use frequently embedded.
I haven't got any outstanding email from you Adam. However I don't believe this issue will affect Keyboard Maestro - if the script is stored in Keyboard Maestro, it's stored uncompiled, and if it is stored externally, then you'll get the expected behaviour (the alert message instead of your code) if you leave auto-saved uncompilable code in your script.
The just released Script Debugger 5.0.3 update addresses the Mountain Lion issues.
Excellent! Thanks for the update, Mark.
Wondering what the historical objections to using .applescript files is (as a newly minted AScripter who is trying to bend AppleScript to his will so that he can use it in a test-driven and source control environment)?
It's possible for a developer to change the terminology you use while keeping the same underlying event codes, and that will cause problems with .applescript files. It has happened: one prime example is Apple's "choose file name" command, which was once "new file". OTOH, I can probably count the number of instances I've seen without taking my shoes off, and there's been at least one big example where things largely played out the other way around (Excel a few years ago). Of course originally there were no file extensions, so technically there was no .applescript file: the alternative to a script file was standard text file like any other text file, but with a different creator code.
A .applescript file cannot be saved as execute-only (non-editable). Some developers who supply scripts to clients or distribute to many prefer to protect their code. For the reasons Shane stated above, execute-only now may become a more preferred mode of distribution in Mountain Lion.
That's true, but note that the current version of AppleScript Editor shipping in 10.8 will also let you save an execute-only uncompiled script. You have to ignore an error dialog to do it, but the result is readable code in main.rtf and an execute-only error message. There are still some rough edges in the whole thing...
In MacOS X, .applescripts are really a great option, especially since with a #! /usr/bin/osascript, they can be turned into executable shell scripts. You also get the benefits of batch find/replace or other operations using your favorite text tools. The potential risk of a developer changing their AppleScript syntax is minimal, and for that matter, is usually as simple as a find/replace to fix. (As I had to do to replace "iCal" with "Calendar" once I went Mountain Lion.)
Thanks for the detailed explanations, Shane!
I still have to wait a couple of days till I install 10.8 but can't you avoid all this hassle by disabling auto-save in system prefs?
Still makes me wonder: what on earth is it for? User-unfriendly, non-intuitive, unsafe... Why I suspect that not a single user ever asked for anything like this? (Not that this rant will help.)
You can't turn autosaving off completely -- only when quitting. A lot of people like autosaving, at least in some apps, and it's the key to versioning. It's also here to stay.
And even if you could turn it off, you have no control of other Macs where scripts might be saved and sent to you.
I'm not sure the AS team made a great choice in how they implemented it, but at least there's a simple-to-use solution for the rest of us.
My Applescripts for handling Mail attachments etc no longer work with Mountain Lion. I found somewhere reference to a new folder Library/Applescripts and I moved the scripts to there and can now see the scripts in the Rules dialogue but no improvement in actioning these. Any ideas?
My scripts which I call from the scripts menu while in Mail still work. They're in ~/Library/Scripts/Applications/Mail
Mike -- did you ever find a workaround? I'm having the same problem :-(