Skip to content

Instantly share code, notes, and snippets.

Created December 9, 2023 17:43
Show Gist options
  • Save andy0130tw/1e560dfae7eedc11a1a371e1c56d71a9 to your computer and use it in GitHub Desktop.
Save andy0130tw/1e560dfae7eedc11a1a371e1c56d71a9 to your computer and use it in GitHub Desktop.
A slim re-implementation of in ghc-wasm-meta
import os
import subprocess
import shlex
import shutil
import sys
import json
if shutil.which('curl') is None:
print('This script requires curl')
if shutil.which('unzip') is None:
print('This script requires unzip')
def run_cmd(*args, shell=True, **kwargs):
print(args, kwargs)
return subprocess.check_output(
*args, **kwargs, shell=shell, universal_newlines=True
def jq_autogen(name):
# cmd = f'''jq -r '."{name}".url' {REPO}/autogen.json'''
# return run_cmd(cmd)
with open(f'{REPO}/autogen.json') as f:
data = json.load('autogen.json')
return data[name]['url']
def run_curl(url, dest, *, pipe_to=None):
cmd = f'curl -f -L --retry 5 {shlex.quote(url)}'
tail = '-o %s'
if pipe_to is not None:
tail = '| ' + pipe_to
run_cmd(cmd + ' ' + (tail % shlex.quote(dest)))
# FLAVOUR = os.environ.get('FLAVOUR', 'gmp')
PREFIX = os.environ.get('PREFIX', run_cmd('realpath ' + shlex.quote(os.path.expandvars('$HOME/.ghc-wasm'))))
REPO = os.environ['PWD']
# print(f'{FLAVOUR=}')
print('--- Setting up WASI SDK ---')
wasi_sdk_dest = f'{PREFIX}/wasi-sdk'
if os.path.exists(wasi_sdk_dest):
print(f'Found "{shlex.quote(wasi_sdk_dest)}", skip downloading...')
run_cmd(f'mkdir -p {shlex.quote(wasi_sdk_dest)}')
wasi_sdk_url = jq_autogen('wasi-sdk_darwin')
run_curl(wasi_sdk_url, wasi_sdk_dest, pipe_to='tar xz -C %s --strip-components=1')
print('--- Setting up ffi-wasm ---')
run_curl(jq_autogen('libffi-wasm'), f'')
run_cmd(['cp', '-a',
f'{PREFIX}/wasi-sdk/share/wasi-sysroot/include'], shell=False)
run_cmd(['cp', '-a',
f'{PREFIX}/wasi-sdk/share/wasi-sysroot/lib/wasm32-wasi'], shell=False)
# TODO: setup proot --
# but does it make sense in mac?
# print('--- Setting up proot ---')
# run_cmd(['mkdir', '-p', f'{PREFIX}/proot/bin'], shell=False)
# proot_url = jq_autogen('proot')
# run_curl(proot_url, f'{PREFIX}/proot/bin/proot', 'cat > %s')
# run_cmd(['chmod', '755', f'{PREFIX}/proot/bin/proot'], shell=False)
# print('--- Setting up wasm-run ---')
# run_cmd(['mkdir', '-p', f'{PREFIX}/wasm-run/bin'], shell=True)
# run_cmd(['cp', '-a',
# f'{REPO}/wasm-run/*.js',
# f'{REPO}/wasm-run/*.mjs',
# f'{REPO}/wasm-run/*.sh',
# f'{PREFIX}/wasm-run/bin'], shell=False)
# run_cmd(['cc',
# f'-DWASM_RUN="\"{PREFIX}/wasm-run/bin/wasm-run.js\""',
# '-Wall', '-O3',
# f"{REPO}/wasm-run/qemu-system-wasm32.c",
# '-o', f'{PREFIX}/wasm-run/bin/qemu-system-wasm32'], shell=True)
# run_cmd(f'echo "#!/bin/sh" >> {shlex.quote(f"{PREFIX}/wasm-run/bin/wasm-run")}')
# with open(f'{PREFIX}/wasm-run/bin/wasm-run', 'a') as f:
# proot_exec = f'{PREFIX}/proot/bin/proot'
# qemu_exec = f'{PREFIX}/wasm-run/bin/qemu-system-wasm32'
# rest = '${1+"$@"}'
# f.write(f'exec {shlex.quote(proot_exec)} -q {shlex.quote(qemu_exec)} \'{rest}\'')
# run_cmd(f'chmod 755 {shlex.quote(f"{PREFIX}/wasm-run/bin/wasm-run")}')
# run_cmd(['gsed',
# '-i', 's@wasmtime@$PREFIX/wasmtime/bin/wasmtime@',
# f'{PREFIX}wasm-run/bin/'], shell=False)
envs = {
'AR': shutil.which('llvm-ar'),
'CC': shutil.which('clang'),
'CC_FOR_BUILD': 'cc',
'CXX': shutil.which('clang++'),
'LD': shutil.which('wasm-ld'),
'NM': shutil.which('llvm-nm'),
'OBJCOPY': shutil.which('llvm-objcopy'),
'OBJDUMP': shutil.which('llvm-objdump'),
'RANLIB': shutil.which('llvm-ranlib'),
'SIZE': shutil.which('llvm-size'),
'STRINGS': shutil.which('llvm-strings'),
'STRIP': shutil.which('llvm-strip'),
# but why?
'LLC': shutil.which('false'),
'OPT': shutil.which('false'),
env_ok = True
print('--- Checking compilers ---')
for env, path in envs.items():
print(f'Checking {env:14s}: ', end='')
if path is None:
env_ok = False
if not env_ok:
print('Aborting due to missing executables.')
# should sync with
cc_opts = '-Wno-error=int-conversion -Wno-error=strict-prototypes -Wno-error=implicit-function-declaration -Oz -msimd128 -mnontrapping-fptoint -msign-ext -mbulk-memory -mmutable-globals -mmultivalue -mreference-types'
cxx_opts = '-Wno-error=int-conversion -Wno-error=strict-prototypes -Wno-error=implicit-function-declaration -fno-exceptions -Oz -msimd128 -mnontrapping-fptoint -msign-ext -mbulk-memory -mmutable-globals -mmultivalue -mreference-types'
ld_opts = '-Wl,--compress-relocations,--error-limit=0,--growable-table,--stack-first,--strip-debug'
'CONF_CC_OPTS_STAGE2': cc_opts,
'CONF_CXX_OPTS_STAGE2': cxx_opts,
'CONF_CC_OPTS_STAGE1': cc_opts,
'CONF_CXX_OPTS_STAGE1': cxx_opts,
# 'CROSS_EMULATOR': f'{PREFIX}/wasm-run/bin/',
print('--- Writing the env file ---')
# TODO: different implementation of gmp (the "flavour")
configure_args = '--host=aarch64-apple-darwin --target=wasm32-wasi --with-intree-gmp --with-system-libffi'
# FIXME: --prefix=?
with open(f'{PREFIX}/env', 'w') as envfile:
for env, value in envs.items():
envfile.write(f'export {env}={value}\n')
for env, value in _EXTRA_ENVS.items():
override = os.environ.get(env)
if override is not None:
value = override
envfile.write(f'export {env}={shlex.quote(value)}\n')
print(f'Source "{PREFIX}/env" and then configure ghc with:')
print(f' ./configure {configure_args}')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment