- Introduce build-sdk.sh script to orchestrate complete TypePHP WASI SDK building - Add numeric-smoke-test.cc and link-numeric-smoke-test.sh for GMP/MPFR/mpdecimal validation - Create high-precision.php example demonstrating bigInt/bigFloat/decimal usage - Add test-typephp-program.sh for WASI integration testing with wasmtime - Update ABI version from v2 to v4 and expand included system libraries - Document new SDK build process and updated extension list in WASI_BUILD.md - Include zlib, sodium, openssl, libxml, sqlite3, zippull/46/head
parent
3d36928e8b
commit
abbaa11c7c
8 changed files with 322 additions and 8 deletions
@ -0,0 +1,136 @@ |
||||
#!/usr/bin/env bash |
||||
|
||||
set -euo pipefail |
||||
|
||||
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) |
||||
compiler_dir=$(cd "${script_dir}/.." && pwd) |
||||
|
||||
usage() |
||||
{ |
||||
cat <<'EOF' |
||||
Usage: ./wasm/build-sdk.sh --prefix <wasm32-wasip2-sdk-dir> [options] |
||||
|
||||
Options: |
||||
--prefix <dir> Required complete SDK installation prefix |
||||
--php-source <dir> PHP source tree (default: projects/php-8.5.9) |
||||
--phpx-source <dir> PHPX source tree (default: PHPX_HOME or vendor package) |
||||
--build-dir <dir> Build root (default: /tmp/typephp-wasip2-sdk-build) |
||||
--jobs <number> Parallel build jobs (default: 8) |
||||
-h, --help Show this help |
||||
EOF |
||||
} |
||||
|
||||
prefix= |
||||
php_source=${compiler_dir}/projects/php-8.5.9 |
||||
phpx_source=${PHPX_HOME:-${compiler_dir}/vendor/swoole/phpx} |
||||
build_root=${TYPEPHP_WASM_SDK_BUILD_DIR:-/tmp/typephp-wasip2-sdk-build} |
||||
jobs=${TYPEPHP_WASM_JOBS:-8} |
||||
|
||||
while [[ $# -gt 0 ]]; do |
||||
case "$1" in |
||||
--prefix) |
||||
[[ $# -ge 2 ]] || { echo "--prefix requires a directory" >&2; exit 2; } |
||||
prefix=$2 |
||||
shift 2 |
||||
;; |
||||
--prefix=*) prefix=${1#*=}; shift ;; |
||||
--php-source) |
||||
[[ $# -ge 2 ]] || { echo "--php-source requires a directory" >&2; exit 2; } |
||||
php_source=$2 |
||||
shift 2 |
||||
;; |
||||
--php-source=*) php_source=${1#*=}; shift ;; |
||||
--phpx-source) |
||||
[[ $# -ge 2 ]] || { echo "--phpx-source requires a directory" >&2; exit 2; } |
||||
phpx_source=$2 |
||||
shift 2 |
||||
;; |
||||
--phpx-source=*) phpx_source=${1#*=}; shift ;; |
||||
--build-dir) |
||||
[[ $# -ge 2 ]] || { echo "--build-dir requires a directory" >&2; exit 2; } |
||||
build_root=$2 |
||||
shift 2 |
||||
;; |
||||
--build-dir=*) build_root=${1#*=}; shift ;; |
||||
--jobs|-j) |
||||
[[ $# -ge 2 ]] || { echo "$1 requires a number" >&2; exit 2; } |
||||
jobs=$2 |
||||
shift 2 |
||||
;; |
||||
--jobs=*|-j*) jobs=${1#*=}; jobs=${jobs#-j}; shift ;; |
||||
-h|--help) usage; exit 0 ;; |
||||
*) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;; |
||||
esac |
||||
done |
||||
|
||||
if [[ -z "${prefix}" ]]; then |
||||
echo "--prefix is required" >&2 |
||||
usage >&2 |
||||
exit 2 |
||||
fi |
||||
if [[ ! "${jobs}" =~ ^[1-9][0-9]*$ ]]; then |
||||
echo "Invalid --jobs value: ${jobs}" >&2 |
||||
exit 2 |
||||
fi |
||||
if [[ ! -x "${php_source}/wasm/build.sh" ]]; then |
||||
echo "PHP/WASI build entry was not found: ${php_source}/wasm/build.sh" >&2 |
||||
exit 1 |
||||
fi |
||||
if [[ ! -x "${phpx_source}/wasm/build.sh" ]]; then |
||||
echo "PHPX/WASI build entry was not found: ${phpx_source}/wasm/build.sh" >&2 |
||||
exit 1 |
||||
fi |
||||
|
||||
mkdir -p "${prefix}" "${build_root}" |
||||
prefix=$(cd "${prefix}" && pwd) |
||||
php_source=$(cd "${php_source}" && pwd) |
||||
phpx_source=$(cd "${phpx_source}" && pwd) |
||||
build_root=$(cd "${build_root}" && pwd) |
||||
|
||||
"${php_source}/wasm/build.sh" \ |
||||
--prefix "${prefix}" \ |
||||
--build-dir "${build_root}/php" \ |
||||
--jobs "${jobs}" |
||||
|
||||
"${phpx_source}/wasm/build.sh" \ |
||||
--prefix "${prefix}" \ |
||||
--build-dir "${build_root}/phpx" \ |
||||
--jobs "${jobs}" |
||||
|
||||
required_files=( |
||||
.typephp-wasi-php-abi |
||||
.typephp-wasi-numeric-abi |
||||
.typephp-wasi-runtime-abi |
||||
include/php/main/php.h |
||||
include/php/main/php_config.h |
||||
include/phpx/phpx.h |
||||
include/phpx/typephp_helper.h |
||||
include/zlib.h |
||||
include/zconf.h |
||||
include/sodium.h |
||||
include/openssl/evp.h |
||||
include/libxml2/libxml/parser.h |
||||
include/sqlite3.h |
||||
include/zip.h |
||||
include/bzlib.h |
||||
include/gmp.h |
||||
include/mpfr.h |
||||
include/mpdecimal.h |
||||
include/decimal.hh |
||||
lib/libphp.a |
||||
lib/libphpx.a |
||||
lib/libgmp.a |
||||
lib/libgmpxx.a |
||||
lib/libmpfr.a |
||||
lib/libmpdec.a |
||||
lib/libmpdec++.a |
||||
) |
||||
for file in "${required_files[@]}"; do |
||||
if [[ ! -f "${prefix}/${file}" ]]; then |
||||
echo "TypePHP WASI SDK is incomplete: ${prefix}/${file}" >&2 |
||||
exit 1 |
||||
fi |
||||
done |
||||
|
||||
printf '%s\n' 'typephp-wasip2-sdk-abi-v4' > "${prefix}/.typephp-wasi-sdk-abi" |
||||
echo "Installed complete TypePHP WASI 0.2 SDK: ${prefix}" |
||||
@ -0,0 +1,15 @@ |
||||
<?php |
||||
declare(strict_types=1); |
||||
use native_types; |
||||
|
||||
function main(): void |
||||
{ |
||||
$integer = std::bigInt("123456789012345678901234567890"); |
||||
echo ($integer * 9)->toString(), "\n"; |
||||
|
||||
$float = std::bigFloat("1000000000000000000000000000000"); |
||||
echo ($float + std::bigFloat("1"))->toString(), "\n"; |
||||
|
||||
$decimal = std::decimal("12345.00000000000000001"); |
||||
echo ($decimal + std::decimal("3.14159265358979323"))->toString(), "\n"; |
||||
} |
||||
@ -0,0 +1,38 @@ |
||||
#!/usr/bin/env bash |
||||
|
||||
set -euo pipefail |
||||
|
||||
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) |
||||
compiler_dir=$(cd "${script_dir}/.." && pwd) |
||||
phpx_home=${PHPX_HOME:-${compiler_dir}/vendor/swoole/phpx} |
||||
prefix=${TYPEPHP_WASI_SDK_DIR:-${phpx_home}/wasm/wasm32-wasip2} |
||||
output=${TYPEPHP_WASM_NUMERIC_OUTPUT:-/tmp/typephp-wasm-numeric.wasm} |
||||
wasi_cxx=${TYPEPHP_WASI_CXX:-$(command -v wasm32-wasip2-clang++ || true)} |
||||
|
||||
if [[ -z "${wasi_cxx}" ]]; then |
||||
echo "Required WASI tool 'wasm32-wasip2-clang++' was not found in PATH" >&2 |
||||
exit 1 |
||||
fi |
||||
|
||||
for library in libgmp.a libgmpxx.a libmpfr.a libmpdec.a libmpdec++.a; do |
||||
if [[ ! -f "${prefix}/lib/${library}" ]]; then |
||||
echo "WASI numeric library not found: ${prefix}/lib/${library}" >&2 |
||||
exit 1 |
||||
fi |
||||
done |
||||
|
||||
"${wasi_cxx}" \ |
||||
-O0 \ |
||||
-std=c++17 \ |
||||
-fwasm-exceptions \ |
||||
-mllvm -wasm-enable-sjlj \ |
||||
-mllvm -wasm-use-legacy-eh=false \ |
||||
-I"${prefix}/include" \ |
||||
"${script_dir}/numeric-smoke-test.cc" \ |
||||
-L"${prefix}/lib" \ |
||||
-lmpdec++ -lmpdec -lmpfr -lgmpxx -lgmp \ |
||||
-lwasi-emulated-signal \ |
||||
-lsetjmp -lunwind -lm \ |
||||
-o "${output}" |
||||
|
||||
echo "Linked numeric WASI smoke test: ${output}" |
||||
@ -0,0 +1,52 @@ |
||||
#include <gmpxx.h> |
||||
#include <mpfr.h> |
||||
#include <decimal.hh> |
||||
|
||||
#include <cstdio> |
||||
#include <string> |
||||
|
||||
int main() |
||||
{ |
||||
mpz_class integer("18446744073709551616"); |
||||
integer = integer * integer + 7; |
||||
if (integer.get_str() != "340282366920938463463374607431768211463") { |
||||
return 1; |
||||
} |
||||
|
||||
mpfr_t value; |
||||
mpfr_init2(value, 256); |
||||
if (mpfr_set_str(value, "2", 10, MPFR_RNDN) != 0) { |
||||
mpfr_clear(value); |
||||
return 2; |
||||
} |
||||
mpfr_sqrt(value, value, MPFR_RNDN); |
||||
char float_buffer[96]; |
||||
mpfr_snprintf(float_buffer, sizeof(float_buffer), "%.40RNf", value); |
||||
mpfr_clear(value); |
||||
if (std::string(float_buffer) != "1.4142135623730950488016887242096980785697") { |
||||
return 3; |
||||
} |
||||
|
||||
decimal::Decimal small_decimal("1.25"); |
||||
if (small_decimal.to_sci() != "1.25") { |
||||
std::fprintf(stderr, "unexpected parsed Decimal: %s\n", small_decimal.to_sci().c_str()); |
||||
return 4; |
||||
} |
||||
small_decimal *= decimal::Decimal("8"); |
||||
if (small_decimal.to_sci() != "10.00") { |
||||
std::fprintf(stderr, "unexpected small Decimal result: %s\n", small_decimal.to_sci().c_str()); |
||||
return 5; |
||||
} |
||||
|
||||
decimal::Context decimal_context(32); |
||||
decimal::Decimal decimal_value("12345678901234567890.125"); |
||||
decimal_value = decimal_value.mul(decimal::Decimal("8"), decimal_context); |
||||
const std::string decimal_string = decimal_value.to_sci(); |
||||
if (decimal_string != "98765431209876543121.000") { |
||||
std::fprintf(stderr, "unexpected Decimal result: %s\n", decimal_string.c_str()); |
||||
return 6; |
||||
} |
||||
|
||||
std::puts("TYPEPHP_WASM_NUMERIC_OK"); |
||||
return 0; |
||||
} |
||||
@ -0,0 +1,35 @@ |
||||
#!/usr/bin/env bash |
||||
|
||||
set -euo pipefail |
||||
|
||||
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) |
||||
compiler_dir=$(cd "${script_dir}/.." && pwd) |
||||
output=${TYPEPHP_WASM_TEST_OUTPUT:-/tmp/typephp-wasm-high-precision.wasm} |
||||
wasmtime_bin=${TYPEPHP_WASMTIME:-$(command -v wasmtime || true)} |
||||
if [[ -z "${wasmtime_bin}" ]]; then |
||||
echo "Required WASI tool 'wasmtime' was not found in PATH" >&2 |
||||
exit 1 |
||||
fi |
||||
|
||||
output_dir=$(dirname "${output}") |
||||
output_name=$(basename "${output}") |
||||
( |
||||
cd "${output_dir}" |
||||
php "${compiler_dir}/bin/tpc.php" --wasm=component "${script_dir}/examples/high-precision.php" |
||||
if [[ high-precision.wasm != "${output_name}" ]]; then |
||||
mv high-precision.wasm "${output_name}" |
||||
fi |
||||
) |
||||
|
||||
actual=$(XDG_CACHE_HOME=${XDG_CACHE_HOME:-/tmp/typephp-wasmtime-cache} \ |
||||
"${wasmtime_bin}" -S http "${output}") |
||||
expected=$'1111111101111111110111111111010\n1000000000000000000000000000001\n12348.14159265358979324' |
||||
|
||||
if [[ "${actual}" != "${expected}" ]]; then |
||||
echo "Unexpected TypePHP/WASI output:" >&2 |
||||
printf '%s\n' "${actual}" >&2 |
||||
exit 1 |
||||
fi |
||||
|
||||
printf '%s\n' "${actual}" |
||||
echo "TypePHP/WASI integration test passed" |
||||
Loading…
Reference in new issue