pyodide/tools/pywasmcross

165 lines
4.5 KiB
Plaintext
Raw Normal View History

2018-06-20 18:54:47 +00:00
#!/usr/bin/env python3
import argparse
import importlib
import json
import os
import re
import subprocess
import sys
ROOTDIR = os.path.abspath(os.path.dirname(__file__))
symlinks = set(['cc', 'c++', 'ld', 'ar', 'gcc'])
def collect_args(basename):
env = dict(os.environ)
path = env['PATH']
while ROOTDIR + ':' in path:
path = path.replace(ROOTDIR + ':', '')
env['PATH'] = path
with open('build.log', 'a') as fd:
json.dump([basename] + sys.argv[1:], fd)
fd.write('\n')
sys.exit(subprocess.run([basename] + sys.argv[1:],
env=env).returncode)
def make_symlinks(env):
exec_path = os.path.abspath(__file__)
for symlink in symlinks:
symlink_path = os.path.join(ROOTDIR, symlink)
if not os.path.exists(symlink_path):
os.symlink(exec_path, symlink_path)
if symlink == 'c++':
var = 'CXX'
else:
var = symlink.upper()
env[var] = symlink
def capture_compile(args):
env = dict(os.environ)
make_symlinks(env)
env['PATH'] = ROOTDIR + ':' + os.environ['PATH']
result = subprocess.run(
[os.path.join(args.host[0], 'bin', 'python3'),
'setup.py',
'install'], env=env)
if result.returncode != 0:
if os.path.exists('build.log'):
os.remove('build.log')
sys.exit(result.returncode)
def handle_command(line, args):
# This is a special case to skip the compilation tests in numpy
for arg in line:
if r'/file.c' in arg or '_configtest' in arg:
return
if arg == '-print-multiarch':
return
if line[0] == 'ar':
line[0] = 'emar'
elif line[0] == 'c++':
line[0] = 'em++'
else:
line[0] = 'emcc'
# distutils doesn't use the c++ compiler when compiling c++ <sigh>
for arg in line:
if arg.endswith('.cpp'):
line[0] = 'em++'
break
shared = '-shared' in line
new_args = [line[0]]
if shared:
new_args.extend(args.ldflags[0].split())
elif line[0] in ('emcc', 'em++'):
new_args.extend(args.cflags[0].split())
skip_next = False
for arg in line[1:]:
if skip_next:
skip_next = False
continue
if arg.startswith('-I'):
if (os.path.abspath(arg[2:]).startswith(args.host[0]) and
'site-packages' not in arg):
arg = arg.replace('-I' + args.host[0], '-I' + args.target[0])
if arg[2:].startswith('/usr'):
continue
if arg.startswith('-L/usr'):
continue
arg = re.sub(r'/python([0-9]\.[0-9]+)m', r'/python\1', arg)
if arg.endswith('.o'):
arg = arg[:-2] + '.bc'
if shared and arg.endswith('.so'):
arg = arg[:-3] + '.wasm'
output = arg
new_args.append(arg)
print(' '.join(new_args))
result = subprocess.run(new_args)
if result.returncode != 0:
sys.exit(result.returncode)
if shared:
renamed = output[:-5] + '.so'
for ext in importlib.machinery.EXTENSION_SUFFIXES:
if ext == '.so':
continue
if renamed.endswith(ext):
renamed = renamed[:-len(ext)] + '.so'
break
os.rename(output, renamed)
def replay_compile(args):
if os.path.isfile('build.log'):
with open('build.log', 'r') as fd:
for line in fd:
line = json.loads(line)
handle_command(line, args)
def clean_out_native_artifacts():
for root, dirs, files in os.walk('.'):
for file in files:
path = os.path.join(root, file)
basename, ext = os.path.splitext(file)
if ext in ('.o', '.so', '.a'):
os.remove(path)
def build_wrap(args):
if not os.path.isfile('build.log'):
capture_compile(args)
clean_out_native_artifacts()
replay_compile(args)
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('--cflags', type=str, nargs=1, default=[''])
parser.add_argument('--ldflags', type=str, nargs=1, default=[''])
parser.add_argument('--host', type=str, nargs=1)
parser.add_argument('--target', type=str, nargs=1)
args = parser.parse_args()
return args
if __name__ == '__main__':
basename = os.path.basename(sys.argv[0])
if basename in symlinks:
collect_args(basename)
else:
args = parse_args()
build_wrap(args)