Skip to content

Commit

Permalink
Explore compiling PHP to WASI for node.js-less CLI compatibility
Browse files Browse the repository at this point in the history
This commit explores using the wasmenv toolkit to compile PHP.
The configure phase works, but the make fails because setjmp is
unsupported yet, see the error message below and the relevant
GitHub issue at WebAssembly/WASI#490

There's a chance "make" would work if we tried a dedicated libc build:

   https://github.com/WebAssembly/wasi-libc/blob/main/libc-top-half/musl/include/setjmp.h

I'm not sure if the final PHP build would be functional, though.

Either way, building using wasi-sdk which has libc and set_jmp.h baked
in would be a sensible next step:

https://github.com/WebAssembly/wasi-sdk/releases

31 69.49 /root/php-src/main/setjmp.h:44:2: warning: setjmp is not yet implemented for WASI [-W#warnings]
31 69.49 #warning setjmp is not yet implemented for WASI
31 69.49  ^
31 69.56 In file included from /root/php-src/ext/bcmath/libbcmath/src/div.c:32:
31 69.56 In file included from /root/php-src/ext/bcmath/libbcmath/src/config.h:7:
31 69.56 In file included from /root/php-src/main/php.h:35:
31 69.56 In file included from /root/php-src/Zend/zend_API.h:25:
31 69.56 In file included from /root/php-src/Zend/zend_modules.h:24:
31 69.56 In file included from /root/php-src/Zend/zend_compile.h:724:
31 69.56 /root/php-src/Zend/zend_globals.h:151:2: error: unknown type name 'jmp_buf'
31 69.56         JMP_BUF *bailout;
31 69.56         ^
31 69.56 /root/php-src/Zend/zend_portability.h:361:18: note: expanded from macro 'JMP_BUF'
31 69.56 # define JMP_BUF jmp_buf
  • Loading branch information
adamziel committed Jan 14, 2023
1 parent 9d777f4 commit 8e269e3
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 28 deletions.
67 changes: 40 additions & 27 deletions src/php-wasm/wasm/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Originally forked from https://github.com/seanmorris/php-wasm
# ubuntu:lunar supports amd64 and arm64 (Apple Silicon) while
# emscripten/emsdk:3.1.24 supports amd64 only.
FROM ubuntu:lunar
FROM --platform=linux/amd64 ubuntu:lunar

SHELL ["/bin/bash", "-c"]
# The PHP version to build.
Expand Down Expand Up @@ -59,7 +59,14 @@ RUN set -euxo pipefail;\
wget \
unzip \
cmake \
python3
python3 \
python3-pip

# Install wasienv
RUN pip3 install -U pip
COPY ./build-assets/install_wasienv.sh /root/install_wasienv.sh
RUN bash /root/install_wasienv.sh
RUN cp /root/.wasienv/local/bin/* /root/.wasienv/bin/

# PHP <= 7.3 requires Bison 2.7
# PHP >= 7.4 and Bison 3.0
Expand All @@ -82,10 +89,10 @@ RUN if [[ "${PHP_VERSION:0:1}" -le "7" && "${PHP_VERSION:2:1}" -le "3" ]] || [ "
# Install Emscripten from the repository. We'd use the official
# Docker image, but there is no arm64 image available which makes
# the build take forever on Apple Silicon.
RUN ln -s /usr/bin/python3 /usr/bin/python
RUN git clone https://github.com/emscripten-core/emsdk.git && \
./emsdk/emsdk install latest && \
/root/emsdk/emsdk activate latest
# RUN ln -s /usr/bin/python3 /usr/bin/python
# RUN git clone https://github.com/emscripten-core/emsdk.git && \
# ./emsdk/emsdk install latest && \
# /root/emsdk/emsdk activate latest

RUN touch /root/.configure-flags && \
touch /root/.emcc-php-wasm-flags && \
Expand All @@ -98,17 +105,17 @@ RUN if [ "$WITH_LIBXML" = "yes" ]; \
then \
source /root/emsdk/emsdk_env.sh && \
env GIT_SSL_NO_VERIFY=true git clone https://gitlab.gnome.org/GNOME/libxml2.git libxml2 \
--branch v2.9.10 \
--branch v2.8.0 \
--single-branch \
--depth 1 && \
cd libxml2 && \
./autogen.sh && \
emconfigure ./configure --with-http=no --with-ftp=no --with-python=no --with-threads=no --enable-shared=no --prefix=/root/lib/ &&\
emmake make && \
emmake make install; \
echo -n ' --with-libxml=/root/lib --enable-dom --enable-xml --enable-simplexml' >> /root/.php-configure-flags; \
echo -n ' -I /root/libxml2' >> /root/.emcc-php-wasm-flags; \
echo -n ' /root/lib/lib/libxml2.a' >> /root/.emcc-php-wasm-sources; \
wasiconfigure ./configure --with-http=no --with-ftp=no --with-python=no --with-threads=no --enable-shared=no --prefix=/root/lib/ &&\
wasimake make && \
wasimake make install; \
echo -n ' --with-libxml --enable-dom --enable-xml --enable-simplexml ' >> /root/.php-configure-flags; \
echo -n ' -I /root/libxml2 ' >> /root/.emcc-php-wasm-flags; \
echo -n ' /root/lib/lib/libxml2.a ' >> /root/.emcc-php-wasm-sources; \
else \
echo -n ' --without-libxml --disable-dom --disable-xml --disable-simplexml' >> /root/.php-configure-flags; \
fi
Expand All @@ -126,13 +133,13 @@ RUN git apply --no-index /root/php${PHP_VERSION:0:3}.patch && \

# Add gzip and libzip if needed
COPY ./build-assets/zlib /root/zlib
RUN cd php-src/ && PKG_CONFIG_PATH=$PKG_CONFIG_PATH ./buildconf --force
RUN if [ "$WITH_LIBZIP" = "yes" ]; then \
source /root/emsdk/emsdk_env.sh && \
cd /root/zlib && \
emconfigure ./configure --prefix=/root/lib && \
wasiconfigure ./configure --prefix=/root/lib && \
# emmake make fails after it built the library files – let's ignore the errors \
(emmake make || true) && \
emmake make install && \
(wasimake make || true) && \
wasimake make install && \
if [[ "${PHP_VERSION:0:1}" -le "7" && "${PHP_VERSION:2:1}" -le "3" ]] || [ "${PHP_VERSION:0:1}" -le "5" ]; then \
apt install -y zlib1g zlib1g-dev; \
export LIBZIP_VERSION=1.2.0; \
Expand All @@ -146,13 +153,13 @@ RUN if [ "$WITH_LIBZIP" = "yes" ]; then \
cd libzip && \
mkdir build && \
cd build && \
emcmake cmake \
wasicmake cmake \
-DCMAKE_INSTALL_PREFIX=/root/lib \
-DZLIB_LIBRARY=/root/lib/lib/libz.a \
-DZLIB_INCLUDE_DIR=/root/lib/include \
.. && \
emmake make && \
emmake make install; \
wasimake make && \
wasimake make install; \
if [[ "${PHP_VERSION:0:1}" -le "7" && "${PHP_VERSION:2:1}" -le "3" ]] || [ "${PHP_VERSION:0:1}" -le "5" ]; then \
echo -n ' --enable-zip --with-libzip=/root/lib ' >> /root/.php-configure-flags; \
echo -n ' -I /root/zlib -I /root/libzip ' >> /root/.emcc-php-wasm-flags; \
Expand Down Expand Up @@ -192,11 +199,13 @@ RUN if [ "$WITH_VRZNO" = "yes" ]; \
fi

# Build the patched PHP
RUN cd php-src/ && PKG_CONFIG_PATH=$PKG_CONFIG_PATH ./buildconf --force
RUN source /root/emsdk/emsdk_env.sh && \
RUN ls -R /root/lib

RUN source /root/.wasienv/wasienv.sh && \
cd php-src/ && \
emconfigure ./configure \
wasiconfigure ./configure \
PKG_CONFIG_PATH=$PKG_CONFIG_PATH \
--host=i686-pc-linux-gnu \
# Fibers are a PHP 8.1+ feature. They are compiled as
# a custom assembly implementation by default. However,
# that implementation does not work with emscripten.
Expand Down Expand Up @@ -242,9 +251,13 @@ RUN if [[ "${PHP_VERSION:0:1}" -le "7" && "${PHP_VERSION:2:1}" -le "3" ]] || [ "
echo '#define HAVE_POSIX_READDIR_R 1' >> php-src/main/php_config.h; \
fi;

RUN source /root/emsdk/emsdk_env.sh && \
RUN git clone https://github.com/WebAssembly/wasi-libc.git wasi-libc
RUN source /root/.wasienv/wasienv.sh && cd wasi-libc && wasimake make
RUN source /root/.wasienv/wasienv.sh && cd wasi-libc && wasimake make install

RUN source /root/.wasienv/wasienv.sh && \
cd php-src/ && \
emmake make -j8
wasimake make -j8

RUN cp -v php-src/.libs/libphp*.la /root/lib/libphp.la
RUN cp -v php-src/.libs/libphp*.a /root/lib/libphp.a
Expand All @@ -269,10 +282,10 @@ RUN if [ "${PHP_VERSION:0:1}" -lt "8" ]; then \

# Build the final .wasm file
RUN mkdir /root/output
RUN source /root/emsdk/emsdk_env.sh && \
RUN source /root/.wasienv/wasienv.sh && \
export EXPORTED_FUNCTIONS='["_php_wasm_init", "_wasm_sapi_handle_request", "_wasm_add_uploaded_file", "_wasm_add_SERVER_entry","_wasm_set_query_string","_wasm_set_cookies","_wasm_set_path_translated","_wasm_set_request_uri","_wasm_set_request_method","_wasm_set_content_type","_wasm_set_content_length","_wasm_set_request_body","_wasm_set_php_code","_wasm_set_request_port", "_wasm_set_request_host", "_phpwasm_init_uploaded_files_hash", "_phpwasm_register_uploaded_file", "_phpwasm_destroy_uploaded_files_hash" '"$(cat /root/.EXTRA_EXPORTED_RUNTIME_METHODS)"']'; \
cd php-src/ && \
emcc -O3 \
wasicc -O3 \
-I . \
-I ext \
-I ext/pdo_sqlite \
Expand Down
144 changes: 144 additions & 0 deletions src/php-wasm/wasm/build-assets/install_wasienv.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#!/bin/sh

# This install script is intended to download and install the latest available
# release of wasienv.

# You can install using this script:
# $ curl https://raw.githubusercontent.com/wasienv/wasienv/master/install.sh | sh
set -e

reset="\033[0m"
blue="\033[44m"
m="\033[34;1m"
bold="\033[1m"
green="\033[32m"
red="\033[31m"
cyan="\033[36m"
white="\033[37m"
dim="\033[2m"

wasienv_detect_profile() {
if [ -n "${PROFILE}" ] && [ -f "${PROFILE}" ]; then
echo "${PROFILE}"
return
fi

local DETECTED_PROFILE
DETECTED_PROFILE=''
local SHELLTYPE
SHELLTYPE="$(basename "/$SHELL")"

if [ "$SHELLTYPE" = "bash" ]; then
if [ -f "$HOME/.bashrc" ]; then
DETECTED_PROFILE="$HOME/.bashrc"
elif [ -f "$HOME/.bash_profile" ]; then
DETECTED_PROFILE="$HOME/.bash_profile"
fi
elif [ "$SHELLTYPE" = "zsh" ]; then
DETECTED_PROFILE="$HOME/.zshrc"
elif [ "$SHELLTYPE" = "fish" ]; then
DETECTED_PROFILE="$HOME/.config/fish/config.fish"
fi

if [ -z "$DETECTED_PROFILE" ]; then
if [ -f "$HOME/.profile" ]; then
DETECTED_PROFILE="$HOME/.profile"
elif [ -f "$HOME/.bashrc" ]; then
DETECTED_PROFILE="$HOME/.bashrc"
elif [ -f "$HOME/.bash_profile" ]; then
DETECTED_PROFILE="$HOME/.bash_profile"
elif [ -f "$HOME/.zshrc" ]; then
DETECTED_PROFILE="$HOME/.zshrc"
elif [ -f "$HOME/.config/fish/config.fish" ]; then
DETECTED_PROFILE="$HOME/.config/fish/config.fish"
fi
fi

if [ ! -z "$DETECTED_PROFILE" ]; then
echo "$DETECTED_PROFILE"
fi
}

wasienv_link() {
printf "${green}${bold}> Adding wasienv to bash profile...${reset}\n"
WASIENV_PROFILE="$(wasienv_detect_profile)"
LOAD_STR="\n# Wasienv\nexport WASIENV_DIR=\"$INSTALL_DIRECTORY\"\n[ -s \"\$WASIENV_DIR/wasienv.sh\" ] && source \"\$WASIENV_DIR/wasienv.sh\"\n"
SOURCE_STR="# Wasienv config\nexport WASIENV_DIR=\"$INSTALL_DIRECTORY\"\nexport PATH=\"\$WASIENV_DIR/bin:\$PATH\"\n"

# We create the wasienv.sh file
printf "$SOURCE_STR" > "$INSTALL_DIRECTORY/wasienv.sh"

if [ -z "${WASIENV_PROFILE-}" ] ; then
printf "${red}Profile not found. Tried:\n* ${WASIENV_PROFILE} (as defined in \$PROFILE)\n* ~/.bashrc\n* ~/.bash_profile\n* ~/.zshrc\n* ~/.profile.\n"
echo "\nHow to solve this issue?\n* Create one of them and run this script again"
echo "* Create it (touch ${WASIENV_PROFILE}) and run this script again"
echo " OR"
printf "* Append the following lines to the correct file yourself:$reset\n"
command printf "${SOURCE_STR}"
else
if ! grep -q 'wasienv.sh' "$WASIENV_PROFILE"; then
# if [[ $WASIENV_PROFILE = *"fish"* ]]; then
# command fish -c 'set -U fish_user_paths $fish_user_paths ~/.wasienv/bin'
# else
command printf "$LOAD_STR" >> "$WASIENV_PROFILE"
# fi
fi
printf "\033[1A${green}${bold}> Adding wasienv to bash profile... ✓${reset}\n"
version=`$INSTALL_DIRECTORY/local/bin/wasienv --version` || (
printf "$red> wasienv was installed, but doesn't seem to be working :($reset\n"
exit 1;
)
fi
}

if [ -z "$INSTALL_DIRECTORY" ]; then
if [ -z "$WASIENV_DIR" ]; then
# If WASMER_DIR is not present
INSTALL_DIRECTORY="$HOME/.wasienv"
else
# If WASMER_DIR is present
INSTALL_DIRECTORY="${WASIENV_DIR}"
fi
fi

echo "
${m}┏━━━━━━━━━┓${reset}
${m}┃ ┃${reset}
${m}${reset}${bold}wasi${m} (${reset} ${bold}env${reset}
${m}┃ ┃${reset}
${m}┗━━━━━━━━━┛${reset}
"

echo "${green}${bold}> Installing wasienv${reset}"

# Create wasienv directory
mkdir -p $INSTALL_DIRECTORY/bin
if [ -x "$(command -v pip3)" ]; then
# Uninstall in case it exists
pip3 uninstall wasienv -y || true
# Install wasienv in the ~/.wasienv/bin directory
pip3 install wasienv --prefix=$INSTALL_DIRECTORY --upgrade
pip3 install wasienv --user
else
# Uninstall in case it exists
pip uninstall wasienv -y || true
# Install wasienv in the ~/.wasienv/bin directory
pip install wasienv --prefix=$INSTALL_DIRECTORY --upgrade
pip install wasienv --user
fi

wasienv_link

echo "\n${green}${bold}> Installing a WebAssembly WASI Runtime${reset}"
curl https://get.wasmer.io -sSfL | sh

echo "\n${green}${bold}> Installing the required WASI SDKs${reset}"
# unstable is the most stable version of the WASI sdk for now
$INSTALL_DIRECTORY/local/bin/wasienv install-sdk unstable
$INSTALL_DIRECTORY/local/bin/wasienv default-sdk unstable

printf "\n${reset}${dim}wasienv will be available the next time you open the terminal.\n"
printf "${reset}${dim}If you want to have the commands available now please execute:\n${reset}source $INSTALL_DIRECTORY/wasienv.sh$reset\n"

# Delete the variables from shell
unset -f wasienv_link wasienv_detect_profile
2 changes: 1 addition & 1 deletion src/php-wasm/wasm/gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async function build() {
'--build-arg',
`WITH_LIBXML=${withLibxml}`,
'--build-arg',
`WITH_LIBZIP=yes`,
`WITH_LIBZIP=no`,
'--build-arg',
`WITH_NODEFS=${withNodeFs}`,
'--build-arg',
Expand Down

0 comments on commit 8e269e3

Please sign in to comment.