Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
16 changes: 16 additions & 0 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,22 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

end-to-end-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
# Fixes `git describe` picking the wrong tag - see https://github.com/php/pie/issues/307
- run: git fetch --tags --force
# Ensure some kind of previous tag exists, otherwise box fails
- run: git describe --tags HEAD || git tag 0.0.0
- uses: ramsey/composer-install@v3
- name: Run the tests
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: test/end-to-end/dockerfile-e2e-test.sh

behaviour-tests:
runs-on: ${{ matrix.operating-system }}
strategy:
Expand Down
6 changes: 0 additions & 6 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -318,12 +318,6 @@ parameters:
count: 4
path: src/Platform/TargetPhp/PhpBinaryPath.php

-
message: '#^Call to function array_key_exists\(\) with 1 and array\{non\-falsy\-string, string\} will always evaluate to true\.$#'
identifier: function.alreadyNarrowedType
count: 1
path: src/Platform/TargetPhp/PhpizePath.php

-
message: '#^Call to function array_key_exists\(\) with 2 and array\{non\-falsy\-string, string, non\-falsy\-string\} will always evaluate to true\.$#'
identifier: function.alreadyNarrowedType
Expand Down
12 changes: 12 additions & 0 deletions src/Command/BuildCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
use Php\Pie\DependencyResolver\InvalidPackageName;
use Php\Pie\DependencyResolver\UnableToResolveRequirement;
use Php\Pie\Installing\InstallForPhpProject\FindMatchingPackages;
use Php\Pie\SelfManage\BuildTools\CheckAllBuildTools;
use Php\Pie\SelfManage\BuildTools\PackageManager;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
Expand All @@ -35,6 +37,7 @@ public function __construct(
private readonly ComposerIntegrationHandler $composerIntegrationHandler,
private readonly FindMatchingPackages $findMatchingPackages,
private readonly IOInterface $io,
private readonly CheckAllBuildTools $checkBuildTools,
) {
parent::__construct();
}
Expand Down Expand Up @@ -64,6 +67,15 @@ public function execute(InputInterface $input, OutputInterface $output): int
$forceInstallPackageVersion = CommandHelper::determineForceInstallingPackageVersion($input);
CommandHelper::applyNoCacheOptionIfSet($input, $this->io);

if (CommandHelper::shouldCheckForBuildTools($input)) {
$this->checkBuildTools->check(
$this->io,
PackageManager::detect(),
$targetPlatform,
CommandHelper::autoInstallBuildTools($input),
);
}

$composer = PieComposerFactory::createPieComposer(
$this->container,
new PieComposerRequest(
Expand Down
31 changes: 31 additions & 0 deletions src/Command/CommandHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ final class CommandHelper
private const OPTION_SKIP_ENABLE_EXTENSION = 'skip-enable-extension';
private const OPTION_FORCE = 'force';
private const OPTION_NO_CACHE = 'no-cache';
private const OPTION_AUTO_INSTALL_BUILD_TOOLS = 'auto-install-build-tools';
private const OPTION_SUPPRESS_BUILD_TOOLS_CHECK = 'no-build-tools-check';

private function __construct()
{
Expand Down Expand Up @@ -139,6 +141,19 @@ public static function configureDownloadBuildInstallOptions(Command $command, bo
'When installing a PHP project, allow non-interactive project installations. Only used in certain contexts.',
);

$command->addOption(
self::OPTION_AUTO_INSTALL_BUILD_TOOLS,
null,
InputOption::VALUE_NONE,
'If build tools are missing, automatically install them, instead of prompting.',
);
$command->addOption(
self::OPTION_SUPPRESS_BUILD_TOOLS_CHECK,
null,
InputOption::VALUE_NONE,
'Do not perform the check to see if build tools are present on the system.',
);

/**
* Allows additional options for the `./configure` command to be passed here.
* Note, this means you probably need to call {@see self::validateInput()} to validate the input manually...
Expand Down Expand Up @@ -228,6 +243,22 @@ public static function determineForceInstallingPackageVersion(InputInterface $in
return $input->hasOption(self::OPTION_FORCE) && $input->getOption(self::OPTION_FORCE);
}

public static function autoInstallBuildTools(InputInterface $input): bool
{
return $input->hasOption(self::OPTION_AUTO_INSTALL_BUILD_TOOLS)
&& $input->getOption(self::OPTION_AUTO_INSTALL_BUILD_TOOLS);
}

public static function shouldCheckForBuildTools(InputInterface $input): bool
{
if (Platform::isWindows()) {
return false;
}

return ! $input->hasOption(self::OPTION_SUPPRESS_BUILD_TOOLS_CHECK)
|| ! $input->getOption(self::OPTION_SUPPRESS_BUILD_TOOLS_CHECK);
}

public static function determinePhpizePathFromInputs(InputInterface $input): PhpizePath|null
{
if ($input->hasOption(self::OPTION_WITH_PHPIZE_PATH)) {
Expand Down
12 changes: 12 additions & 0 deletions src/Command/InstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
use Php\Pie\DependencyResolver\UnableToResolveRequirement;
use Php\Pie\Installing\InstallForPhpProject\FindMatchingPackages;
use Php\Pie\Platform\TargetPlatform;
use Php\Pie\SelfManage\BuildTools\CheckAllBuildTools;
use Php\Pie\SelfManage\BuildTools\PackageManager;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
Expand All @@ -37,6 +39,7 @@ public function __construct(
private readonly InvokeSubCommand $invokeSubCommand,
private readonly FindMatchingPackages $findMatchingPackages,
private readonly IOInterface $io,
private readonly CheckAllBuildTools $checkBuildTools,
) {
parent::__construct();
}
Expand Down Expand Up @@ -78,6 +81,15 @@ public function execute(InputInterface $input, OutputInterface $output): int
$forceInstallPackageVersion = CommandHelper::determineForceInstallingPackageVersion($input);
CommandHelper::applyNoCacheOptionIfSet($input, $this->io);

if (CommandHelper::shouldCheckForBuildTools($input)) {
$this->checkBuildTools->check(
$this->io,
PackageManager::detect(),
$targetPlatform,
CommandHelper::autoInstallBuildTools($input),
);
}

$composer = PieComposerFactory::createPieComposer(
$this->container,
new PieComposerRequest(
Expand Down
8 changes: 8 additions & 0 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
use Php\Pie\Installing\UninstallUsingUnlink;
use Php\Pie\Installing\UnixInstall;
use Php\Pie\Installing\WindowsInstall;
use Php\Pie\SelfManage\BuildTools\CheckAllBuildTools;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
Expand Down Expand Up @@ -197,6 +198,13 @@ static function (ContainerInterface $container): Install {
},
);

$container->singleton(
CheckAllBuildTools::class,
static function (): CheckAllBuildTools {
return CheckAllBuildTools::buildToolsFactory();
},
);

$container->alias(UninstallUsingUnlink::class, Uninstall::class);

$container->alias(Ini\RemoveIniEntryWithFileGetContents::class, Ini\RemoveIniEntry::class);
Expand Down
24 changes: 24 additions & 0 deletions src/Platform/TargetPhp/PhpBinaryPath.php
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,30 @@ public function majorMinorVersion(): string
return $phpVersion;
}

public function majorVersion(): int
{
$phpVersion = self::cleanWarningAndDeprecationsFromOutput(Process::run([
$this->phpBinaryPath,
'-r',
'echo PHP_MAJOR_VERSION;',
]));
Assert::stringNotEmpty($phpVersion, 'Could not determine PHP version');

return (int) $phpVersion;
}

public function minorVersion(): int
{
$phpVersion = self::cleanWarningAndDeprecationsFromOutput(Process::run([
$this->phpBinaryPath,
'-r',
'echo PHP_MINOR_VERSION;',
]));
Assert::stringNotEmpty($phpVersion, 'Could not determine PHP version');

return (int) $phpVersion;
}

public function machineType(): Architecture
{
$phpMachineType = self::cleanWarningAndDeprecationsFromOutput(Process::run([
Expand Down
45 changes: 27 additions & 18 deletions src/Platform/TargetPhp/PhpizePath.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use RuntimeException;
use Symfony\Component\Process\Process;

use function array_key_exists;
use function assert;
use function file_exists;
use function is_executable;
Expand All @@ -27,6 +26,32 @@ public function __construct(public readonly string $phpizeBinaryPath)
{
}

public static function looksLikeValidPhpize(string $phpizePathToCheck, string|null $forPhpApiVersion = null): bool
{
$phpizeAttempt = $phpizePathToCheck; // @todo
if ($phpizeAttempt === '') {
return false;
}

if (! file_exists($phpizeAttempt) || ! is_executable($phpizeAttempt)) {
return false;
}

$phpizeProcess = new Process([$phpizeAttempt, '--version']);
if ($phpizeProcess->run() !== 0) {
return false;
}

if (
! preg_match('/PHP Api Version:\s*(.*)/', $phpizeProcess->getOutput(), $m)
|| $m[1] === ''
) {
return false;
}

return $forPhpApiVersion === null || $forPhpApiVersion === $m[1];
}

public static function guessFrom(PhpBinaryPath $phpBinaryPath): self
{
$expectedApiVersion = $phpBinaryPath->phpApiVersion();
Expand All @@ -45,24 +70,8 @@ public static function guessFrom(PhpBinaryPath $phpBinaryPath): self
foreach ($phpizeAttempts as $phpizeAttempt) {
assert($phpizeAttempt !== null);
assert($phpizeAttempt !== '');
if (! file_exists($phpizeAttempt) || ! is_executable($phpizeAttempt)) {
continue;
}

$phpizeProcess = new Process([$phpizeAttempt, '--version']);
if ($phpizeProcess->run() !== 0) {
continue;
}

if (
! preg_match('/PHP Api Version:\s*(.*)/', $phpizeProcess->getOutput(), $m)
|| ! array_key_exists(1, $m)
|| $m[1] === ''
) {
continue;
}

if ($expectedApiVersion === $m[1]) {
if (self::looksLikeValidPhpize($phpizeAttempt, $expectedApiVersion)) {
return new self($phpizeAttempt);
}
}
Expand Down
47 changes: 47 additions & 0 deletions src/SelfManage/BuildTools/BinaryBuildToolFinder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Php\Pie\SelfManage\BuildTools;

use Php\Pie\Platform\TargetPlatform;
use Symfony\Component\Process\ExecutableFinder;

use function array_key_exists;
use function str_replace;

/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
class BinaryBuildToolFinder
{
/** @param array<PackageManager::*, non-empty-string|null> $packageManagerPackages */
public function __construct(
public readonly string $tool,
private readonly array $packageManagerPackages,
) {
}

public function check(): bool
{
return (new ExecutableFinder())->find($this->tool) !== null;
}

/** @return non-empty-string|null */
public function packageNameFor(PackageManager $packageManager, TargetPlatform $targetPlatform): string|null
{
if (! array_key_exists($packageManager->value, $this->packageManagerPackages) || $this->packageManagerPackages[$packageManager->value] === null) {
return null;
}

// If we need to customise specific package names depending on OS
// specific parameters, this is likely the place to do it
return str_replace(
'{major}',
(string) $targetPlatform->phpBinaryPath->majorVersion(),
str_replace(
'{minor}',
(string) $targetPlatform->phpBinaryPath->minorVersion(),
$this->packageManagerPackages[$packageManager->value],
),
);
}
}
Loading