Skip to content
Merged
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 docs/extension-maintainers.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,12 @@ in the traditional manner.
}
```

> [!NOTE]
> The `pie build` command does not use `pre-packaged-binary` by default, so
> that it always compiles from source. If `pre-packaged-binary` is the only
> `download-url-method` listed, `pie build` will fail for your extension, and
> end users will need to use `pie install` instead.

##### `os-families` restrictions

The `os-families` and `os-families-exclude` directive allow extention maintainers
Expand Down
5 changes: 5 additions & 0 deletions features/build-extensions.feature
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ Feature: Extensions can be built with PIE
When I run a command to build an extension with configure options
Then the extension should have been built with options

# pie build <ext> --suppress-download-url-method=pre-packaged-source
Example: A pre-packaged binary can be used for a build when explicitly requested
When I run a command to build an extension suppressing a different download method
Then the pre-packaged binary should have been used instead of building

# pie build <ext1> <ext2>
Example: Multiple extensions can be built at once
When I run a command to build multiple extensions
Expand Down
2 changes: 2 additions & 0 deletions src/Command/BuildCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Php\Pie\DependencyResolver\InvalidPackageName;
use Php\Pie\DependencyResolver\ResolvedPackageRequest;
use Php\Pie\DependencyResolver\UnableToResolveRequirement;
use Php\Pie\Downloading\DownloadUrlMethod;
use Php\Pie\Installing\InstallForPhpProject\FindMatchingPackages;
use Php\Pie\Platform\PackageManager;
use Php\Pie\SelfManage\BuildTools\CheckAllBuildTools;
Expand Down Expand Up @@ -50,6 +51,7 @@ public function configure(): void
parent::configure();

CommandHelper::configureDownloadBuildInstallOptions($this);
CommandHelper::suppressDownloadUrlMethodsByDefault($this, DownloadUrlMethod::PrePackagedBinary);
}

public function execute(InputInterface $input, OutputInterface $output): int
Expand Down
11 changes: 11 additions & 0 deletions src/Command/CommandHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,17 @@ public static function noDev(InputInterface $input): bool
return $input->hasOption(self::OPTION_NO_DEV) && $input->getOption(self::OPTION_NO_DEV);
}

public static function suppressDownloadUrlMethodsByDefault(Command $command, DownloadUrlMethod ...$downloadUrlMethods): void
{
$command
->getDefinition()
->getOption(self::OPTION_SUPPRESS_DOWNLOAD_URL_METHOD)
->setDefault(array_values(array_map(
static fn (DownloadUrlMethod $downloadUrlMethod): string => $downloadUrlMethod->value,
$downloadUrlMethods,
)));
}

/** @return list<DownloadUrlMethod> */
public static function determineSuppressedDownloadUrlMethods(InputInterface $input): array
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,12 @@ public static function forPackage(Package $piePackage): self
$piePackage->name(),
));
}

public static function forBuildOfPackageOnlyProvidingPrePackagedBinaries(Package $piePackage): self
{
return new self(sprintf(
'Cannot build %1$s from source, as it only provides pre-packaged binaries. Use "pie install %1$s" to install the pre-packaged binary instead.',
$piePackage->name(),
));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Composer\Package\CompletePackageInterface;
use Composer\Util\HttpDownloader;
use Php\Pie\ComposerIntegration\PieComposerRequest;
use Php\Pie\ComposerIntegration\PieOperation;
use Php\Pie\DependencyResolver\Package;
use Php\Pie\Downloading\DownloadUrlMethod;
use Php\Pie\Downloading\PackageReleaseAssets;
Expand Down Expand Up @@ -90,6 +91,10 @@ function (OperationInterface $operation): void {
}

if ($remainingDownloadUrlMethods === []) {
if ($this->composerRequest->operation === PieOperation::Build && $downloadUrlMethods === [DownloadUrlMethod::PrePackagedBinary]) {
throw AllDownloadUrlMethodsSuppressed::forBuildOfPackageOnlyProvidingPrePackagedBinaries($piePackage);
}

throw AllDownloadUrlMethodsSuppressed::forPackage($piePackage);
}

Expand Down
38 changes: 25 additions & 13 deletions test/behaviour/CliContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,6 @@ public function theExtensionShouldHaveBeenBuilt(): void
return;
}

if (str_contains($this->output, 'Found prebuilt archive')) {
Assert::contains($this->output, 'Found prebuilt archive');
Assert::contains($this->output, 'Pre-packaged binary found');

return;
}

Assert::contains($this->output, 'phpize complete.');
Assert::contains($this->output, 'Configure complete');
Assert::contains($this->output, 'Build complete:');
Expand All @@ -230,16 +223,35 @@ public function theExtensionShouldHaveBeenBuiltWithOptions(): void
return;
}

if (str_contains($this->output, 'Found prebuilt archive')) {
Assert::contains($this->output, 'Found prebuilt archive');
Assert::contains($this->output, 'Pre-packaged binary found');
Assert::contains($this->output, 'phpize complete.');
Assert::contains($this->output, 'Configure complete with options: --with-hello-name=sup');
Assert::contains($this->output, 'Build complete:');
}

#[When('I run a command to build an extension suppressing a different download method')]
public function iRunACommandToBuildAnExtensionSuppressingOnlyADifferentDownloadMethod(): void
{
$this->runPieCommand(['build', 'asgrim/example-pie-extension', '--suppress-download-url-method=pre-packaged-source', '-vv']);
}

#[Then('the pre-packaged binary should have been used instead of building')]
public function thePrePackagedBinaryShouldHaveBeenUsedInsteadOfBuilding(): void
{
if (str_contains($this->output . $this->errorOutput, 'Failed locating asset [pre-packaged-binary]')) {
throw new PendingException('The extension has no pre-packaged binary for this PHP version - skipping');
}

$this->assertCommandSuccessful();

if (Platform::isWindows()) {
Assert::contains($this->output, 'Nothing to do on Windows');

return;
}

Assert::contains($this->output, 'phpize complete.');
Assert::contains($this->output, 'Configure complete with options: --with-hello-name=sup');
Assert::contains($this->output, 'Build complete:');
Assert::contains($this->output, 'Found prebuilt archive');
Assert::contains($this->output, 'Pre-packaged binary found');
Assert::notContains($this->output, 'phpize complete.');
}

#[When('I run a command to install an extension')]
Expand Down
28 changes: 28 additions & 0 deletions test/unit/Command/CommandHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,34 @@ public function testDetermineSuppressedDownloadUrlMethodsParsesGivenValues(): vo
);
}

public function testDownloadUrlMethodsSuppressedByDefaultAreUsedWhenOptionIsNotGiven(): void
{
$command = new Command();
$input = new ArrayInput([]);
CommandHelper::configureDownloadBuildInstallOptions($command);
CommandHelper::suppressDownloadUrlMethodsByDefault($command, DownloadUrlMethod::PrePackagedBinary);
CommandHelper::validateInput($input, $command);

self::assertSame(
[DownloadUrlMethod::PrePackagedBinary],
CommandHelper::determineSuppressedDownloadUrlMethods($input),
);
}

public function testDownloadUrlMethodsSuppressedByDefaultAreReplacedWhenOptionIsGiven(): void
{
$command = new Command();
$input = new ArrayInput(['--suppress-download-url-method' => ['pre-packaged-source']]);
CommandHelper::configureDownloadBuildInstallOptions($command);
CommandHelper::suppressDownloadUrlMethodsByDefault($command, DownloadUrlMethod::PrePackagedBinary);
CommandHelper::validateInput($input, $command);

self::assertSame(
[DownloadUrlMethod::PrePackagedSourceDownload],
CommandHelper::determineSuppressedDownloadUrlMethods($input),
);
}

public function testDetermineSuppressedDownloadUrlMethodsThrowsForInvalidValue(): void
{
$command = new Command();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -813,4 +813,56 @@ public function testSuppressingAllDownloadUrlMethodsWillThrowException(): void
$this->expectException(AllDownloadUrlMethodsSuppressed::class);
$listener($installerEvent);
}

public function testBuildingPackageThatOnlyProvidesPrePackagedBinariesExplainsItCannotBeBuilt(): void
{
$composerPackage = new CompletePackage('foo/bar', '1.2.3.0', '1.2.3');
$composerPackage->setDistType('zip');
$composerPackage->setDistUrl('https://example.com/git-archive-zip-url');
$composerPackage->setPhpExt([
'extension-name' => 'foobar',
'download-url-method' => ['pre-packaged-binary'],
]);

$installerEvent = new InstallerEvent(
InstallerEvents::PRE_OPERATIONS_EXEC,
$this->composer,
$this->io,
false,
true,
new Transaction([], [$composerPackage]),
);

$this->container
->expects(self::never())
->method('get');

$listener = new OverrideDownloadUrlInstallListener(
$this->composer,
$this->io,
$this->container,
new PieComposerRequest(
$this->createMock(IOInterface::class),
new TargetPlatform(
OperatingSystem::NonWindows,
OperatingSystemFamily::Linux,
PhpBinaryPath::fromCurrentProcess(),
Architecture::x86_64,
ThreadSafetyMode::NonThreadSafe,
1,
WindowsCompiler::VC15,
null,
),
[new RequestedPackageAndVersion('foo/bar', '^1.1')],
PieOperation::Build,
[],
false,
suppressedDownloadUrlMethods: [DownloadUrlMethod::PrePackagedBinary],
),
);

$this->expectException(AllDownloadUrlMethodsSuppressed::class);
$this->expectExceptionMessage('Cannot build foo/bar from source, as it only provides pre-packaged binaries. Use "pie install foo/bar" to install the pre-packaged binary instead.');
$listener($installerEvent);
}
}
Loading