Skip to content

Instantly share code, notes, and snippets.

@dholth
Last active June 12, 2016 04:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dholth/b9673950b20843afc91c26982788b1e4 to your computer and use it in GitHub Desktop.
Save dholth/b9673950b20843afc91c26982788b1e4 to your computer and use it in GitHub Desktop.
import wheel.metadata
def convert_requirements(requirements, extras):
"""
Convert requirements from a setup()-style dictionary to Requires-Dist
and Provides-Extra.
"""
extras[''] = requirements
for extra, depends in extras.items():
condition = ''
if extra and ':' in extra: # setuptools extra:condition syntax
extra, condition = extra.split(':', 1)
if extra:
yield ('Provides-Extra', extra)
if condition:
condition += " and "
condition += 'extra == %s' % repr(extra)
if condition:
condition = '; ' + condition
for new_req in sorted(wheel.metadata.convert_requirements(depends)):
yield ('Requires-Dist', new_req + condition)
# Example build requirements folling build-deps-pep
[build-system]
requires = ["cffi", "SCons>=2.5.0", "toml"]
[metadata]
name="pysdl2-cffi"
version="0.11.0"
packages=['sdl', '_sdl', '_sdl_image', '_sdl_mixer', '_sdl_ttf']
setup_requires=["cffi>=1.6.0"]
install_requires=["cffi>=1.6.0"]
cffi_modules=[
"_sdl/cdefs.py:ffi",
"_sdl_image/cdefs.py:ffi",
"_sdl_mixer/cdefs.py:ffi",
"_sdl_ttf/cdefs.py:ffi",
]
description="SDL2 wrapper with cffi"
# long_description=README + CHANGES
license="GPLv2+"
classifiers=[
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)"
]
keywords=["sdl", "cffi"]
author="Daniel Holth"
author_email="dholth@fastmail.fm"
url="https://bitbucket.org/dholth/pysdl2-cffi"
[metadata.extras_require]
build = ['pycparser', 'astor', 'cffi>=1.6.0']
doc = ['sphinx']
":sys_platform=='win32'" = ["sdl2_lib"]
# Generate automatic wrapper helpers.
# Shell version:
#
# (cd builder ; python dox.py ~/prog/SDL2-2.0.3/include/xml/)
# Build the API wrappers
# python -m builder.build_sdl
# python -m builder.build_ttf
# python -m builder.build_mixer
# python -m builder.build_image
# # Sphinx and doc extraction don't work in Python 3 yet.
# # echo "Sphinx"
# (cd docs; make clean; make html)
# sdist notes
# unversioned dist_name.egg-info (hyphen to underscore)
# PKG-INFO in root
import sys
from distutils import sysconfig, ccompiler
import bdist
# compiler = distutils.ccompiler.new_compiler()
# distutils.sysconfig.customize_compiler(compiler)
# (parse arguments into scons environment)
compiler = sysconfig.customize_compiler(ccompiler.new_compiler())
# the include dirs only come from build_ext's finalize_options
# (sysconfig.get_python_inc()) maybe also in cpython
# Python environment?
env = Environment(tools=['default', 'packaging'])
conf = Configure(env)
conf.env.Append(CPPPATH=[sysconfig.get_python_inc()])
conf.env.Append(LIBPATH=[sysconfig.get_config_var('LIBDIR')])
# LIBS = ['python' + sysconfig.get_config_var('VERSION')] # only on CPython; ask distutils
env = conf.Finish()
import contoml as toml
metadata = dict(toml.loads(open('pyproject.toml').read()).primitive)['metadata']
# actually it should be the dictionary interface
env.PACKAGE_METADATA = metadata
env['PACKAGE_METADATA'] = metadata
env['PACKAGE_NAME'] = metadata['name']
env['PACKAGE_VERSION'] = metadata['version']
from SCons.Script import Execute, Mkdir # for Execute and Mkdir
def normalize_package(name):
return name.replace('-', '_')
env['PACKAGE_NAME_SAFE'] = normalize_package(env['PACKAGE_NAME'])
# Development .egg-info has no version number
env.EGG_INFO_PATH = env['PACKAGE_NAME_SAFE'] + '.egg-info'
def egg_info_targets():
"""
Write the minimum .egg-info for pip. Full metadata will go into wheel's .dist-info
"""
return [env.fs.Dir(env.EGG_INFO_PATH).File(name)
for name in ['PKG-INFO', 'requires.txt']]
import setuptools.command.egg_info
class Command(object):
"""Mock object to allow setuptools to write files for us"""
def __init__(self, distribution):
self.distribution = distribution
def write_or_delete_file(self, basename, filename, data):
self.data = data
class Distribution(object):
def __init__(self, metadata):
self.__dict__ = metadata
command = Command(Distribution(env.PACKAGE_METADATA))
def egg_info_builder(target, source, env):
"""
Minimum egg_info. To be used only by pip to get dependencies.
"""
for dnode in target:
with open(dnode.get_path(), 'w') as f:
if dnode.name == 'PKG-INFO':
f.write("Metadata-Version: 1.1\n")
f.write("Name: %s\n" % env['PACKAGE_NAME'])
f.write("Version: %s\n" % env['PACKAGE_VERSION'])
elif dnode.name == "requires.txt":
setuptools.command.egg_info.write_requirements(command, dnode.name, 'spamalot')
f.write(command.data)
egg_info = env.Command(egg_info_targets(), 'pyproject.toml',
egg_info_builder)
env.Alias('egg_info', egg_info)
def metadata_builder(target, source, env):
metadata = env['PACKAGE_METADATA']
with open(target[0].get_path(), 'w') as f:
f.write("Metadata-Version: 2.0\n")
f.write("Name: %s\n" % metadata['name'])
f.write("Version: %s\n" % metadata['version'])
f.write("Sumary: %s\n" % metadata['description'])
f.write("Home-Page: %s\n" % metadata['url'])
f.write("Author: %s\n" % metadata['author'])
f.write("Author-email: %s\n" % metadata['author_email'])
f.write("License: %s\n" % metadata['license'])
f.write("Keywords: %s" % " ".join(metadata['keywords']))
f.write("Platform: %s\n" % metadata.get('platform', 'UNKNOWN'))
for classifier in metadata.get('classifiers', []):
f.write("Classifier: %s\n" % classifier)
for requirement in bdist.convert_requirements(metadata.get('install_requires', []),
metadata.get('extras_require', {})):
f.write("%s: %s\n" % requirement)
# XXX long description
metadata = env.Command('METADATA', 'pyproject.toml', metadata_builder)
pkg_info = env.Command('PKG-INFO', egg_info_targets()[0].get_path(),
Copy('$TARGET', '$SOURCE'))
# env.NoClean('important-file')
# env.Precious('foobar')
# egg_info = env.Command(Dir(env.EGG_INFO_PATH), 'pyproject.toml', build_egg_info)
# distutils.command.build_ext._get_c_extension_suffix()
# get_ext_filename()
# get_export_symbols()
def get_build_command(name):
return sys.executable + " -m builder.build_" + name
src_files = Glob('sdl/*.py') + Glob('_sdl*/*.py')
parts_targets = {'sdl': 'sdl/__init__.py',
'image': 'sdl/image.py',
'sdl_image': 'sdl/image.py',
'mixer': 'sdl/mixer.py',
'sdl_mixer': 'sdl/mixer.py',
'ttf': 'sdl/ttf.py',
'sdl_ttf': 'sdl/ttf.py',
}
sdl = env.Command('sdl/__init__.py',
Glob("builder/*.py") + Glob("_sdl/*.py") + Glob("_sdl/*.h"),
get_build_command("sdl"))
for part in ('image', 'mixer', 'ttf'):
env.Command(parts_targets[part],
sdl + Glob("builder/*.py") + Glob("_sdl_%s/*.py" % part) + Glob("_sdl_%s/*.h" % part),
get_build_command(part))
from distutils import dist
from distutils.command.build_ext import build_ext
from _sdl.cdefs import _extension_args
modules = []
for part in ('sdl', 'sdl_image', 'sdl_mixer', 'sdl_ttf'):
# XXX currently this also compiles the extension
env.Command('__%s.c' % part,
parts_targets[part],
sys.executable + " -m _%s.cdefs" % part)
ext = build_ext(dist.Distribution(dict(name='__%s' % part)))
FRAMEWORKS = []
LIBS = []
if sys.platform == 'darwin':
FRAMEWORKS = Split("SDL2 SDL2_image SDL2_mixer SDL2_ttf") # OSX
else:
LIBS = _extension_args(part.rsplit('_')[-1])['libraries']
modules.append(env.LoadableModule(
ext.get_ext_filename('__%s' % part),
['__%s.c' % part],
LIBPREFIX = '',
FRAMEWORKS = FRAMEWORKS,
LIBS = LIBS
))
# noop pythonbuilder?
package = env.Package(
NAME=env['PACKAGE_NAME'],
VERSION=env['PACKAGE_VERSION'],
PACKAGETYPE='src_zip',
LICENSE='gpl',
source=FindSourceFiles() + [pkg_info]
)
def wheelmeta_builder(target, source, env):
with open(target[0].get_path(), 'w') as f:
f.write("""Wheel-Version: 1.0
Generator: SConstruct (0.0.1)
Root-Is-Purelib: false
Tag: cp27-none-linux_x86_64.whl
""")
env.Command('WHEEL', 'pyproject.toml', wheelmeta_builder)
env['WHEEL_PATH'] = 'build/wheel'
# avoid escaping problems with variable name followed by . :
env['WHEEL_DATA'] = '$WHEEL_PATH/$PACKAGE_NAME_SAFE-' + env['PACKAGE_VERSION'] + '.data'
env['DIST_INFO'] = '$WHEEL_PATH/$PACKAGE_NAME_SAFE-' + env['PACKAGE_VERSION'] + '.dist-info'
env.InstallAs('$DIST_INFO/METADATA', 'METADATA')
env.InstallAs('$DIST_INFO/WHEEL', 'WHEEL')
for module in modules:
# assume there's only one file per module...
env.InstallAs('$WHEEL_PATH/' + module[0].get_path(), [module])
py_source = Glob('_sdl*/*.py') + Glob('sdl/*.py')
py_dest = ['$WHEEL_PATH/' + s.get_path() for s in py_source]
env.InstallAs(py_dest, py_source)
whl = env.Zip(target = '$PACKAGE_NAME_SAFE-' + env['PACKAGE_VERSION'] + '-cp27-none-linux_x86_64.whl',
source = 'build/wheel/', ZIPROOT='build/wheel/')
import base64
def urlsafe_b64encode(data):
"""urlsafe_b64encode without padding"""
return base64.urlsafe_b64encode(data).rstrip(b'=')
def add_manifest(target, source, env):
"""
Add the wheel manifest.
"""
# os.path.relpath
import hashlib
import zipfile
archive = zipfile.ZipFile(source[0].get_path(), 'a')
lines = []
for f in archive.namelist():
print("File: %s" % f)
data = archive.read(f)
size = len(data)
digest = hashlib.sha256(data).digest()
digest = "sha256="+(urlsafe_b64encode(digest).decode('ascii'))
lines.append("%s,%s,%s" % (f.replace(',', ',,'), digest, size))
record_path = '%s-%s.dist-info/RECORD' % (env['PACKAGE_NAME_SAFE'], env['PACKAGE_VERSION'])
lines.append(record_path+',,')
RECORD = '\n'.join(lines)
archive.writestr(record_path, RECORD)
env.Command('dummy', whl, add_manifest)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment