Skip to content

[PHP-wasm] Imagick extension #2383

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions packages/php-wasm/compile/imagick/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
FROM playground-php-wasm:base

# Each version of PHP requires its own version of Imagick
# because each version of PHP has a specific Zend Engine API version.
# Consequently, each version of Imagick is designed to be
# compatible with a particular Zend Engine API version.
ARG PHP_VERSION

ARG WITH_JSPI

ARG WITH_DEBUG

# Temporary install vim
RUN apt-get update && apt-get install -y vim

# Install Bison, required to build PHP
RUN mkdir -p /libs
COPY ./bison2.7/dist/ /libs/bison2.7
COPY ./bison2.7/bison27.patch /root/bison27.patch

RUN if [[ "${PHP_VERSION:0:1}" -le "7" && "${PHP_VERSION:2:1}" -le "3" ]]; then \
if /libs/bison2.7/usr/local/bison/bin/bison -h >/dev/null; then \
mv /libs/bison2.7/usr/local/bison /usr/local/bison && \
ln -s /usr/local/bison/bin/bison /usr/bin/bison && \
ln -s /usr/local/bison/bin/yacc /usr/bin/yacc; \
else \
wget https://ftp.gnu.org/gnu/bison/bison-2.7.tar.gz && \
tar -xvf bison-2.7.tar.gz && \
rm bison-2.7.tar.gz && \
cd bison-2.7 && \
git apply --no-index /root/bison27.patch && \
./configure --prefix=/usr/local && \
make && \
make install; \
if [[ $? -ne 0 ]]; then \
echo 'Failed to build Bison 2.7 dependency.'; \
exit -1; \
fi; \
fi; \
else \
apt install -y bison; \
fi;

# Build PHP
RUN git clone https://github.com/php/php-src.git php-src \
--branch PHP-$PHP_VERSION \
--single-branch \
--depth 1;

RUN cd php-src && ./buildconf --force

# PHP <= 7.3 Get and patch PHP
COPY ./php/php*.patch /root/
RUN cd /root && git apply --no-index /root/php${PHP_VERSION:0:3}*.patch -v

RUN source /root/emsdk/emsdk_env.sh && \
cd php-src && \
emconfigure ./configure \
--disable-fiber-asm \
--enable-embed \
--disable-cgi \
--disable-opcache \
--disable-phpdbg \
--without-pcre-jit \
--disable-cli \
--disable-libxml \
--without-libxml \
--disable-dom \
--disable-xml \
--disable-simplexml \
--disable-xmlreader \
--disable-xmlwriter \
--without-sqlite3 \
--without-pdo-sqlite \
# PHP 7.4
--without-iconv

# Disable asm arithmetic.
RUN if [[ ("${PHP_VERSION:0:1}" -eq "7" && "${PHP_VERSION:2:1}" -ge "4") || "${PHP_VERSION:0:1}" -ge "8" ]]; then \
/root/replace.sh 's/ZEND_USE_ASM_ARITHMETIC 1/ZEND_USE_ASM_ARITHMETIC 0/g' /root/php-src/Zend/zend_operators.h; \
elif [[ "${PHP_VERSION:0:1}" -eq "7" && "${PHP_VERSION:2:1}" -eq "3" ]]; then \
/root/replace.sh 's/defined\(HAVE_ASM_GOTO\)/0/g' /root/php-src/Zend/zend_operators.h; \
fi;
RUN /root/replace.sh 's/defined\(__GNUC__\)/0/g' /root/php-src/Zend/zend_multiply.h
RUN /root/replace.sh 's/defined\(__GNUC__\)/0/g' /root/php-src/Zend/zend_cpuinfo.c
RUN /root/replace.sh 's/defined\(__clang__\)/0/g' /root/php-src/Zend/zend_cpuinfo.c

# PHP <= 7.3 is not very good at detecting the presence of the POSIX readdir_r function
# so we need to force it to be enabled.
RUN if [[ "${PHP_VERSION:0:1}" -le "7" && "${PHP_VERSION:2:1}" -le "3" ]]; then \
echo '#define HAVE_POSIX_READDIR_R 1' >> /root/php-src/main/php_config.h; \
fi;

RUN source /root/emsdk/emsdk_env.sh && \
cd /root/php-src && \
emmake make install


# Build ImageMagick library
RUN cd /root/ ; \
wget https://imagemagick.org/archive/ImageMagick.tar.gz; \
tar -xvf ImageMagick.tar.gz; \
rm ImageMagick.tar.gz; \
cd ImageMagick-*; \
source /root/emsdk/emsdk_env.sh; \
# Disable functions that are not supported by Emscripten
## TODO: Check if we can support these functions
ac_cv_func_vfprintf_l=no \
ac_cv_func_vsnprintf_l=no \
ac_cv_func_getexecname=no \
ac_cv_func__NSGetExecutablePath=no \
ac_cv_func_getdtablesize=no \
emconfigure ./configure \
# Disable X11 support
--without-x \
--host="i386-unknown-freebsd" \
--disable-static \
--enable-shared; \
emmake make -j1; \
emmake make install;

# Build Imagick

RUN cd /root/ ; \
wget https://github.com/Imagick/imagick/archive/refs/heads/master.zip; \
unzip master.zip; \
rm master.zip; \
cd imagick-master; \
phpize . ; \
source /root/emsdk/emsdk_env.sh; \
emconfigure ./configure \
--disable-static \
--enable-shared; \
export EMCC_FLAGS="-sSIDE_MODULE=2"; \
emmake make -j1


130 changes: 130 additions & 0 deletions packages/php-wasm/compile/imagick/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import path from 'path';
import { spawn } from 'child_process';
import { phpVersions } from '../../supported-php-versions.mjs';

// yargs parse
import yargs from 'yargs';
const argParser = yargs(process.argv.slice(2))
.usage('Usage: $0 [options]')
.options({
PHP_VERSION: {
type: 'string',
description: 'The PHP version to build',
required: true,
},
['output-dir']: {
type: 'string',
description: 'The output directory',
required: true,
},
WITH_DEBUG: {
type: 'string',
choices: ['yes', 'no'],
description: 'Build with DWARF debug information.',
},
WITH_JSPI: {
type: 'boolean',
default: false,
description: 'Build with JSPI support',
},
});

const args = argParser.argv;

const platformDefaults = {
all: {
PHP_VERSION: '8.0.24',
WITH_DEBUG: 'no',
WITH_JSPI: 'no',
},
};

const getArg = (name) => {
let value =
name in args
? args[name]
: name in platformDefaults.all
? platformDefaults.all[name]
: 'no';
if (name === 'PHP_VERSION') {
value = fullyQualifiedPHPVersion(value);
}
return `${name}=${value}`;
};

const requestedVersion = getArg('PHP_VERSION');
if (!requestedVersion || requestedVersion === 'undefined') {
process.stdout.write(`PHP version ${requestedVersion} is not supported\n`);
process.stdout.write(await argParser.getHelp());
process.exit(1);
}

const sourceDir = path.dirname(new URL(import.meta.url).pathname);
const outputDir = path.resolve(process.cwd(), args.outputDir);

// Build the base image
await asyncSpawn('make', ['base-image'], {
cwd: path.dirname(sourceDir),
stdio: 'inherit',
});

// Build the xdebug.so extension
await asyncSpawn(
'docker',
[
'build',
'-f',
'imagick/Dockerfile',
'.',
'--tag=playground-php-wasm:imagick',
'--progress=plain',
'--build-arg',
getArg('PHP_VERSION'),
'--build-arg',
getArg('WITH_DEBUG'),
'--build-arg',
getArg('WITH_JSPI'),
],
{ cwd: path.dirname(sourceDir), stdio: 'inherit' }
);

const version = args['PHP_VERSION'].replace('.', '_');

await asyncSpawn(
'docker',
[
'run',
'--name',
'playground-php-wasm-tmp',
'--rm',
'-v',
`${outputDir}:/output`,
'playground-php-wasm:imagick',
// Use sh -c because wildcards are a shell feature and
// they don't work without running cp through shell.
'sh',
'-c',
`mkdir -p /output/extensions/imagick/${version} && cp -rf /root/imagick-master/modules/imagick.so /output/extensions/imagick/${version}`,
],
{ cwd: path.dirname(sourceDir), stdio: 'inherit' }
);

function asyncSpawn(...args) {
console.log('Running', args[0], args[1].join(' '), '...');
return new Promise((resolve, reject) => {
const child = spawn(...args);
child.on('close', (code) => {
if (code === 0) resolve(code);
else reject(new Error(`Process exited with code ${code}`));
});
});
}

function fullyQualifiedPHPVersion(requestedVersion) {
for (const { version, lastRelease } of phpVersions) {
if (requestedVersion === version) {
return lastRelease;
}
}
return requestedVersion;
}
9 changes: 9 additions & 0 deletions packages/php-wasm/compile/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@
],
"parallel": false
}
},
"imagick:jspi:8.3": {
"executor": "nx:run-commands",
"options": {
"commands": [
"node packages/php-wasm/compile/imagick/build.js --output-dir=packages/php-wasm/node/jspi --WITH_JSPI=yes --PHP_VERSION=8.3"
],
"parallel": false
}
}
},
"tags": []
Expand Down
Binary file not shown.
12 changes: 10 additions & 2 deletions packages/php-wasm/node/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,19 @@
"write-files.spec.ts",
"php-networking.spec.ts",
"php-dynamic-loading.spec.ts",
"php-request-handler.spec.ts",
"php.spec.ts"
"php-request-handler.spec.ts"
]
}
},
"test-jspi-imagick": {
"executor": "@nx/vite:test",
"outputs": ["{workspaceRoot}/coverage/packages/php-wasm/node"],
"options": {
"configFile": "packages/php-wasm/node/vite.jspi.config.ts",
"reportsDirectory": "../../../coverage/packages/php-wasm/node",
"testFiles": ["imagick.spec.ts"]
}
},
"test-php-file-get-contents-asyncify": {
"executor": "@nx/vite:test",
"outputs": ["{workspaceRoot}/coverage/packages/php-wasm/node"],
Expand Down
Loading
Loading