Skip to content
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
6 changes: 6 additions & 0 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,12 @@ jobs:
libexif-dev \
libgd-dev \
libpng-dev \
libjpeg-dev \
libtiff-dev \
libfreetype-dev \
libfreetype6 \
libfontconfig1-dev \
fonts-dejavu-core \
libgmp-dev \
libssl-dev \
libsodium-dev \
Expand Down Expand Up @@ -199,6 +201,10 @@ jobs:
run: sudo php test/install-bundled-php-exts.php php-config
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Test bundled GD image formats and FreeType rendering
run: sudo php test/install-bundled-gd.php php-config /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

end-to-end-tests:
runs-on: ubuntu-latest
Expand Down
22 changes: 21 additions & 1 deletion src/ComposerIntegration/BundledPhpExtensionsRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class BundledPhpExtensionsRepository extends ArrayRepository
* os-families?: non-empty-list<OperatingSystemFamily>,
* type?: ExtensionType,
* priority?: int,
* configure-options?: list<array{name: non-empty-string, description: string, needs-value: bool}>,
* }>
*/
private static array $bundledPhpExtensions = [
Expand Down Expand Up @@ -69,7 +70,22 @@ class BundledPhpExtensionsRepository extends ArrayRepository
'lib-ffi' => '*',
],
],
// ['name' => 'gd'], // build failure - ext/gd/gd.c:79:11: fatal error: ft2build.h: No such file or directory
[
'name' => 'gd',
'require' => ['php' => '>= 7.4.0'],
'configure-options' => [
[
'name' => 'with-jpeg',
'description' => 'Enable JPEG support in bundled GD',
'needs-value' => false,
],
[
'name' => 'with-freetype',
'description' => 'Enable FreeType support in bundled GD',
'needs-value' => false,
],
],
],
['name' => 'gettext'],
['name' => 'gmp'],
['name' => 'iconv'],
Expand Down Expand Up @@ -259,6 +275,10 @@ static function (string $target, string $constraint) use ($extension, $versionPa
$phpExt['priority'] = $extension['priority'];
}

if (array_key_exists('configure-options', $extension)) {
$phpExt['configure-options'] = $extension['configure-options'];
}

$package->setPhpExt($phpExt);

return $package;
Expand Down
63 changes: 63 additions & 0 deletions test/assets/check-bundled-gd.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

$configured = ($argv[1] ?? '') === 'configured';
$info = gd_info();

foreach (['PNG Support' => true, 'JPEG Support' => $configured, 'FreeType Support' => $configured] as $feature => $expected) {
if ($info[$feature] !== $expected) {
throw new RuntimeException('Unexpected GD capability: ' . $feature);
}
}

$image = imagecreatetruecolor(80, 40);
if ($image === false) {
throw new RuntimeException('Could not create an image');
}

$background = imagecolorallocate($image, 20, 40, 60);
imagefill($image, 0, 0, $background);

foreach ($configured ? ['png', 'jpeg'] : ['png'] as $format) {
ob_start();
$written = $format === 'png' ? imagepng($image) : imagejpeg($image);
$encoded = ob_get_clean();
if (! $written || $encoded === false) {
throw new RuntimeException('Could not encode ' . $format);
}

$decoded = imagecreatefromstring($encoded);
if ($decoded === false || imagesx($decoded) !== 80 || imagesy($decoded) !== 40) {
throw new RuntimeException('Could not round-trip ' . $format);
}

if ($format === 'png' && imagecolorat($decoded, 0, 0) !== $background) {
throw new RuntimeException('PNG round-trip changed the pixel colour');
}
}

if ($configured) {
$font = $argv[2] ?? '';
if (! is_file($font)) {
throw new RuntimeException('Missing TrueType test font: ' . $font);
}

$foreground = imagecolorallocate($image, 255, 255, 255);
if (imagettftext($image, 16, 0, 5, 25, $foreground, $font, 'GD') === false) {
throw new RuntimeException('FreeType could not render text');
}

$rendered = false;
for ($x = 0; $x < 80; $x++) {
for ($y = 0; $y < 40; $y++) {
$rendered = $rendered || imagecolorat($image, $x, $y) !== $background;
}
}

if (! $rendered) {
throw new RuntimeException('FreeType did not draw any pixels');
}
}

echo 'GD ' . ($configured ? 'JPEG/FreeType' : 'default') . ' checks passed.' . PHP_EOL;
41 changes: 41 additions & 0 deletions test/install-bundled-gd.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

use Php\Pie\Platform\TargetPhp\PhpBinaryPath;
use Php\Pie\Util\Process;

require __DIR__ . '/../vendor/autoload.php';

$phpConfigPath = $argv[1] ?? '';
$fontPath = $argv[2] ?? '';

if ($phpConfigPath === '' || $fontPath === '') {
echo 'Usage: ' . __FILE__ . ' <php-config-path> <TrueType-font-path>' . PHP_EOL;
exit(1);
}

$phpBinaryPath = PhpBinaryPath::fromPhpConfigExecutable($phpConfigPath);

foreach (['default' => [], 'configured' => ['--with-jpeg', '--with-freetype']] as $variant => $options) {
echo Process::run([
$phpBinaryPath->phpBinaryPath,
__DIR__ . '/../bin/pie',
'install',
'php/gd',
'--force',
'--no-interaction',
'--with-php-config=' . $phpBinaryPath->phpConfigPath(),
'-v',
'-j2',
...$options,
], timeout: null) . PHP_EOL;

// Start a new PHP process so it loads the extension just installed by PIE.
echo Process::run([
$phpBinaryPath->phpBinaryPath,
__DIR__ . '/assets/check-bundled-gd.php',
$variant,
$fontPath,
]) . PHP_EOL;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Composer\Package\CompletePackageInterface;
use Php\Pie\ComposerIntegration\BundledPhpExtensionsRepository;
use Php\Pie\ConfigureOption;
use Php\Pie\DependencyResolver\Package;
use Php\Pie\Downloading\DownloadedPackage;
use Php\Pie\ExtensionName;
Expand All @@ -22,6 +23,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Process\Exception\ProcessFailedException;

use function array_map;
use function Safe\mkdir;
use function Safe\realpath;
use function sys_get_temp_dir;
Expand All @@ -46,6 +48,7 @@ public static function bundledRepositoryPackageNames(): array
['php/enchant'],
['php/exif'],
['php/ffi'],
['php/gd'],
['php/gettext'],
['php/gmp'],
['php/iconv'],
Expand Down Expand Up @@ -111,6 +114,38 @@ public function testBundledRepository(string $packageName): void
self::assertSame('8.1.0', $package->getPrettyVersion());
}

public function testGdExposesOptionalJpegAndFreeTypeFlags(): void
{
$phpBinary = $this->createMock(PhpBinaryPath::class);
$phpBinary->method('version')->willReturn('8.1.0');

$repository = BundledPhpExtensionsRepository::forTargetPlatform(
new TargetPlatform(
OperatingSystem::NonWindows,
OperatingSystemFamily::Linux,
$phpBinary,
Architecture::x86_64,
ThreadSafetyMode::NonThreadSafe,
1,
null,
null,
),
);

$composerPackage = $repository->findPackage('php/gd', '8.1.0');
self::assertInstanceOf(CompletePackageInterface::class, $composerPackage);

$package = Package::fromComposerCompletePackage($composerPackage);

self::assertSame(
[['with-jpeg', false], ['with-freetype', false]],
array_map(
static fn (ConfigureOption $option): array => [$option->name, $option->needsValue],
$package->configureOptions(),
),
);
}

public function testMakeCommandForXmlReader(): void
{
$phpPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('pie_test_bundled_', true);
Expand Down