pyodide/tools/buildpkg.py

183 lines
5.3 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import hashlib
import os
import shutil
import subprocess
import common
ROOTDIR = os.path.abspath(os.path.dirname(__file__))
def do_checksum(path, checksum):
CHUNK_SIZE = 1 << 16
h = hashlib.md5()
with open(path, 'rb') as fd:
while True:
chunk = fd.read(CHUNK_SIZE)
h.update(chunk)
if len(chunk) < CHUNK_SIZE:
break
if h.hexdigest() != checksum:
raise ValueError("Invalid checksum")
def download_and_extract(buildpath, packagedir, pkg, args):
tarballpath = os.path.join(
buildpath, os.path.basename(pkg['source']['url']))
if not os.path.isfile(tarballpath):
subprocess.run([
'wget', '-q', '-O', tarballpath, pkg['source']['url']
], check=True)
do_checksum(tarballpath, pkg['source']['md5'])
srcpath = os.path.join(buildpath, packagedir)
if not os.path.isdir(srcpath):
shutil.unpack_archive(tarballpath, buildpath)
return srcpath
def patch(path, srcpath, pkg, args):
if os.path.isfile(os.path.join(srcpath, '.patched')):
return
orig_dir = os.getcwd()
pkgdir = os.path.abspath(os.path.dirname(path))
os.chdir(srcpath)
try:
for patch in pkg['source'].get('patches', []):
subprocess.run([
'patch', '-p1', '--binary', '-i', os.path.join(pkgdir, patch)
], check=True)
finally:
os.chdir(orig_dir)
for src, dst in pkg['source'].get('extras', []):
shutil.copyfile(os.path.join(pkgdir, src), os.path.join(srcpath, dst))
with open(os.path.join(srcpath, '.patched'), 'wb') as fd:
fd.write(b'\n')
def get_libdir(srcpath, args):
slug = subprocess.check_output([
os.path.join(args.host[0], 'bin', 'python3'),
'-c',
'import sysconfig, sys; '
'print("{}-{}.{}".format('
'sysconfig.get_platform(), '
'sys.version_info[0], '
'sys.version_info[1]))']).decode('ascii').strip()
purelib = os.path.join(srcpath, 'build', 'lib')
if os.path.isdir(purelib):
libdir = purelib
else:
libdir = os.path.join(srcpath, 'build', 'lib.' + slug)
return libdir
def compile(path, srcpath, pkg, args):
if os.path.isfile(os.path.join(srcpath, '.built')):
return
orig_dir = os.getcwd()
os.chdir(srcpath)
try:
subprocess.run([
os.path.join(ROOTDIR, 'pywasmcross'),
'--cflags',
args.cflags[0] + ' ' +
pkg.get('build', {}).get('cflags', ''),
'--ldflags',
args.ldflags[0] + ' ' +
pkg.get('build', {}).get('ldflags', ''),
'--host', args.host[0],
'--target', args.target[0]], check=True)
finally:
os.chdir(orig_dir)
post = pkg.get('build', {}).get('post')
if post is not None:
libdir = get_libdir(srcpath, args)
pkgdir = os.path.abspath(os.path.dirname(path))
env = {
'BUILD': libdir,
'PKGDIR': pkgdir
}
subprocess.run([
'bash', '-c', post], env=env, check=True)
with open(os.path.join(srcpath, '.built'), 'wb') as fd:
fd.write(b'\n')
def package_files(buildpath, srcpath, pkg, args):
if os.path.isfile(os.path.join(buildpath, '.packaged')):
return
name = pkg['package']['name']
libdir = get_libdir(srcpath, args)
subprocess.run([
'python2',
os.path.join(os.environ['EMSCRIPTEN'], 'tools', 'file_packager.py'),
os.path.join(buildpath, name + '.data'),
'--preload',
'{}@/lib/python3.6/site-packages'.format(libdir),
'--js-output={}'.format(os.path.join(buildpath, name + '.js')),
'--export-name=pyodide',
'--exclude', '*.wasm.pre',
'--exclude', '__pycache__'], check=True)
subprocess.run([
'uglifyjs',
os.path.join(buildpath, name + '.js'),
'-o',
os.path.join(buildpath, name + '.js')], check=True)
with open(os.path.join(buildpath, '.packaged'), 'wb') as fd:
fd.write(b'\n')
def build_package(path, args):
pkg = common.parse_package(path)
packagedir = pkg['package']['name'] + '-' + pkg['package']['version']
dirpath = os.path.dirname(path)
orig_path = os.getcwd()
os.chdir(dirpath)
try:
buildpath = os.path.join(dirpath, 'build')
if not os.path.exists(buildpath):
os.makedirs(buildpath)
srcpath = download_and_extract(buildpath, packagedir, pkg, args)
patch(path, srcpath, pkg, args)
compile(path, srcpath, pkg, args)
package_files(buildpath, srcpath, pkg, args)
finally:
os.chdir(orig_path)
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('package', type=str, nargs=1)
parser.add_argument(
'--cflags', type=str, nargs=1, default=[''])
parser.add_argument(
'--ldflags', type=str, nargs=1, default=[common.DEFAULT_LD])
parser.add_argument(
'--host', type=str, nargs=1, default=[common.HOSTPYTHON])
parser.add_argument(
'--target', type=str, nargs=1, default=[common.TARGETPYTHON])
return parser.parse_args()
def main(args):
path = os.path.abspath(args.package[0])
build_package(path, args)
if __name__ == '__main__':
args = parse_args()
main(args)