makewheel changes for macOS, manylinux1, Python 2.6

This commit is contained in:
rdb 2016-12-25 16:10:06 +01:00
parent 45356e85e1
commit 23a345437a

View File

@ -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)