Updated bundlebuilder from Python CVS and added ability to specify
libs to be added to the bundle. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_4_BRANCH@19145 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -81,7 +81,7 @@ class BundleBuilder(Defaults):
|
|||||||
|
|
||||||
# The property list ("plist")
|
# The property list ("plist")
|
||||||
plist = Plist(CFBundleDevelopmentRegion = "English",
|
plist = Plist(CFBundleDevelopmentRegion = "English",
|
||||||
CFBundleInfoDictionaryVersion = "6.0")
|
CFBundleInfoDictionaryVersion = "6.0")
|
||||||
|
|
||||||
# The type of the bundle.
|
# The type of the bundle.
|
||||||
type = "APPL"
|
type = "APPL"
|
||||||
@@ -95,6 +95,10 @@ class BundleBuilder(Defaults):
|
|||||||
# (eg. "Contents/Resources/MyStuff/SomeFile.ext).
|
# (eg. "Contents/Resources/MyStuff/SomeFile.ext).
|
||||||
files = []
|
files = []
|
||||||
|
|
||||||
|
# List of shared libraries (dylibs, Frameworks) to bundle with the app
|
||||||
|
# will be placed in Contents/Frameworks
|
||||||
|
libs = []
|
||||||
|
|
||||||
# Directory where the bundle will be assembled.
|
# Directory where the bundle will be assembled.
|
||||||
builddir = "build"
|
builddir = "build"
|
||||||
|
|
||||||
@@ -127,6 +131,8 @@ class BundleBuilder(Defaults):
|
|||||||
else:
|
else:
|
||||||
self.creator = "????"
|
self.creator = "????"
|
||||||
plist.CFBundleSignature = self.creator
|
plist.CFBundleSignature = self.creator
|
||||||
|
if not hasattr(plist, "CFBundleIdentifier"):
|
||||||
|
plist.CFBundleIdentifier = self.name
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
"""Build the bundle."""
|
"""Build the bundle."""
|
||||||
@@ -171,6 +177,9 @@ class BundleBuilder(Defaults):
|
|||||||
for path in self.resources:
|
for path in self.resources:
|
||||||
files.append((path, pathjoin("Contents", "Resources",
|
files.append((path, pathjoin("Contents", "Resources",
|
||||||
os.path.basename(path))))
|
os.path.basename(path))))
|
||||||
|
for path in self.libs:
|
||||||
|
files.append((path, pathjoin("Contents", "Frameworks",
|
||||||
|
os.path.basename(path))))
|
||||||
if self.symlink:
|
if self.symlink:
|
||||||
self.message("Making symbolic links", 1)
|
self.message("Making symbolic links", 1)
|
||||||
msg = "Making symlink from"
|
msg = "Making symlink from"
|
||||||
@@ -207,7 +216,7 @@ else:
|
|||||||
PYC_EXT = ".pyo"
|
PYC_EXT = ".pyo"
|
||||||
|
|
||||||
MAGIC = imp.get_magic()
|
MAGIC = imp.get_magic()
|
||||||
USE_FROZEN = hasattr(imp, "set_frozenmodules")
|
USE_ZIPIMPORT = 0 ## "zipimport" in sys.builtin_module_names
|
||||||
|
|
||||||
# For standalone apps, we have our own minimal site.py. We don't need
|
# For standalone apps, we have our own minimal site.py. We don't need
|
||||||
# all the cruft of the real site.py.
|
# all the cruft of the real site.py.
|
||||||
@@ -216,28 +225,30 @@ import sys
|
|||||||
del sys.path[1:] # sys.path[0] is Contents/Resources/
|
del sys.path[1:] # sys.path[0] is Contents/Resources/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if USE_FROZEN:
|
if USE_ZIPIMPORT:
|
||||||
FROZEN_ARCHIVE = "FrozenModules.marshal"
|
ZIP_ARCHIVE = "Modules.zip"
|
||||||
SITE_PY += """\
|
SITE_PY += "sys.path.append(sys.path[0] + '/%s')\n" % ZIP_ARCHIVE
|
||||||
# bootstrapping
|
def getPycData(fullname, code, ispkg):
|
||||||
import imp, marshal
|
if ispkg:
|
||||||
f = open(sys.path[0] + "/%s", "rb")
|
fullname += ".__init__"
|
||||||
imp.set_frozenmodules(marshal.load(f))
|
path = fullname.replace(".", os.sep) + PYC_EXT
|
||||||
f.close()
|
return path, MAGIC + '\0\0\0\0' + marshal.dumps(code)
|
||||||
""" % FROZEN_ARCHIVE
|
|
||||||
|
|
||||||
SITE_CO = compile(SITE_PY, "<-bundlebuilder.py->", "exec")
|
SITE_CO = compile(SITE_PY, "<-bundlebuilder.py->", "exec")
|
||||||
|
|
||||||
EXT_LOADER = """\
|
EXT_LOADER = """\
|
||||||
import imp, sys, os
|
def __load():
|
||||||
for p in sys.path:
|
import imp, sys, os
|
||||||
path = os.path.join(p, "%(filename)s")
|
for p in sys.path:
|
||||||
if os.path.exists(path):
|
path = os.path.join(p, "%(filename)s")
|
||||||
break
|
if os.path.exists(path):
|
||||||
else:
|
break
|
||||||
assert 0, "file not found: %(filename)s"
|
else:
|
||||||
mod = imp.load_dynamic("%(name)s", path)
|
assert 0, "file not found: %(filename)s"
|
||||||
sys.modules["%(name)s"] = mod
|
mod = imp.load_dynamic("%(name)s", path)
|
||||||
|
|
||||||
|
__load()
|
||||||
|
del __load
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MAYMISS_MODULES = ['mac', 'os2', 'nt', 'ntpath', 'dos', 'dospath',
|
MAYMISS_MODULES = ['mac', 'os2', 'nt', 'ntpath', 'dos', 'dospath',
|
||||||
@@ -250,13 +261,15 @@ STRIP_EXEC = "/usr/bin/strip"
|
|||||||
BOOTSTRAP_SCRIPT = """\
|
BOOTSTRAP_SCRIPT = """\
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
execdir=$(dirname ${0})
|
execdir=$(dirname "${0}")
|
||||||
|
DYLD_LIBRARY_PATH=$(dirname "${execdir}")/Frameworks
|
||||||
|
export DYLD_LIBRARY_PATH
|
||||||
executable=${execdir}/%(executable)s
|
executable=${execdir}/%(executable)s
|
||||||
resdir=$(dirname ${execdir})/Resources
|
resdir=$(dirname "${execdir}")/Resources
|
||||||
main=${resdir}/%(mainprogram)s
|
main=${resdir}/%(mainprogram)s
|
||||||
PYTHONPATH=$resdir
|
PYTHONPATH=$resdir
|
||||||
export PYTHONPATH
|
export PYTHONPATH
|
||||||
exec ${executable} ${main} ${1}
|
exec "${executable}" "${main}" "${1}"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@@ -277,7 +290,8 @@ class AppBuilder(BundleBuilder):
|
|||||||
# when building a Cocoa app.
|
# when building a Cocoa app.
|
||||||
nibname = None
|
nibname = None
|
||||||
|
|
||||||
# The name of the icon file to be copied to Resources and used for the Finder icon.
|
# The name of the icon file to be copied to Resources and used for
|
||||||
|
# the Finder icon.
|
||||||
iconfile = None
|
iconfile = None
|
||||||
|
|
||||||
# Symlink the executable instead of copying it.
|
# Symlink the executable instead of copying it.
|
||||||
@@ -371,7 +385,7 @@ class AppBuilder(BundleBuilder):
|
|||||||
if self.iconfile is not None:
|
if self.iconfile is not None:
|
||||||
iconbase = os.path.basename(self.iconfile)
|
iconbase = os.path.basename(self.iconfile)
|
||||||
self.plist.CFBundleIconFile = iconbase
|
self.plist.CFBundleIconFile = iconbase
|
||||||
self.files.append( (self.iconfile, pathjoin(resdir, iconbase)) )
|
self.files.append((self.iconfile, pathjoin(resdir, iconbase)))
|
||||||
|
|
||||||
def postProcess(self):
|
def postProcess(self):
|
||||||
if self.standalone:
|
if self.standalone:
|
||||||
@@ -392,23 +406,17 @@ class AppBuilder(BundleBuilder):
|
|||||||
def addPythonModules(self):
|
def addPythonModules(self):
|
||||||
self.message("Adding Python modules", 1)
|
self.message("Adding Python modules", 1)
|
||||||
|
|
||||||
if USE_FROZEN:
|
if USE_ZIPIMPORT:
|
||||||
# This anticipates the acceptance of this patch:
|
# Create a zip file containing all modules as pyc.
|
||||||
# http://www.python.org/sf/642578
|
import zipfile
|
||||||
# Create a file containing all modules, frozen.
|
relpath = pathjoin("Contents", "Resources", ZIP_ARCHIVE)
|
||||||
frozenmodules = []
|
|
||||||
for name, code, ispkg in self.pymodules:
|
|
||||||
if ispkg:
|
|
||||||
self.message("Adding Python package %s" % name, 2)
|
|
||||||
else:
|
|
||||||
self.message("Adding Python module %s" % name, 2)
|
|
||||||
frozenmodules.append((name, marshal.dumps(code), ispkg))
|
|
||||||
frozenmodules = tuple(frozenmodules)
|
|
||||||
relpath = pathjoin("Contents", "Resources", FROZEN_ARCHIVE)
|
|
||||||
abspath = pathjoin(self.bundlepath, relpath)
|
abspath = pathjoin(self.bundlepath, relpath)
|
||||||
f = open(abspath, "wb")
|
zf = zipfile.ZipFile(abspath, "w", zipfile.ZIP_DEFLATED)
|
||||||
marshal.dump(frozenmodules, f)
|
for name, code, ispkg in self.pymodules:
|
||||||
f.close()
|
self.message("Adding Python module %s" % name, 2)
|
||||||
|
path, pyc = getPycData(name, code, ispkg)
|
||||||
|
zf.writestr(path, pyc)
|
||||||
|
zf.close()
|
||||||
# add site.pyc
|
# add site.pyc
|
||||||
sitepath = pathjoin(self.bundlepath, "Contents", "Resources",
|
sitepath = pathjoin(self.bundlepath, "Contents", "Resources",
|
||||||
"site" + PYC_EXT)
|
"site" + PYC_EXT)
|
||||||
@@ -446,6 +454,9 @@ class AppBuilder(BundleBuilder):
|
|||||||
self.message("Finding module dependencies", 1)
|
self.message("Finding module dependencies", 1)
|
||||||
import modulefinder
|
import modulefinder
|
||||||
mf = modulefinder.ModuleFinder(excludes=self.excludeModules)
|
mf = modulefinder.ModuleFinder(excludes=self.excludeModules)
|
||||||
|
if USE_ZIPIMPORT:
|
||||||
|
# zipimport imports zlib, must add it manually
|
||||||
|
mf.import_hook("zlib")
|
||||||
# manually add our own site.py
|
# manually add our own site.py
|
||||||
site = mf.add_module("site")
|
site = mf.add_module("site")
|
||||||
site.__code__ = SITE_CO
|
site.__code__ = SITE_CO
|
||||||
@@ -468,9 +479,10 @@ class AppBuilder(BundleBuilder):
|
|||||||
# C extension
|
# C extension
|
||||||
path = mod.__file__
|
path = mod.__file__
|
||||||
filename = os.path.basename(path)
|
filename = os.path.basename(path)
|
||||||
if USE_FROZEN:
|
if USE_ZIPIMPORT:
|
||||||
# "proper" freezing, put extensions in Contents/Resources/,
|
# Python modules are stored in a Zip archive, but put
|
||||||
# freeze a tiny "loader" program. Due to Thomas Heller.
|
# extensions in Contents/Resources/.a and add a tiny "loader"
|
||||||
|
# program in the Zip archive. Due to Thomas Heller.
|
||||||
dstpath = pathjoin("Contents", "Resources", filename)
|
dstpath = pathjoin("Contents", "Resources", filename)
|
||||||
source = EXT_LOADER % {"name": name, "filename": filename}
|
source = EXT_LOADER % {"name": name, "filename": filename}
|
||||||
code = compile(source, "<dynloader for %s>" % name, "exec")
|
code = compile(source, "<dynloader for %s>" % name, "exec")
|
||||||
@@ -483,9 +495,9 @@ class AppBuilder(BundleBuilder):
|
|||||||
self.binaries.append(dstpath)
|
self.binaries.append(dstpath)
|
||||||
if mod.__code__ is not None:
|
if mod.__code__ is not None:
|
||||||
ispkg = mod.__path__ is not None
|
ispkg = mod.__path__ is not None
|
||||||
if not USE_FROZEN or name != "site":
|
if not USE_ZIPIMPORT or name != "site":
|
||||||
# Our site.py is doing the bootstrapping, so we must
|
# Our site.py is doing the bootstrapping, so we must
|
||||||
# include a real .pyc file if USE_FROZEN is True.
|
# include a real .pyc file if USE_ZIPIMPORT is True.
|
||||||
self.pymodules.append((name, mod.__code__, ispkg))
|
self.pymodules.append((name, mod.__code__, ispkg))
|
||||||
|
|
||||||
if hasattr(mf, "any_missing_maybe"):
|
if hasattr(mf, "any_missing_maybe"):
|
||||||
@@ -508,17 +520,17 @@ class AppBuilder(BundleBuilder):
|
|||||||
maybe.sort()
|
maybe.sort()
|
||||||
if maybe:
|
if maybe:
|
||||||
self.message("Warning: couldn't find the following submodules:", 1)
|
self.message("Warning: couldn't find the following submodules:", 1)
|
||||||
self.message(" (Note that these could be false alarms -- "
|
self.message(" (Note that these could be false alarms -- "
|
||||||
"it's not always", 1)
|
"it's not always", 1)
|
||||||
self.message(" possible to distinguish between \"from package "
|
self.message(" possible to distinguish between \"from package "
|
||||||
"import submodule\" ", 1)
|
"import submodule\" ", 1)
|
||||||
self.message(" and \"from package import name\")", 1)
|
self.message(" and \"from package import name\")", 1)
|
||||||
for name in maybe:
|
for name in maybe:
|
||||||
self.message(" ? " + name, 1)
|
self.message(" ? " + name, 1)
|
||||||
if missing:
|
if missing:
|
||||||
self.message("Warning: couldn't find the following modules:", 1)
|
self.message("Warning: couldn't find the following modules:", 1)
|
||||||
for name in missing:
|
for name in missing:
|
||||||
self.message(" ? " + name, 1)
|
self.message(" ? " + name, 1)
|
||||||
|
|
||||||
def report(self):
|
def report(self):
|
||||||
# XXX something decent
|
# XXX something decent
|
||||||
@@ -554,10 +566,9 @@ def findPackageContents(name, searchpath=None):
|
|||||||
|
|
||||||
def writePyc(code, path):
|
def writePyc(code, path):
|
||||||
f = open(path, "wb")
|
f = open(path, "wb")
|
||||||
f.write("\0" * 8) # don't bother about a time stamp
|
|
||||||
marshal.dump(code, f)
|
|
||||||
f.seek(0, 0)
|
|
||||||
f.write(MAGIC)
|
f.write(MAGIC)
|
||||||
|
f.write("\0" * 4) # don't bother about a time stamp
|
||||||
|
marshal.dump(code, f)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
def copy(src, dst, mkdirs=0):
|
def copy(src, dst, mkdirs=0):
|
||||||
@@ -585,6 +596,8 @@ def makedirs(dir):
|
|||||||
|
|
||||||
def symlink(src, dst, mkdirs=0):
|
def symlink(src, dst, mkdirs=0):
|
||||||
"""Copy a file or a directory."""
|
"""Copy a file or a directory."""
|
||||||
|
if not os.path.exists(src):
|
||||||
|
raise IOError, "No such file or directory: '%s'" % src
|
||||||
if mkdirs:
|
if mkdirs:
|
||||||
makedirs(os.path.dirname(dst))
|
makedirs(os.path.dirname(dst))
|
||||||
os.symlink(os.path.abspath(src), dst)
|
os.symlink(os.path.abspath(src), dst)
|
||||||
@@ -603,31 +616,33 @@ Usage:
|
|||||||
python mybuildscript.py [options] command
|
python mybuildscript.py [options] command
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
build build the application
|
build build the application
|
||||||
report print a report
|
report print a report
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-b, --builddir=DIR the build directory; defaults to "build"
|
-b, --builddir=DIR the build directory; defaults to "build"
|
||||||
-n, --name=NAME application name
|
-n, --name=NAME application name
|
||||||
-r, --resource=FILE extra file or folder to be copied to Resources
|
-r, --resource=FILE extra file or folder to be copied to Resources
|
||||||
-e, --executable=FILE the executable to be used
|
-e, --executable=FILE the executable to be used
|
||||||
-m, --mainprogram=FILE the Python main program
|
-m, --mainprogram=FILE the Python main program
|
||||||
-p, --plist=FILE .plist file (default: generate one)
|
-p, --plist=FILE .plist file (default: generate one)
|
||||||
--nib=NAME main nib name
|
--nib=NAME main nib name
|
||||||
-c, --creator=CCCC 4-char creator code (default: '????')
|
-c, --creator=CCCC 4-char creator code (default: '????')
|
||||||
--iconfile=FILE Filename of the icon to be copied to Resources and
|
--iconfile=FILE filename of the icon (an .icns file) to be used
|
||||||
used as the Finder icon
|
as the Finder icon
|
||||||
-l, --link symlink files/folder instead of copying them
|
-l, --link symlink files/folder instead of copying them
|
||||||
--link-exec symlink the executable instead of copying it
|
--link-exec symlink the executable instead of copying it
|
||||||
--standalone build a standalone application, which is fully
|
--standalone build a standalone application, which is fully
|
||||||
independent of a Python installation
|
independent of a Python installation
|
||||||
-x, --exclude=MODULE exclude module (with --standalone)
|
--lib=FILE shared library or framework to be copied into
|
||||||
-i, --include=MODULE include module (with --standalone)
|
the bundle
|
||||||
--package=PACKAGE include a whole package (with --standalone)
|
-x, --exclude=MODULE exclude module (with --standalone)
|
||||||
--strip strip binaries (remove debug info)
|
-i, --include=MODULE include module (with --standalone)
|
||||||
-v, --verbose increase verbosity level
|
--package=PACKAGE include a whole package (with --standalone)
|
||||||
-q, --quiet decrease verbosity level
|
--strip strip binaries (remove debug info)
|
||||||
-h, --help print this message
|
-v, --verbose increase verbosity level
|
||||||
|
-q, --quiet decrease verbosity level
|
||||||
|
-h, --help print this message
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def usage(msg=None):
|
def usage(msg=None):
|
||||||
@@ -644,7 +659,8 @@ def main(builder=None):
|
|||||||
longopts = ("builddir=", "name=", "resource=", "executable=",
|
longopts = ("builddir=", "name=", "resource=", "executable=",
|
||||||
"mainprogram=", "creator=", "nib=", "plist=", "link",
|
"mainprogram=", "creator=", "nib=", "plist=", "link",
|
||||||
"link-exec", "help", "verbose", "quiet", "standalone",
|
"link-exec", "help", "verbose", "quiet", "standalone",
|
||||||
"exclude=", "include=", "package=", "strip", "iconfile=")
|
"exclude=", "include=", "package=", "strip", "iconfile=",
|
||||||
|
"lib=")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
options, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
|
options, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
|
||||||
@@ -666,6 +682,8 @@ def main(builder=None):
|
|||||||
builder.creator = arg
|
builder.creator = arg
|
||||||
elif opt == '--iconfile':
|
elif opt == '--iconfile':
|
||||||
builder.iconfile = arg
|
builder.iconfile = arg
|
||||||
|
elif opt == "--lib":
|
||||||
|
builder.libs.append(arg)
|
||||||
elif opt == "--nib":
|
elif opt == "--nib":
|
||||||
builder.nibname = arg
|
builder.nibname = arg
|
||||||
elif opt in ('-p', '--plist'):
|
elif opt in ('-p', '--plist'):
|
||||||
|
Reference in New Issue
Block a user