Skip to content

Commit

Permalink
Add support for os-families and os-families-exclude in extensions…
Browse files Browse the repository at this point in the history
… `composer.json`
  • Loading branch information
alexandre-daubois committed Nov 26, 2024
1 parent 3abe8f5 commit f934832
Show file tree
Hide file tree
Showing 25 changed files with 492 additions and 2 deletions.
35 changes: 35 additions & 0 deletions src/DependencyResolver/IncompatibleOperatingSystemFamily.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Php\Pie\DependencyResolver;

use Php\Pie\Platform\OperatingSystemFamily;
use RuntimeException;

use function array_map;
use function implode;
use function sprintf;

class IncompatibleOperatingSystemFamily extends RuntimeException
{
/** @param list<OperatingSystemFamily> $required */
public static function notInCompatibleOperatingSystemFamilies(array $required, OperatingSystemFamily $current): self
{
return new self(sprintf(
'This extension does not support the "%s" operating system family. It is compatible with the following families: "%s".',
$current->value,
implode('", "', array_map(static fn (OperatingSystemFamily $osFamily): string => $osFamily->value, $required)),
));
}

/** @param list<OperatingSystemFamily> $incompatibleOsFamilies */
public static function inIncompatibleOperatingSystemFamily(array $incompatibleOsFamilies, OperatingSystemFamily $current): self
{
return new self(sprintf(
'This extension does not support the "%s" operating system family. It is incompatible with the following families: "%s".',
$current->value,
implode('", "', array_map(static fn (OperatingSystemFamily $osFamily): string => $osFamily->value, $incompatibleOsFamilies)),
));
}
}
52 changes: 51 additions & 1 deletion src/DependencyResolver/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,23 @@
namespace Php\Pie\DependencyResolver;

use Composer\Package\CompletePackageInterface;
use InvalidArgumentException;
use Php\Pie\ConfigureOption;
use Php\Pie\ExtensionName;
use Php\Pie\ExtensionType;
use Php\Pie\Platform\OperatingSystemFamily;

use function array_key_exists;
use function array_map;
use function array_slice;
use function explode;
use function implode;
use function parse_url;
use function sprintf;
use function str_contains;
use function str_starts_with;
use function strtolower;
use function ucfirst;

/**
* @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks
Expand All @@ -25,7 +30,11 @@
*/
final class Package
{
/** @param list<ConfigureOption> $configureOptions */
/**
* @param list<ConfigureOption> $configureOptions
* @param list<OperatingSystemFamily> $compatibleOsFamilies
* @param list<OperatingSystemFamily> $incompatibleOsFamilies
*/
public function __construct(
public readonly CompletePackageInterface $composerPackage,
public readonly ExtensionType $extensionType,
Expand All @@ -37,6 +46,8 @@ public function __construct(
public readonly bool $supportZts,
public readonly bool $supportNts,
public readonly string|null $buildPath,
public readonly array $compatibleOsFamilies,
public readonly array $incompatibleOsFamilies,
) {
}

Expand All @@ -63,6 +74,20 @@ public static function fromComposerCompletePackage(CompletePackageInterface $com
? $phpExtOptions['build-path']
: null;

/** @var list<string> $compatibleOsFamilies */
$compatibleOsFamilies = $phpExtOptions !== null && array_key_exists('os-families', $phpExtOptions)
? $phpExtOptions['os-families']
: [];

/** @var list<string> $incompatibleOsFamilies */
$incompatibleOsFamilies = $phpExtOptions !== null && array_key_exists('os-families-exclude', $phpExtOptions)
? $phpExtOptions['os-families-exclude']
: [];

if ($compatibleOsFamilies && $incompatibleOsFamilies) {
throw new InvalidArgumentException('Cannot specify both "os-families" and "os-families-exclude" in composer.json');
}

return new self(
$completePackage,
ExtensionType::tryFrom($completePackage->getType()) ?? ExtensionType::PhpModule,
Expand All @@ -74,6 +99,8 @@ public static function fromComposerCompletePackage(CompletePackageInterface $com
$supportZts,
$supportNts,
$buildPath,
self::convertInputStringsToOperatingSystemFamilies($compatibleOsFamilies),
self::convertInputStringsToOperatingSystemFamilies($incompatibleOsFamilies),
);
}

Expand All @@ -100,4 +127,27 @@ public function githubOrgAndRepository(): string
// Converts https://api.github.com/repos/<user>/<repository>/zipball/<sha>" to "<user>/<repository>"
return implode('/', array_slice(explode('/', $parsed['path']), 2, 2));
}

/**
* @param list<string> $input
*
* @return list<OperatingSystemFamily>
*/
private static function convertInputStringsToOperatingSystemFamilies(array $input): array
{
$osFamilies = [];
foreach ($input as $value) {
// try to normalize a bit the input
$valueToTry = ucfirst(strtolower($value));

$family = OperatingSystemFamily::tryFrom($valueToTry);
if ($family === null) {
throw new InvalidArgumentException(sprintf('Expected operating system family to be one of "%s", got "%s".', implode('", "', OperatingSystemFamily::asValuesList()), $value));
}

$osFamilies[] = $family;
}

return $osFamilies;
}
}
19 changes: 19 additions & 0 deletions src/DependencyResolver/ResolveDependencyWithComposer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Php\Pie\Platform\TargetPlatform;
use Php\Pie\Platform\ThreadSafetyMode;

use function in_array;
use function preg_match;

/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
Expand Down Expand Up @@ -55,6 +56,7 @@ public function __invoke(Composer $composer, TargetPlatform $targetPlatform, Req

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

$this->assertCompatibleOsFamily($targetPlatform, $piePackage);
$this->assertCompatibleThreadSafetyMode($targetPlatform->threadSafety, $piePackage);

return $piePackage;
Expand All @@ -70,4 +72,21 @@ private function assertCompatibleThreadSafetyMode(ThreadSafetyMode $threadSafety
throw IncompatibleThreadSafetyMode::ntsExtensionOnZtsPlatform();
}
}

private function assertCompatibleOsFamily(TargetPlatform $targetPlatform, Package $resolvedPackage): void
{
if ($resolvedPackage->compatibleOsFamilies && ! in_array($targetPlatform->operatingSystemFamily, $resolvedPackage->compatibleOsFamilies, true)) {
throw IncompatibleOperatingSystemFamily::notInCompatibleOperatingSystemFamilies(
$resolvedPackage->compatibleOsFamilies,
$targetPlatform->operatingSystemFamily,
);
}

if ($resolvedPackage->incompatibleOsFamilies && in_array($targetPlatform->operatingSystemFamily, $resolvedPackage->incompatibleOsFamilies, true)) {
throw IncompatibleOperatingSystemFamily::inIncompatibleOperatingSystemFamily(
$resolvedPackage->incompatibleOsFamilies,
$targetPlatform->operatingSystemFamily,
);
}
}
}
27 changes: 27 additions & 0 deletions src/Platform/OperatingSystemFamily.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Php\Pie\Platform;

use function array_map;

/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
enum OperatingSystemFamily: string
{
case Windows = 'Windows';
case Bsd = 'BSD';
case Darwin = 'Darwin';
case Solaris = 'Solaris';
case Linux = 'Linux';
case Unknown = 'Unknown';

/** @return array<string> */
public static function asValuesList(): array
{
return array_map(
static fn (OperatingSystemFamily $osFamily): string => $osFamily->value,
self::cases(),
);
}
}
15 changes: 15 additions & 0 deletions src/Platform/TargetPhp/PhpBinaryPath.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Composer\Util\Platform;
use Php\Pie\Platform\Architecture;
use Php\Pie\Platform\OperatingSystem;
use Php\Pie\Platform\OperatingSystemFamily;
use Php\Pie\Util\Process;
use RuntimeException;
use Symfony\Component\Process\PhpExecutableFinder;
Expand Down Expand Up @@ -164,6 +165,20 @@ public function operatingSystem(): OperatingSystem
return $winOrNot === 'win' ? OperatingSystem::Windows : OperatingSystem::NonWindows;
}

public function operatingSystemFamily(): OperatingSystemFamily
{
$output = Process::run([
$this->phpBinaryPath,
'-r',
'echo PHP_OS_FAMILY;',
]);

$osFamily = OperatingSystemFamily::tryFrom(trim($output));
Assert::notNull($osFamily, 'Could not determine operating system family');

return $osFamily;
}

/** @return non-empty-string */
public function version(): string
{
Expand Down
5 changes: 4 additions & 1 deletion src/Platform/TargetPlatform.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class TargetPlatform
{
public function __construct(
public readonly OperatingSystem $operatingSystem,
public readonly OperatingSystemFamily $operatingSystemFamily,
public readonly PhpBinaryPath $phpBinaryPath,
public readonly Architecture $architecture,
public readonly ThreadSafetyMode $threadSafety,
Expand All @@ -38,7 +39,8 @@ public static function isRunningAsRoot(): bool

public static function fromPhpBinaryPath(PhpBinaryPath $phpBinaryPath, int|null $makeParallelJobs): self
{
$os = $phpBinaryPath->operatingSystem();
$os = $phpBinaryPath->operatingSystem();
$osFamily = $phpBinaryPath->operatingSystemFamily();

$phpinfo = $phpBinaryPath->phpinfo();

Expand Down Expand Up @@ -114,6 +116,7 @@ public static function fromPhpBinaryPath(PhpBinaryPath $phpBinaryPath, int|null

return new self(
$os,
$osFamily,
$phpBinaryPath,
$architecture,
$threadSafety,
Expand Down
10 changes: 10 additions & 0 deletions test/integration/Building/UnixBuildTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public function testUnixBuildCanBuildExtension(): void
true,
true,
null,
[],
[],
),
self::TEST_EXTENSION_PATH,
);
Expand Down Expand Up @@ -101,6 +103,8 @@ public function testUnixBuildWillThrowExceptionWhenExpectedBinaryNameMismatches(
true,
true,
null,
[],
[],
),
self::TEST_EXTENSION_PATH,
);
Expand Down Expand Up @@ -142,6 +146,8 @@ public function testUnixBuildCanBuildExtensionWithBuildPath(): void
true,
true,
'pie_test_ext',
[],
[],
),
dirname(self::TEST_EXTENSION_PATH),
);
Expand Down Expand Up @@ -199,6 +205,8 @@ public function testCleanupDoesNotCleanWhenConfigureIsMissing(): void
true,
true,
null,
[],
[],
),
self::TEST_EXTENSION_PATH,
);
Expand Down Expand Up @@ -241,6 +249,8 @@ public function testVerboseOutputShowsCleanupMessages(): void
true,
true,
null,
[],
[],
),
self::TEST_EXTENSION_PATH,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Php\Pie\ExtensionType;
use Php\Pie\Platform\Architecture;
use Php\Pie\Platform\OperatingSystem;
use Php\Pie\Platform\OperatingSystemFamily;
use Php\Pie\Platform\TargetPhp\PhpBinaryPath;
use Php\Pie\Platform\TargetPlatform;
use Php\Pie\Platform\ThreadSafetyMode;
Expand All @@ -34,6 +35,7 @@ public function testDeterminingReleaseAssetUrlForWindows(): void

$targetPlatform = new TargetPlatform(
OperatingSystem::Windows,
OperatingSystemFamily::Windows,
$phpBinaryPath,
Architecture::x86_64,
ThreadSafetyMode::ThreadSafe,
Expand All @@ -52,6 +54,8 @@ public function testDeterminingReleaseAssetUrlForWindows(): void
true,
true,
null,
[],
[],
);

$io = $this->createMock(IOInterface::class);
Expand Down
2 changes: 2 additions & 0 deletions test/integration/Installing/UnixInstallTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ public function testUnixInstallCanInstallExtension(string $phpConfig): void
true,
true,
null,
[],
[],
),
self::TEST_EXTENSION_PATH,
);
Expand Down
4 changes: 4 additions & 0 deletions test/integration/Installing/WindowsInstallTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Php\Pie\Installing\WindowsInstall;
use Php\Pie\Platform\Architecture;
use Php\Pie\Platform\OperatingSystem;
use Php\Pie\Platform\OperatingSystemFamily;
use Php\Pie\Platform\TargetPhp\PhpBinaryPath;
use Php\Pie\Platform\TargetPlatform;
use Php\Pie\Platform\ThreadSafetyMode;
Expand Down Expand Up @@ -54,12 +55,15 @@ public function testWindowsInstallCanInstallExtension(): void
true,
true,
null,
[],
[],
),
self::TEST_EXTENSION_PATH,
);
$output = new BufferedOutput();
$targetPlatform = new TargetPlatform(
OperatingSystem::Windows,
OperatingSystemFamily::Windows,
PhpBinaryPath::fromCurrentProcess(),
Architecture::x86_64,
ThreadSafetyMode::ThreadSafe,
Expand Down
2 changes: 2 additions & 0 deletions test/unit/Command/CommandHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ public function testProcessingConfigureOptionsFromInput(): void
true,
true,
null,
[],
[],
);
$inputDefinition = new InputDefinition();
$inputDefinition->addOption(new InputOption('with-stuff', null, InputOption::VALUE_REQUIRED));
Expand Down
Loading

0 comments on commit f934832

Please sign in to comment.