diff options
Diffstat (limited to 'Mac/Tools/bundlebuilder.py')
| -rwxr-xr-x | Mac/Tools/bundlebuilder.py | 934 | 
1 files changed, 0 insertions, 934 deletions
diff --git a/Mac/Tools/bundlebuilder.py b/Mac/Tools/bundlebuilder.py deleted file mode 100755 index f5679d3..0000000 --- a/Mac/Tools/bundlebuilder.py +++ /dev/null @@ -1,934 +0,0 @@ -#! /usr/bin/env python - -"""\ -bundlebuilder.py -- Tools to assemble MacOS X (application) bundles. - -This module contains two classes to build so called "bundles" for -MacOS X. BundleBuilder is a general tool, AppBuilder is a subclass -specialized in building application bundles. - -[Bundle|App]Builder objects are instantiated with a bunch of keyword -arguments, and have a build() method that will do all the work. See -the class doc strings for a description of the constructor arguments. - -The module contains a main program that can be used in two ways: - -  % python bundlebuilder.py [options] build -  % python buildapp.py [options] build - -Where "buildapp.py" is a user-supplied setup.py-like script following -this model: - -  from bundlebuilder import buildapp -  buildapp(<lots-of-keyword-args>) - -""" - - -__all__ = ["BundleBuilder", "BundleBuilderError", "AppBuilder", "buildapp"] - - -import sys -import os, errno, shutil -import imp, marshal -import re -from copy import deepcopy -import getopt -from plistlib import Plist -from types import FunctionType as function - -class BundleBuilderError(Exception): pass - - -class Defaults: - -    """Class attributes that don't start with an underscore and are -    not functions or classmethods are (deep)copied to self.__dict__. -    This allows for mutable default values. -    """ - -    def __init__(self, **kwargs): -        defaults = self._getDefaults() -        defaults.update(kwargs) -        self.__dict__.update(defaults) - -    def _getDefaults(cls): -        defaults = {} -        for base in cls.__bases__: -            if hasattr(base, "_getDefaults"): -                defaults.update(base._getDefaults()) -        for name, value in list(cls.__dict__.items()): -            if name[0] != "_" and not isinstance(value, -                    (function, classmethod)): -                defaults[name] = deepcopy(value) -        return defaults -    _getDefaults = classmethod(_getDefaults) - - -class BundleBuilder(Defaults): - -    """BundleBuilder is a barebones class for assembling bundles. It -    knows nothing about executables or icons, it only copies files -    and creates the PkgInfo and Info.plist files. -    """ - -    # (Note that Defaults.__init__ (deep)copies these values to -    # instance variables. Mutable defaults are therefore safe.) - -    # Name of the bundle, with or without extension. -    name = None - -    # The property list ("plist") -    plist = Plist(CFBundleDevelopmentRegion = "English", -                  CFBundleInfoDictionaryVersion = "6.0") - -    # The type of the bundle. -    type = "BNDL" -    # The creator code of the bundle. -    creator = None - -    # the CFBundleIdentifier (this is used for the preferences file name) -    bundle_id = None - -    # List of files that have to be copied to <bundle>/Contents/Resources. -    resources = [] - -    # List of (src, dest) tuples; dest should be a path relative to the bundle -    # (eg. "Contents/Resources/MyStuff/SomeFile.ext). -    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. -    builddir = "build" - -    # Make symlinks instead copying files. This is handy during debugging, but -    # makes the bundle non-distributable. -    symlink = 0 - -    # Verbosity level. -    verbosity = 1 - -    # Destination root directory -    destroot = "" - -    def setup(self): -        # XXX rethink self.name munging, this is brittle. -        self.name, ext = os.path.splitext(self.name) -        if not ext: -            ext = ".bundle" -        bundleextension = ext -        # misc (derived) attributes -        self.bundlepath = pathjoin(self.builddir, self.name + bundleextension) - -        plist = self.plist -        plist.CFBundleName = self.name -        plist.CFBundlePackageType = self.type -        if self.creator is None: -            if hasattr(plist, "CFBundleSignature"): -                self.creator = plist.CFBundleSignature -            else: -                self.creator = "????" -        plist.CFBundleSignature = self.creator -        if self.bundle_id: -            plist.CFBundleIdentifier = self.bundle_id -        elif not hasattr(plist, "CFBundleIdentifier"): -            plist.CFBundleIdentifier = self.name - -    def build(self): -        """Build the bundle.""" -        builddir = self.builddir -        if builddir and not os.path.exists(builddir): -            os.mkdir(builddir) -        self.message("Building %s" % repr(self.bundlepath), 1) -        if os.path.exists(self.bundlepath): -            shutil.rmtree(self.bundlepath) -        if os.path.exists(self.bundlepath + '~'): -            shutil.rmtree(self.bundlepath + '~') -        bp = self.bundlepath - -        # Create the app bundle in a temporary location and then -        # rename the completed bundle. This way the Finder will -        # never see an incomplete bundle (where it might pick up -        # and cache the wrong meta data) -        self.bundlepath = bp + '~' -        try: -            os.mkdir(self.bundlepath) -            self.preProcess() -            self._copyFiles() -            self._addMetaFiles() -            self.postProcess() -            os.rename(self.bundlepath, bp) -        finally: -            self.bundlepath = bp -        self.message("Done.", 1) - -    def preProcess(self): -        """Hook for subclasses.""" -        pass -    def postProcess(self): -        """Hook for subclasses.""" -        pass - -    def _addMetaFiles(self): -        contents = pathjoin(self.bundlepath, "Contents") -        makedirs(contents) -        # -        # Write Contents/PkgInfo -        assert len(self.type) == len(self.creator) == 4, \ -                "type and creator must be 4-byte strings." -        pkginfo = pathjoin(contents, "PkgInfo") -        f = open(pkginfo, "wb") -        f.write((self.type + self.creator).encode('latin1')) -        f.close() -        # -        # Write Contents/Info.plist -        infoplist = pathjoin(contents, "Info.plist") -        self.plist.write(infoplist) - -    def _copyFiles(self): -        files = self.files[:] -        for path in self.resources: -            files.append((path, pathjoin("Contents", "Resources", -                os.path.basename(path)))) -        for path in self.libs: -            files.append((path, pathjoin("Contents", "Frameworks", -                os.path.basename(path)))) -        if self.symlink: -            self.message("Making symbolic links", 1) -            msg = "Making symlink from" -        else: -            self.message("Copying files", 1) -            msg = "Copying" -        files.sort() -        for src, dst in files: -            if os.path.isdir(src): -                self.message("%s %s/ to %s/" % (msg, src, dst), 2) -            else: -                self.message("%s %s to %s" % (msg, src, dst), 2) -            dst = pathjoin(self.bundlepath, dst) -            if self.symlink: -                symlink(src, dst, mkdirs=1) -            else: -                copy(src, dst, mkdirs=1) - -    def message(self, msg, level=0): -        if level <= self.verbosity: -            indent = "" -            if level > 1: -                indent = (level - 1) * "  " -            sys.stderr.write(indent + msg + "\n") - -    def report(self): -        # XXX something decent -        pass - - -if __debug__: -    PYC_EXT = ".pyc" -else: -    PYC_EXT = ".pyo" - -MAGIC = imp.get_magic() -USE_ZIPIMPORT = "zipimport" in sys.builtin_module_names - -# For standalone apps, we have our own minimal site.py. We don't need -# all the cruft of the real site.py. -SITE_PY = """\ -import sys -if not %(semi_standalone)s: -    del sys.path[1:]  # sys.path[0] is Contents/Resources/ -""" - -if USE_ZIPIMPORT: -    ZIP_ARCHIVE = "Modules.zip" -    SITE_PY += "sys.path.append(sys.path[0] + '/%s')\n" % ZIP_ARCHIVE -    def getPycData(fullname, code, ispkg): -        if ispkg: -            fullname += ".__init__" -        path = fullname.replace(".", os.sep) + PYC_EXT -        return path, MAGIC + '\0\0\0\0' + marshal.dumps(code) - -# -# Extension modules can't be in the modules zip archive, so a placeholder -# is added instead, that loads the extension from a specified location. -# -EXT_LOADER = """\ -def __load(): -    import imp, sys, os -    for p in sys.path: -        path = os.path.join(p, "%(filename)s") -        if os.path.exists(path): -            break -    else: -        assert 0, "file not found: %(filename)s" -    mod = imp.load_dynamic("%(name)s", path) - -__load() -del __load -""" - -MAYMISS_MODULES = ['mac', 'nt', 'ntpath', 'dos', 'dospath', -    'win32api', 'ce', '_winreg', 'nturl2path', 'sitecustomize', -    'org.python.core', 'riscos', 'riscosenviron', 'riscospath' -] - -STRIP_EXEC = "/usr/bin/strip" - -# -# We're using a stock interpreter to run the app, yet we need -# a way to pass the Python main program to the interpreter. The -# bootstrapping script fires up the interpreter with the right -# arguments. os.execve() is used as OSX doesn't like us to -# start a real new process. Also, the executable name must match -# the CFBundleExecutable value in the Info.plist, so we lie -# deliberately with argv[0]. The actual Python executable is -# passed in an environment variable so we can "repair" -# sys.executable later. -# -BOOTSTRAP_SCRIPT = """\ -#!%(hashbang)s - -import sys, os -execdir = os.path.dirname(sys.argv[0]) -executable = os.path.join(execdir, "%(executable)s") -resdir = os.path.join(os.path.dirname(execdir), "Resources") -libdir = os.path.join(os.path.dirname(execdir), "Frameworks") -mainprogram = os.path.join(resdir, "%(mainprogram)s") - -sys.argv.insert(1, mainprogram) -if %(standalone)s or %(semi_standalone)s: -    os.environ["PYTHONPATH"] = resdir -    if %(standalone)s: -        os.environ["PYTHONHOME"] = resdir -else: -    pypath = os.getenv("PYTHONPATH", "") -    if pypath: -        pypath = ":" + pypath -    os.environ["PYTHONPATH"] = resdir + pypath -os.environ["PYTHONEXECUTABLE"] = executable -os.environ["DYLD_LIBRARY_PATH"] = libdir -os.environ["DYLD_FRAMEWORK_PATH"] = libdir -os.execve(executable, sys.argv, os.environ) -""" - - -# -# Optional wrapper that converts "dropped files" into sys.argv values. -# -ARGV_EMULATOR = """\ -import argvemulator, os - -argvemulator.ArgvCollector().mainloop() -execfile(os.path.join(os.path.split(__file__)[0], "%(realmainprogram)s")) -""" - -# -# When building a standalone app with Python.framework, we need to copy -# a subset from Python.framework to the bundle. The following list -# specifies exactly what items we'll copy. -# -PYTHONFRAMEWORKGOODIES = [ -    "Python",  # the Python core library -    "Resources/English.lproj", -    "Resources/Info.plist", -    "Resources/version.plist", -] - -def isFramework(): -    return sys.exec_prefix.find("Python.framework") > 0 - - -LIB = os.path.join(sys.prefix, "lib", "python" + sys.version[:3]) -SITE_PACKAGES = os.path.join(LIB, "site-packages") - - -class AppBuilder(BundleBuilder): - -    # Override type of the bundle. -    type = "APPL" - -    # platform, name of the subfolder of Contents that contains the executable. -    platform = "MacOS" - -    # A Python main program. If this argument is given, the main -    # executable in the bundle will be a small wrapper that invokes -    # the main program. (XXX Discuss why.) -    mainprogram = None - -    # The main executable. If a Python main program is specified -    # the executable will be copied to Resources and be invoked -    # by the wrapper program mentioned above. Otherwise it will -    # simply be used as the main executable. -    executable = None - -    # The name of the main nib, for Cocoa apps. *Must* be specified -    # when building a Cocoa app. -    nibname = None - -    # The name of the icon file to be copied to Resources and used for -    # the Finder icon. -    iconfile = None - -    # Symlink the executable instead of copying it. -    symlink_exec = 0 - -    # If True, build standalone app. -    standalone = 0 - -    # If True, build semi-standalone app (only includes third-party modules). -    semi_standalone = 0 - -    # If set, use this for #! lines in stead of sys.executable -    python = None - -    # If True, add a real main program that emulates sys.argv before calling -    # mainprogram -    argv_emulation = 0 - -    # The following attributes are only used when building a standalone app. - -    # Exclude these modules. -    excludeModules = [] - -    # Include these modules. -    includeModules = [] - -    # Include these packages. -    includePackages = [] - -    # Strip binaries from debug info. -    strip = 0 - -    # Found Python modules: [(name, codeobject, ispkg), ...] -    pymodules = [] - -    # Modules that modulefinder couldn't find: -    missingModules = [] -    maybeMissingModules = [] - -    def setup(self): -        if ((self.standalone or self.semi_standalone) -            and self.mainprogram is None): -            raise BundleBuilderError("must specify 'mainprogram' when " -                    "building a standalone application.") -        if self.mainprogram is None and self.executable is None: -            raise BundleBuilderError("must specify either or both of " -                    "'executable' and 'mainprogram'") - -        self.execdir = pathjoin("Contents", self.platform) - -        if self.name is not None: -            pass -        elif self.mainprogram is not None: -            self.name = os.path.splitext(os.path.basename(self.mainprogram))[0] -        elif executable is not None: -            self.name = os.path.splitext(os.path.basename(self.executable))[0] -        if self.name[-4:] != ".app": -            self.name += ".app" - -        if self.executable is None: -            if not self.standalone and not isFramework(): -                self.symlink_exec = 1 -            if self.python: -                self.executable = self.python -            else: -                self.executable = sys.executable - -        if self.nibname: -            self.plist.NSMainNibFile = self.nibname -            if not hasattr(self.plist, "NSPrincipalClass"): -                self.plist.NSPrincipalClass = "NSApplication" - -        if self.standalone and isFramework(): -            self.addPythonFramework() - -        BundleBuilder.setup(self) - -        self.plist.CFBundleExecutable = self.name - -        if self.standalone or self.semi_standalone: -            self.findDependencies() - -    def preProcess(self): -        resdir = "Contents/Resources" -        if self.executable is not None: -            if self.mainprogram is None: -                execname = self.name -            else: -                execname = os.path.basename(self.executable) -            execpath = pathjoin(self.execdir, execname) -            if not self.symlink_exec: -                self.files.append((self.destroot + self.executable, execpath)) -            self.execpath = execpath - -        if self.mainprogram is not None: -            mainprogram = os.path.basename(self.mainprogram) -            self.files.append((self.mainprogram, pathjoin(resdir, mainprogram))) -            if self.argv_emulation: -                # Change the main program, and create the helper main program (which -                # does argv collection and then calls the real main). -                # Also update the included modules (if we're creating a standalone -                # program) and the plist -                realmainprogram = mainprogram -                mainprogram = '__argvemulator_' + mainprogram -                resdirpath = pathjoin(self.bundlepath, resdir) -                mainprogrampath = pathjoin(resdirpath, mainprogram) -                makedirs(resdirpath) -                open(mainprogrampath, "w").write(ARGV_EMULATOR % locals()) -                if self.standalone or self.semi_standalone: -                    self.includeModules.append("argvemulator") -                    self.includeModules.append("os") -                if "CFBundleDocumentTypes" not in self.plist: -                    self.plist["CFBundleDocumentTypes"] = [ -                        { "CFBundleTypeOSTypes" : [ -                            "****", -                            "fold", -                            "disk"], -                          "CFBundleTypeRole": "Viewer"}] -            # Write bootstrap script -            executable = os.path.basename(self.executable) -            execdir = pathjoin(self.bundlepath, self.execdir) -            bootstrappath = pathjoin(execdir, self.name) -            makedirs(execdir) -            if self.standalone or self.semi_standalone: -                # XXX we're screwed when the end user has deleted -                # /usr/bin/python -                hashbang = "/usr/bin/python" -            elif self.python: -                hashbang = self.python -            else: -                hashbang = os.path.realpath(sys.executable) -            standalone = self.standalone -            semi_standalone = self.semi_standalone -            open(bootstrappath, "w").write(BOOTSTRAP_SCRIPT % locals()) -            os.chmod(bootstrappath, 0o775) - -        if self.iconfile is not None: -            iconbase = os.path.basename(self.iconfile) -            self.plist.CFBundleIconFile = iconbase -            self.files.append((self.iconfile, pathjoin(resdir, iconbase))) - -    def postProcess(self): -        if self.standalone or self.semi_standalone: -            self.addPythonModules() -        if self.strip and not self.symlink: -            self.stripBinaries() - -        if self.symlink_exec and self.executable: -            self.message("Symlinking executable %s to %s" % (self.executable, -                    self.execpath), 2) -            dst = pathjoin(self.bundlepath, self.execpath) -            makedirs(os.path.dirname(dst)) -            os.symlink(os.path.abspath(self.executable), dst) - -        if self.missingModules or self.maybeMissingModules: -            self.reportMissing() - -    def addPythonFramework(self): -        # If we're building a standalone app with Python.framework, -        # include a minimal subset of Python.framework, *unless* -        # Python.framework was specified manually in self.libs. -        for lib in self.libs: -            if os.path.basename(lib) == "Python.framework": -                # a Python.framework was specified as a library -                return - -        frameworkpath = sys.exec_prefix[:sys.exec_prefix.find( -            "Python.framework") + len("Python.framework")] - -        version = sys.version[:3] -        frameworkpath = pathjoin(frameworkpath, "Versions", version) -        destbase = pathjoin("Contents", "Frameworks", "Python.framework", -                            "Versions", version) -        for item in PYTHONFRAMEWORKGOODIES: -            src = pathjoin(frameworkpath, item) -            dst = pathjoin(destbase, item) -            self.files.append((src, dst)) - -    def _getSiteCode(self): -        return compile(SITE_PY % {"semi_standalone": self.semi_standalone}, -                     "<-bundlebuilder.py->", "exec") - -    def addPythonModules(self): -        self.message("Adding Python modules", 1) - -        if USE_ZIPIMPORT: -            # Create a zip file containing all modules as pyc. -            import zipfile -            relpath = pathjoin("Contents", "Resources", ZIP_ARCHIVE) -            abspath = pathjoin(self.bundlepath, relpath) -            zf = zipfile.ZipFile(abspath, "w", zipfile.ZIP_DEFLATED) -            for name, code, ispkg in self.pymodules: -                self.message("Adding Python module %s" % name, 2) -                path, pyc = getPycData(name, code, ispkg) -                zf.writestr(path, pyc) -            zf.close() -            # add site.pyc -            sitepath = pathjoin(self.bundlepath, "Contents", "Resources", -                    "site" + PYC_EXT) -            writePyc(self._getSiteCode(), sitepath) -        else: -            # Create individual .pyc files. -            for name, code, ispkg in self.pymodules: -                if ispkg: -                    name += ".__init__" -                path = name.split(".") -                path = pathjoin("Contents", "Resources", *path) + PYC_EXT - -                if ispkg: -                    self.message("Adding Python package %s" % path, 2) -                else: -                    self.message("Adding Python module %s" % path, 2) - -                abspath = pathjoin(self.bundlepath, path) -                makedirs(os.path.dirname(abspath)) -                writePyc(code, abspath) - -    def stripBinaries(self): -        if not os.path.exists(STRIP_EXEC): -            self.message("Error: can't strip binaries: no strip program at " -                "%s" % STRIP_EXEC, 0) -        else: -            import stat -            self.message("Stripping binaries", 1) -            def walk(top): -                for name in os.listdir(top): -                    path = pathjoin(top, name) -                    if os.path.islink(path): -                        continue -                    if os.path.isdir(path): -                        walk(path) -                    else: -                        mod = os.stat(path)[stat.ST_MODE] -                        if not (mod & 0o100): -                            continue -                        relpath = path[len(self.bundlepath):] -                        self.message("Stripping %s" % relpath, 2) -                        inf, outf = os.popen4("%s -S \"%s\"" % -                                              (STRIP_EXEC, path)) -                        output = outf.read().strip() -                        if output: -                            # usually not a real problem, like when we're -                            # trying to strip a script -                            self.message("Problem stripping %s:" % relpath, 3) -                            self.message(output, 3) -            walk(self.bundlepath) - -    def findDependencies(self): -        self.message("Finding module dependencies", 1) -        import modulefinder -        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 -        site = mf.add_module("site") -        site.__code__ = self._getSiteCode() -        mf.scan_code(site.__code__, site) - -        # warnings.py gets imported implicitly from C -        mf.import_hook("warnings") - -        includeModules = self.includeModules[:] -        for name in self.includePackages: -            includeModules.extend(list(findPackageContents(name).keys())) -        for name in includeModules: -            try: -                mf.import_hook(name) -            except ImportError: -                self.missingModules.append(name) - -        mf.run_script(self.mainprogram) -        modules = list(mf.modules.items()) -        modules.sort() -        for name, mod in modules: -            path = mod.__file__ -            if path and self.semi_standalone: -                # skip the standard library -                if path.startswith(LIB) and not path.startswith(SITE_PACKAGES): -                    continue -            if path and mod.__code__ is None: -                # C extension -                filename = os.path.basename(path) -                pathitems = name.split(".")[:-1] + [filename] -                dstpath = pathjoin(*pathitems) -                if USE_ZIPIMPORT: -                    if name != "zlib": -                        # neatly pack all extension modules in a subdirectory, -                        # except zlib, since it's necessary for bootstrapping. -                        dstpath = pathjoin("ExtensionModules", dstpath) -                    # Python modules are stored in a Zip archive, but put -                    # extensions in Contents/Resources/. Add a tiny "loader" -                    # program in the Zip archive. Due to Thomas Heller. -                    source = EXT_LOADER % {"name": name, "filename": dstpath} -                    code = compile(source, "<dynloader for %s>" % name, "exec") -                    mod.__code__ = code -                self.files.append((path, pathjoin("Contents", "Resources", dstpath))) -            if mod.__code__ is not None: -                ispkg = mod.__path__ is not None -                if not USE_ZIPIMPORT or name != "site": -                    # Our site.py is doing the bootstrapping, so we must -                    # include a real .pyc file if USE_ZIPIMPORT is True. -                    self.pymodules.append((name, mod.__code__, ispkg)) - -        if hasattr(mf, "any_missing_maybe"): -            missing, maybe = mf.any_missing_maybe() -        else: -            missing = mf.any_missing() -            maybe = [] -        self.missingModules.extend(missing) -        self.maybeMissingModules.extend(maybe) - -    def reportMissing(self): -        missing = [name for name in self.missingModules -                if name not in MAYMISS_MODULES] -        if self.maybeMissingModules: -            maybe = self.maybeMissingModules -        else: -            maybe = [name for name in missing if "." in name] -            missing = [name for name in missing if "." not in name] -        missing.sort() -        maybe.sort() -        if maybe: -            self.message("Warning: couldn't find the following submodules:", 1) -            self.message("    (Note that these could be false alarms -- " -                         "it's not always", 1) -            self.message("    possible to distinguish between \"from package " -                         "import submodule\" ", 1) -            self.message("    and \"from package import name\")", 1) -            for name in maybe: -                self.message("  ? " + name, 1) -        if missing: -            self.message("Warning: couldn't find the following modules:", 1) -            for name in missing: -                self.message("  ? " + name, 1) - -    def report(self): -        # XXX something decent -        import pprint -        pprint.pprint(self.__dict__) -        if self.standalone or self.semi_standalone: -            self.reportMissing() - -# -# Utilities. -# - -SUFFIXES = [_suf for _suf, _mode, _tp in imp.get_suffixes()] -identifierRE = re.compile(r"[_a-zA-z][_a-zA-Z0-9]*$") - -def findPackageContents(name, searchpath=None): -    head = name.split(".")[-1] -    if identifierRE.match(head) is None: -        return {} -    try: -        fp, path, (ext, mode, tp) = imp.find_module(head, searchpath) -    except ImportError: -        return {} -    modules = {name: None} -    if tp == imp.PKG_DIRECTORY and path: -        files = os.listdir(path) -        for sub in files: -            sub, ext = os.path.splitext(sub) -            fullname = name + "." + sub -            if sub != "__init__" and fullname not in modules: -                modules.update(findPackageContents(fullname, [path])) -    return modules - -def writePyc(code, path): -    f = open(path, "wb") -    f.write(MAGIC) -    f.write("\0" * 4)  # don't bother about a time stamp -    marshal.dump(code, f) -    f.close() - -def copy(src, dst, mkdirs=0): -    """Copy a file or a directory.""" -    if mkdirs: -        makedirs(os.path.dirname(dst)) -    if os.path.isdir(src): -        shutil.copytree(src, dst, symlinks=1) -    else: -        shutil.copy2(src, dst) - -def copytodir(src, dstdir): -    """Copy a file or a directory to an existing directory.""" -    dst = pathjoin(dstdir, os.path.basename(src)) -    copy(src, dst) - -def makedirs(dir): -    """Make all directories leading up to 'dir' including the leaf -    directory. Don't moan if any path element already exists.""" -    try: -        os.makedirs(dir) -    except OSError as why: -        if why.errno != errno.EEXIST: -            raise - -def symlink(src, dst, mkdirs=0): -    """Copy a file or a directory.""" -    if not os.path.exists(src): -        raise IOError("No such file or directory: '%s'" % src) -    if mkdirs: -        makedirs(os.path.dirname(dst)) -    os.symlink(os.path.abspath(src), dst) - -def pathjoin(*args): -    """Safe wrapper for os.path.join: asserts that all but the first -    argument are relative paths.""" -    for seg in args[1:]: -        assert seg[0] != "/" -    return os.path.join(*args) - - -cmdline_doc = """\ -Usage: -  python bundlebuilder.py [options] command -  python mybuildscript.py [options] command - -Commands: -  build      build the application -  report     print a report - -Options: -  -b, --builddir=DIR     the build directory; defaults to "build" -  -n, --name=NAME        application name -  -r, --resource=FILE    extra file or folder to be copied to Resources -  -f, --file=SRC:DST     extra file or folder to be copied into the bundle; -                         DST must be a path relative to the bundle root -  -e, --executable=FILE  the executable to be used -  -m, --mainprogram=FILE the Python main program -  -a, --argv             add a wrapper main program to create sys.argv -  -p, --plist=FILE       .plist file (default: generate one) -      --nib=NAME         main nib name -  -c, --creator=CCCC     4-char creator code (default: '????') -      --iconfile=FILE    filename of the icon (an .icns file) to be used -                         as the Finder icon -      --bundle-id=ID     the CFBundleIdentifier, in reverse-dns format -                         (eg. org.python.BuildApplet; this is used for -                         the preferences file name) -  -l, --link             symlink files/folder instead of copying them -      --link-exec        symlink the executable instead of copying it -      --standalone       build a standalone application, which is fully -                         independent of a Python installation -      --semi-standalone  build a standalone application, which depends on -                         an installed Python, yet includes all third-party -                         modules. -      --python=FILE      Python to use in #! line in stead of current Python -      --lib=FILE         shared library or framework to be copied into -                         the bundle -  -x, --exclude=MODULE   exclude module (with --(semi-)standalone) -  -i, --include=MODULE   include module (with --(semi-)standalone) -      --package=PACKAGE  include a whole package (with --(semi-)standalone) -      --strip            strip binaries (remove debug info) -  -v, --verbose          increase verbosity level -  -q, --quiet            decrease verbosity level -  -h, --help             print this message -""" - -def usage(msg=None): -    if msg: -        print(msg) -    print(cmdline_doc) -    sys.exit(1) - -def main(builder=None): -    if builder is None: -        builder = AppBuilder(verbosity=1) - -    shortopts = "b:n:r:f:e:m:c:p:lx:i:hvqa" -    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=") - -    try: -        options, args = getopt.getopt(sys.argv[1:], shortopts, longopts) -    except getopt.error: -        usage() - -    for opt, arg in options: -        if opt in ('-b', '--builddir'): -            builder.builddir = arg -        elif opt in ('-n', '--name'): -            builder.name = arg -        elif opt in ('-r', '--resource'): -            builder.resources.append(os.path.normpath(arg)) -        elif opt in ('-f', '--file'): -            srcdst = arg.split(':') -            if len(srcdst) != 2: -                usage("-f or --file argument must be two paths, " -                      "separated by a colon") -            builder.files.append(srcdst) -        elif opt in ('-e', '--executable'): -            builder.executable = arg -        elif opt in ('-m', '--mainprogram'): -            builder.mainprogram = arg -        elif opt in ('-a', '--argv'): -            builder.argv_emulation = 1 -        elif opt in ('-c', '--creator'): -            builder.creator = arg -        elif opt == '--bundle-id': -            builder.bundle_id = arg -        elif opt == '--iconfile': -            builder.iconfile = arg -        elif opt == "--lib": -            builder.libs.append(os.path.normpath(arg)) -        elif opt == "--nib": -            builder.nibname = arg -        elif opt in ('-p', '--plist'): -            builder.plist = Plist.fromFile(arg) -        elif opt in ('-l', '--link'): -            builder.symlink = 1 -        elif opt == '--link-exec': -            builder.symlink_exec = 1 -        elif opt in ('-h', '--help'): -            usage() -        elif opt in ('-v', '--verbose'): -            builder.verbosity += 1 -        elif opt in ('-q', '--quiet'): -            builder.verbosity -= 1 -        elif opt == '--standalone': -            builder.standalone = 1 -        elif opt == '--semi-standalone': -            builder.semi_standalone = 1 -        elif opt == '--python': -            builder.python = arg -        elif opt in ('-x', '--exclude'): -            builder.excludeModules.append(arg) -        elif opt in ('-i', '--include'): -            builder.includeModules.append(arg) -        elif opt == '--package': -            builder.includePackages.append(arg) -        elif opt == '--strip': -            builder.strip = 1 -        elif opt == '--destroot': -            builder.destroot = arg - -    if len(args) != 1: -        usage("Must specify one command ('build', 'report' or 'help')") -    command = args[0] - -    if command == "build": -        builder.setup() -        builder.build() -    elif command == "report": -        builder.setup() -        builder.report() -    elif command == "help": -        usage() -    else: -        usage("Unknown command '%s'" % command) - - -def buildapp(**kwargs): -    builder = AppBuilder(**kwargs) -    main(builder) - - -if __name__ == "__main__": -    main()  | 
