Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow extensions to override download URLs #187

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 11 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
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
],
"require": {
"php": "8.1.*||8.2.*||8.3.*||8.4.*",
"composer/composer": "^2.8.5",
"composer/composer": "dev-main",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably pin to the next stable Composer release, when that happens 👍

Copy link

@stof stof Feb 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest using ^2.9.0@dev instead as constraint here. It will be more explicit about the intent

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^2.8.6@dev

"composer/pcre": "^3.3.2",
"composer/semver": "^3.4.3",
"fidry/cpu-core-counter": "^1.2",
Expand Down
21 changes: 12 additions & 9 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions docs/extension-maintainers.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,26 @@ should specify this path in `build-path`, for example:
}
```

The `build-path` may contain some templated values which are replaced:

* `{version}` to be replaced with the package version. For example a package
with version 1.2.3 with a `build-path` of `myext-{version}` the actual build
path would become `myext-1.2.3`.
Comment on lines +163 to +167
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The build-path could be detected: if the root directory doesn't contain a configure file but there is a single directory that contains this file, then go into it.

For the mongodb extension, the build-path is at the root of the Git repository, so this setting seems incompatible with installation from Git clone.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Leave it with me and I'll see what we can do, thank you


#### `download-url-method`

The `download-url-method` directive allows extension maintainers to
change the behaviour of downloading the source package.

* Setting this to `composer-default`, which is the default value if not
specified, will use the default behaviour implemented by Composer, which is
to use the standard ZIP archive from the GitHub API (or other source control
system).
* Using `pre-packaged-source` will locate a source code package in the release
assets list based matching one of the following naming conventions:
* `php_{ExtensionName}-{Version}-src.tgz` (e.g. `php_myext-1.20.1-src.tgz`)
* `php_{ExtensionName}-{Version}-src.zip` (e.g. `php_myext-1.20.1-src.zip`)

### Extension dependencies

Extension authors may define some dependencies in `require`, but practically,
Expand Down
106 changes: 106 additions & 0 deletions resources/composer-json-php-ext-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://raw.githubusercontent.com/php/pie/main/composer-json-php-ext-schema.json",
"title": "composer.json php-ext schema",
"description": "Schema for the proposed php-ext section in composer.json that the new PECL will use to build packages",
"type": "object",
"properties": {
"php-ext": {
"type": "object",
"description": "Settings for PHP extension packages.",
"properties": {
"extension-name": {
"type": "string",
"description": "If specified, this will be used as the name of the extension, where needed by tooling. If this is not specified, the extension name will be derived from the Composer package name (e.g. `vendor/name` would become `ext-name`). The extension name may be specified with or without the `ext-` prefix, and tools that use this must normalise this appropriately.",
"example": "ext-xdebug"
},
"priority": {
"type": "integer",
"description": "This is used to add a prefix to the INI file, e.g. `90-xdebug.ini` which affects the loading order. The priority is a number in the range 10-99 inclusive, with 10 being the highest priority (i.e. will be processed first), and 99 being the lowest priority (i.e. will be processed last). There are two digits so that the files sort correctly on any platform, whether the sorting is natural or not.",
"minimum": 10,
"maximum": 99,
"example": 80,
"default": 80
},
"support-zts": {
"type": "boolean",
"description": "Does this package support Zend Thread Safety",
"example": false,
"default": true
},
"support-nts": {
"type": "boolean",
"description": "Does this package support non-Thread Safe mode",
"example": false,
"default": true
},
"build-path": {
"type": ["string", "null"],
"description": "If specified, this is the subdirectory that will be used to build the extension instead of the root of the project.",
"example": "my-extension-source",
"default": null
},
"download-url-method": {
"type": "string",
"description": "If specified, this technique will be used to override the URL that PIE uses to download the asset. The default, if not specified, is composer-default.",
"enum": ["composer-default", "pre-packaged-source"],
"example": "composer-default"
},
"os-families": {
"type": "array",
"minItems": 1,
"description": "An array of OS families to mark as compatible with the extension. Specifying this property will mean this package is not installable with PIE on any OS family not listed here. Must not be specified alongside os-families-exclude.",
"items": {
"type": "string",
"enum": ["windows", "bsd", "darwin", "solaris", "linux", "unknown"],
"description": "The name of the OS family to mark as compatible."
}
},
"os-families-exclude": {
"type": "array",
"minItems": 1,
"description": "An array of OS families to mark as incompatible with the extension. Specifying this property will mean this package is installable on any OS family except those listed here. Must not be specified alongside os-families.",
"items": {
"type": "string",
"enum": ["windows", "bsd", "darwin", "solaris", "linux", "unknown"],
"description": "The name of the OS family to exclude."
}
},
"configure-options": {
"type": "array",
"description": "These configure options make up the flags that can be passed to ./configure when installing the extension.",
"items": {
"type": "object",
"required": ["name"],
"properties": {
"name": {
"type": "string",
"description": "The name of the flag, this would typically be prefixed with `--`, for example, the value 'the-flag' would be passed as `./configure --the-flag`.",
"example": "without-xdebug-compression",
"pattern": "^[a-zA-Z0-9][a-zA-Z0-9-_]*$"
},
"needs-value": {
"type": "boolean",
"description": "If this is set to true, the flag needs a value (e.g. --with-somelib=<path>), otherwise it is a flag without a value (e.g. --enable-some-feature).",
"example": false,
"default": false
},
"description": {
"type": "string",
"description": "The description of what the flag does or means.",
"example": "Disable compression through zlib"
}
}
}
}
},
"allOf": [
{
"not": {
"required": ["os-families", "os-families-exclude"]
}
}
]
}
}
}
2 changes: 1 addition & 1 deletion src/Building/UnixBuild.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public function __invoke(

$this->make($targetPlatform, $downloadedPackage, $output, $outputCallback);

$expectedSoFile = $downloadedPackage->extractedSourcePath . '/modules/' . $downloadedPackage->package->extensionName->name() . '.so';
$expectedSoFile = $downloadedPackage->extractedSourcePath . '/modules/' . $downloadedPackage->package->extensionName()->name() . '.so';

if (! file_exists($expectedSoFile)) {
throw ExtensionBinaryNotFound::fromExpectedBinary($expectedSoFile);
Expand Down
2 changes: 1 addition & 1 deletion src/Command/BuildCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
$requestedNameAndVersion,
$forceInstallPackageVersion,
);
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName->nameWithExtPrefix()));
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix()));

// Now we know what package we have, we can validate the configure options for the command and re-create the
// Composer instance with the populated configure options
Expand Down
4 changes: 2 additions & 2 deletions src/Command/CommandHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ public static function requestedNameAndVersionPair(InputInterface $input): Reque

public static function bindConfigureOptionsFromPackage(Command $command, Package $package, InputInterface $input): void
{
foreach ($package->configureOptions as $configureOption) {
foreach ($package->configureOptions() as $configureOption) {
$command->addOption(
$configureOption->name,
null,
Expand All @@ -242,7 +242,7 @@ public static function bindConfigureOptionsFromPackage(Command $command, Package
public static function processConfigureOptionsFromInput(Package $package, InputInterface $input): array
{
$configureOptionsValues = [];
foreach ($package->configureOptions as $configureOption) {
foreach ($package->configureOptions() as $configureOption) {
if (! $input->hasOption($configureOption->name)) {
continue;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Command/DownloadCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
$requestedNameAndVersion,
$forceInstallPackageVersion,
);
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName->nameWithExtPrefix()));
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix()));

try {
($this->composerIntegrationHandler)(
Expand Down
16 changes: 8 additions & 8 deletions src/Command/InfoCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,17 @@ public function execute(InputInterface $input, OutputInterface $output): int
$requestedNameAndVersion,
CommandHelper::determineForceInstallingPackageVersion($input),
);
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName->nameWithExtPrefix()));
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix()));

$output->writeln(sprintf('Extension name: %s', $package->extensionName->name()));
$output->writeln(sprintf('Extension type: %s (%s)', $package->extensionType->value, $package->extensionType->name));
$output->writeln(sprintf('Composer package name: %s', $package->name));
$output->writeln(sprintf('Version: %s', $package->version));
$output->writeln(sprintf('Download URL: %s', $package->downloadUrl ?? '(not specified)'));
$output->writeln(sprintf('Extension name: %s', $package->extensionName()->name()));
$output->writeln(sprintf('Extension type: %s (%s)', $package->extensionType()->value, $package->extensionType()->name));
$output->writeln(sprintf('Composer package name: %s', $package->name()));
$output->writeln(sprintf('Version: %s', $package->version()));
$output->writeln(sprintf('Download URL: %s', $package->downloadUrl() ?? '(not specified)'));

if (count($package->configureOptions)) {
if (count($package->configureOptions())) {
$output->writeln('Configure options:');
foreach ($package->configureOptions as $configureOption) {
foreach ($package->configureOptions() as $configureOption) {
$output->writeln(sprintf(' --%s%s (%s)', $configureOption->name, $configureOption->needsValue ? '=?' : '', $configureOption->description));
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/Command/InstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
$requestedNameAndVersion,
$forceInstallPackageVersion,
);
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName->nameWithExtPrefix()));
$output->writeln(sprintf('<info>Found package:</info> %s which provides <info>%s</info>', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix()));

// Now we know what package we have, we can validate the configure options for the command and re-create the
// Composer instance with the populated configure options
Expand Down
4 changes: 2 additions & 2 deletions src/Command/ShowCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ static function (string $version, string $phpExtensionName) use ($output, $piePa
$extensionPath,
$phpExtensionName,
$extensionEnding,
PieInstalledJsonMetadataKeys::pieMetadataFromComposerPackage($piePackage->composerPackage),
PieInstalledJsonMetadataKeys::pieMetadataFromComposerPackage($piePackage->composerPackage()),
),
));
},
Expand Down Expand Up @@ -152,7 +152,7 @@ static function (BasePackage $basePackage): bool {
array_map(
/** @return non-empty-string */
static function (Package $package): string {
return $package->extensionName->name();
return $package->extensionName()->name();
},
$composerInstalledPackages,
),
Expand Down
4 changes: 2 additions & 2 deletions src/ComposerIntegration/ComposerIntegrationHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public function __invoke(

// If user did not request a specific require version, use Composer to recommend one for the pie.json
if ($recommendedRequireVersion === null) {
$recommendedRequireVersion = $versionSelector->findRecommendedRequireVersion($package->composerPackage);
$recommendedRequireVersion = $versionSelector->findRecommendedRequireVersion($package->composerPackage());
}

// Write the new requirement to pie.json; because we later essentially just do a `composer install` using that file
Expand All @@ -60,7 +60,7 @@ public function __invoke(

$composerInstaller = PieComposerInstaller::createWithPhpBinary(
$targetPlatform->phpBinaryPath,
$package->extensionName,
$package->extensionName(),
$this->arrayCollectionIo,
$composer,
);
Expand Down
Loading