Skip to content

Commit 1fa74f0

Browse files
authored
Merge pull request #526 from asgrim/435-prompt-to-install-missing-system-deps
435: prompt to install missing system deps
2 parents 36fbea6 + e7053cb commit 1fa74f0

33 files changed

Lines changed: 1020 additions & 78 deletions

.noai

Whitespace-only changes.

README.md

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33
## What is PIE?
44

5-
PIE is a new installer for PHP extensions, intended to eventually replace PECL.
6-
It is distributed as a [PHAR](https://www.php.net/manual/en/intro.phar.php),
7-
just like Composer, and works in a similar way to Composer, but it installs PHP
8-
extensions (PHP Modules or Zend Extensions) to your PHP installation, rather
9-
than pulling PHP packages into your project or library.
5+
PIE is the official installer for PHP extensions, which replaces
6+
[PECL](https://pecl.php.net/) (which is now deprecated). PIE is distributed as a
7+
[PHAR](https://www.php.net/manual/en/intro.phar.php), just like Composer, and
8+
works in a similar way to Composer, but it installs PHP extensions (PHP Modules
9+
or Zend Extensions) to your PHP installation, rather than pulling PHP packages
10+
into your project or library.
1011

1112
# Using PIE - what do I need to get started?
1213

@@ -15,12 +16,8 @@ than pulling PHP packages into your project or library.
1516
You will need PHP 8.1 or newer to run PIE, but PIE can install an extension to
1617
any other installed PHP version.
1718

18-
On Linux, you will need a build toolchain installed. On Debian/Ubuntu type
19-
systems, you could run something like:
20-
21-
```shell
22-
sudo apt install gcc make autoconf libtool bison re2c pkg-config php-dev
23-
```
19+
On Linux/OSX, if any build tools needed are missing, PIE will ask if you would
20+
like to automatically install them first (this is a new feature in 1.4.0).
2421

2522
On Windows, you do not need any build toolchain installed, since PHP extensions
2623
for Windows are distributed as pre-compiled packages containing the extension
@@ -38,7 +35,9 @@ Further installation details can be found in the [usage](./docs/usage.md) docs.
3835
This documentation assumes you have moved `pie.phar` into your `$PATH`, e.g.
3936
`/usr/local/bin/pie` on non-Windows systems or created an alias in your shell RC file.
4037

41-
## Installing a single extension using PIE
38+
## Using PIE
39+
40+
### Installing a single extension using PIE
4241

4342
You can install an extension using the `install` command. For example, to
4443
install the `example_pie_extension` extension, you would run:
@@ -57,7 +56,7 @@ You must now add "extension=example_pie_extension" to your php.ini
5756
$
5857
```
5958

60-
## Installing all extensions for a PHP project
59+
### Installing all extensions for a PHP project
6160

6261
When in your PHP project, you can install any missing top-level extensions:
6362

@@ -87,6 +86,12 @@ The following packages may be suitable, which would you like to install:
8786
Finished checking extensions.
8887
```
8988

89+
> [!TIP]
90+
> If you are running PIE in a non-interactive shell (for example, CI, a
91+
> container), pass the `--allow-non-interactive-project-install` flag to run
92+
> this command. It may still fail if more than one PIE package provides a
93+
> particular extension.
94+
9095
## Extensions that support PIE
9196

9297
A list of extensions that support PIE can be found on
@@ -105,6 +110,6 @@ A list of extensions that support PIE can be found on
105110
If you are an extension maintainer wanting to add PIE support to your extension,
106111
please read [extension-maintainers](./docs/extension-maintainers.md).
107112

108-
## More documentation...
113+
# More documentation...
109114

110115
The full documentation for PIE can be found in [usage](./docs/usage.md) docs.

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"bnf/phpstan-psr-container": "^1.1",
4747
"doctrine/coding-standard": "^14.0.0",
4848
"phpstan/phpstan": "^2.1.38",
49+
"phpstan/phpstan-phpunit": "^2.0",
4950
"phpstan/phpstan-webmozart-assert": "^2.0",
5051
"phpunit/phpunit": "^10.5.63"
5152
},

composer.lock

Lines changed: 57 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/extension-maintainers.md

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -345,11 +345,16 @@ The list of accepted OS families: "windows", "bsd", "darwin", "solaris", "linux"
345345
346346
#### Extension dependencies
347347

348-
Extension authors may define some dependencies in `require`, but practically,
348+
Extension authors may define some dependencies in `require`, but typically,
349349
most extensions would not need to define dependencies, except for the PHP
350-
versions supported by the extension. Dependencies on other extensions may be
351-
defined, for example `ext-json`. However, dependencies on a regular PHP package
352-
(such as `monolog/monolog`) SHOULD NOT be specified in your `require` section.
350+
versions supported by the extension, and system libraries.
351+
352+
Dependencies on a regular PHP package (such as `monolog/monolog`) SHOULD NOT be
353+
specified in your extension's `require` section.
354+
355+
##### Dependencies on other extensions
356+
357+
Dependencies on other extensions may be defined, for example `ext-json`.
353358

354359
It is worth noting that if your extension does define a dependency on another
355360
dependency, and this is not available, someone installing your extension would
@@ -360,6 +365,47 @@ Cannot use myvendor/myextension's latest version 1.2.3 as it requires
360365
ext-something * which is missing from your platform.
361366
```
362367

368+
##### System Library Dependencies
369+
370+
In PIE 1.4.0, the ability for extension authors to define system library
371+
dependencies was added, and in some cases, automatically install them.
372+
373+
The following libraries are supported at the moment. **If you would like to add
374+
a library, please [open a discussion](https://github.com/php/pie/discussions)
375+
in the first instance.** Don't just open a PR without discussing first please!
376+
377+
We are adding libraries and improving this feature over time. If the automatic
378+
install of a system dependency that is supported below in your package manager
379+
is NOT working, then please [report a bug](https://github.com/php/pie/issues).
380+
381+
| Library | Checked by PIE | Auto-installs in |
382+
|---------------|----------------|--------------------|
383+
| lib-curl || apt, apk, dnf, yum |
384+
| lib-enchant |||
385+
| lib-enchant-2 |||
386+
| lib-sodium || apt, apk, dnf, yum |
387+
| lib-ffi || apt, apk, dnf, yum |
388+
| lib-xslt || apt, apk, dnf, yum |
389+
| lib-zip || apt, apk, dnf, yum |
390+
| lib-png |||
391+
| lib-avif |||
392+
| lib-webp |||
393+
| lib-jpeg || apt, apk, dnf, yum |
394+
| lib-xpm |||
395+
| lib-freetype2 |||
396+
| lib-gdlib |||
397+
| lib-gmp |||
398+
| lib-sasl |||
399+
| lib-onig |||
400+
| lib-odbc |||
401+
| lib-capstone |||
402+
| lib-pcre |||
403+
| lib-edit |||
404+
| lib-snmp |||
405+
| lib-argon2 |||
406+
| lib-uriparser |||
407+
| lib-exslt |||
408+
363409
#### Checking the extension will work
364410

365411
First up, you can use `composer validate` to check your `composer.json` is

docs/usage.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,31 @@ pie install example/some-extension --with-some-library-name=/path/to/the/lib
281281
pie install example/some-extension --with-some-library-name=/path/to/the/lib --enable-some-functionality
282282
```
283283

284+
### Build tools check
285+
286+
PIE will attempt to check the presence of build tools (such as gcc, make, etc.)
287+
before running. If any are missing, an interactive prompt will ask if you would
288+
like to install the missing tools. If you are running in non-interactive mode
289+
(for example, in a CI pipeline, container build, etc), PIE will **not**
290+
install these tools automatically. If you would like to install the build tools
291+
in a non-interactive terminal, pass the `--auto-install-build-tools` and the
292+
prompt will be skipped.
293+
294+
To skip the build tools check entirely, pass the `--no-build-tools-check` flag.
295+
296+
### System library dependencies check
297+
298+
PIE will attempt to check the presence of system library dependencies before
299+
installing an extension. If any are missing, an interactive prompt will ask if
300+
you would like to install the missing tools. If you are running in
301+
non-interactive mode (for example, in a CI pipeline, container build, etc), PIE
302+
will **not** install these dependencies automatically. If you would like to
303+
install the system dependencies in a non-interactive terminal, pass the
304+
`--auto-install-system-dependencies` and the prompt will be skipped.
305+
306+
To skip the dependencies check entirely, pass the
307+
`--no-system-dependencies-check` flag.
308+
284309
### Configuring the INI file
285310

286311
PIE will automatically try to enable the extension by adding `extension=...` or

phpstan-baseline.neon

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,12 @@ parameters:
288288
count: 1
289289
path: src/Platform.php
290290

291+
-
292+
message: '#^Parameter \#1 \$callback of function array_map expects \(callable\(Composer\\Package\\BasePackage\)\: mixed\)\|null, Closure\(Composer\\Package\\CompletePackageInterface\)\: Php\\Pie\\DependencyResolver\\Package given\.$#'
293+
identifier: argument.type
294+
count: 1
295+
path: src/Platform/InstalledPiePackages.php
296+
291297
-
292298
message: '#^Call to function array_key_exists\(\) with 1 and array\{non\-falsy\-string, non\-empty\-string, non\-empty\-string\} will always evaluate to true\.$#'
293299
identifier: function.alreadyNarrowedType
@@ -354,18 +360,6 @@ parameters:
354360
count: 1
355361
path: test/integration/Command/InstallCommandTest.php
356362

357-
-
358-
message: '#^Parameter \#1 \$originalClassName of method PHPUnit\\Framework\\TestCase\:\:createMock\(\) expects class\-string\<object\>, string given\.$#'
359-
identifier: argument.type
360-
count: 1
361-
path: test/integration/Command/InstallExtensionsForProjectCommandTest.php
362-
363-
-
364-
message: '#^Unable to resolve the template type RealInstanceType in call to method PHPUnit\\Framework\\TestCase\:\:createMock\(\)$#'
365-
identifier: argument.templateType
366-
count: 1
367-
path: test/integration/Command/InstallExtensionsForProjectCommandTest.php
368-
369363
-
370364
message: '#^Call to function assert\(\) with true will always evaluate to true\.$#'
371365
identifier: function.alreadyNarrowedType

phpstan.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ includes:
22
- phpstan-baseline.neon
33
- vendor/bnf/phpstan-psr-container/extension.neon
44
- vendor/phpstan/phpstan-webmozart-assert/extension.neon
5+
- vendor/phpstan/phpstan-phpunit/extension.neon
56

67
parameters:
78
level: 10

src/Command/BuildCommand.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,19 @@
1111
use Php\Pie\ComposerIntegration\PieComposerRequest;
1212
use Php\Pie\ComposerIntegration\PieOperation;
1313
use Php\Pie\DependencyResolver\BundledPhpExtensionRefusal;
14+
use Php\Pie\DependencyResolver\DependencyInstaller\PrescanSystemDependencies;
1415
use Php\Pie\DependencyResolver\DependencyResolver;
1516
use Php\Pie\DependencyResolver\InvalidPackageName;
1617
use Php\Pie\DependencyResolver\UnableToResolveRequirement;
1718
use Php\Pie\Installing\InstallForPhpProject\FindMatchingPackages;
19+
use Php\Pie\Platform\PackageManager;
1820
use Php\Pie\SelfManage\BuildTools\CheckAllBuildTools;
19-
use Php\Pie\SelfManage\BuildTools\PackageManager;
2021
use Psr\Container\ContainerInterface;
2122
use Symfony\Component\Console\Attribute\AsCommand;
2223
use Symfony\Component\Console\Command\Command;
2324
use Symfony\Component\Console\Input\InputInterface;
2425
use Symfony\Component\Console\Output\OutputInterface;
26+
use Throwable;
2527

2628
use function sprintf;
2729

@@ -34,6 +36,7 @@ final class BuildCommand extends Command
3436
public function __construct(
3537
private readonly ContainerInterface $container,
3638
private readonly DependencyResolver $dependencyResolver,
39+
private readonly PrescanSystemDependencies $prescanSystemDependencies,
3740
private readonly ComposerIntegrationHandler $composerIntegrationHandler,
3841
private readonly FindMatchingPackages $findMatchingPackages,
3942
private readonly IOInterface $io,
@@ -88,6 +91,22 @@ public function execute(InputInterface $input, OutputInterface $output): int
8891
),
8992
);
9093

94+
if (CommandHelper::shouldCheckSystemDependencies($input)) {
95+
try {
96+
($this->prescanSystemDependencies)(
97+
$composer,
98+
$targetPlatform,
99+
$requestedNameAndVersion,
100+
CommandHelper::autoInstallSystemDependencies($input),
101+
);
102+
} catch (Throwable $anything) {
103+
$this->io->writeError(
104+
'<comment>Skipping system dependency pre-scan due to exception:</comment> ' . $anything->getMessage(),
105+
verbosity: IOInterface::VERBOSE,
106+
);
107+
}
108+
}
109+
91110
try {
92111
$package = ($this->dependencyResolver)(
93112
$composer,

src/Command/CommandHelper.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ final class CommandHelper
6363
private const OPTION_NO_CACHE = 'no-cache';
6464
private const OPTION_AUTO_INSTALL_BUILD_TOOLS = 'auto-install-build-tools';
6565
private const OPTION_SUPPRESS_BUILD_TOOLS_CHECK = 'no-build-tools-check';
66+
private const OPTION_AUTO_INSTALL_SYSTEM_DEPENDENCIES = 'auto-install-system-dependencies';
67+
private const OPTION_SUPPRESS_SYSTEM_DEPENDENCIES_CHECK = 'no-system-dependencies-check';
6668

6769
private function __construct()
6870
{
@@ -154,6 +156,19 @@ public static function configureDownloadBuildInstallOptions(Command $command, bo
154156
'Do not perform the check to see if build tools are present on the system.',
155157
);
156158

159+
$command->addOption(
160+
self::OPTION_AUTO_INSTALL_SYSTEM_DEPENDENCIES,
161+
null,
162+
InputOption::VALUE_NONE,
163+
'If system dependencies missing, automatically install them, instead of prompting.',
164+
);
165+
$command->addOption(
166+
self::OPTION_SUPPRESS_SYSTEM_DEPENDENCIES_CHECK,
167+
null,
168+
InputOption::VALUE_NONE,
169+
'Do not perform the check to see if system dependencies are present on the system.',
170+
);
171+
157172
/**
158173
* Allows additional options for the `./configure` command to be passed here.
159174
* Note, this means you probably need to call {@see self::validateInput()} to validate the input manually...
@@ -267,6 +282,22 @@ public static function shouldCheckForBuildTools(InputInterface $input): bool
267282
|| ! $input->getOption(self::OPTION_SUPPRESS_BUILD_TOOLS_CHECK);
268283
}
269284

285+
public static function autoInstallSystemDependencies(InputInterface $input): bool
286+
{
287+
return $input->hasOption(self::OPTION_AUTO_INSTALL_SYSTEM_DEPENDENCIES)
288+
&& $input->getOption(self::OPTION_AUTO_INSTALL_SYSTEM_DEPENDENCIES);
289+
}
290+
291+
public static function shouldCheckSystemDependencies(InputInterface $input): bool
292+
{
293+
if (Platform::isWindows()) {
294+
return false;
295+
}
296+
297+
return ! $input->hasOption(self::OPTION_SUPPRESS_SYSTEM_DEPENDENCIES_CHECK)
298+
|| ! $input->getOption(self::OPTION_SUPPRESS_SYSTEM_DEPENDENCIES_CHECK);
299+
}
300+
270301
public static function requestedNameAndVersionPair(InputInterface $input): RequestedPackageAndVersion
271302
{
272303
$requestedPackageString = $input->getArgument(self::ARG_REQUESTED_PACKAGE_AND_VERSION);

0 commit comments

Comments
 (0)