diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py index 81caf24701..dda4bc75e0 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -3,9 +3,11 @@ Generates a wheel (.whl) file from the output of makepanda. Since the wheel requires special linking, this will only work if compiled with the `--wheel` parameter. + +Please keep this file work with Panda3D 1.9 until that reaches EOL. """ from __future__ import print_function, unicode_literals -from distutils.util import get_platform as get_dist +from distutils.util import get_platform import json import sys @@ -16,20 +18,18 @@ import zipfile import hashlib import tempfile import subprocess -from sysconfig import get_config_var +from distutils.sysconfig import get_config_var from optparse import OptionParser from makepandacore import ColorText, LocateBinary, ParsePandaVersion, GetExtensionSuffix, SetVerbose, GetVerbose from base64 import urlsafe_b64encode -def get_platform(): - p = get_dist().replace('-', '_').replace('.', '_') - #if "linux" in p: - # print(ColorText("red", "WARNING:") + - # " Linux-specific wheel files are not supported." - # " We will generate this wheel as a generic package instead.") - # return "any" - return p +default_platform = get_platform() + +if default_platform.startswith("linux-"): + # Is this manylinux1? + if os.path.isfile("/lib/libc-2.5.so") and os.path.isdir("/opt/python"): + default_platform = platform.replace("linux", "manylinux1") def get_abi_tag(): @@ -71,7 +71,9 @@ def is_elf_file(path): def is_mach_o_file(path): base = os.path.basename(path) return os.path.isfile(path) and '.' not in base and \ - open(path, 'rb').read(4) == b'\xCA\xFE\xBA\xBE' + open(path, 'rb').read(4) in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\bCA', + b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE', + b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE') if sys.platform in ('win32', 'cygwin'): @@ -83,18 +85,17 @@ else: # Other global parameters -PY_VERSION = "cp{}{}".format(sys.version_info.major, sys.version_info.minor) +PY_VERSION = "cp{0}{1}".format(*sys.version_info) ABI_TAG = get_abi_tag() -PLATFORM_TAG = get_platform() EXCLUDE_EXT = [".pyc", ".pyo", ".N", ".prebuilt", ".xcf", ".plist", ".vcproj", ".sln"] # Plug-ins to install. -PLUGIN_LIBS = ["pandagl", "pandagles", "pandagles2", "p3ptloader", "p3assimp", "p3ffmpeg", "p3openal_audio", "p3fmod_audio"] +PLUGIN_LIBS = ["pandagl", "pandagles", "pandagles2", "pandadx9", "p3tinydisplay", "p3ptloader", "p3assimp", "p3ffmpeg", "p3openal_audio", "p3fmod_audio"] WHEEL_DATA = """Wheel-Version: 1.0 Generator: makepanda Root-Is-Purelib: false -Tag: {}-{}-{} +Tag: {0}-{1}-{2} """ METADATA = { @@ -234,7 +235,11 @@ def scan_dependencies(pathname): else: command = ['ldd', pathname] - output = subprocess.check_output(command, universal_newlines=True) + process = subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + raise subprocess.CalledProcessError(retcode, command[0], output=output) filenames = None if sys.platform in ("win32", "cygwin"): @@ -245,16 +250,21 @@ def scan_dependencies(pathname): if filenames is None: sys.exit("Unable to determine dependencies from %s" % (pathname)) + if sys.platform == "darwin" and len(filenames) > 0: + # Filter out the library ID. + if os.path.basename(filenames[0]).split('.', 1)[0] == os.path.basename(pathname).split('.', 1)[0]: + del filenames[0] + return filenames class WheelFile(object): - def __init__(self, name, version): + def __init__(self, name, version, platform): self.name = name self.version = version - wheel_name = "{}-{}-{}-{}-{}.whl".format( - name, version, PY_VERSION, ABI_TAG, PLATFORM_TAG) + wheel_name = "{0}-{1}-{2}-{3}-{4}.whl".format( + name, version, PY_VERSION, ABI_TAG, platform) print("Writing %s" % (wheel_name)) self.zip_file = zipfile.ZipFile(wheel_name, 'w', zipfile.ZIP_DEFLATED) @@ -279,6 +289,10 @@ class WheelFile(object): # Don't include the Python library. return + if sys.platform == "darwin" and dep.endswith(".so"): + # Temporary hack for 1.9, which had link deps on modules. + return + source_path = None if search_path is None: @@ -372,7 +386,7 @@ class WheelFile(object): # Save it in PEP-0376 format for writing out later. digest = str(urlsafe_b64encode(sha.digest())) digest = digest.rstrip('=') - self.records.append("{},sha256={},{}\n".format(target_path, digest, size)) + self.records.append("{0},sha256={1},{2}\n".format(target_path, digest, size)) if GetVerbose(): print("Adding %s from %s" % (target_path, source_path)) @@ -388,7 +402,7 @@ class WheelFile(object): sha.update(source_data.encode()) digest = str(urlsafe_b64encode(sha.digest())) digest = digest.rstrip('=') - self.records.append("{},sha256={},{}\n".format(target_path, digest, len(source_data))) + self.records.append("{0},sha256={1},{2}\n".format(target_path, digest, len(source_data))) if GetVerbose(): print("Adding %s from data" % target_path) @@ -409,18 +423,20 @@ class WheelFile(object): def close(self): # Write the RECORD file. - record_file = "{}-{}.dist-info/RECORD".format(self.name, self.version) + record_file = "{0}-{1}.dist-info/RECORD".format(self.name, self.version) self.records.append(record_file + ",,\n") self.zip_file.writestr(record_file, "".join(self.records)) self.zip_file.close() -def makewheel(version, output_dir): +def makewheel(version, output_dir, platform=default_platform): if sys.platform not in ("win32", "darwin") and not sys.platform.startswith("cygwin"): if not LocateBinary("patchelf"): raise Exception("patchelf is required when building a Linux wheel.") + platform = platform.replace('-', '_').replace('.', '_') + # Global filepaths panda3d_dir = join(output_dir, "panda3d") pandac_dir = join(output_dir, "pandac") @@ -438,8 +454,8 @@ def makewheel(version, output_dir): # Update relevant METADATA entries METADATA['version'] = version version_classifiers = [ - "Programming Language :: Python :: {}".format(*sys.version_info), - "Programming Language :: Python :: {}.{}".format(*sys.version_info), + "Programming Language :: Python :: {0}".format(*sys.version_info), + "Programming Language :: Python :: {0}.{1}".format(*sys.version_info), ] METADATA['classifiers'].extend(version_classifiers) @@ -454,14 +470,14 @@ def makewheel(version, output_dir): "Version: {version}\n" \ "Summary: {summary}\n" \ "License: {license}\n".format(**METADATA), - "Home-page: {}\n".format(homepage), - "Author: {}\n".format(author), - "Author-email: {}\n".format(email), - "Platform: {}\n".format(PLATFORM_TAG), - ] + ["Classifier: {}\n".format(c) for c in METADATA['classifiers']]) + "Home-page: {0}\n".format(homepage), + "Author: {0}\n".format(author), + "Author-email: {0}\n".format(email), + "Platform: {0}\n".format(platform), + ] + ["Classifier: {0}\n".format(c) for c in METADATA['classifiers']]) # Zip it up and name it the right thing - whl = WheelFile('panda3d', version) + whl = WheelFile('panda3d', version, platform) whl.lib_path = [libs_dir] # Add the trees with Python modules. @@ -479,11 +495,12 @@ def makewheel(version, output_dir): elif file.endswith(ext_suffix) or file.endswith('.py'): source_path = os.path.join(panda3d_dir, file) - if file.endswith('.pyd') and PLATFORM_TAG.startswith('cygwin'): + if file.endswith('.pyd') and platform.startswith('cygwin'): # Rename it to .dll for cygwin Python to be able to load it. target_path = 'panda3d/' + os.path.splitext(file)[0] + '.dll' else: target_path = 'panda3d/' + file + whl.write_file(target_path, source_path) # Add plug-ins. @@ -499,31 +516,8 @@ def makewheel(version, output_dir): if os.path.isfile(plugin_path): whl.write_file('panda3d/' + plugin_name, plugin_path) - # Add the pandac tree for backward compatibility. - for file in os.listdir(pandac_dir): - if file.endswith('.py'): - whl.write_file('pandac/' + file, os.path.join(pandac_dir, file)) - - # Add a panda3d-tools directory containing the executables. - entry_points = '[console_scripts]\n' - tools_init = '' - for file in os.listdir(bin_dir): - source_path = os.path.join(bin_dir, file) - - if is_executable(source_path): - # Put the .exe files inside the panda3d-tools directory. - whl.write_file('panda3d_tools/' + file, source_path) - - # Tell pip to create a wrapper script. - basename = os.path.splitext(file)[0] - funcname = basename.replace('-', '_') - entry_points += '{0} = panda3d_tools:{1}\n'.format(basename, funcname) - tools_init += '{0} = lambda: _exec_tool({1!r})\n'.format(funcname, file) - - whl.write_file_data('panda3d_tools/__init__.py', PANDA3D_TOOLS_INIT.format(tools_init)) - # Add the .data directory, containing additional files. - data_dir = 'panda3d-{}.data'.format(version) + data_dir = 'panda3d-{0}.data'.format(version) #whl.write_directory(data_dir + '/data/etc', etc_dir) #whl.write_directory(data_dir + '/data/models', models_dir) @@ -532,12 +526,40 @@ def makewheel(version, output_dir): whl.write_directory('panda3d/etc', etc_dir) whl.write_directory('panda3d/models', models_dir) + # Add the pandac tree for backward compatibility. + for file in os.listdir(pandac_dir): + if file.endswith('.py'): + whl.write_file('pandac/' + file, os.path.join(pandac_dir, file)) + + # Add a panda3d-tools directory containing the executables. + entry_points = '[console_scripts]\n' + entry_points += 'eggcacher = direct.directscripts.eggcacher:main\n' + entry_points += 'packpanda = direct.directscripts.packpanda:main\n' + tools_init = '' + for file in os.listdir(bin_dir): + basename = os.path.splitext(file)[0] + if basename in ('eggcacher', 'packpanda'): + continue + + source_path = os.path.join(bin_dir, file) + + if is_executable(source_path): + # Put the .exe files inside the panda3d-tools directory. + whl.write_file('panda3d_tools/' + file, source_path) + + # Tell pip to create a wrapper script. + funcname = basename.replace('-', '_') + entry_points += '{0} = panda3d_tools:{1}\n'.format(basename, funcname) + tools_init += '{0} = lambda: _exec_tool({1!r})\n'.format(funcname, file) + + whl.write_file_data('panda3d_tools/__init__.py', PANDA3D_TOOLS_INIT.format(tools_init)) + # Add the dist-info directory last. - info_dir = 'panda3d-{}.dist-info'.format(version) + info_dir = 'panda3d-{0}.dist-info'.format(version) whl.write_file_data(info_dir + '/entry_points.txt', entry_points) whl.write_file_data(info_dir + '/metadata.json', json.dumps(METADATA, indent=4, separators=(',', ': '))) whl.write_file_data(info_dir + '/METADATA', metadata) - whl.write_file_data(info_dir + '/WHEEL', WHEEL_DATA.format(PY_VERSION, ABI_TAG, PLATFORM_TAG)) + whl.write_file_data(info_dir + '/WHEEL', WHEEL_DATA.format(PY_VERSION, ABI_TAG, platform)) whl.write_file(info_dir + '/LICENSE.txt', license_src) whl.write_file(info_dir + '/README.md', readme_src) whl.write_file_data(info_dir + '/top_level.txt', 'direct\npanda3d\npandac\npanda3d_tools\n') @@ -552,6 +574,7 @@ if __name__ == "__main__": parser.add_option('', '--version', dest = 'version', help = 'Panda3D version number (default: %s)' % (version), default = version) parser.add_option('', '--outputdir', dest = 'outputdir', help = 'Makepanda\'s output directory (default: built)', default = 'built') parser.add_option('', '--verbose', dest = 'verbose', help = 'Enable verbose output', action = 'store_true', default = False) + parser.add_option('', '--platform', dest = 'platform', help = 'Override platform tag (default: %s)' % (default_platform), default = get_platform()) (options, args) = parser.parse_args() SetVerbose(options.verbose)