22import subprocess
33import shlex
44from shlex import quote
5- from shutil import which
5+ from shutil import Error , which
66import sys
77import json
88from collections import namedtuple
99from functools import partial
1010
11+ # This script is a manual translation of ghc-wasm-meta's setup.sh.
12+ # The snapshot of this script is based on is at (accessed around May 2025):
13+ # https://gitlab.haskell.org/haskell-wasm/ghc-wasm-meta/-/blob/fe5573f28327d12a1c47ec61d6bbe0cc9d7983dd/setup.sh
1114
15+ # TODO: sed
1216for cmd in ['curl' , 'unzip' , 'tar' , 'xz' ]:
1317 if which (cmd ) is None :
1418 print (f'This script requires { cmd } ' )
1721print_err = partial (print , file = sys .stderr )
1822HostVars = namedtuple ('HostVars' , 'HOST WASI_SDK WASI_SDK_JOB_NAME WASI_SDK_ARTIFACT_PATH WASMTIME NODEJS CABAL BINARYEN GHC FLAVOUR' )
1923
20- def run_cmd (arg , ** kwargs ):
21- is_shell = isinstance (arg , str )
24+ REPO = os .environ ['PWD' ]
25+
26+ def _log_cmd (is_shell , arg , ** kwargs ):
2227 if is_shell :
2328 print_err ('+' , arg , kwargs )
2429 else :
2530 print_err ('+' , shlex .join (arg ), kwargs )
31+
32+ def run_cmd (arg , ** kwargs ):
33+ is_shell = isinstance (arg , str )
34+ _log_cmd (arg , ** kwargs )
2635 return subprocess .check_output (
2736 arg , ** kwargs , shell = is_shell , universal_newlines = True
2837 ).strip ()
2938
39+ def run_cmd_and_get_exit_code (arg , ** kwargs ):
40+ is_shell = isinstance (arg , str )
41+ _log_cmd (is_shell , arg , ** kwargs )
42+ return subprocess .run (
43+ arg , ** kwargs , shell = is_shell , stdout = subprocess .DEVNULL , stderr = subprocess .DEVNULL
44+ ).returncode
45+
3046def jq_autogen (name ):
3147 # cmd = f'''jq -r '."{name}".url' {REPO}/autogen.json'''
3248 # return run_cmd(cmd)
3349 with open (f'{ REPO } /autogen.json' ) as f :
3450 data = json .load (f )
3551 return data [name ]['url' ]
3652
53+ # TODO: remove pipe_to to mandate all output to be saved
3754def run_curl (url , dest , * , pipe_to = None , ** kwargs ):
3855 cmd = f'curl -f -L --retry 5 { quote (url )} '
3956 tail = '-o %s'
4057 if pipe_to is not None :
4158 tail = '| ' + pipe_to
4259 return run_cmd (cmd + ' ' + (tail % quote (dest )), ** kwargs )
4360
61+ def curl_upstream_wasi_sdk_pipeline_id (upstreamWasiSdkPipelineId , targetJobName ):
62+ url = f'https://gitlab.haskell.org/api/v4/projects/3212/pipelines/{ upstreamWasiSdkPipelineId } /jobs?scope[]=success'
63+ output = run_cmd (f'curl { quote (url )} ' )
64+ jobs = json .loads (output )
65+ for job in jobs :
66+ if job ['name' ] == targetJobName :
67+ return job ['id' ]
68+ raise Error (f'Cannot find the job with name "{ targetJobName } " from the upstream WASI SDK pipeline.' )
69+
4470def path_is_fresh (s ):
4571 if os .path .exists (s ):
4672 print (f'Found "{ s } ", skip downloading...' )
@@ -52,7 +78,6 @@ def path_is_fresh(s):
5278ARCH = run_cmd ('uname -m' )
5379PREFIX = os .environ .get ('PREFIX' , run_cmd ('realpath ' + quote (os .path .expandvars ('$HOME/.ghc-wasm' ))))
5480WASI_SDK_ROOT = f'{ PREFIX } /wasi-sdk'
55- REPO = os .environ ['PWD' ]
5681
5782wasm_ghc_prefix = f'{ PREFIX } /wasm32-wasi-ghc'
5883cabal_prefix = f'{ PREFIX } /wasm32-wasi-cabal'
@@ -67,7 +92,7 @@ def host_specific():
6792 'x86_64-linux' ,
6893 'wasi-sdk' ,
6994 'x86_64-linux' ,
70- 'dist/wasi-sdk-25 .0-x86_64-linux.tar.gz' ,
95+ 'dist/wasi-sdk-26 .0-x86_64-linux.tar.gz' ,
7196 'wasmtime' ,
7297 'nodejs' ,
7398 'cabal' ,
@@ -83,7 +108,7 @@ def host_specific():
83108 'aarch64-linux' ,
84109 'wasi-sdk-aarch64-linux' ,
85110 'aarch64-linux' ,
86- 'dist/wasi-sdk-25 .0-aarch64-linux.tar.gz' ,
111+ 'dist/wasi-sdk-26 .0-aarch64-linux.tar.gz' ,
87112 'wasmtime_aarch64_linux' ,
88113 'nodejs_aarch64_linux' ,
89114 'cabal_aarch64_linux' ,
@@ -99,7 +124,7 @@ def host_specific():
99124 'aarch64-apple-darwin' ,
100125 'wasi-sdk-aarch64-darwin' ,
101126 'aarch64-darwin' ,
102- 'dist/wasi-sdk-25 .0-arm64-macos.tar.gz' ,
127+ 'dist/wasi-sdk-26 .0-arm64-macos.tar.gz' ,
103128 'wasmtime_aarch64_darwin' ,
104129 'nodejs_aarch64_darwin' ,
105130 'cabal_aarch64_darwin' ,
@@ -114,27 +139,40 @@ def host_specific():
114139 'x86_64-apple-darwin' ,
115140 'wasi-sdk-x86_64-darwin' ,
116141 'x86_64-darwin' ,
117- 'dist/wasi-sdk-25 .0-arm64-macos.tar.gz' ,
142+ 'dist/wasi-sdk-26 .0-arm64-macos.tar.gz' ,
118143 'wasmtime_x86_64_darwin' ,
119144 'nodejs_x86_64_darwin' ,
120145 'cabal_x86_64_darwin' ,
121146 'binaryen_x86_64_darwin' ,
122- 'wasm32-wasi-ghc-gmp-x86_64-darwin' ,
147+ 'wasm32-wasi-ghc-gmp-x86_64-darwin' , # no prebuilt ghc available
123148 'gmp' )
124149
125150 print (f'Host not supported: ({ OS } , { ARCH } , { flavour } )' )
126151 sys .exit (1 )
127152
128153
129154HOST_VARS = host_specific ()
155+ # unused; to be used in wasm-run's setup
156+ # BSD sed does not accept long options such as "--version".
157+ # SED_IS_GNU = run_cmd_and_get_exit_code('sed --version') == 0
130158GHC_TMP_DIR = f'{ REPO } /ghc.{ HOST_VARS .FLAVOUR } '
131159
160+ # TODO: workdir; currently it unzips everything to the project dir and also use them to
161+ # avoid redownloading, but it may cause problems on readonly filesystems.
162+ # Since we do not have popd
163+
164+ def determine_wasi_sdk_bindist ():
165+ UPSTREAM_WASI_SDK_PIPELINE_ID = os .environ .get ('UPSTREAM_WASI_SDK_PIPELINE_ID' , None )
166+ if UPSTREAM_WASI_SDK_PIPELINE_ID is not None :
167+ jobId = curl_upstream_wasi_sdk_pipeline_id (UPSTREAM_WASI_SDK_PIPELINE_ID , HOST_VARS .WASI_SDK_JOB_NAME )
168+ return f'https://gitlab.haskell.org/haskell-wasm/wasi-sdk/-/jobs/{ jobId } /artifacts/raw/{ HOST_VARS .WASI_SDK_ARTIFACT_PATH } '
169+ else :
170+ return jq_autogen (HOST_VARS .WASI_SDK )
132171
133172def setup_wasi_sdk ():
134173 print ('--- Setting up WASI SDK ---' )
135174 if path_is_fresh (WASI_SDK_ROOT ):
136- # TODO: support specifying UPSTREAM_WASI_SDK_JOB_ID
137- wasi_sdk_bindist = jq_autogen (HOST_VARS .WASI_SDK )
175+ wasi_sdk_bindist = determine_wasi_sdk_bindist ()
138176 print (f'Installing wasi-sdk from { wasi_sdk_bindist } ' )
139177 run_cmd (['mkdir' , '-p' , WASI_SDK_ROOT ])
140178 run_curl (wasi_sdk_bindist , WASI_SDK_ROOT , pipe_to = 'tar xz -C %s --no-same-owner --strip-components=1' )
@@ -159,10 +197,7 @@ def setup_wasi_sdk():
159197 run_cmd (['cp' , 'binaryen/bin/wasm-opt' , f'{ WASI_SDK_ROOT } /bin' ])
160198
161199# utilities are NOT included, please install them by yourself:
162- # nodejs, wabt, wasmtime
163-
164- def setup_proot ():
165- pass
200+ # nodejs, playwright, wabt, wasmtime
166201
167202def setup_wasm_run ():
168203 pass
@@ -172,8 +207,8 @@ def write_to_github_script():
172207 pass
173208
174209# should sync with setup.sh
175- cc_opts = '-Wno-error=int-conversion -O3 -msimd128 -mnontrapping-fptoint -msign-ext -mbulk-memory -mmutable-globals -mmultivalue -mreference-types '
176- cxx_opts = '-fno-exceptions -Wno-error=int-conversion -O3 -msimd128 -mnontrapping-fptoint -msign-ext -mbulk-memory -mmutable-globals -mmultivalue -mreference-types '
210+ cc_opts = '-Wno-error=int-conversion -O3 -mcpu=lime1 -mreference-types -msimd128 -mtail-call '
211+ cxx_opts = '-fno-exceptions -Wno-error=int-conversion -O3 -mcpu=lime1 -mreference-types -msimd128 -mtail-call '
177212ld_opts = '-Wl,--error-limit=0,--keep-section=ghc_wasm_jsffi,--keep-section=target_features,--stack-first,--strip-debug '
178213
179214_EXTRA_ENVS = {
@@ -185,6 +220,7 @@ def write_to_github_script():
185220 'CONF_GCC_LINKER_OPTS_STAGE1' : ld_opts ,
186221 # 'CONFIGURE_ARGS': "",
187222 # 'CROSS_EMULATOR': f'{PREFIX}/wasm-run/bin/wasm-run.mjs',
223+ # 'NODE_PATH': f'{PREFIX}/nodejs/lib/node_modules'
188224}
189225
190226# --prefix is defined part of the configure command
@@ -270,8 +306,9 @@ def install_ghc():
270306 run_cmd (
271307 f'. { quote (envpath )} && truncate -s0 ghc.log && ' +
272308 f'./configure { configure_args } --prefix={ quote (wasm_ghc_prefix )} | tee ghc.log && ' +
273- f'exec make install | tee ghc.log' ,
309+ f'RelocatableBuild=YES exec make install | tee ghc.log' ,
274310 cwd = GHC_TMP_DIR )
311+ # TODO: wasm32-wasi-ghc-pkg recache
275312
276313def setup_cabal ():
277314 print ('--- Downloading cabal ---' )
@@ -293,6 +330,7 @@ def setup_cabal():
293330
294331 with open (wasm_cabal , 'w' ) as f :
295332 f .write ('#!/bin/sh\n ' )
333+ # echo 'PREFIX=$(realpath "$(dirname "$0")"/../..)'
296334
297335 wasm_ghc = f'{ wasm_ghc_prefix } /bin/wasm32-wasi-ghc'
298336 wasm_hcpkg = f'{ wasm_ghc_prefix } /bin/wasm32-wasi-ghc-pkg'
0 commit comments