Skip to content

Commit f934832

Browse files
Add support for os-families and os-families-exclude in extensions composer.json
1 parent 3abe8f5 commit f934832

25 files changed

+492
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Php\Pie\DependencyResolver;
6+
7+
use Php\Pie\Platform\OperatingSystemFamily;
8+
use RuntimeException;
9+
10+
use function array_map;
11+
use function implode;
12+
use function sprintf;
13+
14+
class IncompatibleOperatingSystemFamily extends RuntimeException
15+
{
16+
/** @param list<OperatingSystemFamily> $required */
17+
public static function notInCompatibleOperatingSystemFamilies(array $required, OperatingSystemFamily $current): self
18+
{
19+
return new self(sprintf(
20+
'This extension does not support the "%s" operating system family. It is compatible with the following families: "%s".',
21+
$current->value,
22+
implode('", "', array_map(static fn (OperatingSystemFamily $osFamily): string => $osFamily->value, $required)),
23+
));
24+
}
25+
26+
/** @param list<OperatingSystemFamily> $incompatibleOsFamilies */
27+
public static function inIncompatibleOperatingSystemFamily(array $incompatibleOsFamilies, OperatingSystemFamily $current): self
28+
{
29+
return new self(sprintf(
30+
'This extension does not support the "%s" operating system family. It is incompatible with the following families: "%s".',
31+
$current->value,
32+
implode('", "', array_map(static fn (OperatingSystemFamily $osFamily): string => $osFamily->value, $incompatibleOsFamilies)),
33+
));
34+
}
35+
}

src/DependencyResolver/Package.php

+51-1
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,23 @@
55
namespace Php\Pie\DependencyResolver;
66

77
use Composer\Package\CompletePackageInterface;
8+
use InvalidArgumentException;
89
use Php\Pie\ConfigureOption;
910
use Php\Pie\ExtensionName;
1011
use Php\Pie\ExtensionType;
12+
use Php\Pie\Platform\OperatingSystemFamily;
1113

1214
use function array_key_exists;
1315
use function array_map;
1416
use function array_slice;
1517
use function explode;
1618
use function implode;
1719
use function parse_url;
20+
use function sprintf;
1821
use function str_contains;
1922
use function str_starts_with;
23+
use function strtolower;
24+
use function ucfirst;
2025

2126
/**
2227
* @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks
@@ -25,7 +30,11 @@
2530
*/
2631
final class Package
2732
{
28-
/** @param list<ConfigureOption> $configureOptions */
33+
/**
34+
* @param list<ConfigureOption> $configureOptions
35+
* @param list<OperatingSystemFamily> $compatibleOsFamilies
36+
* @param list<OperatingSystemFamily> $incompatibleOsFamilies
37+
*/
2938
public function __construct(
3039
public readonly CompletePackageInterface $composerPackage,
3140
public readonly ExtensionType $extensionType,
@@ -37,6 +46,8 @@ public function __construct(
3746
public readonly bool $supportZts,
3847
public readonly bool $supportNts,
3948
public readonly string|null $buildPath,
49+
public readonly array $compatibleOsFamilies,
50+
public readonly array $incompatibleOsFamilies,
4051
) {
4152
}
4253

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

77+
/** @var list<string> $compatibleOsFamilies */
78+
$compatibleOsFamilies = $phpExtOptions !== null && array_key_exists('os-families', $phpExtOptions)
79+
? $phpExtOptions['os-families']
80+
: [];
81+
82+
/** @var list<string> $incompatibleOsFamilies */
83+
$incompatibleOsFamilies = $phpExtOptions !== null && array_key_exists('os-families-exclude', $phpExtOptions)
84+
? $phpExtOptions['os-families-exclude']
85+
: [];
86+
87+
if ($compatibleOsFamilies && $incompatibleOsFamilies) {
88+
throw new InvalidArgumentException('Cannot specify both "os-families" and "os-families-exclude" in composer.json');
89+
}
90+
6691
return new self(
6792
$completePackage,
6893
ExtensionType::tryFrom($completePackage->getType()) ?? ExtensionType::PhpModule,
@@ -74,6 +99,8 @@ public static function fromComposerCompletePackage(CompletePackageInterface $com
7499
$supportZts,
75100
$supportNts,
76101
$buildPath,
102+
self::convertInputStringsToOperatingSystemFamilies($compatibleOsFamilies),
103+
self::convertInputStringsToOperatingSystemFamilies($incompatibleOsFamilies),
77104
);
78105
}
79106

@@ -100,4 +127,27 @@ public function githubOrgAndRepository(): string
100127
// Converts https://api.github.com/repos/<user>/<repository>/zipball/<sha>" to "<user>/<repository>"
101128
return implode('/', array_slice(explode('/', $parsed['path']), 2, 2));
102129
}
130+
131+
/**
132+
* @param list<string> $input
133+
*
134+
* @return list<OperatingSystemFamily>
135+
*/
136+
private static function convertInputStringsToOperatingSystemFamilies(array $input): array
137+
{
138+
$osFamilies = [];
139+
foreach ($input as $value) {
140+
// try to normalize a bit the input
141+
$valueToTry = ucfirst(strtolower($value));
142+
143+
$family = OperatingSystemFamily::tryFrom($valueToTry);
144+
if ($family === null) {
145+
throw new InvalidArgumentException(sprintf('Expected operating system family to be one of "%s", got "%s".', implode('", "', OperatingSystemFamily::asValuesList()), $value));
146+
}
147+
148+
$osFamilies[] = $family;
149+
}
150+
151+
return $osFamilies;
152+
}
103153
}

src/DependencyResolver/ResolveDependencyWithComposer.php

+19
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Php\Pie\Platform\TargetPlatform;
1313
use Php\Pie\Platform\ThreadSafetyMode;
1414

15+
use function in_array;
1516
use function preg_match;
1617

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

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

59+
$this->assertCompatibleOsFamily($targetPlatform, $piePackage);
5860
$this->assertCompatibleThreadSafetyMode($targetPlatform->threadSafety, $piePackage);
5961

6062
return $piePackage;
@@ -70,4 +72,21 @@ private function assertCompatibleThreadSafetyMode(ThreadSafetyMode $threadSafety
7072
throw IncompatibleThreadSafetyMode::ntsExtensionOnZtsPlatform();
7173
}
7274
}
75+
76+
private function assertCompatibleOsFamily(TargetPlatform $targetPlatform, Package $resolvedPackage): void
77+
{
78+
if ($resolvedPackage->compatibleOsFamilies && ! in_array($targetPlatform->operatingSystemFamily, $resolvedPackage->compatibleOsFamilies, true)) {
79+
throw IncompatibleOperatingSystemFamily::notInCompatibleOperatingSystemFamilies(
80+
$resolvedPackage->compatibleOsFamilies,
81+
$targetPlatform->operatingSystemFamily,
82+
);
83+
}
84+
85+
if ($resolvedPackage->incompatibleOsFamilies && in_array($targetPlatform->operatingSystemFamily, $resolvedPackage->incompatibleOsFamilies, true)) {
86+
throw IncompatibleOperatingSystemFamily::inIncompatibleOperatingSystemFamily(
87+
$resolvedPackage->incompatibleOsFamilies,
88+
$targetPlatform->operatingSystemFamily,
89+
);
90+
}
91+
}
7392
}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Php\Pie\Platform;
6+
7+
use function array_map;
8+
9+
/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
10+
enum OperatingSystemFamily: string
11+
{
12+
case Windows = 'Windows';
13+
case Bsd = 'BSD';
14+
case Darwin = 'Darwin';
15+
case Solaris = 'Solaris';
16+
case Linux = 'Linux';
17+
case Unknown = 'Unknown';
18+
19+
/** @return array<string> */
20+
public static function asValuesList(): array
21+
{
22+
return array_map(
23+
static fn (OperatingSystemFamily $osFamily): string => $osFamily->value,
24+
self::cases(),
25+
);
26+
}
27+
}

src/Platform/TargetPhp/PhpBinaryPath.php

+15
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Composer\Util\Platform;
99
use Php\Pie\Platform\Architecture;
1010
use Php\Pie\Platform\OperatingSystem;
11+
use Php\Pie\Platform\OperatingSystemFamily;
1112
use Php\Pie\Util\Process;
1213
use RuntimeException;
1314
use Symfony\Component\Process\PhpExecutableFinder;
@@ -164,6 +165,20 @@ public function operatingSystem(): OperatingSystem
164165
return $winOrNot === 'win' ? OperatingSystem::Windows : OperatingSystem::NonWindows;
165166
}
166167

168+
public function operatingSystemFamily(): OperatingSystemFamily
169+
{
170+
$output = Process::run([
171+
$this->phpBinaryPath,
172+
'-r',
173+
'echo PHP_OS_FAMILY;',
174+
]);
175+
176+
$osFamily = OperatingSystemFamily::tryFrom(trim($output));
177+
Assert::notNull($osFamily, 'Could not determine operating system family');
178+
179+
return $osFamily;
180+
}
181+
167182
/** @return non-empty-string */
168183
public function version(): string
169184
{

src/Platform/TargetPlatform.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class TargetPlatform
2323
{
2424
public function __construct(
2525
public readonly OperatingSystem $operatingSystem,
26+
public readonly OperatingSystemFamily $operatingSystemFamily,
2627
public readonly PhpBinaryPath $phpBinaryPath,
2728
public readonly Architecture $architecture,
2829
public readonly ThreadSafetyMode $threadSafety,
@@ -38,7 +39,8 @@ public static function isRunningAsRoot(): bool
3839

3940
public static function fromPhpBinaryPath(PhpBinaryPath $phpBinaryPath, int|null $makeParallelJobs): self
4041
{
41-
$os = $phpBinaryPath->operatingSystem();
42+
$os = $phpBinaryPath->operatingSystem();
43+
$osFamily = $phpBinaryPath->operatingSystemFamily();
4244

4345
$phpinfo = $phpBinaryPath->phpinfo();
4446

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

115117
return new self(
116118
$os,
119+
$osFamily,
117120
$phpBinaryPath,
118121
$architecture,
119122
$threadSafety,

test/integration/Building/UnixBuildTest.php

+10
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public function testUnixBuildCanBuildExtension(): void
4848
true,
4949
true,
5050
null,
51+
[],
52+
[],
5153
),
5254
self::TEST_EXTENSION_PATH,
5355
);
@@ -101,6 +103,8 @@ public function testUnixBuildWillThrowExceptionWhenExpectedBinaryNameMismatches(
101103
true,
102104
true,
103105
null,
106+
[],
107+
[],
104108
),
105109
self::TEST_EXTENSION_PATH,
106110
);
@@ -142,6 +146,8 @@ public function testUnixBuildCanBuildExtensionWithBuildPath(): void
142146
true,
143147
true,
144148
'pie_test_ext',
149+
[],
150+
[],
145151
),
146152
dirname(self::TEST_EXTENSION_PATH),
147153
);
@@ -199,6 +205,8 @@ public function testCleanupDoesNotCleanWhenConfigureIsMissing(): void
199205
true,
200206
true,
201207
null,
208+
[],
209+
[],
202210
),
203211
self::TEST_EXTENSION_PATH,
204212
);
@@ -241,6 +249,8 @@ public function testVerboseOutputShowsCleanupMessages(): void
241249
true,
242250
true,
243251
null,
252+
[],
253+
[],
244254
),
245255
self::TEST_EXTENSION_PATH,
246256
);

test/integration/Downloading/GithubPackageReleaseAssetsTest.php

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Php\Pie\ExtensionType;
1616
use Php\Pie\Platform\Architecture;
1717
use Php\Pie\Platform\OperatingSystem;
18+
use Php\Pie\Platform\OperatingSystemFamily;
1819
use Php\Pie\Platform\TargetPhp\PhpBinaryPath;
1920
use Php\Pie\Platform\TargetPlatform;
2021
use Php\Pie\Platform\ThreadSafetyMode;
@@ -34,6 +35,7 @@ public function testDeterminingReleaseAssetUrlForWindows(): void
3435

3536
$targetPlatform = new TargetPlatform(
3637
OperatingSystem::Windows,
38+
OperatingSystemFamily::Windows,
3739
$phpBinaryPath,
3840
Architecture::x86_64,
3941
ThreadSafetyMode::ThreadSafe,
@@ -52,6 +54,8 @@ public function testDeterminingReleaseAssetUrlForWindows(): void
5254
true,
5355
true,
5456
null,
57+
[],
58+
[],
5559
);
5660

5761
$io = $this->createMock(IOInterface::class);

test/integration/Installing/UnixInstallTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ public function testUnixInstallCanInstallExtension(string $phpConfig): void
9191
true,
9292
true,
9393
null,
94+
[],
95+
[],
9496
),
9597
self::TEST_EXTENSION_PATH,
9698
);

test/integration/Installing/WindowsInstallTest.php

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Php\Pie\Installing\WindowsInstall;
1313
use Php\Pie\Platform\Architecture;
1414
use Php\Pie\Platform\OperatingSystem;
15+
use Php\Pie\Platform\OperatingSystemFamily;
1516
use Php\Pie\Platform\TargetPhp\PhpBinaryPath;
1617
use Php\Pie\Platform\TargetPlatform;
1718
use Php\Pie\Platform\ThreadSafetyMode;
@@ -54,12 +55,15 @@ public function testWindowsInstallCanInstallExtension(): void
5455
true,
5556
true,
5657
null,
58+
[],
59+
[],
5760
),
5861
self::TEST_EXTENSION_PATH,
5962
);
6063
$output = new BufferedOutput();
6164
$targetPlatform = new TargetPlatform(
6265
OperatingSystem::Windows,
66+
OperatingSystemFamily::Windows,
6367
PhpBinaryPath::fromCurrentProcess(),
6468
Architecture::x86_64,
6569
ThreadSafetyMode::ThreadSafe,

test/unit/Command/CommandHelperTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ public function testProcessingConfigureOptionsFromInput(): void
110110
true,
111111
true,
112112
null,
113+
[],
114+
[],
113115
);
114116
$inputDefinition = new InputDefinition();
115117
$inputDefinition->addOption(new InputOption('with-stuff', null, InputOption::VALUE_REQUIRED));

0 commit comments

Comments
 (0)