Python and MPW
==============
There is conditional code in Python for MPW. This has been used with
different compilers at various points in time. Right now it is being
used to turn the entire interpreter into a shared library on 68K Macs,
so we can build "applets" (see below). I have used MPW 3.2 and the OpenDoc
development environment from an OpenDoc CD released in 1984. This
contains the Symantec C compiler for MPW (version 7.0.4), the
Universal Headers (version 2.0a1), and early versions of CFM-68K (version 1.0a1)
(the Code Fragment Manager ported back to the 68K Mac) and
MixedModeInit (version 1.0d12), which are required to use shared libraries.
I've created a Makefile that does everything, plus a three-line Build
script that calls Make and runs its output. The Makefile assumes that
it lives in a 1-deep subdirectory of the root, so e.g. the Python
Include directory can be referenced through "::Include". All object
files are collected in the subsubdirectory Objcode.
I use these feature test macros:
MPW for all MPW compilers (e.g. long double in <math.h>)
__SC__ for things specific to the Symantec C compiler
(e.g. doesn't like static forward)
__CFM68K__ for things specific to CFM-68K
(e.g. it requires the use of #pragma lib_export on|off)
HAVE_UNIVERSAL_HEADERS for things not yet in Think's headers (e.g. UPPs)
GENERATINGCFM for both PPC and 68K Code Fragment Manager
MPW is defined in config.h (if it finds that applec is defined);
HAVE_UNIVERSAL_HEADERS is defined in macglue.h depending on whether it
thinks we are using Universal Headers. The others are defined by the
compiler or by the system headers.
Compiler switches were a nightmare until I found I had to use -b.
This wasn't mentioned in the CFM-68K docs that came on the OpenDoc
CD-ROM. Apparently it is only needed for large projects...
Warning: Mixing Think C and MPW
===============================
(XXX Need to check what convention SC uses -- I hope it uses Think's.)
If you are mixing Think C and MPW, you may experience weird errors in
previously correct modules. These disappear when you throw away the
module's .pyc file. The errors usually have to do with string
literals containing '\n' or '\r'. The reason is an incompatibility
between their handling of '\n' and '\r' -- in MPW C, '\n' actually is
ASCII CR while '\r' is ASCII LF, which is the reverse situation from
any other ASCII based C implementation. This behaviour is inherited
by Python compiled with MPW C. This is normally not a problem, but
*binary* files written by one system will be mis-interpreted by the
other, and this is what happens to the .pyc files. There is no easy
way to fix this in the source. (This is a real shame, since the
format of .pyc files was carefully designed to be independent of byte
order and integer size -- deviations in the ASCII character codes were
never anticipated.)
Building "Applets" for the Mac
==============================
An "applet" is a tiny application that's written in a scripting language
but behaves like a real application. The behavior is much like that of
executable scripts in Unix -- but the implementation is entirely different.
The applet's file can be small because it doesn't contain the actual
interpreter for the scripting language -- this has to be installed in the
Extensions folder (usually) before the applet will work. The applet file
itself only contains a tiny bootstrap program and the script itself --
possibly "compiled" or otherwise encoded to save on parsing time and space,
and to make it harder to reverse engineer the script (some people care about
this).
In Python's case, the Python interpreter, without its main program, is built
as a shared library that is dropped in the Extensions folder. Some more
shared libraries must also be present -- these form the C run-time system.
[[XXX perhaps we should link these in statically with the Python library,
for simpler distribution???]] On the 68K Mac, two more extensions are needed:
CFM-68K (the Code Fragment Manager) and MixedModeInit. These provide
functionality that's built in the Power Mac's OS. It seems that System 7.1.1
or higher is also required.
The applet file contains a small main program program, plus a 'PYC ' resource
named __main__ which contains the "compiled" version of the script. A 'PYC '
resource contains exactly the same data as a ".pyc" file. (The advantage of
storing compiled modules as resources instead of files is that many modules
can be stored in a single file.) The applet's main
program initializes most of the toolbox managers (it uses the same sequence
as stdwin or the Think C console I/O library), then initializes Python,
then loads the resource and decodes it into a Python code object, and finally
passes the code object to the Python interpreter for execution. [[XXX Actually,
the applet's main program could be moved entirely to the shared library --
there's nothing in it that's dependent on the applet's configuration.
The applet itself could then be reduced to main() { applet_main(); } ]]
[[XXX I tried this but it only save 512 bytes on a total of 10K -- the rest
is boilerplate that the linker always seems to create. Wonder how this is on
the Power Mac...]]
A big restriction for applets is that they have no standard input and their
standard output and error streams are diverted to files called "stdout" and
"stderr". This means that in order to interact with the user, or even just
to provide some feedback while they're grinding along, they must make use of
Mac toolbox calls to create windows, etc. I plan to provide a library that at
least has the output functionality of the Think C Console I/O library or
CodeWarrior's SIOX.
The current procedure to create an applet is not as simple as it could be.
I have written a Python script (which itself can be -- and has been -- made
into an applet!) which asks for a Python source file (input) and an existing
applet file (output). It adds a 'PYC ' resource to the applet named __main__,
which contains the compiled code of the script (it compiles on the fly,
so you don't need to have a .pyc file for the script).
Although this seems fairly simple, the practical complication is that you need
to copy the applet template first -- if you specify the template as the output,
you will overwrite the template! [[XXX I guess a simplification could be made
by using the convention that the applet built from a script has the same name
as the script but with ".py" stripped; the applet-making script could then
search for the template in a few common locations (e.g. the Python module
search path) and copy it, reducing the user interaction to just indicating the
Python source file to be converted into an applet.]]