- 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