home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 May / maximum-cd-2010-05.iso / DiscContents / boxee-0.9.20.10711.exe / system / python / Lib / plat-mac / bundlebuilder.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-07-20  |  26.6 KB  |  775 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.4)
  3.  
  4. '''bundlebuilder.py -- Tools to assemble MacOS X (application) bundles.
  5.  
  6. This module contains two classes to build so called "bundles" for
  7. MacOS X. BundleBuilder is a general tool, AppBuilder is a subclass
  8. specialized in building application bundles.
  9.  
  10. [Bundle|App]Builder objects are instantiated with a bunch of keyword
  11. arguments, and have a build() method that will do all the work. See
  12. the class doc strings for a description of the constructor arguments.
  13.  
  14. The 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.  
  19. Where "buildapp.py" is a user-supplied setup.py-like script following
  20. this model:
  21.  
  22.   from bundlebuilder import buildapp
  23.   buildapp(<lots-of-keyword-args>)
  24.  
  25. '''
  26. __all__ = [
  27.     'BundleBuilder',
  28.     'BundleBuilderError',
  29.     'AppBuilder',
  30.     'buildapp']
  31. import sys
  32. import os
  33. import errno
  34. import shutil
  35. import imp
  36. import marshal
  37. import re
  38. from copy import deepcopy
  39. import getopt
  40. from plistlib import Plist
  41. from types import FunctionType as function
  42.  
  43. class BundleBuilderError(Exception):
  44.     pass
  45.  
  46.  
  47. class Defaults:
  48.     """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.     """
  52.     
  53.     def __init__(self, **kwargs):
  54.         defaults = self._getDefaults()
  55.         defaults.update(kwargs)
  56.         self.__dict__.update(defaults)
  57.  
  58.     
  59.     def _getDefaults(cls):
  60.         defaults = { }
  61.         for base in cls.__bases__:
  62.             if hasattr(base, '_getDefaults'):
  63.                 defaults.update(base._getDefaults())
  64.                 continue
  65.         
  66.         for name, value in cls.__dict__.items():
  67.             if name[0] != '_' and not isinstance(value, (function, classmethod)):
  68.                 defaults[name] = deepcopy(value)
  69.                 continue
  70.         
  71.         return defaults
  72.  
  73.     _getDefaults = classmethod(_getDefaults)
  74.  
  75.  
  76. class BundleBuilder(Defaults):
  77.     '''BundleBuilder is a barebones class for assembling bundles. It
  78.     knows nothing about executables or icons, it only copies files
  79.     and creates the PkgInfo and Info.plist files.
  80.     '''
  81.     name = None
  82.     plist = Plist(CFBundleDevelopmentRegion = 'English', CFBundleInfoDictionaryVersion = '6.0')
  83.     type = 'BNDL'
  84.     creator = None
  85.     bundle_id = None
  86.     resources = []
  87.     files = []
  88.     libs = []
  89.     builddir = 'build'
  90.     symlink = 0
  91.     verbosity = 1
  92.     destroot = ''
  93.     
  94.     def setup(self):
  95.         (self.name, ext) = os.path.splitext(self.name)
  96.         if not ext:
  97.             ext = '.bundle'
  98.         
  99.         bundleextension = ext
  100.         self.bundlepath = pathjoin(self.builddir, self.name + bundleextension)
  101.         plist = self.plist
  102.         plist.CFBundleName = self.name
  103.         plist.CFBundlePackageType = self.type
  104.         if self.creator is None:
  105.             if hasattr(plist, 'CFBundleSignature'):
  106.                 self.creator = plist.CFBundleSignature
  107.             else:
  108.                 self.creator = '????'
  109.         
  110.         plist.CFBundleSignature = self.creator
  111.         if self.bundle_id:
  112.             plist.CFBundleIdentifier = self.bundle_id
  113.         elif not hasattr(plist, 'CFBundleIdentifier'):
  114.             plist.CFBundleIdentifier = self.name
  115.         
  116.  
  117.     
  118.     def build(self):
  119.         '''Build the bundle.'''
  120.         builddir = self.builddir
  121.         if builddir and not os.path.exists(builddir):
  122.             os.mkdir(builddir)
  123.         
  124.         self.message('Building %s' % repr(self.bundlepath), 1)
  125.         if os.path.exists(self.bundlepath):
  126.             shutil.rmtree(self.bundlepath)
  127.         
  128.         os.mkdir(self.bundlepath)
  129.         self.preProcess()
  130.         self._copyFiles()
  131.         self._addMetaFiles()
  132.         self.postProcess()
  133.         self.message('Done.', 1)
  134.  
  135.     
  136.     def preProcess(self):
  137.         '''Hook for subclasses.'''
  138.         pass
  139.  
  140.     
  141.     def postProcess(self):
  142.         '''Hook for subclasses.'''
  143.         pass
  144.  
  145.     
  146.     def _addMetaFiles(self):
  147.         contents = pathjoin(self.bundlepath, 'Contents')
  148.         makedirs(contents)
  149.         pkginfo = pathjoin(contents, 'PkgInfo')
  150.         f = open(pkginfo, 'wb')
  151.         f.write(self.type + self.creator)
  152.         f.close()
  153.         infoplist = pathjoin(contents, 'Info.plist')
  154.         self.plist.write(infoplist)
  155.  
  156.     
  157.     def _copyFiles(self):
  158.         files = self.files[:]
  159.         for path in self.resources:
  160.             files.append((path, pathjoin('Contents', 'Resources', os.path.basename(path))))
  161.         
  162.         for path in self.libs:
  163.             files.append((path, pathjoin('Contents', 'Frameworks', os.path.basename(path))))
  164.         
  165.         if self.symlink:
  166.             self.message('Making symbolic links', 1)
  167.             msg = 'Making symlink from'
  168.         else:
  169.             self.message('Copying files', 1)
  170.             msg = 'Copying'
  171.         files.sort()
  172.         for src, dst in files:
  173.             if os.path.isdir(src):
  174.                 self.message('%s %s/ to %s/' % (msg, src, dst), 2)
  175.             else:
  176.                 self.message('%s %s to %s' % (msg, src, dst), 2)
  177.             dst = pathjoin(self.bundlepath, dst)
  178.             if self.symlink:
  179.                 symlink(src, dst, mkdirs = 1)
  180.                 continue
  181.             copy(src, dst, mkdirs = 1)
  182.         
  183.  
  184.     
  185.     def message(self, msg, level = 0):
  186.         if level <= self.verbosity:
  187.             indent = ''
  188.             if level > 1:
  189.                 indent = (level - 1) * '  '
  190.             
  191.             sys.stderr.write(indent + msg + '\n')
  192.         
  193.  
  194.     
  195.     def report(self):
  196.         pass
  197.  
  198.  
  199. PYC_EXT = '.pyo'
  200. MAGIC = imp.get_magic()
  201. USE_ZIPIMPORT = 'zipimport' in sys.builtin_module_names
  202. SITE_PY = 'import sys\nif not %(semi_standalone)s:\n    del sys.path[1:]  # sys.path[0] is Contents/Resources/\n'
  203. if USE_ZIPIMPORT:
  204.     ZIP_ARCHIVE = 'Modules.zip'
  205.     SITE_PY += "sys.path.append(sys.path[0] + '/%s')\n" % ZIP_ARCHIVE
  206.     
  207.     def getPycData(fullname, code, ispkg):
  208.         if ispkg:
  209.             fullname += '.__init__'
  210.         
  211.         path = fullname.replace('.', os.sep) + PYC_EXT
  212.         return (path, MAGIC + '\x00\x00\x00\x00' + marshal.dumps(code))
  213.  
  214.  
  215. EXT_LOADER = 'def __load():\n    import imp, sys, os\n    for p in sys.path:\n        path = os.path.join(p, "%(filename)s")\n        if os.path.exists(path):\n            break\n    else:\n        assert 0, "file not found: %(filename)s"\n    mod = imp.load_dynamic("%(name)s", path)\n\n__load()\ndel __load\n'
  216. MAYMISS_MODULES = [
  217.     'mac',
  218.     'os2',
  219.     'nt',
  220.     'ntpath',
  221.     'dos',
  222.     'dospath',
  223.     'win32api',
  224.     'ce',
  225.     '_winreg',
  226.     'nturl2path',
  227.     'sitecustomize',
  228.     'org.python.core',
  229.     'riscos',
  230.     'riscosenviron',
  231.     'riscospath']
  232. STRIP_EXEC = '/usr/bin/strip'
  233. BOOTSTRAP_SCRIPT = '#!%(hashbang)s\n\nimport sys, os\nexecdir = os.path.dirname(sys.argv[0])\nexecutable = os.path.join(execdir, "%(executable)s")\nresdir = os.path.join(os.path.dirname(execdir), "Resources")\nlibdir = os.path.join(os.path.dirname(execdir), "Frameworks")\nmainprogram = os.path.join(resdir, "%(mainprogram)s")\n\nsys.argv.insert(1, mainprogram)\nif %(standalone)s or %(semi_standalone)s:\n    os.environ["PYTHONPATH"] = resdir\n    if %(standalone)s:\n        os.environ["PYTHONHOME"] = resdir\nelse:\n    pypath = os.getenv("PYTHONPATH", "")\n    if pypath:\n        pypath = ":" + pypath\n    os.environ["PYTHONPATH"] = resdir + pypath\nos.environ["PYTHONEXECUTABLE"] = executable\nos.environ["DYLD_LIBRARY_PATH"] = libdir\nos.environ["DYLD_FRAMEWORK_PATH"] = libdir\nos.execve(executable, sys.argv, os.environ)\n'
  234. ARGV_EMULATOR = 'import argvemulator, os\n\nargvemulator.ArgvCollector().mainloop()\nexecfile(os.path.join(os.path.split(__file__)[0], "%(realmainprogram)s"))\n'
  235. PYTHONFRAMEWORKGOODIES = [
  236.     'Python',
  237.     'Resources/English.lproj',
  238.     'Resources/Info.plist',
  239.     'Resources/version.plist']
  240.  
  241. def isFramework():
  242.     return sys.exec_prefix.find('Python.framework') > 0
  243.  
  244. LIB = os.path.join(sys.prefix, 'lib', 'python' + sys.version[:3])
  245. SITE_PACKAGES = os.path.join(LIB, 'site-packages')
  246.  
  247. class AppBuilder(BundleBuilder):
  248.     type = 'APPL'
  249.     platform = 'MacOS'
  250.     mainprogram = None
  251.     executable = None
  252.     nibname = None
  253.     iconfile = None
  254.     symlink_exec = 0
  255.     standalone = 0
  256.     semi_standalone = 0
  257.     python = None
  258.     argv_emulation = 0
  259.     excludeModules = []
  260.     includeModules = []
  261.     includePackages = []
  262.     strip = 0
  263.     pymodules = []
  264.     missingModules = []
  265.     maybeMissingModules = []
  266.     
  267.     def setup(self):
  268.         if (self.standalone or self.semi_standalone) and self.mainprogram is None:
  269.             raise BundleBuilderError, "must specify 'mainprogram' when building a standalone application."
  270.         
  271.         if self.mainprogram is None and self.executable is None:
  272.             raise BundleBuilderError, "must specify either or both of 'executable' and 'mainprogram'"
  273.         
  274.         self.execdir = pathjoin('Contents', self.platform)
  275.         if self.name is not None:
  276.             pass
  277.         elif self.mainprogram is not None:
  278.             self.name = os.path.splitext(os.path.basename(self.mainprogram))[0]
  279.         elif executable is not None:
  280.             self.name = os.path.splitext(os.path.basename(self.executable))[0]
  281.         
  282.         if self.name[-4:] != '.app':
  283.             self.name += '.app'
  284.         
  285.         if self.executable is None:
  286.             if not (self.standalone) and not isFramework():
  287.                 self.symlink_exec = 1
  288.             
  289.             if self.python:
  290.                 self.executable = self.python
  291.             else:
  292.                 self.executable = sys.executable
  293.         
  294.         if self.nibname:
  295.             self.plist.NSMainNibFile = self.nibname
  296.             if not hasattr(self.plist, 'NSPrincipalClass'):
  297.                 self.plist.NSPrincipalClass = 'NSApplication'
  298.             
  299.         
  300.         if self.standalone and isFramework():
  301.             self.addPythonFramework()
  302.         
  303.         BundleBuilder.setup(self)
  304.         self.plist.CFBundleExecutable = self.name
  305.         if self.standalone or self.semi_standalone:
  306.             self.findDependencies()
  307.         
  308.  
  309.     
  310.     def preProcess(self):
  311.         resdir = 'Contents/Resources'
  312.         if self.executable is not None:
  313.             if self.mainprogram is None:
  314.                 execname = self.name
  315.             else:
  316.                 execname = os.path.basename(self.executable)
  317.             execpath = pathjoin(self.execdir, execname)
  318.             if not self.symlink_exec:
  319.                 self.files.append((self.destroot + self.executable, execpath))
  320.             
  321.             self.execpath = execpath
  322.         
  323.         if self.mainprogram is not None:
  324.             mainprogram = os.path.basename(self.mainprogram)
  325.             self.files.append((self.mainprogram, pathjoin(resdir, mainprogram)))
  326.             if self.argv_emulation:
  327.                 realmainprogram = mainprogram
  328.                 mainprogram = '__argvemulator_' + mainprogram
  329.                 resdirpath = pathjoin(self.bundlepath, resdir)
  330.                 mainprogrampath = pathjoin(resdirpath, mainprogram)
  331.                 makedirs(resdirpath)
  332.                 open(mainprogrampath, 'w').write(ARGV_EMULATOR % locals())
  333.                 if self.standalone or self.semi_standalone:
  334.                     self.includeModules.append('argvemulator')
  335.                     self.includeModules.append('os')
  336.                 
  337.                 if not self.plist.has_key('CFBundleDocumentTypes'):
  338.                     self.plist['CFBundleDocumentTypes'] = [
  339.                         {
  340.                             'CFBundleTypeOSTypes': [
  341.                                 '****',
  342.                                 'fold',
  343.                                 'disk'],
  344.                             'CFBundleTypeRole': 'Viewer' }]
  345.                 
  346.             
  347.             executable = os.path.basename(self.executable)
  348.             execdir = pathjoin(self.bundlepath, self.execdir)
  349.             bootstrappath = pathjoin(execdir, self.name)
  350.             makedirs(execdir)
  351.             if self.standalone or self.semi_standalone:
  352.                 hashbang = '/usr/bin/python'
  353.             elif self.python:
  354.                 hashbang = self.python
  355.             else:
  356.                 hashbang = os.path.realpath(sys.executable)
  357.             standalone = self.standalone
  358.             semi_standalone = self.semi_standalone
  359.             open(bootstrappath, 'w').write(BOOTSTRAP_SCRIPT % locals())
  360.             os.chmod(bootstrappath, 509)
  361.         
  362.         if self.iconfile is not None:
  363.             iconbase = os.path.basename(self.iconfile)
  364.             self.plist.CFBundleIconFile = iconbase
  365.             self.files.append((self.iconfile, pathjoin(resdir, iconbase)))
  366.         
  367.  
  368.     
  369.     def postProcess(self):
  370.         if self.standalone or self.semi_standalone:
  371.             self.addPythonModules()
  372.         
  373.         if self.strip and not (self.symlink):
  374.             self.stripBinaries()
  375.         
  376.         if self.symlink_exec and self.executable:
  377.             self.message('Symlinking executable %s to %s' % (self.executable, self.execpath), 2)
  378.             dst = pathjoin(self.bundlepath, self.execpath)
  379.             makedirs(os.path.dirname(dst))
  380.             os.symlink(os.path.abspath(self.executable), dst)
  381.         
  382.         if self.missingModules or self.maybeMissingModules:
  383.             self.reportMissing()
  384.         
  385.  
  386.     
  387.     def addPythonFramework(self):
  388.         for lib in self.libs:
  389.             if os.path.basename(lib) == 'Python.framework':
  390.                 return None
  391.                 continue
  392.         
  393.         frameworkpath = sys.exec_prefix[:sys.exec_prefix.find('Python.framework') + len('Python.framework')]
  394.         version = sys.version[:3]
  395.         frameworkpath = pathjoin(frameworkpath, 'Versions', version)
  396.         destbase = pathjoin('Contents', 'Frameworks', 'Python.framework', 'Versions', version)
  397.         for item in PYTHONFRAMEWORKGOODIES:
  398.             src = pathjoin(frameworkpath, item)
  399.             dst = pathjoin(destbase, item)
  400.             self.files.append((src, dst))
  401.         
  402.  
  403.     
  404.     def _getSiteCode(self):
  405.         return compile(SITE_PY % {
  406.             'semi_standalone': self.semi_standalone }, '<-bundlebuilder.py->', 'exec')
  407.  
  408.     
  409.     def addPythonModules(self):
  410.         self.message('Adding Python modules', 1)
  411.         if USE_ZIPIMPORT:
  412.             import zipfile
  413.             relpath = pathjoin('Contents', 'Resources', ZIP_ARCHIVE)
  414.             abspath = pathjoin(self.bundlepath, relpath)
  415.             zf = zipfile.ZipFile(abspath, 'w', zipfile.ZIP_DEFLATED)
  416.             for name, code, ispkg in self.pymodules:
  417.                 self.message('Adding Python module %s' % name, 2)
  418.                 (path, pyc) = getPycData(name, code, ispkg)
  419.                 zf.writestr(path, pyc)
  420.             
  421.             zf.close()
  422.             sitepath = pathjoin(self.bundlepath, 'Contents', 'Resources', 'site' + PYC_EXT)
  423.             writePyc(self._getSiteCode(), sitepath)
  424.         else:
  425.             for name, code, ispkg in self.pymodules:
  426.                 if ispkg:
  427.                     name += '.__init__'
  428.                 
  429.                 path = name.split('.')
  430.                 path = pathjoin('Contents', 'Resources', *path) + PYC_EXT
  431.                 if ispkg:
  432.                     self.message('Adding Python package %s' % path, 2)
  433.                 else:
  434.                     self.message('Adding Python module %s' % path, 2)
  435.                 abspath = pathjoin(self.bundlepath, path)
  436.                 makedirs(os.path.dirname(abspath))
  437.                 writePyc(code, abspath)
  438.             
  439.  
  440.     
  441.     def stripBinaries(self):
  442.         if not os.path.exists(STRIP_EXEC):
  443.             self.message("Error: can't strip binaries: no strip program at %s" % STRIP_EXEC, 0)
  444.         else:
  445.             import stat
  446.             self.message('Stripping binaries', 1)
  447.             
  448.             def walk(top):
  449.                 for name in os.listdir(top):
  450.                     path = pathjoin(top, name)
  451.                     if os.path.islink(path):
  452.                         continue
  453.                     
  454.                     if os.path.isdir(path):
  455.                         walk(path)
  456.                         continue
  457.                     mod = os.stat(path)[stat.ST_MODE]
  458.                     if not mod & 64:
  459.                         continue
  460.                     
  461.                     relpath = path[len(self.bundlepath):]
  462.                     self.message('Stripping %s' % relpath, 2)
  463.                     (inf, outf) = os.popen4('%s -S "%s"' % (STRIP_EXEC, path))
  464.                     output = outf.read().strip()
  465.                     if output:
  466.                         self.message('Problem stripping %s:' % relpath, 3)
  467.                         self.message(output, 3)
  468.                         continue
  469.                 
  470.  
  471.             walk(self.bundlepath)
  472.  
  473.     
  474.     def findDependencies(self):
  475.         self.message('Finding module dependencies', 1)
  476.         import modulefinder
  477.         mf = modulefinder.ModuleFinder(excludes = self.excludeModules)
  478.         if USE_ZIPIMPORT:
  479.             mf.import_hook('zlib')
  480.         
  481.         site = mf.add_module('site')
  482.         site.__code__ = self._getSiteCode()
  483.         mf.scan_code(site.__code__, site)
  484.         mf.import_hook('warnings')
  485.         includeModules = self.includeModules[:]
  486.         for name in self.includePackages:
  487.             includeModules.extend(findPackageContents(name).keys())
  488.         
  489.         for name in includeModules:
  490.             
  491.             try:
  492.                 mf.import_hook(name)
  493.             continue
  494.             except ImportError:
  495.                 self.missingModules.append(name)
  496.                 continue
  497.             
  498.  
  499.         
  500.         mf.run_script(self.mainprogram)
  501.         modules = mf.modules.items()
  502.         modules.sort()
  503.         for name, mod in modules:
  504.             path = mod.__file__
  505.             if path and self.semi_standalone:
  506.                 if path.startswith(LIB) and not path.startswith(SITE_PACKAGES):
  507.                     continue
  508.                 
  509.             
  510.             if path and mod.__code__ is None:
  511.                 filename = os.path.basename(path)
  512.                 pathitems = name.split('.')[:-1] + [
  513.                     filename]
  514.                 dstpath = pathjoin(*pathitems)
  515.                 if USE_ZIPIMPORT:
  516.                     if name != 'zlib':
  517.                         dstpath = pathjoin('ExtensionModules', dstpath)
  518.                     
  519.                     source = EXT_LOADER % {
  520.                         'name': name,
  521.                         'filename': dstpath }
  522.                     code = compile(source, '<dynloader for %s>' % name, 'exec')
  523.                     mod.__code__ = code
  524.                 
  525.                 self.files.append((path, pathjoin('Contents', 'Resources', dstpath)))
  526.             
  527.             if mod.__code__ is not None:
  528.                 ispkg = mod.__path__ is not None
  529.                 if not USE_ZIPIMPORT or name != 'site':
  530.                     self.pymodules.append((name, mod.__code__, ispkg))
  531.                 
  532.             name != 'site'
  533.         
  534.         if hasattr(mf, 'any_missing_maybe'):
  535.             (missing, maybe) = mf.any_missing_maybe()
  536.         else:
  537.             missing = mf.any_missing()
  538.             maybe = []
  539.         self.missingModules.extend(missing)
  540.         self.maybeMissingModules.extend(maybe)
  541.  
  542.     
  543.     def reportMissing(self):
  544.         missing = _[1]
  545.         missing.sort()
  546.         maybe.sort()
  547.  
  548.     
  549.     def report(self):
  550.         import pprint
  551.         pprint.pprint(self.__dict__)
  552.         if self.standalone or self.semi_standalone:
  553.             self.reportMissing()
  554.         
  555.  
  556.  
  557. SUFFIXES = [ _suf for _suf, _mode, _tp in imp.get_suffixes() ]
  558. identifierRE = re.compile('[_a-zA-z][_a-zA-Z0-9]*$')
  559.  
  560. def findPackageContents(name, searchpath = None):
  561.     head = name.split('.')[-1]
  562.     if identifierRE.match(head) is None:
  563.         return { }
  564.     
  565.     
  566.     try:
  567.         (ext, mode, tp) = (fp, path)
  568.     except ImportError:
  569.         return { }
  570.  
  571.     modules = {
  572.         name: None }
  573.     if tp == imp.PKG_DIRECTORY and path:
  574.         files = os.listdir(path)
  575.         for sub in files:
  576.             (sub, ext) = os.path.splitext(sub)
  577.             fullname = name + '.' + sub
  578.             if sub != '__init__' and fullname not in modules:
  579.                 modules.update(findPackageContents(fullname, [
  580.                     path]))
  581.                 continue
  582.         
  583.     
  584.     return modules
  585.  
  586.  
  587. def writePyc(code, path):
  588.     f = open(path, 'wb')
  589.     f.write(MAGIC)
  590.     f.write('\x00' * 4)
  591.     marshal.dump(code, f)
  592.     f.close()
  593.  
  594.  
  595. def copy(src, dst, mkdirs = 0):
  596.     '''Copy a file or a directory.'''
  597.     if mkdirs:
  598.         makedirs(os.path.dirname(dst))
  599.     
  600.     if os.path.isdir(src):
  601.         shutil.copytree(src, dst, symlinks = 1)
  602.     else:
  603.         shutil.copy2(src, dst)
  604.  
  605.  
  606. def copytodir(src, dstdir):
  607.     '''Copy a file or a directory to an existing directory.'''
  608.     dst = pathjoin(dstdir, os.path.basename(src))
  609.     copy(src, dst)
  610.  
  611.  
  612. def makedirs(dir):
  613.     """Make all directories leading up to 'dir' including the leaf
  614.     directory. Don't moan if any path element already exists."""
  615.     
  616.     try:
  617.         os.makedirs(dir)
  618.     except OSError:
  619.         why = None
  620.         if why.errno != errno.EEXIST:
  621.             raise 
  622.         
  623.     except:
  624.         why.errno != errno.EEXIST
  625.  
  626.  
  627.  
  628. def symlink(src, dst, mkdirs = 0):
  629.     '''Copy a file or a directory.'''
  630.     if not os.path.exists(src):
  631.         raise IOError, "No such file or directory: '%s'" % src
  632.     
  633.     if mkdirs:
  634.         makedirs(os.path.dirname(dst))
  635.     
  636.     os.symlink(os.path.abspath(src), dst)
  637.  
  638.  
  639. def pathjoin(*args):
  640.     '''Safe wrapper for os.path.join: asserts that all but the first
  641.     argument are relative paths.'''
  642.     for seg in args[1:]:
  643.         pass
  644.     
  645.     return os.path.join(*args)
  646.  
  647. cmdline_doc = 'Usage:\n  python bundlebuilder.py [options] command\n  python mybuildscript.py [options] command\n\nCommands:\n  build      build the application\n  report     print a report\n\nOptions:\n  -b, --builddir=DIR     the build directory; defaults to "build"\n  -n, --name=NAME        application name\n  -r, --resource=FILE    extra file or folder to be copied to Resources\n  -f, --file=SRC:DST     extra file or folder to be copied into the bundle;\n                         DST must be a path relative to the bundle root\n  -e, --executable=FILE  the executable to be used\n  -m, --mainprogram=FILE the Python main program\n  -a, --argv             add a wrapper main program to create sys.argv\n  -p, --plist=FILE       .plist file (default: generate one)\n      --nib=NAME         main nib name\n  -c, --creator=CCCC     4-char creator code (default: \'????\')\n      --iconfile=FILE    filename of the icon (an .icns file) to be used\n                         as the Finder icon\n      --bundle-id=ID     the CFBundleIdentifier, in reverse-dns format\n                         (eg. org.python.BuildApplet; this is used for\n                         the preferences file name)\n  -l, --link             symlink files/folder instead of copying them\n      --link-exec        symlink the executable instead of copying it\n      --standalone       build a standalone application, which is fully\n                         independent of a Python installation\n      --semi-standalone  build a standalone application, which depends on\n                         an installed Python, yet includes all third-party\n                         modules.\n      --python=FILE      Python to use in #! line in stead of current Python\n      --lib=FILE         shared library or framework to be copied into\n                         the bundle\n  -x, --exclude=MODULE   exclude module (with --(semi-)standalone)\n  -i, --include=MODULE   include module (with --(semi-)standalone)\n      --package=PACKAGE  include a whole package (with --(semi-)standalone)\n      --strip            strip binaries (remove debug info)\n  -v, --verbose          increase verbosity level\n  -q, --quiet            decrease verbosity level\n  -h, --help             print this message\n'
  648.  
  649. def usage(msg = None):
  650.     if msg:
  651.         print msg
  652.     
  653.     print cmdline_doc
  654.     sys.exit(1)
  655.  
  656.  
  657. def main(builder = None):
  658.     if builder is None:
  659.         builder = AppBuilder(verbosity = 1)
  660.     
  661.     shortopts = 'b:n:r:f:e:m:c:p:lx:i:hvqa'
  662.     longopts = ('builddir=', 'name=', 'resource=', 'file=', 'executable=', 'mainprogram=', 'creator=', 'nib=', 'plist=', 'link', 'link-exec', 'help', 'verbose', 'quiet', 'argv', 'standalone', 'exclude=', 'include=', 'package=', 'strip', 'iconfile=', 'lib=', 'python=', 'semi-standalone', 'bundle-id=', 'destroot=')
  663.     
  664.     try:
  665.         (options, args) = getopt.getopt(sys.argv[1:], shortopts, longopts)
  666.     except getopt.error:
  667.         usage()
  668.  
  669.     for opt, arg in options:
  670.         if opt in ('-b', '--builddir'):
  671.             builder.builddir = arg
  672.             continue
  673.         if opt in ('-n', '--name'):
  674.             builder.name = arg
  675.             continue
  676.         if opt in ('-r', '--resource'):
  677.             builder.resources.append(os.path.normpath(arg))
  678.             continue
  679.         if opt in ('-f', '--file'):
  680.             srcdst = arg.split(':')
  681.             if len(srcdst) != 2:
  682.                 usage('-f or --file argument must be two paths, separated by a colon')
  683.             
  684.             builder.files.append(srcdst)
  685.             continue
  686.         if opt in ('-e', '--executable'):
  687.             builder.executable = arg
  688.             continue
  689.         if opt in ('-m', '--mainprogram'):
  690.             builder.mainprogram = arg
  691.             continue
  692.         if opt in ('-a', '--argv'):
  693.             builder.argv_emulation = 1
  694.             continue
  695.         if opt in ('-c', '--creator'):
  696.             builder.creator = arg
  697.             continue
  698.         if opt == '--bundle-id':
  699.             builder.bundle_id = arg
  700.             continue
  701.         if opt == '--iconfile':
  702.             builder.iconfile = arg
  703.             continue
  704.         if opt == '--lib':
  705.             builder.libs.append(os.path.normpath(arg))
  706.             continue
  707.         if opt == '--nib':
  708.             builder.nibname = arg
  709.             continue
  710.         if opt in ('-p', '--plist'):
  711.             builder.plist = Plist.fromFile(arg)
  712.             continue
  713.         if opt in ('-l', '--link'):
  714.             builder.symlink = 1
  715.             continue
  716.         if opt == '--link-exec':
  717.             builder.symlink_exec = 1
  718.             continue
  719.         if opt in ('-h', '--help'):
  720.             usage()
  721.             continue
  722.         if opt in ('-v', '--verbose'):
  723.             builder.verbosity += 1
  724.             continue
  725.         builder
  726.         if opt in ('-q', '--quiet'):
  727.             builder.verbosity -= 1
  728.             continue
  729.         builder
  730.         if opt == '--standalone':
  731.             builder.standalone = 1
  732.             continue
  733.         if opt == '--semi-standalone':
  734.             builder.semi_standalone = 1
  735.             continue
  736.         if opt == '--python':
  737.             builder.python = arg
  738.             continue
  739.         if opt in ('-x', '--exclude'):
  740.             builder.excludeModules.append(arg)
  741.             continue
  742.         if opt in ('-i', '--include'):
  743.             builder.includeModules.append(arg)
  744.             continue
  745.         if opt == '--package':
  746.             builder.includePackages.append(arg)
  747.             continue
  748.         if opt == '--strip':
  749.             builder.strip = 1
  750.             continue
  751.         if opt == '--destroot':
  752.             builder.destroot = arg
  753.             continue
  754.     
  755.     if len(args) != 1:
  756.         usage("Must specify one command ('build', 'report' or 'help')")
  757.     
  758.     command = args[0]
  759.     if command == 'build':
  760.         builder.setup()
  761.         builder.build()
  762.     elif command == 'report':
  763.         builder.setup()
  764.         builder.report()
  765.     elif command == 'help':
  766.         usage()
  767.     else:
  768.         usage("Unknown command '%s'" % command)
  769.  
  770.  
  771. def buildapp(**kwargs):
  772.     builder = AppBuilder(**kwargs)
  773.     main(builder)
  774.  
  775.