|
|
@@ -216,6 +216,7 @@ class build_apps(setuptools.Command):
|
|
|
def initialize_options(self):
|
|
|
self.build_base = os.path.join(os.getcwd(), 'build')
|
|
|
self.application_id = None
|
|
|
+ self.android_abis = None
|
|
|
self.android_debuggable = False
|
|
|
self.android_version_code = 1
|
|
|
self.android_min_sdk_version = 21
|
|
|
@@ -418,6 +419,18 @@ class build_apps(setuptools.Command):
|
|
|
tmp.update(self.package_data_dirs)
|
|
|
self.package_data_dirs = tmp
|
|
|
|
|
|
+ # Default to all supported ABIs (for the given Android version).
|
|
|
+ if self.android_max_sdk_version and self.android_max_sdk_version < 21:
|
|
|
+ if self.android_abis:
|
|
|
+ for abi in self.android_abis:
|
|
|
+ assert abi in ('mips64', 'x86_64', 'arm64-v8a'), \
|
|
|
+ f'{abi} was not a valid Android ABI before Android 21!'
|
|
|
+ else:
|
|
|
+ self.android_abis = ['armeabi-v7a', 'x86']
|
|
|
+
|
|
|
+ elif not self.android_abis:
|
|
|
+ self.android_abis = ['arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86']
|
|
|
+
|
|
|
self.icon_objects = {}
|
|
|
for app, iconpaths in self.icons.items():
|
|
|
if not isinstance(iconpaths, list) and not isinstance(iconpaths, tuple):
|
|
|
@@ -434,7 +447,68 @@ class build_apps(setuptools.Command):
|
|
|
self.announce('Building platforms: {0}'.format(','.join(self.platforms)), distutils.log.INFO)
|
|
|
|
|
|
for platform in self.platforms:
|
|
|
- self.build_runtimes(platform, True)
|
|
|
+ # Create the build directory, or ensure it is empty.
|
|
|
+ build_dir = os.path.join(self.build_base, platform)
|
|
|
+
|
|
|
+ if os.path.exists(build_dir):
|
|
|
+ for entry in os.listdir(build_dir):
|
|
|
+ path = os.path.join(build_dir, entry)
|
|
|
+ if os.path.islink(path) or os.path.isfile(path):
|
|
|
+ os.unlink(path)
|
|
|
+ else:
|
|
|
+ shutil.rmtree(path)
|
|
|
+ else:
|
|
|
+ os.makedirs(build_dir)
|
|
|
+
|
|
|
+ if platform == 'android':
|
|
|
+ # Make a multi-arch build for Android.
|
|
|
+ data_dir = os.path.join(build_dir, 'assets')
|
|
|
+ os.makedirs(data_dir, exist_ok=True)
|
|
|
+
|
|
|
+ for abi in self.android_abis:
|
|
|
+ lib_dir = os.path.join(build_dir, 'lib', abi)
|
|
|
+ os.makedirs(lib_dir, exist_ok=True)
|
|
|
+
|
|
|
+ suffix = None
|
|
|
+ if abi == 'arm64-v8a':
|
|
|
+ suffix = '_arm64'
|
|
|
+ elif abi == 'armeabi-v7a':
|
|
|
+ suffix = '_armv7a'
|
|
|
+ elif abi == 'armeabi':
|
|
|
+ suffix = '_arm'
|
|
|
+ else: # e.g. x86, x86_64, mips, mips64
|
|
|
+ suffix = '_' + abi.replace('-', '_')
|
|
|
+
|
|
|
+ self.build_binaries(lib_dir, platform + suffix)
|
|
|
+
|
|
|
+ # Write out the icons to the res directory.
|
|
|
+ for appname, icon in self.icon_objects.items():
|
|
|
+ if appname == '*' or (appname == self.macos_main_app and '*' not in self.icon_objects):
|
|
|
+ # Conventional name for icon on Android.
|
|
|
+ basename = 'ic_launcher.png'
|
|
|
+ else:
|
|
|
+ basename = f'ic_{appname}.png'
|
|
|
+
|
|
|
+ res_dir = os.path.join(build_dir, 'res')
|
|
|
+ icon.writeSize(48, os.path.join(res_dir, 'mipmap-mdpi-v4', basename))
|
|
|
+ icon.writeSize(72, os.path.join(res_dir, 'mipmap-hdpi-v4', basename))
|
|
|
+ icon.writeSize(96, os.path.join(res_dir, 'mipmap-xhdpi-v4', basename))
|
|
|
+ icon.writeSize(144, os.path.join(res_dir, 'mipmap-xxhdpi-v4', basename))
|
|
|
+
|
|
|
+ if icon.getLargestSize() >= 192:
|
|
|
+ icon.writeSize(192, os.path.join(res_dir, 'mipmap-xxxhdpi-v4', basename))
|
|
|
+
|
|
|
+ self.build_data(data_dir, platform)
|
|
|
+
|
|
|
+ # Generate an AndroidManifest.xml
|
|
|
+ self.generate_android_manifest(os.path.join(build_dir, 'AndroidManifest.xml'))
|
|
|
+ else:
|
|
|
+ self.build_binaries(build_dir, platform)
|
|
|
+ self.build_data(build_dir, platform)
|
|
|
+
|
|
|
+ # Bundle into an .app on macOS
|
|
|
+ if self.macos_main_app and 'macosx' in platform:
|
|
|
+ self.bundle_macos_app(build_dir)
|
|
|
|
|
|
def download_wheels(self, platform):
|
|
|
""" Downloads wheels for the given platform using pip. This includes panda3d
|
|
|
@@ -637,15 +711,10 @@ class build_apps(setuptools.Command):
|
|
|
with open(path, 'wb') as fh:
|
|
|
tree.write(fh, encoding='utf-8', xml_declaration=True)
|
|
|
|
|
|
- def build_runtimes(self, platform, use_wheels):
|
|
|
- """ Builds the distributions for the given platform. """
|
|
|
-
|
|
|
- builddir = os.path.join(self.build_base, platform)
|
|
|
-
|
|
|
- if os.path.exists(builddir):
|
|
|
- shutil.rmtree(builddir)
|
|
|
- os.makedirs(builddir)
|
|
|
+ def build_binaries(self, binary_dir, platform):
|
|
|
+ """ Builds the binary data for the given platform. """
|
|
|
|
|
|
+ use_wheels = True
|
|
|
path = sys.path[:]
|
|
|
p3dwhl = None
|
|
|
wheelpaths = []
|
|
|
@@ -774,15 +843,14 @@ class build_apps(setuptools.Command):
|
|
|
prcexport = '\n'.join(prcexport)
|
|
|
if not self.embed_prc_data:
|
|
|
prcdir = self.default_prc_dir.replace('<auto>', '')
|
|
|
- prcdir = os.path.join(builddir, prcdir)
|
|
|
+ prcdir = os.path.join(binary_dir, prcdir)
|
|
|
os.makedirs(prcdir)
|
|
|
- with open (os.path.join(prcdir, '00-panda3d.prc'), 'w') as f:
|
|
|
+ with open(os.path.join(prcdir, '00-panda3d.prc'), 'w') as f:
|
|
|
f.write(prcexport)
|
|
|
|
|
|
# Create runtimes
|
|
|
freezer_extras = set()
|
|
|
freezer_modules = set()
|
|
|
- freezer_modpaths = set()
|
|
|
ext_suffixes = set()
|
|
|
|
|
|
def get_search_path_for(source_path):
|
|
|
@@ -815,7 +883,7 @@ class build_apps(setuptools.Command):
|
|
|
|
|
|
return search_path
|
|
|
|
|
|
- def create_runtime(appname, mainscript, target_dir, use_console):
|
|
|
+ def create_runtime(platform, appname, mainscript, use_console):
|
|
|
freezer = FreezeTool.Freezer(
|
|
|
platform=platform,
|
|
|
path=path,
|
|
|
@@ -872,7 +940,7 @@ class build_apps(setuptools.Command):
|
|
|
if not self.log_filename or '%' not in self.log_filename:
|
|
|
use_strftime = False
|
|
|
|
|
|
- target_path = os.path.join(target_dir, target_name)
|
|
|
+ target_path = os.path.join(binary_dir, target_name)
|
|
|
freezer.generateRuntimeFromStub(target_path, stub_file, use_console, {
|
|
|
'prc_data': prcexport if self.embed_prc_data else None,
|
|
|
'default_prc_dir': self.default_prc_dir,
|
|
|
@@ -892,53 +960,23 @@ class build_apps(setuptools.Command):
|
|
|
os.unlink(temp_file.name)
|
|
|
|
|
|
# Copy the dependencies.
|
|
|
- search_path = [target_dir]
|
|
|
+ search_path = [binary_dir]
|
|
|
if use_wheels:
|
|
|
search_path.append(os.path.join(p3dwhlfn, 'panda3d'))
|
|
|
search_path.append(os.path.join(p3dwhlfn, 'deploy_libs'))
|
|
|
- self.copy_dependencies(target_path, target_dir, search_path, stub_name)
|
|
|
+ self.copy_dependencies(target_path, binary_dir, search_path, stub_name)
|
|
|
|
|
|
freezer_extras.update(freezer.extras)
|
|
|
freezer_modules.update(freezer.getAllModuleNames())
|
|
|
- freezer_modpaths.update({
|
|
|
- mod[1].filename.to_os_specific()
|
|
|
- for mod in freezer.getModuleDefs() if mod[1].filename
|
|
|
- })
|
|
|
for suffix in freezer.moduleSuffixes:
|
|
|
if suffix[2] == imp.C_EXTENSION:
|
|
|
ext_suffixes.add(suffix[0])
|
|
|
|
|
|
- # Where should we copy the various file types to?
|
|
|
- lib_dir = builddir
|
|
|
- data_dir = builddir
|
|
|
-
|
|
|
- if platform.startswith('android'):
|
|
|
- data_dir = os.path.join(data_dir, 'assets')
|
|
|
- if platform == 'android_arm64':
|
|
|
- lib_dir = os.path.join(lib_dir, 'lib', 'arm64-v8a')
|
|
|
- elif platform == 'android_armv7a':
|
|
|
- lib_dir = os.path.join(lib_dir, 'lib', 'armeabi-v7a')
|
|
|
- elif platform == 'android_arm':
|
|
|
- lib_dir = os.path.join(lib_dir, 'lib', 'armeabi')
|
|
|
- elif platform == 'androidmips':
|
|
|
- lib_dir = os.path.join(lib_dir, 'lib', 'mips')
|
|
|
- elif platform == 'android_mips64':
|
|
|
- lib_dir = os.path.join(lib_dir, 'lib', 'mips64')
|
|
|
- elif platform == 'android_x86':
|
|
|
- lib_dir = os.path.join(lib_dir, 'lib', 'x86')
|
|
|
- elif platform == 'android_x86_64':
|
|
|
- lib_dir = os.path.join(lib_dir, 'lib', 'x86_64')
|
|
|
- else:
|
|
|
- self.announce('Unrecognized Android architecture {}'.format(platform.split('_', 1)[-1]), distutils.log.ERROR)
|
|
|
-
|
|
|
- os.makedirs(data_dir, exist_ok=True)
|
|
|
- os.makedirs(lib_dir, exist_ok=True)
|
|
|
-
|
|
|
for appname, scriptname in self.gui_apps.items():
|
|
|
- create_runtime(appname, scriptname, lib_dir, False)
|
|
|
+ create_runtime(platform, appname, scriptname, False)
|
|
|
|
|
|
for appname, scriptname in self.console_apps.items():
|
|
|
- create_runtime(appname, scriptname, lib_dir, True)
|
|
|
+ create_runtime(platform, appname, scriptname, True)
|
|
|
|
|
|
# Copy extension modules
|
|
|
whl_modules = []
|
|
|
@@ -974,7 +1012,7 @@ class build_apps(setuptools.Command):
|
|
|
plugname = lib.split('.', 1)[0]
|
|
|
if plugname in plugin_list:
|
|
|
source_path = os.path.join(p3dwhlfn, lib)
|
|
|
- target_path = os.path.join(lib_dir, os.path.basename(lib))
|
|
|
+ target_path = os.path.join(binary_dir, os.path.basename(lib))
|
|
|
search_path = [os.path.dirname(source_path)]
|
|
|
self.copy_with_dependencies(source_path, target_path, search_path)
|
|
|
|
|
|
@@ -1021,7 +1059,7 @@ class build_apps(setuptools.Command):
|
|
|
basename = 'libpy.' + basename
|
|
|
|
|
|
# If this is a dynamic library, search for dependencies.
|
|
|
- target_path = os.path.join(lib_dir, basename)
|
|
|
+ target_path = os.path.join(binary_dir, basename)
|
|
|
search_path = get_search_path_for(source_path)
|
|
|
self.copy_with_dependencies(source_path, target_path, search_path)
|
|
|
|
|
|
@@ -1032,19 +1070,19 @@ class build_apps(setuptools.Command):
|
|
|
|
|
|
if os.path.isdir(tcl_dir) and 'tkinter' in freezer_modules:
|
|
|
self.announce('Copying Tcl files', distutils.log.INFO)
|
|
|
- os.makedirs(os.path.join(data_dir, 'tcl'))
|
|
|
+ os.makedirs(os.path.join(binary_dir, 'tcl'))
|
|
|
|
|
|
for dir in os.listdir(tcl_dir):
|
|
|
sub_dir = os.path.join(tcl_dir, dir)
|
|
|
if os.path.isdir(sub_dir):
|
|
|
- target_dir = os.path.join(data_dir, 'tcl', dir)
|
|
|
+ target_dir = os.path.join(binary_dir, 'tcl', dir)
|
|
|
self.announce('copying {0} -> {1}'.format(sub_dir, target_dir))
|
|
|
shutil.copytree(sub_dir, target_dir)
|
|
|
|
|
|
# Copy classes.dex on Android
|
|
|
if use_wheels and platform.startswith('android'):
|
|
|
self.copy(os.path.join(p3dwhlfn, 'deploy_libs', 'classes.dex'),
|
|
|
- os.path.join(builddir, 'classes.dex'))
|
|
|
+ os.path.join(binary_dir, '..', '..', 'classes.dex'))
|
|
|
|
|
|
# Extract any other data files from dependency packages.
|
|
|
for module, datadesc in self.package_data_dirs.items():
|
|
|
@@ -1082,15 +1120,18 @@ class build_apps(setuptools.Command):
|
|
|
else:
|
|
|
self.copy(source_path, target_path)
|
|
|
|
|
|
+ def build_data(self, data_dir, platform):
|
|
|
+ """ Builds the data files for the given platform. """
|
|
|
+
|
|
|
# Copy Game Files
|
|
|
self.announce('Copying game files for platform: {}'.format(platform), distutils.log.INFO)
|
|
|
ignore_copy_list = [
|
|
|
'**/__pycache__/**',
|
|
|
'**/*.pyc',
|
|
|
+ '**/*.py',
|
|
|
'{}/**'.format(self.build_base),
|
|
|
]
|
|
|
ignore_copy_list += self.exclude_patterns
|
|
|
- ignore_copy_list += freezer_modpaths
|
|
|
ignore_copy_list += self.extra_prc_files
|
|
|
ignore_copy_list = [p3d.GlobPattern(p3d.Filename.from_os_specific(i).get_fullpath()) for i in ignore_copy_list]
|
|
|
|
|
|
@@ -1191,31 +1232,6 @@ class build_apps(setuptools.Command):
|
|
|
|
|
|
copy_file(src, dst)
|
|
|
|
|
|
- if 'android' in platform:
|
|
|
- # Generate an AndroidManifest.xml
|
|
|
- self.generate_android_manifest(os.path.join(builddir, 'AndroidManifest.xml'))
|
|
|
-
|
|
|
- # Write out the icons to the res directory.
|
|
|
- for appname, icon in self.icon_objects.items():
|
|
|
- if appname == '*' or (appname == self.macos_main_app and '*' not in self.icon_objects):
|
|
|
- # Conventional name for icon on Android.
|
|
|
- basename = 'ic_launcher.png'
|
|
|
- else:
|
|
|
- basename = f'ic_{appname}.png'
|
|
|
-
|
|
|
- res_dir = os.path.join(builddir, 'res')
|
|
|
- icon.writeSize(48, os.path.join(res_dir, 'mipmap-mdpi-v4', basename))
|
|
|
- icon.writeSize(72, os.path.join(res_dir, 'mipmap-hdpi-v4', basename))
|
|
|
- icon.writeSize(96, os.path.join(res_dir, 'mipmap-xhdpi-v4', basename))
|
|
|
- icon.writeSize(144, os.path.join(res_dir, 'mipmap-xxhdpi-v4', basename))
|
|
|
-
|
|
|
- if icon.getLargestSize() >= 192:
|
|
|
- icon.writeSize(192, os.path.join(res_dir, 'mipmap-xxxhdpi-v4', basename))
|
|
|
-
|
|
|
- # Bundle into an .app on macOS
|
|
|
- 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,
|
|
|
copies it to the target_dir. """
|
|
|
@@ -1533,13 +1549,7 @@ class bdist_apps(setuptools.Command):
|
|
|
'manylinux1_i686': ['gztar'],
|
|
|
'manylinux2010_x86_64': ['gztar'],
|
|
|
'manylinux2010_i686': ['gztar'],
|
|
|
- 'android_arm64': ['aab'],
|
|
|
- 'android_armv7a': ['aab'],
|
|
|
- 'android_arm': ['aab'],
|
|
|
- 'android_mips': ['aab'],
|
|
|
- 'android_mips64': ['aab'],
|
|
|
- 'android_x86': ['aab'],
|
|
|
- 'android_x86_64': ['aab'],
|
|
|
+ 'android': ['aab'],
|
|
|
# Everything else defaults to ['zip']
|
|
|
}
|
|
|
|