def freeze(dist): """Freeze the given distribution data using py2app.""" includes = dist.includes excludes = dist.excludes options = dist.freezer_options # Merge in any includes/excludes given in freezer_options includes.append("esky") for inc in options.pop("includes",()): includes.append(inc) for exc in options.pop("excludes",()): excludes.append(exc) if "pypy" not in includes and "pypy" not in excludes: excludes.append("pypy") options["includes"] = includes options["excludes"] = excludes # py2app can't simultaneously freeze multiple scripts. # We do a separate freeze of each then merge them together. # The control info (name, icon, etc) for the app will be taken from # the first script in the list. exes = list(dist.get_executables()) if not exes: raise RuntimeError("no scripts specified") cmd = _make_py2app_cmd(dist.freeze_dir,dist.distribution,options,exes[0]) cmd.run() for exe in exes[1:]: tempdir = tempfile.mkdtemp() try: cmd = _make_py2app_cmd(tempdir,dist.distribution,options,exe) cmd.run() _merge_dir(tempdir,dist.freeze_dir) finally: shutil.rmtree(tempdir) # Remove any .pyc files with a corresponding .py file. # This helps avoid timestamp changes that might interfere with # the generation of useful patches between versions. appnm = dist.distribution.get_name()+".app" app_dir = os.path.join(dist.freeze_dir,appnm) resdir = os.path.join(app_dir,"Contents/Resources") for (dirnm,_,filenms) in os.walk(resdir): for nm in filenms: if nm.endswith(".pyc"): pyfile = os.path.join(dirnm,nm[:-1]) if os.path.exists(pyfile): os.unlink(pyfile+"c") if nm.endswith(".pyo"): pyfile = os.path.join(dirnm,nm[:-1]) if os.path.exists(pyfile): os.unlink(pyfile+"o") # Copy data files into the freeze dir for (src,dst) in dist.get_data_files(): dst = os.path.join(app_dir,"Contents","Resources",dst) dstdir = os.path.dirname(dst) if not os.path.isdir(dstdir): dist.mkpath(dstdir) dist.copy_file(src,dst) # Copy package data into site-packages.zip zfpath = os.path.join(cmd.lib_dir,get_zipfile(dist.distribution)) lib = zipfile.ZipFile(zfpath,"a") for (src,arcnm) in dist.get_package_data(): lib.write(src,arcnm) lib.close() # Create the bootstraping code, using custom code if specified. esky_name = dist.distribution.get_name() code_source = ["__esky_name__ = %r" % (esky_name,)] code_source.append(inspect.getsource(esky.bootstrap)) if not dist.compile_bootstrap_exes: code_source.append(_FAKE_ESKY_BOOTSTRAP_MODULE) code_source.append(_EXTRA_BOOTSTRAP_CODE) code_source.append(dist.get_bootstrap_code()) code_source.append("if not __rpython__:") code_source.append(" bootstrap()") code_source = "\n".join(code_source) def copy_to_bootstrap_env(src,dst=None): if dst is None: dst = src src = os.path.join(appnm,src) dist.copy_to_bootstrap_env(src,dst) if dist.compile_bootstrap_exes: for exe in dist.get_executables(normalise=False): if not exe.include_in_bootstrap_env: continue relpath = os.path.join("Contents","MacOS",exe.name) dist.compile_to_bootstrap_exe(exe,code_source,relpath) else: # Copy the core dependencies into the bootstrap env. pydir = "python%d.%d" % sys.version_info[:2] for nm in ("Python.framework","lib"+pydir+".dylib",): try: copy_to_bootstrap_env("Contents/Frameworks/" + nm) except Exception, e: # Distutils does its own crazy exception-raising which I # have no interest in examining right now. Eventually this # guard will be more conservative. pass copy_to_bootstrap_env("Contents/Resources/include") copy_to_bootstrap_env("Contents/Resources/lib/"+pydir+"/config") if "fcntl" not in sys.builtin_module_names: dynload = "Contents/Resources/lib/"+pydir+"/lib-dynload" for nm in os.listdir(os.path.join(app_dir,dynload)): if nm.startswith("fcntl"): copy_to_bootstrap_env(os.path.join(dynload,nm)) copy_to_bootstrap_env("Contents/Resources/__error__.sh") copy_to_bootstrap_env("Contents/Resources/__boot__.py") copy_to_bootstrap_env("Contents/Resources/site.py") # Copy the bootstrapping code into the __boot__.py file. bsdir = dist.bootstrap_dir with open(bsdir+"/Contents/Resources/__boot__.py","wt") as f: f.write(code_source) # Clear site.py in the bootstrap dir, it doesn't do anything useful. with open(bsdir+"/Contents/Resources/site.py","wt") as f: f.write("") # Copy the loader program for each script into the bootstrap env. copy_to_bootstrap_env("Contents/MacOS/python") for exe in dist.get_executables(normalise=False): if not exe.include_in_bootstrap_env: continue exepath = copy_to_bootstrap_env("Contents/MacOS/"+exe.name)
def freeze(dist): """Freeze the given distribution data using py2app.""" includes = dist.includes excludes = dist.excludes options = dist.freezer_options # Merge in any includes/excludes given in freezer_options includes.append("esky") for inc in options.pop("includes",()): includes.append(inc) for exc in options.pop("excludes",()): excludes.append(exc) if "pypy" not in includes and "pypy" not in excludes: excludes.append("pypy") options["includes"] = includes options["excludes"] = excludes # The control info (name, icon, etc) for the app will be taken from # the first script in the list. Subsequent scripts will be passed # as the extra_scripts argument. exes = list(dist.get_executables()) if not exes: raise RuntimeError("no scripts specified") cmd = _make_py2app_cmd(dist.freeze_dir,dist.distribution,options,exes) cmd.run() # Remove any .pyc files with a corresponding .py file. # This helps avoid timestamp changes that might interfere with # the generation of useful patches between versions. appnm = dist.distribution.get_name()+".app" app_dir = os.path.join(dist.freeze_dir,appnm) resdir = os.path.join(app_dir,"Contents/Resources") for (dirnm,_,filenms) in os.walk(resdir): for nm in filenms: if nm.endswith(".pyc"): pyfile = os.path.join(dirnm,nm[:-1]) if os.path.exists(pyfile): os.unlink(pyfile+"c") if nm.endswith(".pyo"): pyfile = os.path.join(dirnm,nm[:-1]) if os.path.exists(pyfile): os.unlink(pyfile+"o") # Copy data files into the freeze dir for (src,dst) in dist.get_data_files(): dst = os.path.join(app_dir,"Contents","Resources",dst) dstdir = os.path.dirname(dst) if not os.path.isdir(dstdir): dist.mkpath(dstdir) dist.copy_file(src,dst) # Copy package data into site-packages.zip zfpath = os.path.join(cmd.lib_dir,get_zipfile(dist.distribution)) lib = zipfile.ZipFile(zfpath,"a") for (src,arcnm) in dist.get_package_data(): lib.write(src,arcnm) lib.close() # Create the bootstraping code, using custom code if specified. esky_name = dist.distribution.get_name() code_source = ["__esky_name__ = %r" % (esky_name,)] code_source.append(inspect.getsource(esky.bootstrap)) if not dist.compile_bootstrap_exes: code_source.append(_FAKE_ESKY_BOOTSTRAP_MODULE) code_source.append(_EXTRA_BOOTSTRAP_CODE) code_source.append(dist.get_bootstrap_code()) code_source.append("if not __rpython__:") code_source.append(" bootstrap()") code_source = "\n".join(code_source) def copy_to_bootstrap_env(src,dst=None): if dst is None: dst = src src = os.path.join(appnm,src) dist.copy_to_bootstrap_env(src,dst) if dist.compile_bootstrap_exes: for exe in dist.get_executables(normalise=False): if not exe.include_in_bootstrap_env: continue relpath = os.path.join("Contents","MacOS",exe.name) dist.compile_to_bootstrap_exe(exe,code_source,relpath) else: # Copy the core dependencies into the bootstrap env. pydir = "python%d.%d" % sys.version_info[:2] for nm in ("Python.framework","lib"+pydir+".dylib",): try: copy_to_bootstrap_env("Contents/Frameworks/" + nm) except Exception, e: # Distutils does its own crazy exception-raising which I # have no interest in examining right now. Eventually this # guard will be more conservative. pass bsdir = dist.bootstrap_dir copy_to_bootstrap_env("Contents/Resources/include") if sys.version_info[:2] < (3, 3): copy_to_bootstrap_env("Contents/Resources/lib/"+pydir+"/config") else: copy_to_bootstrap_env("Contents/Resources/lib/"+pydir+"/config-%d.%dm" % sys.version_info[:2]) # copy across the zip file that we need to run the boostrap application # from the inner package. This only needs to contain # a mimimal set of files for the bootstrap # handle the bootstrap lib dependencies python_name = 'python%d%d' % sys.version_info[:2] zip_name = os.path.join('Contents', 'Resources', 'lib', '{}.zip'.format(python_name)) app_zfname = os.path.join(app_dir, zip_name) zfname = os.path.join(bsdir, zip_name) with tempfile.TemporaryDirectory() as tdir: esky.util.extract_zipfile(app_zfname, tdir) member_list = ['_weakrefset.pyc', 'abc.pyc', 'codecs.pyc', 'io.pyc'] for enc in os.listdir(os.path.join(tdir, 'encodings')): member_list.append(os.path.join('encodings',enc)) esky.util.create_zipfile(tdir, zfname, members=member_list) if sys.version_info[:2] < (3, 3): required_libs = ['fcntl'] else: required_libs = ['fcntl', 'zlib'] for req_lib in required_libs: if req_lib not in sys.builtin_module_names: dynload = "Contents/Resources/lib/"+pydir+"/lib-dynload" for nm in os.listdir(os.path.join(app_dir,dynload)): if nm.startswith(req_lib): copy_to_bootstrap_env(os.path.join(dynload,nm)) copy_to_bootstrap_env("Contents/Resources/__error__.sh") # Copy site.py/site.pyc into the boostrap env, then zero them out. if os.path.exists(os.path.join(app_dir, "Contents/Resources/site.py")): copy_to_bootstrap_env("Contents/Resources/site.py") with open(bsdir + "/Contents/Resources/site.py", "wt") as f: pass if os.path.exists(os.path.join(app_dir, "Contents/Resources/site.pyc")): copy_to_bootstrap_env("Contents/Resources/site.pyc") with open(bsdir + "/Contents/Resources/site.pyc", "wb") as f: f.write(esky.util.compile_to_bytecode("", "site.py")) if os.path.exists(os.path.join(app_dir, "Contents/Resources/site.pyo")): copy_to_bootstrap_env("Contents/Resources/site.pyo") with open(bsdir + "/Contents/Resources/site.pyo", "wb") as f: f.write(imp.get_magic() + struct.pack("<i", 0)) # Copy the bootstrapping code into the __boot__.py file. copy_to_bootstrap_env("Contents/Resources/__boot__.py") with open(bsdir+"/Contents/Resources/__boot__.py","wt") as f: f.write(code_source) # Copy the loader program for each script into the bootstrap env. copy_to_bootstrap_env("Contents/MacOS/python") for exe in dist.get_executables(normalise=False): if not exe.include_in_bootstrap_env: continue exepath = copy_to_bootstrap_env("Contents/MacOS/"+exe.name)