Mac version of freeze. Uses standard freeze modules where it can,

augmenting them here and there. For now, it works more-or-less similar
to unix/windows freeze, generating a config.c file, but storing
modules in PYC resources. A template project is also copied.

The hooks are in place to freeze by merging shared libraries so you
can freeze without a C compiler/linker, but this does not work yet.
This commit is contained in:
Jack Jansen 1998-06-26 14:56:00 +00:00
parent fa68b07526
commit 144fa678d2
10 changed files with 557 additions and 0 deletions

View File

@ -0,0 +1,39 @@
import re
import os
# The regular expression for freeze directives. These are comments with the
# word macfreeze immedeately followed by a colon, followed by a directive,
# followed by argument(s)
#
# The directives supported are
# include - Include a module or file
# exclude - Exclude a module
# path - Add sys.path entries. Relative paths are relative to the source file.
#
# See the macfreeze.py main program for a real live example.
#
DIRECTIVE_RE=r'^\s*#\s*macfreeze:\s*(\S*)\s*(.*)\s*$'
REPROG=re.compile(DIRECTIVE_RE)
def findfreezedirectives(program):
extra_modules = []
exclude_modules = []
extra_path = []
progdir, filename = os.path.split(program)
fp = open(program)
for line in fp.readlines():
match = REPROG.match(line)
if match:
directive = match.group(1)
argument = match.group(2)
if directive == 'include':
extra_modules.append(argument)
elif directive == 'exclude':
exclude_modules.append(argument)
elif directive == 'path':
argument = os.path.join(progdir, argument)
extra_path.append(argument)
else:
print '** Unknown directive', line
return extra_modules, exclude_modules, extra_path

View File

@ -0,0 +1,72 @@
"""macfreeze - Main program and GUI
macfreeze allows you to turn Python scripts into fully self-contained
Mac applications, by including all the Python and C code needed in a single
executable. Like unix/windows freeze it can produce a config.c allowing you
to build the application with a development environment (CodeWarrior, to be
precise), but unlike the standard freeze it is also possible to create frozen
applications without a development environment, by glueing all the
shared libraries and extension modules needed together in a single
executable, using some Code Fragment Manager tricks."""
import macfs
import sys
import EasyDialogs
import string
import macfreezegui
import macmodulefinder
#
# Here are the macfreeze directives, used when freezing macfreeze itself
# (see directives.py for an explanation)
#
# macfreeze: path ::::Tools:freeze
# macfreeze: exclude win32api
#
def main():
if len(sys.argv) < 2:
gentype, program, output, debug = macfreezegui.dialog()
elif len(sys.argv) == 2:
gentype, program, output, debug = macfreezegui.dialog(sys.argv[1])
else:
EasyDialog.Message(
"Please pass a single script. Additional modules can be specified with directives")
sys.exit(0)
mustwait = process(gentype, program, output, debug=debug)
if mustwait:
sys.exit(1)
def process(gentype, program, output, modules=[], module_files=[], debug=0):
try:
module_dict = macmodulefinder.process(program, modules, module_files, debug)
except macmodulefinder.Missing, arg:
arg.sort()
print '** Missing modules:', string.join(arg, ' ')
sys.exit(1)
#
# And generate
#
if gentype == 'info':
import macgen_info
macgen_info.generate(output, module_dict)
return 1 # So the user can inspect it
elif gentype == 'source':
import macgen_src
warnings = macgen_src.generate(output, module_dict, debug)
return warnings
elif gentype == 'resource':
import macgen_rsrc
macgen_rsrc.generate(output, module_dict, debug)
warnings = macgen_rsrc.warnings(module_dict)
return warnings
elif gentype == 'applet':
import macgen_bin
macgen_bin.generate(output, module_dict, debug)
else:
raise 'unknown gentype', gentype
if __name__ == '__main__':
main()

View File

@ -0,0 +1,21 @@
(This file must be converted with BinHex 4.0)
:$QeKBfCbC@9kC5jbFh*M!(*cFQ058d9%!3#3"`1-AQF!N!3"!!!!!cS!!!)k!!!
!8M4lA$"F-M!dA$)`0(YF-&`b-$4F-M!dHe``A$)`0&`b$QeKBfCbC@9kC5jbFh*
MFfaKH@9bFR0bG(4iG#ia0f`!-(*cFQ058d9%!3!!J!#"!*!5XE4#FJ#3"J0ZA$-
b-&`c0cGF-&`b06"F-c)`A$-h0e``A$)e-&`c-M"F-cFhA$"F-M8`A$-b-&`c0cG
F-&`b06"F-c)`A$-h0e``A$)e-&`c-M"F-cFhA$"F-M8`A$-b-&`c0cGF-&`b06"
F-c)`A$-h0e``A$)e-&`c-M"F-cFhA$"F-M8`A$-b-!!!!"J!2!!U!@)"m3!&!*!
)!J!!!$!+!!!#'J!6!*!&$`!*!"m!9)J(8f0bDA"d1J#3"Ji!L`!H!@@)"R0MFQP
`G!#3"3d"F3!K!Ed%#8*bEhGcC5iZ,R)!N!93!!N!BJ&P"L0(C@jPFQ&dC5"bCA0
[GA*MC5"KEQ3JFfpeFQ0P)'C[FL"$9f`!N!9S!"X!H!#"L!j2GA4`GA3JCQpXC'9
b1J#3"@J!L`"i!@@)"R0MFQP`G!#3"@F"F3"k!Ed%#8*bEhGcC5iZ,Q%!N!@%!!N
!PJ&P"Kp(C@jPFQ&dC5"3@8-JFQ9cEh9bBf8JCQPXC5"[EQajG!#3"Cd!'`#Y!(f
)$%peG("eG#"QD@aP1J#3"Ci!L!#Y!@@)"R0MFQP`G!#3"CX"F!#[!Ed%#8*bEhG
cC5iZ,Q%!N!@j!!N!bJ&L"L9(C@jPFQ&dC5"KF("XD@0KG'P[EL"QFQpY)(0SBA*
PC#"XD@*cE!#3"G%!'`$K!(b)$%peG("eG#"QD@aP1J#3"G%!L!$K!@@)"R0MFQP
`G!#3"Fm"E`$M!Ed%#8*bEhGcC5iZ,T`!N!3""`&[!4X"[33#6dX!N!3""J!,!4S
!8!3'3f&ZBf9X!*!&+J!*!$X!ISJ04'9LG@FJ)'aPGQ9X1XJ!N!8U!)X!1J$@%!%
aG!#3"H`!#3$q!@-'%%PZCQpbE@&dD@pZ)'pZE(N!!!%!!!!$1J!!!MS!!!"5"cH
I$#UD!!!!(!"'!!&%6%p(!!!!%N4*9%`!!!!H!J#3"`FhXI3#!2rr!!!!(!FhY5!
,6@&TEL"ND@&XEfGZ5!:

View File

@ -0,0 +1,158 @@
"""macfreezegui - The GUI for macfreeze"""
import Dlg
import macfs
import EasyDialogs
import sys
import os
import string
import Res
ID_MAINDIALOG=512
ITEM_SCRIPTNAME=2
ITEM_SCRIPTBROWSE=3
ITEM_GENSOURCE=4
ITEM_GENSOURCE_ITEMS=(7,)
ITEM_SOURCEDIRNAME=6
ITEM_SOURCEDIRBROWSE=7
ITEM_GENRESOURCE=8
ITEM_GENRESOURCE_ITEMS=(11,)
ITEM_RESOURCENAME=10
ITEM_RESOURCEBROWSE=11
ITEM_GENAPPLET=12
ITEM_GENAPPLET_ITEMS=(15,)
ITEM_APPLETNAME=14
ITEM_APPLETBROWSE=15
ITEM_OK=16
ITEM_CANCEL=17
ITEM_DEBUG=19
ITEM_GENINFO=20
RADIO_GROUPING={
ITEM_GENSOURCE: ITEM_GENSOURCE_ITEMS,
ITEM_GENRESOURCE: ITEM_GENRESOURCE_ITEMS,
ITEM_GENAPPLET: ITEM_GENAPPLET_ITEMS,
ITEM_GENINFO: ()
}
def dialog(script=None):
# Invent the various names
if not script:
fss, ok = macfs.PromptGetFile("Script?", "TEXT")
if not ok:
sys.exit(0)
script = fss.as_pathname()
basename, ext = os.path.splitext(script)
if ext:
appletname = basename
rsrcname = basename + 'modules.rsrc'
else:
appletname = script + '.applet'
rsrcname = script + 'modules.rsrc'
dirname, basebase = os.path.split(basename)
dirname = os.path.join(dirname, 'build.'+basebase)
# Get the dialog, possibly opening the resource file (if needed)
try:
d = Dlg.GetNewDialog(ID_MAINDIALOG, -1)
except Dlg.Error:
d = None
if d == None:
try:
Res.OpenResFile('macfreeze.rsrc')
except Res.Error:
d = None
else:
d = Dlg.GetNewDialog(ID_MAINDIALOG, -1)
if d == None:
EasyDialogs.Message("Dialog resource not found or faulty")
sys.exit(1)
# Fill the dialog
d.SetDialogDefaultItem(ITEM_OK)
d.SetDialogCancelItem(ITEM_CANCEL)
_dialogsetfile(d, ITEM_SCRIPTNAME, script)
_dialogsetfile(d, ITEM_SOURCEDIRNAME, dirname)
_dialogsetfile(d, ITEM_RESOURCENAME, rsrcname)
_dialogsetfile(d, ITEM_APPLETNAME, appletname)
gentype = ITEM_GENSOURCE
_dialogradiogroup(d, ITEM_GENSOURCE)
# Interact
d.GetDialogWindow().SetWTitle("Standalone application creation options")
d.GetDialogWindow().ShowWindow()
d.DrawDialog()
while 1:
item = Dlg.ModalDialog(None)
if item == ITEM_OK:
break
elif item == ITEM_CANCEL:
sys.exit(0)
elif item in RADIO_GROUPING.keys():
gentype = item
_dialogradiogroup(d, item)
elif item == ITEM_SCRIPTBROWSE:
fss, ok = macfs.PromptGetFile("Script?")
if ok:
script = fss.as_pathname()
_dialogsetfile(d, ITEM_SCRIPTNAME, script)
elif item == ITEM_SOURCEDIRBROWSE:
fss, ok = macfs.StandardPutFile("Output folder name", os.path.split(dirname)[1])
if ok:
dirname = fss.as_pathname()
_dialogsetfile(d, ITEM_SOURCEDIRNAME, dirname)
elif item == ITEM_RESOURCEBROWSE:
fss, ok = macfs.StandardPutFile("Resource output file", os.path.split(rsrcname)[1])
if ok:
rsrcname = fss.as_pathname()
_dialogsetfile(d, ITEM_RESOURCENAME, rsrcname)
elif item == ITEM_APPLETBROWSE:
fss, ok = macfs.StandardPutFile("Applet output file", os.path.split(appletname)[1])
if ok:
appletname = fss.as_pathname()
_dialogsetfile(d, ITEM_APPLETNAME, appletname)
else:
pass
tp, h, rect = d.GetDialogItem(ITEM_DEBUG)
debug = Dlg.GetDialogItemText(h)
try:
debug = string.atoi(string.strip(debug))
except ValueError:
EasyDialogs.Message("Illegal debug value %s, set to zero."%`debug`)
debug = 0
if gentype == ITEM_GENSOURCE:
return 'source', script, dirname, debug
elif gentype == ITEM_GENRESOURCE:
return 'resource', script, rsrcname, debug
elif gentype == ITEM_GENAPPLET:
return 'applet', script, appletname, debug
elif gentype == ITEM_GENINFO:
return 'info', script, '', debug
raise 'Error in gentype', gentype
def _dialogradiogroup(d, item):
for k in RADIO_GROUPING.keys():
subitems = RADIO_GROUPING[k]
tp, h, rect = d.GetDialogItem(k)
if k == item:
h.as_Control().SetControlValue(1)
for i2 in subitems:
d.ShowDialogItem(i2)
else:
h.as_Control().SetControlValue(0)
for i2 in subitems:
d.HideDialogItem(i2)
def _dialogsetfile(d, item, file):
if len(file) > 32:
file = '\311:' + os.path.split(file)[1]
tp, h, rect = d.GetDialogItem(item)
Dlg.SetDialogItemText(h, file)
if __name__ == '__main__':
type, script, file, debug = dialog()
print type, script, file, 'debug=%d'%debug
sys.exit(1)

View File

@ -0,0 +1,6 @@
"""macgen_bin - Generate application from shared libraries"""
import EasyDialogs
def generate(output, module_dict):
EasyDialogs.Message('Not yet implemented')

View File

@ -0,0 +1,8 @@
"""macgen_info - Generate informational output"""
def generate(output, module_dict):
for name in module_dict.keys():
print 'Include %-20s\t'%name,
module = module_dict[name]
print module.gettype(), '\t', `module`
return 0

View File

@ -0,0 +1,35 @@
"""macgen_info - Generate PYC resource file only"""
import EasyDialogs
import py_resource
import Res
import sys
def generate(output, module_dict, debug=0, preload=1):
fsid = py_resource.create(output)
for name, module in module_dict.items():
if module.gettype() != 'module':
continue
location = module.__file__
if location[-4:] == '.pyc':
# Attempt corresponding .py
location = location[:-1]
if location[-3:] != '.py':
print '*** skipping', location
continue
id, name = py_resource.frompyfile(location, name, preload=preload)
if debug > 0:
print 'PYC resource %5d\t%s\t%s'%(id, name, location)
Res.CloseResFile(fsid)
def warnings(module_dict):
problems = 0
for name, module in module_dict.items():
if module.gettype() not in ('builtin', 'module'):
problems = problems + 1
print 'Warning: %s not included: %s %s'%(name, module.gettype(), module)
return problems

View File

@ -0,0 +1,109 @@
"""macgen_info - Generate CodeWarrior project, config source, resource file"""
import EasyDialogs
import os
import sys
import macfs
import MacOS
import macostools
import macgen_rsrc
# Note: this depends on being frozen, or on sys.path already being
# modified by macmodulefinder.
import makeconfig
TEMPLATEDIR=os.path.join(sys.prefix, ':Mac:mwerks:projects:build.macfreeze')
PROJECT_TEMPLATE=os.path.join(TEMPLATEDIR, ':frozen.prj')
CONFIG_TEMPLATE=os.path.join(TEMPLATEDIR, ':templatefrozenconfig.c')
BUNDLE_TEMPLATE=os.path.join(TEMPLATEDIR, ':frozenbundle.rsrc')
def generate(output, module_dict, debug=0):
problems = 0
output_created=0
if not os.path.exists(output):
print 'Creating project folder', output
os.mkdir(output)
output_created = 1
# Resolve aliases, if needed
try:
fss, dummy1, dummy2 = macfs.ResolveAliasFile(output)
except macfs.error:
pass
else:
newname = fss.as_pathname()
if newname != output:
if debug:
print 'Alias', output
print 'Resolved to', newname
output = newname
# Construct the filenames
dummy, outfile = os.path.split(output)
build, ext = os.path.splitext(outfile)
if build == 'build' and ext[0] == '.':
# This is probably a good name for the project
projname = ext[1:]
else:
projname = 'frozenapplet.prj'
config_name = os.path.join(output, ':macfrozenconfig.c')
project_name = os.path.join(output, ':' + projname + '.prj')
resource_name = os.path.join(output, ':frozenmodules.rsrc')
bundle_name = os.path.join(output, ':frozenbundle.rsrc')
# Fill the output folder, if needed.
if output_created:
# Create the project, if needed
if not os.path.exists(project_name):
print 'Creating project', project_name
if not os.path.exists(PROJECT_TEMPLATE):
print '** No template CodeWarrior project found at', PROJECT_TEMPLATE
print ' To generate standalone Python applications from source you need'
print ' a full source distribution. Check http://www.cwi.nl/~jack/macpython.html'
print ' for details.'
problems = 1
else:
macostools.copy(PROJECT_TEMPLATE, project_name)
print 'A template CodeWarrior project has been copied to', project_name
print 'It is up to you to make the following changes:'
print '- Change the output file name'
print '- Change the search path, unless the folder is in the python home'
print '- Add sourcefiles/libraries for any extension modules used'
print '- Remove unused sources, to speed up the build process'
print '- Remove unused resource files (like tcl/tk) for a smaller binary'
problems = 1
macostools.copy(BUNDLE_TEMPLATE, bundle_name)
print 'A template bundle file has also been copied to', bundle_name
print 'You may want to adapt signature, size resource, etc'
# Create the resource file
macgen_rsrc.generate(resource_name, module_dict, debug=debug)
# Create the config.c file
if not os.path.exists(CONFIG_TEMPLATE):
print '** No template config.c found at', PROJECT_TEMPLATE
print ' To generate standalone Python applications from source you need'
print ' a full source distribution. Check http://www.cwi.nl/~jack/macpython.html'
print ' for details.'
problems = 1
else:
# Find elegible modules (builtins and dynamically loaded modules)
c_modules = []
for module in module_dict.keys():
if module_dict[module].gettype() in ('builtin', 'dynamic'):
c_modules.append(module)
ifp = open(CONFIG_TEMPLATE)
ofp = open(config_name, 'w')
makeconfig.makeconfig(ifp, ofp, c_modules)
ifp.close()
ofp.close()
MacOS.SetCreatorAndType(config_name, 'CWIE', 'TEXT')
if warnings(module_dict):
problems = 1
return problems
def warnings(module_dict):
problems = 0
for name, module in module_dict.items():
if module.gettype() not in ('builtin', 'module', 'dynamic'):
problems = problems + 1
print 'Warning: %s not included: %s %s'%(name, module.gettype(), module)
return problems

View File

@ -0,0 +1,8 @@
"""macgenerate - Generate the out for macfreeze"""
def generate(program, module_dict):
for name in module_dict.keys():
print 'Include %-20s\t'%name,
module = module_dict[name]
print module.gettype(), '\t', `module`
return 0

View File

@ -0,0 +1,101 @@
"""macmodulefinder - Find modules used in a script. Only slightly
mac-specific, really."""
import sys
import os
import directives
try:
# This will work if we are frozen ourselves
import modulefinder
except ImportError:
# And this will work otherwise
_FREEZEDIR=os.path.join(sys.prefix, ':Tools:freeze')
sys.path.insert(0, _FREEZEDIR)
import modulefinder
#
# Modules that must be included, and modules that need not be included
# (but are if they are found)
#
MAC_INCLUDE_MODULES=['site', 'exceptions']
MAC_MAYMISS_MODULES=['posix', 'os2', 'nt', 'dos', 'dospath', 'nturl2path', 'pwd', 'sitecustomize']
# An exception:
Missing="macmodulefinder.Missing"
class Module(modulefinder.Module):
def gettype(self):
"""Return type of module"""
if self.__path__:
return 'package'
if self.__code__:
return 'module'
if self.__file__:
return 'dynamic'
return 'builtin'
class ModuleFinder(modulefinder.ModuleFinder):
def add_module(self, fqname):
if self.modules.has_key(fqname):
return self.modules[fqname]
self.modules[fqname] = m = Module(fqname)
return m
def process(program, modules=[], module_files = [], debug=0):
error = []
#
# Add the standard modules needed for startup
#
modules = modules + MAC_INCLUDE_MODULES
#
# search the main source for directives
#
extra_modules, exclude_modules, extra_path = \
directives.findfreezedirectives(program)
for m in extra_modules:
if os.sep in m:
# It is a file
module_files.append(m)
else:
modules.append(m)
path = extra_path + sys.path[:]
#
# Create the module finder and let it do its work
#
modfinder = ModuleFinder(path,
excludes=exclude_modules, debug=debug)
for m in modules:
modfinder.import_hook(m)
for m in module_files:
modfinder.load_file(m)
modfinder.run_script(program)
module_dict = modfinder.modules
#
# Tell the user about missing modules
#
maymiss = exclude_modules + MAC_MAYMISS_MODULES
for m in modfinder.badmodules.keys():
if not m in maymiss:
if debug > 0:
print 'Missing', m
error.append(m)
#
# Warn the user about unused builtins
#
for m in sys.builtin_module_names:
if m in ('__main__', '__builtin__'):
pass
elif not module_dict.has_key(m):
if debug > 0:
print 'Unused', m
elif module_dict[m].gettype() != 'builtin':
# XXXX Can this happen?
if debug > 0:
print 'Conflict', m
if error:
raise Missing, error
return module_dict