deploy-ng: changes to make macOS app bundles work correctly

This commit is contained in:
rdb 2017-12-21 15:19:50 +01:00
parent c911ba8059
commit 2c35d7a8e9
5 changed files with 64 additions and 15 deletions

View File

@ -1619,7 +1619,7 @@ class Freezer:
return target
def generateRuntimeFromStub(self, target, stub_file, fields={}):
def generateRuntimeFromStub(self, target, stub_file, use_console, fields={}):
# We must have a __main__ module to make an exe file.
if not self.__writingModule('__main__'):
message = "Can't generate an executable without a __main__ module."
@ -1700,7 +1700,11 @@ class Freezer:
# trouble importing it as a builtin module. Synthesize a frozen
# module that loads it dynamically.
if '.' in moduleName:
code = 'import sys;del sys.modules["%s"];import sys,os,imp;imp.load_dynamic("%s",os.path.join(os.path.dirname(sys.executable), "%s%s"))' % (moduleName, moduleName, moduleName, modext)
if self.platform.startswith("macosx") and not use_console:
# We write the Frameworks directory to sys.path[0].
code = 'import sys;del sys.modules["%s"];import sys,os,imp;imp.load_dynamic("%s",os.path.join(sys.path[0], "%s%s"))' % (moduleName, moduleName, moduleName, modext)
else:
code = 'import sys;del sys.modules["%s"];import sys,os,imp;imp.load_dynamic("%s",os.path.join(os.path.dirname(sys.executable), "%s%s"))' % (moduleName, moduleName, moduleName, modext)
code = compile(code, moduleName, 'exec')
code = marshal.dumps(code)
moduleList.append((moduleName, len(pool), len(code)))

View File

@ -33,6 +33,12 @@ def egg2bam(_build_cmd, srcpath, dstpath):
])
return dstpath
macosx_binary_magics = (
b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE',
b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE',
b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\xCA',
b'\xCA\xFE\xBA\xBF', b'\xBF\xBA\xFE\xCA')
class build_apps(distutils.core.Command):
description = 'build Panda3D applications'
@ -121,15 +127,11 @@ class build_apps(distutils.core.Command):
num_gui_apps = len(self.gui_apps)
num_console_apps = len(self.console_apps)
if self.macos_main_app is None:
if not self.macos_main_app:
if num_gui_apps > 1:
assert True, 'macos_main_app must be defined if more than one gui_app is defined'
assert False, 'macos_main_app must be defined if more than one gui_app is defined'
elif num_gui_apps == 1:
self.macos_main_app = list(self.gui_apps.keys())[0]
elif num_console_apps > 1:
assert True, 'macos_main_app must be defined if more than one console_app is defined with no gui_apps'
elif num_console_apps == 1:
self.macos_main_app = list(self.console_apps.keys())[0]
assert os.path.exists(self.requirements_path), 'Requirements.txt path does not exist: {}'.format(self.requirements_path)
assert num_gui_apps + num_console_apps != 0, 'Must specify at least one app in either gui_apps or console_apps'
@ -209,10 +211,9 @@ class build_apps(distutils.core.Command):
if fname in self.gui_apps or self.console_apps:
dst = macosdir
elif src.endswith('.so') or src.endswith('dylib'):
elif os.path.isfile(src) and open(src, 'rb').read(4) in macosx_binary_magics:
dst = fwdir
else:
# TODO Cg still shows up in Resources when it should be in Frameworks
dst = resdir
shutil.move(src, dst)
@ -283,7 +284,7 @@ class build_apps(distutils.core.Command):
target_path = os.path.join(builddir, appname)
stub_name = 'deploy-stub'
if platform.startswith('win'):
if platform.startswith('win') or 'macosx' in platform:
if not use_console:
stub_name = 'deploy-stubw'
@ -298,7 +299,7 @@ class build_apps(distutils.core.Command):
stub_path = os.path.join(os.path.dirname(dtool_path), '..', 'bin', stub_name)
stub_file = open(stub_path, 'rb')
freezer.generateRuntimeFromStub(target_path, stub_file, {
freezer.generateRuntimeFromStub(target_path, stub_file, use_console, {
'prc_data': None,
'default_prc_dir': None,
'prc_dir_envvars': None,
@ -481,8 +482,8 @@ class build_apps(distutils.core.Command):
copy_file(src, dst)
# Bundle into an .app on macOS
#if 'macosx' in platform:
# self.bundle_macos_app(builddir)
if self.macos_main_app and 'macosx' in platform:
self.bundle_macos_app(builddir)
def add_dependency(self, name, target_dir, search_path, referenced_by):
""" Searches for the given DLL on the search path. If it exists,
@ -699,6 +700,10 @@ class build_apps(distutils.core.Command):
if cmd == 0x0c: # LC_LOAD_DYLIB
dylib = cmd_data[16:].decode('ascii').split('\x00', 1)[0]
if dylib.startswith('@loader_path/../Frameworks/'):
dylib = dylib.replace('@loader_path/../Frameworks/', '')
if dylib.startswith('@executable_path/../Frameworks/'):
dylib = dylib.replace('@executable_path/../Frameworks/', '')
if dylib.startswith('@loader_path/'):
dylib = dylib.replace('@loader_path/', '')
load_dylibs.append(dylib)

View File

@ -6617,6 +6617,12 @@ if PkgSkip("PYTHON") == 0:
TargetAdd('deploy-stubw.exe', input='deploy-stub.obj')
TargetAdd('deploy-stubw.exe', input='frozen_dllmain.obj')
TargetAdd('deploy-stubw.exe', opts=['SUBSYSTEM:WINDOWS', 'PYTHON', 'DEPLOYSTUB', 'NOICON'])
elif GetTarget() == 'darwin':
DefSymbol('MACOS_APP_BUNDLE', 'MACOS_APP_BUNDLE')
OPTS = OPTS + ['MACOS_APP_BUNDLE']
TargetAdd('deploy-stubw.obj', opts=OPTS, input='deploy-stub.c')
TargetAdd('deploy-stubw.exe', input='deploy-stubw.obj')
TargetAdd('deploy-stubw.exe', opts=['MACOS_APP_BUNDLE', 'PYTHON', 'DEPLOYSTUB', 'NOICON'])
#
# Generate the models directory and samples directory

View File

@ -349,7 +349,10 @@ class WheelFile(object):
# Fix things like @loader_path/../lib references
if sys.platform == "darwin":
deps_path = '@loader_path'
if source_path.endswith('deploy-stubw'):
deps_path = '@executable_path/../Frameworks'
else:
deps_path = '@loader_path'
loader_path = [os.path.dirname(source_path)]
for dep in deps:
if dep.endswith('/Python'):

View File

@ -237,6 +237,37 @@ int Py_FrozenMain(int argc, char **argv)
PySys_SetArgv(argc, argv);
#endif
#ifdef MACOS_APP_BUNDLE
// Add the Frameworks directory to sys.path.
char buffer[PATH_MAX];
uint32_t bufsize = sizeof(buffer);
if (_NSGetExecutablePath(buffer, &bufsize) != 0) {
assert(false);
return 1;
}
char resolved[PATH_MAX];
if (!realpath(buffer, resolved)) {
perror("realpath");
return 1;
}
const char *dir = dirname(resolved);
sprintf(buffer, "%s/../Frameworks", dir);
PyObject *sys_path = PyList_New(1);
#if PY_MAJOR_VERSION >= 3
PyList_SET_ITEM(sys_path, 0, PyUnicode_FromString(buffer));
#else
PyList_SET_ITEM(sys_path, 0, PyString_FromString(buffer));
#endif
PySys_SetObject("path", sys_path);
Py_DECREF(sys_path);
// Now, store a path to the Resources directory into the main_dir pointer,
// for ConfigPageManager to read out and assign to MAIN_DIR.
sprintf(buffer, "%s/../Resources", dir);
set_main_dir(buffer);
#endif
n = PyImport_ImportFrozenModule("__main__");
if (n == 0)
Py_FatalError("__main__ not frozen");