blob: 433991394deb9e4792821055cdb5043ae8f9dcab [file] [log] [blame]
Just van Rossumad33d722002-11-21 10:23:04 +00001#! /usr/bin/env python
2
3"""\
4bundlebuilder.py -- Tools to assemble MacOS X (application) bundles.
5
Just van Rossumceeb9622002-11-21 23:19:37 +00006This module contains two classes to build so called "bundles" for
Just van Rossumad33d722002-11-21 10:23:04 +00007MacOS X. BundleBuilder is a general tool, AppBuilder is a subclass
Just van Rossumceeb9622002-11-21 23:19:37 +00008specialized in building application bundles.
Just van Rossumad33d722002-11-21 10:23:04 +00009
Just van Rossumceeb9622002-11-21 23:19:37 +000010[Bundle|App]Builder objects are instantiated with a bunch of keyword
11arguments, and have a build() method that will do all the work. See
12the class doc strings for a description of the constructor arguments.
13
14The module contains a main program that can be used in two ways:
15
16 % python bundlebuilder.py [options] build
17 % python buildapp.py [options] build
18
19Where "buildapp.py" is a user-supplied setup.py-like script following
20this model:
21
22 from bundlebuilder import buildapp
23 buildapp(<lots-of-keyword-args>)
Just van Rossumad33d722002-11-21 10:23:04 +000024
25"""
26
Just van Rossumad33d722002-11-21 10:23:04 +000027
Just van Rossumcef32882002-11-26 00:34:52 +000028__all__ = ["BundleBuilder", "BundleBuilderError", "AppBuilder", "buildapp"]
Just van Rossumad33d722002-11-21 10:23:04 +000029
30
Benjamin Peterson23681932008-05-12 21:42:13 +000031from warnings import warnpy3k
Benjamin Petersona6864e02008-07-14 17:42:17 +000032warnpy3k("In 3.x, the bundlebuilder module is removed.", stacklevel=2)
Benjamin Peterson23681932008-05-12 21:42:13 +000033
Just van Rossumad33d722002-11-21 10:23:04 +000034import sys
35import os, errno, shutil
Just van Rossumcef32882002-11-26 00:34:52 +000036import imp, marshal
37import re
Just van Rossumda302da2002-11-23 22:26:44 +000038from copy import deepcopy
Just van Rossumceeb9622002-11-21 23:19:37 +000039import getopt
Just van Rossumad33d722002-11-21 10:23:04 +000040from plistlib import Plist
Just van Rossumda302da2002-11-23 22:26:44 +000041from types import FunctionType as function
Just van Rossumad33d722002-11-21 10:23:04 +000042
Just van Rossumcef32882002-11-26 00:34:52 +000043class BundleBuilderError(Exception): pass
44
45
Just van Rossumda302da2002-11-23 22:26:44 +000046class Defaults:
47
Jack Jansen0ae32202003-04-09 13:25:43 +000048 """Class attributes that don't start with an underscore and are
49 not functions or classmethods are (deep)copied to self.__dict__.
50 This allows for mutable default values.
51 """
Just van Rossumda302da2002-11-23 22:26:44 +000052
Jack Jansen0ae32202003-04-09 13:25:43 +000053 def __init__(self, **kwargs):
54 defaults = self._getDefaults()
55 defaults.update(kwargs)
56 self.__dict__.update(defaults)
Just van Rossumda302da2002-11-23 22:26:44 +000057
Jack Jansen0ae32202003-04-09 13:25:43 +000058 def _getDefaults(cls):
59 defaults = {}
Just van Rossumed8bfce2003-07-10 14:53:27 +000060 for base in cls.__bases__:
61 if hasattr(base, "_getDefaults"):
62 defaults.update(base._getDefaults())
Jack Jansen0ae32202003-04-09 13:25:43 +000063 for name, value in cls.__dict__.items():
64 if name[0] != "_" and not isinstance(value,
65 (function, classmethod)):
66 defaults[name] = deepcopy(value)
Jack Jansen0ae32202003-04-09 13:25:43 +000067 return defaults
68 _getDefaults = classmethod(_getDefaults)
Just van Rossumad33d722002-11-21 10:23:04 +000069
70
Just van Rossumda302da2002-11-23 22:26:44 +000071class BundleBuilder(Defaults):
Just van Rossumad33d722002-11-21 10:23:04 +000072
Jack Jansen0ae32202003-04-09 13:25:43 +000073 """BundleBuilder is a barebones class for assembling bundles. It
74 knows nothing about executables or icons, it only copies files
75 and creates the PkgInfo and Info.plist files.
76 """
Just van Rossumad33d722002-11-21 10:23:04 +000077
Jack Jansen0ae32202003-04-09 13:25:43 +000078 # (Note that Defaults.__init__ (deep)copies these values to
79 # instance variables. Mutable defaults are therefore safe.)
Just van Rossumda302da2002-11-23 22:26:44 +000080
Jack Jansen0ae32202003-04-09 13:25:43 +000081 # Name of the bundle, with or without extension.
82 name = None
Just van Rossumda302da2002-11-23 22:26:44 +000083
Jack Jansen0ae32202003-04-09 13:25:43 +000084 # The property list ("plist")
85 plist = Plist(CFBundleDevelopmentRegion = "English",
86 CFBundleInfoDictionaryVersion = "6.0")
Just van Rossumda302da2002-11-23 22:26:44 +000087
Jack Jansen0ae32202003-04-09 13:25:43 +000088 # The type of the bundle.
89 type = "BNDL"
90 # The creator code of the bundle.
91 creator = None
Just van Rossumda302da2002-11-23 22:26:44 +000092
Just van Rossumbe56aae2003-07-04 14:20:03 +000093 # the CFBundleIdentifier (this is used for the preferences file name)
94 bundle_id = None
95
Jack Jansen0ae32202003-04-09 13:25:43 +000096 # List of files that have to be copied to <bundle>/Contents/Resources.
97 resources = []
Just van Rossumda302da2002-11-23 22:26:44 +000098
Jack Jansen0ae32202003-04-09 13:25:43 +000099 # List of (src, dest) tuples; dest should be a path relative to the bundle
100 # (eg. "Contents/Resources/MyStuff/SomeFile.ext).
101 files = []
Just van Rossumda302da2002-11-23 22:26:44 +0000102
Jack Jansen0ae32202003-04-09 13:25:43 +0000103 # List of shared libraries (dylibs, Frameworks) to bundle with the app
104 # will be placed in Contents/Frameworks
105 libs = []
Just van Rossum15624d82003-03-21 09:26:59 +0000106
Jack Jansen0ae32202003-04-09 13:25:43 +0000107 # Directory where the bundle will be assembled.
108 builddir = "build"
Just van Rossumda302da2002-11-23 22:26:44 +0000109
Jack Jansen0ae32202003-04-09 13:25:43 +0000110 # Make symlinks instead copying files. This is handy during debugging, but
111 # makes the bundle non-distributable.
112 symlink = 0
Just van Rossumda302da2002-11-23 22:26:44 +0000113
Jack Jansen0ae32202003-04-09 13:25:43 +0000114 # Verbosity level.
115 verbosity = 1
Tim Peters5a9fb3c2005-01-07 16:01:32 +0000116
Jack Jansenc77f6df2004-12-27 15:51:03 +0000117 # Destination root directory
118 destroot = ""
Just van Rossumad33d722002-11-21 10:23:04 +0000119
Jack Jansen0ae32202003-04-09 13:25:43 +0000120 def setup(self):
121 # XXX rethink self.name munging, this is brittle.
122 self.name, ext = os.path.splitext(self.name)
123 if not ext:
124 ext = ".bundle"
125 bundleextension = ext
126 # misc (derived) attributes
127 self.bundlepath = pathjoin(self.builddir, self.name + bundleextension)
Just van Rossumceeb9622002-11-21 23:19:37 +0000128
Jack Jansen0ae32202003-04-09 13:25:43 +0000129 plist = self.plist
130 plist.CFBundleName = self.name
131 plist.CFBundlePackageType = self.type
132 if self.creator is None:
133 if hasattr(plist, "CFBundleSignature"):
134 self.creator = plist.CFBundleSignature
135 else:
136 self.creator = "????"
137 plist.CFBundleSignature = self.creator
Just van Rossumbe56aae2003-07-04 14:20:03 +0000138 if self.bundle_id:
139 plist.CFBundleIdentifier = self.bundle_id
140 elif not hasattr(plist, "CFBundleIdentifier"):
Jack Jansen0ae32202003-04-09 13:25:43 +0000141 plist.CFBundleIdentifier = self.name
Just van Rossumceeb9622002-11-21 23:19:37 +0000142
Jack Jansen0ae32202003-04-09 13:25:43 +0000143 def build(self):
144 """Build the bundle."""
145 builddir = self.builddir
146 if builddir and not os.path.exists(builddir):
147 os.mkdir(builddir)
148 self.message("Building %s" % repr(self.bundlepath), 1)
149 if os.path.exists(self.bundlepath):
150 shutil.rmtree(self.bundlepath)
Ronald Oussoren836b0392006-05-14 19:56:34 +0000151 if os.path.exists(self.bundlepath + '~'):
152 shutil.rmtree(self.bundlepath + '~')
153 bp = self.bundlepath
154
155 # Create the app bundle in a temporary location and then
Tim Peterscbd7b752006-05-16 23:22:20 +0000156 # rename the completed bundle. This way the Finder will
Ronald Oussoren836b0392006-05-14 19:56:34 +0000157 # never see an incomplete bundle (where it might pick up
158 # and cache the wrong meta data)
159 self.bundlepath = bp + '~'
160 try:
161 os.mkdir(self.bundlepath)
162 self.preProcess()
163 self._copyFiles()
164 self._addMetaFiles()
165 self.postProcess()
166 os.rename(self.bundlepath, bp)
167 finally:
168 self.bundlepath = bp
Jack Jansen0ae32202003-04-09 13:25:43 +0000169 self.message("Done.", 1)
Just van Rossumad33d722002-11-21 10:23:04 +0000170
Jack Jansen0ae32202003-04-09 13:25:43 +0000171 def preProcess(self):
172 """Hook for subclasses."""
173 pass
174 def postProcess(self):
175 """Hook for subclasses."""
176 pass
Just van Rossumad33d722002-11-21 10:23:04 +0000177
Jack Jansen0ae32202003-04-09 13:25:43 +0000178 def _addMetaFiles(self):
179 contents = pathjoin(self.bundlepath, "Contents")
180 makedirs(contents)
181 #
182 # Write Contents/PkgInfo
183 assert len(self.type) == len(self.creator) == 4, \
184 "type and creator must be 4-byte strings."
185 pkginfo = pathjoin(contents, "PkgInfo")
186 f = open(pkginfo, "wb")
187 f.write(self.type + self.creator)
188 f.close()
189 #
190 # Write Contents/Info.plist
191 infoplist = pathjoin(contents, "Info.plist")
192 self.plist.write(infoplist)
Just van Rossumad33d722002-11-21 10:23:04 +0000193
Jack Jansen0ae32202003-04-09 13:25:43 +0000194 def _copyFiles(self):
195 files = self.files[:]
196 for path in self.resources:
197 files.append((path, pathjoin("Contents", "Resources",
Just van Rossumdc31dc02003-06-20 21:43:36 +0000198 os.path.basename(path))))
Jack Jansen0ae32202003-04-09 13:25:43 +0000199 for path in self.libs:
200 files.append((path, pathjoin("Contents", "Frameworks",
Just van Rossumdc31dc02003-06-20 21:43:36 +0000201 os.path.basename(path))))
Jack Jansen0ae32202003-04-09 13:25:43 +0000202 if self.symlink:
203 self.message("Making symbolic links", 1)
204 msg = "Making symlink from"
205 else:
206 self.message("Copying files", 1)
207 msg = "Copying"
208 files.sort()
209 for src, dst in files:
210 if os.path.isdir(src):
211 self.message("%s %s/ to %s/" % (msg, src, dst), 2)
212 else:
213 self.message("%s %s to %s" % (msg, src, dst), 2)
214 dst = pathjoin(self.bundlepath, dst)
215 if self.symlink:
216 symlink(src, dst, mkdirs=1)
217 else:
218 copy(src, dst, mkdirs=1)
Just van Rossumad33d722002-11-21 10:23:04 +0000219
Jack Jansen0ae32202003-04-09 13:25:43 +0000220 def message(self, msg, level=0):
221 if level <= self.verbosity:
222 indent = ""
223 if level > 1:
224 indent = (level - 1) * " "
225 sys.stderr.write(indent + msg + "\n")
Just van Rossumceeb9622002-11-21 23:19:37 +0000226
Jack Jansen0ae32202003-04-09 13:25:43 +0000227 def report(self):
228 # XXX something decent
229 pass
Just van Rossumad33d722002-11-21 10:23:04 +0000230
231
Just van Rossumcef32882002-11-26 00:34:52 +0000232if __debug__:
Jack Jansen0ae32202003-04-09 13:25:43 +0000233 PYC_EXT = ".pyc"
Just van Rossumcef32882002-11-26 00:34:52 +0000234else:
Jack Jansen0ae32202003-04-09 13:25:43 +0000235 PYC_EXT = ".pyo"
Just van Rossumcef32882002-11-26 00:34:52 +0000236
237MAGIC = imp.get_magic()
Just van Rossum109ecbf2003-01-02 13:13:01 +0000238USE_ZIPIMPORT = "zipimport" in sys.builtin_module_names
Just van Rossumcef32882002-11-26 00:34:52 +0000239
240# For standalone apps, we have our own minimal site.py. We don't need
241# all the cruft of the real site.py.
242SITE_PY = """\
243import sys
Just van Rossum762d2cc2003-06-29 21:54:12 +0000244if not %(semi_standalone)s:
245 del sys.path[1:] # sys.path[0] is Contents/Resources/
Just van Rossumcef32882002-11-26 00:34:52 +0000246"""
247
Ronald Oussoren072bb402009-01-02 15:25:36 +0000248ZIP_ARCHIVE = "Modules.zip"
249SITE_PY_ZIP = SITE_PY + ("sys.path.append(sys.path[0] + '/%s')\n" % ZIP_ARCHIVE)
250
251def getPycData(fullname, code, ispkg):
252 if ispkg:
253 fullname += ".__init__"
254 path = fullname.replace(".", os.sep) + PYC_EXT
255 return path, MAGIC + '\0\0\0\0' + marshal.dumps(code)
Just van Rossumcef32882002-11-26 00:34:52 +0000256
Just van Rossum8a3ed3f2003-02-25 20:53:12 +0000257#
258# Extension modules can't be in the modules zip archive, so a placeholder
259# is added instead, that loads the extension from a specified location.
260#
Just van Rossum535ffa22002-11-29 20:06:52 +0000261EXT_LOADER = """\
Just van Rossum109ecbf2003-01-02 13:13:01 +0000262def __load():
Jack Jansen0ae32202003-04-09 13:25:43 +0000263 import imp, sys, os
264 for p in sys.path:
265 path = os.path.join(p, "%(filename)s")
266 if os.path.exists(path):
267 break
268 else:
269 assert 0, "file not found: %(filename)s"
270 mod = imp.load_dynamic("%(name)s", path)
Just van Rossum109ecbf2003-01-02 13:13:01 +0000271
272__load()
273del __load
Just van Rossum535ffa22002-11-29 20:06:52 +0000274"""
275
Ronald Oussoren9545a232010-05-05 19:09:31 +0000276MAYMISS_MODULES = ['os2', 'nt', 'ntpath', 'dos', 'dospath',
Jack Jansen0ae32202003-04-09 13:25:43 +0000277 'win32api', 'ce', '_winreg', 'nturl2path', 'sitecustomize',
278 'org.python.core', 'riscos', 'riscosenviron', 'riscospath'
Just van Rossumcef32882002-11-26 00:34:52 +0000279]
280
281STRIP_EXEC = "/usr/bin/strip"
282
Just van Rossum8a3ed3f2003-02-25 20:53:12 +0000283#
284# We're using a stock interpreter to run the app, yet we need
285# a way to pass the Python main program to the interpreter. The
286# bootstrapping script fires up the interpreter with the right
287# arguments. os.execve() is used as OSX doesn't like us to
288# start a real new process. Also, the executable name must match
289# the CFBundleExecutable value in the Info.plist, so we lie
290# deliberately with argv[0]. The actual Python executable is
291# passed in an environment variable so we can "repair"
292# sys.executable later.
293#
Just van Rossum74bdca82002-11-28 11:30:56 +0000294BOOTSTRAP_SCRIPT = """\
Just van Rossum0ff7a4e2003-02-26 11:27:56 +0000295#!%(hashbang)s
Just van Rossumad33d722002-11-21 10:23:04 +0000296
Just van Rossum7322b1a2003-02-25 20:15:40 +0000297import sys, os
298execdir = os.path.dirname(sys.argv[0])
299executable = os.path.join(execdir, "%(executable)s")
300resdir = os.path.join(os.path.dirname(execdir), "Resources")
Just van Rossum15624d82003-03-21 09:26:59 +0000301libdir = os.path.join(os.path.dirname(execdir), "Frameworks")
Just van Rossum7322b1a2003-02-25 20:15:40 +0000302mainprogram = os.path.join(resdir, "%(mainprogram)s")
303
Ronald Oussoren072bb402009-01-02 15:25:36 +0000304if %(optimize)s:
305 sys.argv.insert(1, '-O')
306
Just van Rossum7322b1a2003-02-25 20:15:40 +0000307sys.argv.insert(1, mainprogram)
Just van Rossumbe56aae2003-07-04 14:20:03 +0000308if %(standalone)s or %(semi_standalone)s:
309 os.environ["PYTHONPATH"] = resdir
310 if %(standalone)s:
311 os.environ["PYTHONHOME"] = resdir
312else:
313 pypath = os.getenv("PYTHONPATH", "")
314 if pypath:
315 pypath = ":" + pypath
316 os.environ["PYTHONPATH"] = resdir + pypath
Ronald Oussoren072bb402009-01-02 15:25:36 +0000317
Just van Rossum7322b1a2003-02-25 20:15:40 +0000318os.environ["PYTHONEXECUTABLE"] = executable
Just van Rossum15624d82003-03-21 09:26:59 +0000319os.environ["DYLD_LIBRARY_PATH"] = libdir
Just van Rossum3166f592003-06-20 18:56:10 +0000320os.environ["DYLD_FRAMEWORK_PATH"] = libdir
Just van Rossum7322b1a2003-02-25 20:15:40 +0000321os.execve(executable, sys.argv, os.environ)
Just van Rossumad33d722002-11-21 10:23:04 +0000322"""
323
Just van Rossum8a3ed3f2003-02-25 20:53:12 +0000324
325#
326# Optional wrapper that converts "dropped files" into sys.argv values.
327#
328ARGV_EMULATOR = """\
Jack Jansena03adde2003-02-18 23:29:46 +0000329import argvemulator, os
330
331argvemulator.ArgvCollector().mainloop()
332execfile(os.path.join(os.path.split(__file__)[0], "%(realmainprogram)s"))
333"""
Just van Rossumcef32882002-11-26 00:34:52 +0000334
Just van Rossum3166f592003-06-20 18:56:10 +0000335#
336# When building a standalone app with Python.framework, we need to copy
337# a subset from Python.framework to the bundle. The following list
338# specifies exactly what items we'll copy.
339#
340PYTHONFRAMEWORKGOODIES = [
341 "Python", # the Python core library
342 "Resources/English.lproj",
343 "Resources/Info.plist",
Just van Rossum3166f592003-06-20 18:56:10 +0000344]
345
Just van Rossum79b0ae12003-06-29 22:20:26 +0000346def isFramework():
347 return sys.exec_prefix.find("Python.framework") > 0
348
Just van Rossum8a3ed3f2003-02-25 20:53:12 +0000349
Just van Rossum762d2cc2003-06-29 21:54:12 +0000350LIB = os.path.join(sys.prefix, "lib", "python" + sys.version[:3])
351SITE_PACKAGES = os.path.join(LIB, "site-packages")
352
353
Just van Rossumad33d722002-11-21 10:23:04 +0000354class AppBuilder(BundleBuilder):
355
Ronald Oussoren072bb402009-01-02 15:25:36 +0000356 use_zipimport = USE_ZIPIMPORT
357
Jack Jansen0ae32202003-04-09 13:25:43 +0000358 # Override type of the bundle.
359 type = "APPL"
Jack Jansencc81b802003-03-05 14:42:18 +0000360
Jack Jansen0ae32202003-04-09 13:25:43 +0000361 # platform, name of the subfolder of Contents that contains the executable.
362 platform = "MacOS"
Jack Jansencc81b802003-03-05 14:42:18 +0000363
Jack Jansen0ae32202003-04-09 13:25:43 +0000364 # A Python main program. If this argument is given, the main
365 # executable in the bundle will be a small wrapper that invokes
366 # the main program. (XXX Discuss why.)
367 mainprogram = None
Just van Rossumceeb9622002-11-21 23:19:37 +0000368
Jack Jansen0ae32202003-04-09 13:25:43 +0000369 # The main executable. If a Python main program is specified
370 # the executable will be copied to Resources and be invoked
371 # by the wrapper program mentioned above. Otherwise it will
372 # simply be used as the main executable.
373 executable = None
Just van Rossumceeb9622002-11-21 23:19:37 +0000374
Jack Jansen0ae32202003-04-09 13:25:43 +0000375 # The name of the main nib, for Cocoa apps. *Must* be specified
376 # when building a Cocoa app.
377 nibname = None
Just van Rossumad33d722002-11-21 10:23:04 +0000378
Jack Jansen0ae32202003-04-09 13:25:43 +0000379 # The name of the icon file to be copied to Resources and used for
380 # the Finder icon.
381 iconfile = None
Just van Rossum2aa09562003-02-01 08:34:46 +0000382
Jack Jansen0ae32202003-04-09 13:25:43 +0000383 # Symlink the executable instead of copying it.
384 symlink_exec = 0
Just van Rossumad33d722002-11-21 10:23:04 +0000385
Jack Jansen0ae32202003-04-09 13:25:43 +0000386 # If True, build standalone app.
387 standalone = 0
Just van Rossum3166f592003-06-20 18:56:10 +0000388
Just van Rossum762d2cc2003-06-29 21:54:12 +0000389 # If True, build semi-standalone app (only includes third-party modules).
390 semi_standalone = 0
391
Jack Jansen8ba0e802003-05-25 22:00:17 +0000392 # If set, use this for #! lines in stead of sys.executable
393 python = None
Just van Rossum3166f592003-06-20 18:56:10 +0000394
Jack Jansen0ae32202003-04-09 13:25:43 +0000395 # If True, add a real main program that emulates sys.argv before calling
396 # mainprogram
397 argv_emulation = 0
Just van Rossumcef32882002-11-26 00:34:52 +0000398
Jack Jansen0ae32202003-04-09 13:25:43 +0000399 # The following attributes are only used when building a standalone app.
Just van Rossumcef32882002-11-26 00:34:52 +0000400
Jack Jansen0ae32202003-04-09 13:25:43 +0000401 # Exclude these modules.
402 excludeModules = []
Just van Rossumcef32882002-11-26 00:34:52 +0000403
Jack Jansen0ae32202003-04-09 13:25:43 +0000404 # Include these modules.
405 includeModules = []
Just van Rossumcef32882002-11-26 00:34:52 +0000406
Jack Jansen0ae32202003-04-09 13:25:43 +0000407 # Include these packages.
408 includePackages = []
Just van Rossumcef32882002-11-26 00:34:52 +0000409
Just van Rossum00a0b972003-06-20 21:18:22 +0000410 # Strip binaries from debug info.
Jack Jansen0ae32202003-04-09 13:25:43 +0000411 strip = 0
Just van Rossumcef32882002-11-26 00:34:52 +0000412
Jack Jansen0ae32202003-04-09 13:25:43 +0000413 # Found Python modules: [(name, codeobject, ispkg), ...]
414 pymodules = []
Just van Rossumcef32882002-11-26 00:34:52 +0000415
Jack Jansen0ae32202003-04-09 13:25:43 +0000416 # Modules that modulefinder couldn't find:
417 missingModules = []
418 maybeMissingModules = []
Just van Rossumcef32882002-11-26 00:34:52 +0000419
Jack Jansen0ae32202003-04-09 13:25:43 +0000420 def setup(self):
Just van Rossum762d2cc2003-06-29 21:54:12 +0000421 if ((self.standalone or self.semi_standalone)
422 and self.mainprogram is None):
Jack Jansen0ae32202003-04-09 13:25:43 +0000423 raise BundleBuilderError, ("must specify 'mainprogram' when "
424 "building a standalone application.")
425 if self.mainprogram is None and self.executable is None:
426 raise BundleBuilderError, ("must specify either or both of "
427 "'executable' and 'mainprogram'")
Just van Rossumceeb9622002-11-21 23:19:37 +0000428
Jack Jansen0ae32202003-04-09 13:25:43 +0000429 self.execdir = pathjoin("Contents", self.platform)
Jack Jansencc81b802003-03-05 14:42:18 +0000430
Jack Jansen0ae32202003-04-09 13:25:43 +0000431 if self.name is not None:
432 pass
433 elif self.mainprogram is not None:
434 self.name = os.path.splitext(os.path.basename(self.mainprogram))[0]
Georg Brandl1f6176e2010-02-06 23:54:43 +0000435 elif self.executable is not None:
Jack Jansen0ae32202003-04-09 13:25:43 +0000436 self.name = os.path.splitext(os.path.basename(self.executable))[0]
437 if self.name[-4:] != ".app":
438 self.name += ".app"
Just van Rossumceeb9622002-11-21 23:19:37 +0000439
Jack Jansen0ae32202003-04-09 13:25:43 +0000440 if self.executable is None:
Just van Rossum79b0ae12003-06-29 22:20:26 +0000441 if not self.standalone and not isFramework():
Jack Jansen0ae32202003-04-09 13:25:43 +0000442 self.symlink_exec = 1
Jack Jansenbbaa0832003-07-04 11:05:35 +0000443 if self.python:
444 self.executable = self.python
445 else:
446 self.executable = sys.executable
Just van Rossum74bdca82002-11-28 11:30:56 +0000447
Jack Jansen0ae32202003-04-09 13:25:43 +0000448 if self.nibname:
449 self.plist.NSMainNibFile = self.nibname
450 if not hasattr(self.plist, "NSPrincipalClass"):
451 self.plist.NSPrincipalClass = "NSApplication"
Just van Rossumceeb9622002-11-21 23:19:37 +0000452
Just van Rossum79b0ae12003-06-29 22:20:26 +0000453 if self.standalone and isFramework():
Just van Rossum3166f592003-06-20 18:56:10 +0000454 self.addPythonFramework()
455
Jack Jansen0ae32202003-04-09 13:25:43 +0000456 BundleBuilder.setup(self)
Just van Rossumceeb9622002-11-21 23:19:37 +0000457
Jack Jansen0ae32202003-04-09 13:25:43 +0000458 self.plist.CFBundleExecutable = self.name
Just van Rossumf7aba232002-11-22 00:31:50 +0000459
Just van Rossum762d2cc2003-06-29 21:54:12 +0000460 if self.standalone or self.semi_standalone:
Jack Jansen0ae32202003-04-09 13:25:43 +0000461 self.findDependencies()
Just van Rossumcef32882002-11-26 00:34:52 +0000462
Jack Jansen0ae32202003-04-09 13:25:43 +0000463 def preProcess(self):
464 resdir = "Contents/Resources"
465 if self.executable is not None:
466 if self.mainprogram is None:
467 execname = self.name
468 else:
469 execname = os.path.basename(self.executable)
470 execpath = pathjoin(self.execdir, execname)
471 if not self.symlink_exec:
Jack Jansenc77f6df2004-12-27 15:51:03 +0000472 self.files.append((self.destroot + self.executable, execpath))
Jack Jansen0ae32202003-04-09 13:25:43 +0000473 self.execpath = execpath
Just van Rossumad33d722002-11-21 10:23:04 +0000474
Jack Jansen0ae32202003-04-09 13:25:43 +0000475 if self.mainprogram is not None:
476 mainprogram = os.path.basename(self.mainprogram)
477 self.files.append((self.mainprogram, pathjoin(resdir, mainprogram)))
478 if self.argv_emulation:
479 # Change the main program, and create the helper main program (which
480 # does argv collection and then calls the real main).
481 # Also update the included modules (if we're creating a standalone
482 # program) and the plist
483 realmainprogram = mainprogram
484 mainprogram = '__argvemulator_' + mainprogram
485 resdirpath = pathjoin(self.bundlepath, resdir)
486 mainprogrampath = pathjoin(resdirpath, mainprogram)
487 makedirs(resdirpath)
488 open(mainprogrampath, "w").write(ARGV_EMULATOR % locals())
Just van Rossum762d2cc2003-06-29 21:54:12 +0000489 if self.standalone or self.semi_standalone:
Jack Jansen0ae32202003-04-09 13:25:43 +0000490 self.includeModules.append("argvemulator")
491 self.includeModules.append("os")
Mark Dickinsona3a50502010-04-03 18:17:54 +0000492 if "CFBundleDocumentTypes" not in self.plist:
Jack Jansen0ae32202003-04-09 13:25:43 +0000493 self.plist["CFBundleDocumentTypes"] = [
494 { "CFBundleTypeOSTypes" : [
495 "****",
496 "fold",
497 "disk"],
498 "CFBundleTypeRole": "Viewer"}]
499 # Write bootstrap script
500 executable = os.path.basename(self.executable)
501 execdir = pathjoin(self.bundlepath, self.execdir)
502 bootstrappath = pathjoin(execdir, self.name)
503 makedirs(execdir)
Just van Rossum762d2cc2003-06-29 21:54:12 +0000504 if self.standalone or self.semi_standalone:
Jack Jansen0ae32202003-04-09 13:25:43 +0000505 # XXX we're screwed when the end user has deleted
506 # /usr/bin/python
507 hashbang = "/usr/bin/python"
Jack Jansen8ba0e802003-05-25 22:00:17 +0000508 elif self.python:
509 hashbang = self.python
Jack Jansen0ae32202003-04-09 13:25:43 +0000510 else:
511 hashbang = os.path.realpath(sys.executable)
512 standalone = self.standalone
Just van Rossumbe56aae2003-07-04 14:20:03 +0000513 semi_standalone = self.semi_standalone
Ronald Oussoren072bb402009-01-02 15:25:36 +0000514 optimize = sys.flags.optimize
Jack Jansen0ae32202003-04-09 13:25:43 +0000515 open(bootstrappath, "w").write(BOOTSTRAP_SCRIPT % locals())
516 os.chmod(bootstrappath, 0775)
Just van Rossumad33d722002-11-21 10:23:04 +0000517
Jack Jansen0ae32202003-04-09 13:25:43 +0000518 if self.iconfile is not None:
519 iconbase = os.path.basename(self.iconfile)
520 self.plist.CFBundleIconFile = iconbase
521 self.files.append((self.iconfile, pathjoin(resdir, iconbase)))
Just van Rossum2aa09562003-02-01 08:34:46 +0000522
Jack Jansen0ae32202003-04-09 13:25:43 +0000523 def postProcess(self):
Just van Rossum762d2cc2003-06-29 21:54:12 +0000524 if self.standalone or self.semi_standalone:
Jack Jansen0ae32202003-04-09 13:25:43 +0000525 self.addPythonModules()
526 if self.strip and not self.symlink:
527 self.stripBinaries()
Just van Rossumcef32882002-11-26 00:34:52 +0000528
Jack Jansen0ae32202003-04-09 13:25:43 +0000529 if self.symlink_exec and self.executable:
530 self.message("Symlinking executable %s to %s" % (self.executable,
531 self.execpath), 2)
532 dst = pathjoin(self.bundlepath, self.execpath)
533 makedirs(os.path.dirname(dst))
534 os.symlink(os.path.abspath(self.executable), dst)
Just van Rossum16aebf72002-11-22 11:43:10 +0000535
Jack Jansen0ae32202003-04-09 13:25:43 +0000536 if self.missingModules or self.maybeMissingModules:
537 self.reportMissing()
Just van Rossumcef32882002-11-26 00:34:52 +0000538
Just van Rossum3166f592003-06-20 18:56:10 +0000539 def addPythonFramework(self):
540 # If we're building a standalone app with Python.framework,
Just van Rossumdc31dc02003-06-20 21:43:36 +0000541 # include a minimal subset of Python.framework, *unless*
542 # Python.framework was specified manually in self.libs.
543 for lib in self.libs:
544 if os.path.basename(lib) == "Python.framework":
545 # a Python.framework was specified as a library
546 return
547
Just van Rossum3166f592003-06-20 18:56:10 +0000548 frameworkpath = sys.exec_prefix[:sys.exec_prefix.find(
549 "Python.framework") + len("Python.framework")]
Just van Rossumdc31dc02003-06-20 21:43:36 +0000550
Just van Rossum3166f592003-06-20 18:56:10 +0000551 version = sys.version[:3]
552 frameworkpath = pathjoin(frameworkpath, "Versions", version)
553 destbase = pathjoin("Contents", "Frameworks", "Python.framework",
554 "Versions", version)
555 for item in PYTHONFRAMEWORKGOODIES:
556 src = pathjoin(frameworkpath, item)
557 dst = pathjoin(destbase, item)
558 self.files.append((src, dst))
559
Just van Rossum762d2cc2003-06-29 21:54:12 +0000560 def _getSiteCode(self):
Ronald Oussoren072bb402009-01-02 15:25:36 +0000561 if self.use_zipimport:
562 return compile(SITE_PY % {"semi_standalone": self.semi_standalone},
Just van Rossum762d2cc2003-06-29 21:54:12 +0000563 "<-bundlebuilder.py->", "exec")
564
Jack Jansen0ae32202003-04-09 13:25:43 +0000565 def addPythonModules(self):
566 self.message("Adding Python modules", 1)
Just van Rossumcef32882002-11-26 00:34:52 +0000567
Ronald Oussoren072bb402009-01-02 15:25:36 +0000568 if self.use_zipimport:
Jack Jansen0ae32202003-04-09 13:25:43 +0000569 # Create a zip file containing all modules as pyc.
570 import zipfile
571 relpath = pathjoin("Contents", "Resources", ZIP_ARCHIVE)
572 abspath = pathjoin(self.bundlepath, relpath)
573 zf = zipfile.ZipFile(abspath, "w", zipfile.ZIP_DEFLATED)
574 for name, code, ispkg in self.pymodules:
575 self.message("Adding Python module %s" % name, 2)
576 path, pyc = getPycData(name, code, ispkg)
577 zf.writestr(path, pyc)
578 zf.close()
579 # add site.pyc
580 sitepath = pathjoin(self.bundlepath, "Contents", "Resources",
581 "site" + PYC_EXT)
Just van Rossum762d2cc2003-06-29 21:54:12 +0000582 writePyc(self._getSiteCode(), sitepath)
Jack Jansen0ae32202003-04-09 13:25:43 +0000583 else:
584 # Create individual .pyc files.
585 for name, code, ispkg in self.pymodules:
586 if ispkg:
587 name += ".__init__"
588 path = name.split(".")
589 path = pathjoin("Contents", "Resources", *path) + PYC_EXT
Just van Rossumcef32882002-11-26 00:34:52 +0000590
Jack Jansen0ae32202003-04-09 13:25:43 +0000591 if ispkg:
592 self.message("Adding Python package %s" % path, 2)
593 else:
594 self.message("Adding Python module %s" % path, 2)
Just van Rossumcef32882002-11-26 00:34:52 +0000595
Jack Jansen0ae32202003-04-09 13:25:43 +0000596 abspath = pathjoin(self.bundlepath, path)
597 makedirs(os.path.dirname(abspath))
598 writePyc(code, abspath)
Just van Rossumcef32882002-11-26 00:34:52 +0000599
Jack Jansen0ae32202003-04-09 13:25:43 +0000600 def stripBinaries(self):
601 if not os.path.exists(STRIP_EXEC):
602 self.message("Error: can't strip binaries: no strip program at "
603 "%s" % STRIP_EXEC, 0)
604 else:
Just van Rossum00a0b972003-06-20 21:18:22 +0000605 import stat
Jack Jansen0ae32202003-04-09 13:25:43 +0000606 self.message("Stripping binaries", 1)
Just van Rossum00a0b972003-06-20 21:18:22 +0000607 def walk(top):
608 for name in os.listdir(top):
609 path = pathjoin(top, name)
610 if os.path.islink(path):
611 continue
612 if os.path.isdir(path):
613 walk(path)
614 else:
615 mod = os.stat(path)[stat.ST_MODE]
616 if not (mod & 0100):
617 continue
618 relpath = path[len(self.bundlepath):]
619 self.message("Stripping %s" % relpath, 2)
620 inf, outf = os.popen4("%s -S \"%s\"" %
621 (STRIP_EXEC, path))
622 output = outf.read().strip()
623 if output:
624 # usually not a real problem, like when we're
625 # trying to strip a script
626 self.message("Problem stripping %s:" % relpath, 3)
627 self.message(output, 3)
628 walk(self.bundlepath)
Just van Rossumcef32882002-11-26 00:34:52 +0000629
Jack Jansen0ae32202003-04-09 13:25:43 +0000630 def findDependencies(self):
631 self.message("Finding module dependencies", 1)
632 import modulefinder
633 mf = modulefinder.ModuleFinder(excludes=self.excludeModules)
Ronald Oussoren072bb402009-01-02 15:25:36 +0000634 if self.use_zipimport:
Jack Jansen0ae32202003-04-09 13:25:43 +0000635 # zipimport imports zlib, must add it manually
636 mf.import_hook("zlib")
637 # manually add our own site.py
638 site = mf.add_module("site")
Just van Rossum762d2cc2003-06-29 21:54:12 +0000639 site.__code__ = self._getSiteCode()
640 mf.scan_code(site.__code__, site)
Just van Rossumcef32882002-11-26 00:34:52 +0000641
Jack Jansen0ae32202003-04-09 13:25:43 +0000642 # warnings.py gets imported implicitly from C
643 mf.import_hook("warnings")
Just van Rossum7322b1a2003-02-25 20:15:40 +0000644
Jack Jansen0ae32202003-04-09 13:25:43 +0000645 includeModules = self.includeModules[:]
646 for name in self.includePackages:
647 includeModules.extend(findPackageContents(name).keys())
648 for name in includeModules:
649 try:
650 mf.import_hook(name)
651 except ImportError:
652 self.missingModules.append(name)
Just van Rossumcef32882002-11-26 00:34:52 +0000653
Jack Jansen0ae32202003-04-09 13:25:43 +0000654 mf.run_script(self.mainprogram)
655 modules = mf.modules.items()
656 modules.sort()
657 for name, mod in modules:
Just van Rossum762d2cc2003-06-29 21:54:12 +0000658 path = mod.__file__
659 if path and self.semi_standalone:
660 # skip the standard library
661 if path.startswith(LIB) and not path.startswith(SITE_PACKAGES):
662 continue
663 if path and mod.__code__ is None:
Jack Jansen0ae32202003-04-09 13:25:43 +0000664 # C extension
Jack Jansen0ae32202003-04-09 13:25:43 +0000665 filename = os.path.basename(path)
Just van Rossum79b0ae12003-06-29 22:20:26 +0000666 pathitems = name.split(".")[:-1] + [filename]
667 dstpath = pathjoin(*pathitems)
Ronald Oussoren072bb402009-01-02 15:25:36 +0000668 if self.use_zipimport:
Just van Rossum79b0ae12003-06-29 22:20:26 +0000669 if name != "zlib":
670 # neatly pack all extension modules in a subdirectory,
Mark Dickinson3e4caeb2009-02-21 20:27:01 +0000671 # except zlib, since it's necessary for bootstrapping.
Just van Rossum79b0ae12003-06-29 22:20:26 +0000672 dstpath = pathjoin("ExtensionModules", dstpath)
Jack Jansen0ae32202003-04-09 13:25:43 +0000673 # Python modules are stored in a Zip archive, but put
Just van Rossum762d2cc2003-06-29 21:54:12 +0000674 # extensions in Contents/Resources/. Add a tiny "loader"
Jack Jansen0ae32202003-04-09 13:25:43 +0000675 # program in the Zip archive. Due to Thomas Heller.
Just van Rossum762d2cc2003-06-29 21:54:12 +0000676 source = EXT_LOADER % {"name": name, "filename": dstpath}
Jack Jansen0ae32202003-04-09 13:25:43 +0000677 code = compile(source, "<dynloader for %s>" % name, "exec")
678 mod.__code__ = code
Just van Rossum762d2cc2003-06-29 21:54:12 +0000679 self.files.append((path, pathjoin("Contents", "Resources", dstpath)))
Jack Jansen0ae32202003-04-09 13:25:43 +0000680 if mod.__code__ is not None:
681 ispkg = mod.__path__ is not None
Ronald Oussoren072bb402009-01-02 15:25:36 +0000682 if not self.use_zipimport or name != "site":
Jack Jansen0ae32202003-04-09 13:25:43 +0000683 # Our site.py is doing the bootstrapping, so we must
Ronald Oussoren072bb402009-01-02 15:25:36 +0000684 # include a real .pyc file if self.use_zipimport is True.
Jack Jansen0ae32202003-04-09 13:25:43 +0000685 self.pymodules.append((name, mod.__code__, ispkg))
Just van Rossumcef32882002-11-26 00:34:52 +0000686
Jack Jansen0ae32202003-04-09 13:25:43 +0000687 if hasattr(mf, "any_missing_maybe"):
688 missing, maybe = mf.any_missing_maybe()
689 else:
690 missing = mf.any_missing()
691 maybe = []
692 self.missingModules.extend(missing)
693 self.maybeMissingModules.extend(maybe)
Just van Rossumcef32882002-11-26 00:34:52 +0000694
Jack Jansen0ae32202003-04-09 13:25:43 +0000695 def reportMissing(self):
696 missing = [name for name in self.missingModules
697 if name not in MAYMISS_MODULES]
698 if self.maybeMissingModules:
699 maybe = self.maybeMissingModules
700 else:
701 maybe = [name for name in missing if "." in name]
702 missing = [name for name in missing if "." not in name]
703 missing.sort()
704 maybe.sort()
705 if maybe:
706 self.message("Warning: couldn't find the following submodules:", 1)
707 self.message(" (Note that these could be false alarms -- "
708 "it's not always", 1)
709 self.message(" possible to distinguish between \"from package "
710 "import submodule\" ", 1)
711 self.message(" and \"from package import name\")", 1)
712 for name in maybe:
713 self.message(" ? " + name, 1)
714 if missing:
715 self.message("Warning: couldn't find the following modules:", 1)
716 for name in missing:
717 self.message(" ? " + name, 1)
Just van Rossum74bdca82002-11-28 11:30:56 +0000718
Jack Jansen0ae32202003-04-09 13:25:43 +0000719 def report(self):
720 # XXX something decent
721 import pprint
722 pprint.pprint(self.__dict__)
Just van Rossum762d2cc2003-06-29 21:54:12 +0000723 if self.standalone or self.semi_standalone:
Jack Jansen0ae32202003-04-09 13:25:43 +0000724 self.reportMissing()
Just van Rossumcef32882002-11-26 00:34:52 +0000725
726#
727# Utilities.
728#
729
730SUFFIXES = [_suf for _suf, _mode, _tp in imp.get_suffixes()]
731identifierRE = re.compile(r"[_a-zA-z][_a-zA-Z0-9]*$")
732
733def findPackageContents(name, searchpath=None):
Jack Jansen0ae32202003-04-09 13:25:43 +0000734 head = name.split(".")[-1]
735 if identifierRE.match(head) is None:
736 return {}
737 try:
738 fp, path, (ext, mode, tp) = imp.find_module(head, searchpath)
739 except ImportError:
740 return {}
741 modules = {name: None}
742 if tp == imp.PKG_DIRECTORY and path:
743 files = os.listdir(path)
744 for sub in files:
745 sub, ext = os.path.splitext(sub)
746 fullname = name + "." + sub
747 if sub != "__init__" and fullname not in modules:
748 modules.update(findPackageContents(fullname, [path]))
749 return modules
Just van Rossumcef32882002-11-26 00:34:52 +0000750
751def writePyc(code, path):
Jack Jansen0ae32202003-04-09 13:25:43 +0000752 f = open(path, "wb")
753 f.write(MAGIC)
754 f.write("\0" * 4) # don't bother about a time stamp
755 marshal.dump(code, f)
756 f.close()
Just van Rossumad33d722002-11-21 10:23:04 +0000757
Just van Rossumad33d722002-11-21 10:23:04 +0000758def copy(src, dst, mkdirs=0):
Jack Jansen0ae32202003-04-09 13:25:43 +0000759 """Copy a file or a directory."""
760 if mkdirs:
761 makedirs(os.path.dirname(dst))
762 if os.path.isdir(src):
Just van Rossumdc31dc02003-06-20 21:43:36 +0000763 shutil.copytree(src, dst, symlinks=1)
Jack Jansen0ae32202003-04-09 13:25:43 +0000764 else:
765 shutil.copy2(src, dst)
Just van Rossumad33d722002-11-21 10:23:04 +0000766
767def copytodir(src, dstdir):
Jack Jansen0ae32202003-04-09 13:25:43 +0000768 """Copy a file or a directory to an existing directory."""
769 dst = pathjoin(dstdir, os.path.basename(src))
770 copy(src, dst)
Just van Rossumad33d722002-11-21 10:23:04 +0000771
772def makedirs(dir):
Jack Jansen0ae32202003-04-09 13:25:43 +0000773 """Make all directories leading up to 'dir' including the leaf
774 directory. Don't moan if any path element already exists."""
775 try:
776 os.makedirs(dir)
777 except OSError, why:
778 if why.errno != errno.EEXIST:
779 raise
Just van Rossumad33d722002-11-21 10:23:04 +0000780
781def symlink(src, dst, mkdirs=0):
Jack Jansen0ae32202003-04-09 13:25:43 +0000782 """Copy a file or a directory."""
783 if not os.path.exists(src):
784 raise IOError, "No such file or directory: '%s'" % src
785 if mkdirs:
786 makedirs(os.path.dirname(dst))
787 os.symlink(os.path.abspath(src), dst)
Just van Rossumad33d722002-11-21 10:23:04 +0000788
789def pathjoin(*args):
Jack Jansen0ae32202003-04-09 13:25:43 +0000790 """Safe wrapper for os.path.join: asserts that all but the first
791 argument are relative paths."""
792 for seg in args[1:]:
793 assert seg[0] != "/"
794 return os.path.join(*args)
Just van Rossumad33d722002-11-21 10:23:04 +0000795
796
Just van Rossumceeb9622002-11-21 23:19:37 +0000797cmdline_doc = """\
798Usage:
Just van Rossumf7aba232002-11-22 00:31:50 +0000799 python bundlebuilder.py [options] command
Just van Rossumceeb9622002-11-21 23:19:37 +0000800 python mybuildscript.py [options] command
801
802Commands:
803 build build the application
804 report print a report
805
806Options:
807 -b, --builddir=DIR the build directory; defaults to "build"
808 -n, --name=NAME application name
809 -r, --resource=FILE extra file or folder to be copied to Resources
Just van Rossum7215e082003-02-25 21:00:55 +0000810 -f, --file=SRC:DST extra file or folder to be copied into the bundle;
811 DST must be a path relative to the bundle root
Just van Rossumceeb9622002-11-21 23:19:37 +0000812 -e, --executable=FILE the executable to be used
813 -m, --mainprogram=FILE the Python main program
Jack Jansena03adde2003-02-18 23:29:46 +0000814 -a, --argv add a wrapper main program to create sys.argv
Just van Rossumceeb9622002-11-21 23:19:37 +0000815 -p, --plist=FILE .plist file (default: generate one)
816 --nib=NAME main nib name
817 -c, --creator=CCCC 4-char creator code (default: '????')
Just van Rossum9af69682003-02-02 18:56:37 +0000818 --iconfile=FILE filename of the icon (an .icns file) to be used
Just van Rossum2aa09562003-02-01 08:34:46 +0000819 as the Finder icon
Just van Rossumbe56aae2003-07-04 14:20:03 +0000820 --bundle-id=ID the CFBundleIdentifier, in reverse-dns format
821 (eg. org.python.BuildApplet; this is used for
822 the preferences file name)
Just van Rossumceeb9622002-11-21 23:19:37 +0000823 -l, --link symlink files/folder instead of copying them
Just van Rossum16aebf72002-11-22 11:43:10 +0000824 --link-exec symlink the executable instead of copying it
Just van Rossumcef32882002-11-26 00:34:52 +0000825 --standalone build a standalone application, which is fully
826 independent of a Python installation
Just van Rossum762d2cc2003-06-29 21:54:12 +0000827 --semi-standalone build a standalone application, which depends on
828 an installed Python, yet includes all third-party
829 modules.
Ronald Oussoren072bb402009-01-02 15:25:36 +0000830 --no-zipimport Do not copy code into a zip file
Jack Jansen8ba0e802003-05-25 22:00:17 +0000831 --python=FILE Python to use in #! line in stead of current Python
Just van Rossum15624d82003-03-21 09:26:59 +0000832 --lib=FILE shared library or framework to be copied into
833 the bundle
Just van Rossum762d2cc2003-06-29 21:54:12 +0000834 -x, --exclude=MODULE exclude module (with --(semi-)standalone)
835 -i, --include=MODULE include module (with --(semi-)standalone)
836 --package=PACKAGE include a whole package (with --(semi-)standalone)
Just van Rossumcef32882002-11-26 00:34:52 +0000837 --strip strip binaries (remove debug info)
Just van Rossumceeb9622002-11-21 23:19:37 +0000838 -v, --verbose increase verbosity level
839 -q, --quiet decrease verbosity level
840 -h, --help print this message
841"""
842
843def usage(msg=None):
Jack Jansen0ae32202003-04-09 13:25:43 +0000844 if msg:
845 print msg
846 print cmdline_doc
847 sys.exit(1)
Just van Rossumceeb9622002-11-21 23:19:37 +0000848
849def main(builder=None):
Jack Jansen0ae32202003-04-09 13:25:43 +0000850 if builder is None:
851 builder = AppBuilder(verbosity=1)
Just van Rossumceeb9622002-11-21 23:19:37 +0000852
Jack Jansen0ae32202003-04-09 13:25:43 +0000853 shortopts = "b:n:r:f:e:m:c:p:lx:i:hvqa"
854 longopts = ("builddir=", "name=", "resource=", "file=", "executable=",
855 "mainprogram=", "creator=", "nib=", "plist=", "link",
856 "link-exec", "help", "verbose", "quiet", "argv", "standalone",
857 "exclude=", "include=", "package=", "strip", "iconfile=",
Ronald Oussoren072bb402009-01-02 15:25:36 +0000858 "lib=", "python=", "semi-standalone", "bundle-id=", "destroot="
859 "no-zipimport"
860 )
Just van Rossumceeb9622002-11-21 23:19:37 +0000861
Jack Jansen0ae32202003-04-09 13:25:43 +0000862 try:
863 options, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
864 except getopt.error:
865 usage()
Just van Rossumceeb9622002-11-21 23:19:37 +0000866
Jack Jansen0ae32202003-04-09 13:25:43 +0000867 for opt, arg in options:
868 if opt in ('-b', '--builddir'):
869 builder.builddir = arg
870 elif opt in ('-n', '--name'):
871 builder.name = arg
872 elif opt in ('-r', '--resource'):
Just van Rossumdc31dc02003-06-20 21:43:36 +0000873 builder.resources.append(os.path.normpath(arg))
Jack Jansen0ae32202003-04-09 13:25:43 +0000874 elif opt in ('-f', '--file'):
875 srcdst = arg.split(':')
876 if len(srcdst) != 2:
877 usage("-f or --file argument must be two paths, "
878 "separated by a colon")
879 builder.files.append(srcdst)
880 elif opt in ('-e', '--executable'):
881 builder.executable = arg
882 elif opt in ('-m', '--mainprogram'):
883 builder.mainprogram = arg
884 elif opt in ('-a', '--argv'):
885 builder.argv_emulation = 1
886 elif opt in ('-c', '--creator'):
887 builder.creator = arg
Just van Rossumbe56aae2003-07-04 14:20:03 +0000888 elif opt == '--bundle-id':
889 builder.bundle_id = arg
Jack Jansen0ae32202003-04-09 13:25:43 +0000890 elif opt == '--iconfile':
891 builder.iconfile = arg
892 elif opt == "--lib":
Just van Rossumdc31dc02003-06-20 21:43:36 +0000893 builder.libs.append(os.path.normpath(arg))
Jack Jansen0ae32202003-04-09 13:25:43 +0000894 elif opt == "--nib":
895 builder.nibname = arg
896 elif opt in ('-p', '--plist'):
897 builder.plist = Plist.fromFile(arg)
898 elif opt in ('-l', '--link'):
899 builder.symlink = 1
900 elif opt == '--link-exec':
901 builder.symlink_exec = 1
902 elif opt in ('-h', '--help'):
903 usage()
904 elif opt in ('-v', '--verbose'):
905 builder.verbosity += 1
906 elif opt in ('-q', '--quiet'):
907 builder.verbosity -= 1
908 elif opt == '--standalone':
909 builder.standalone = 1
Just van Rossum762d2cc2003-06-29 21:54:12 +0000910 elif opt == '--semi-standalone':
911 builder.semi_standalone = 1
Jack Jansen8ba0e802003-05-25 22:00:17 +0000912 elif opt == '--python':
913 builder.python = arg
Jack Jansen0ae32202003-04-09 13:25:43 +0000914 elif opt in ('-x', '--exclude'):
915 builder.excludeModules.append(arg)
916 elif opt in ('-i', '--include'):
917 builder.includeModules.append(arg)
918 elif opt == '--package':
919 builder.includePackages.append(arg)
920 elif opt == '--strip':
921 builder.strip = 1
Jack Jansenc77f6df2004-12-27 15:51:03 +0000922 elif opt == '--destroot':
923 builder.destroot = arg
Ronald Oussoren072bb402009-01-02 15:25:36 +0000924 elif opt == '--no-zipimport':
925 builder.use_zipimport = False
Just van Rossumceeb9622002-11-21 23:19:37 +0000926
Jack Jansen0ae32202003-04-09 13:25:43 +0000927 if len(args) != 1:
928 usage("Must specify one command ('build', 'report' or 'help')")
929 command = args[0]
Just van Rossumceeb9622002-11-21 23:19:37 +0000930
Jack Jansen0ae32202003-04-09 13:25:43 +0000931 if command == "build":
932 builder.setup()
933 builder.build()
934 elif command == "report":
935 builder.setup()
936 builder.report()
937 elif command == "help":
938 usage()
939 else:
940 usage("Unknown command '%s'" % command)
Just van Rossumceeb9622002-11-21 23:19:37 +0000941
942
Just van Rossumad33d722002-11-21 10:23:04 +0000943def buildapp(**kwargs):
Jack Jansen0ae32202003-04-09 13:25:43 +0000944 builder = AppBuilder(**kwargs)
945 main(builder)
Just van Rossumad33d722002-11-21 10:23:04 +0000946
947
948if __name__ == "__main__":
Jack Jansen0ae32202003-04-09 13:25:43 +0000949 main()