Skip to content

Commit

Permalink
Optimize show -a by loading only the requested package (#11659)
Browse files Browse the repository at this point in the history
Fixes #11648
  • Loading branch information
Seldaek committed Sep 27, 2023
1 parent c7e696d commit 892eaac
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 13 deletions.
22 changes: 19 additions & 3 deletions src/Composer/DependencyResolver/PoolBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,21 @@ class PoolBuilder
* @var BasePackage[]
*/
private $unacceptableFixedOrLockedPackages = [];
/** @var string[] */
/** @var array<string> */
private $updateAllowList = [];
/** @var array<string, array<PackageInterface>> */
private $skippedLoad = [];

/**
* If provided, only these package names are loaded
*
* This is a special-use functionality of the Request class to optimize the pool creation process
* when only a minimal subset of packages is needed and we do not need their dependencies.
*
* @var array<string, int>|null
*/
private $restrictedPackagesList = null;

/**
* Keeps a list of dependencies which are locked but were auto-unlocked as they are path repositories
*
Expand Down Expand Up @@ -165,6 +175,8 @@ public function __construct(array $acceptableStabilities, array $stabilityFlags,
*/
public function buildPool(array $repositories, Request $request): Pool
{
$this->restrictedPackagesList = $request->getRestrictedPackages() !== null ? array_flip($request->getRestrictedPackages()) : null;

if ($request->getUpdateAllowList()) {
$this->updateAllowList = $request->getUpdateAllowList();
$this->warnAboutNonMatchingUpdateAllowList($request);
Expand Down Expand Up @@ -366,6 +378,10 @@ private function markPackageNameForLoading(Request $request, string $name, Const
private function loadPackagesMarkedForLoading(Request $request, array $repositories): void
{
foreach ($this->packagesToLoad as $name => $constraint) {
if ($this->restrictedPackagesList !== null && !isset($this->restrictedPackagesList[$name])) {
unset($this->packagesToLoad[$name]);
continue;
}
$this->loadedPackages[$name] = $constraint;
}

Expand Down Expand Up @@ -559,7 +575,7 @@ private function getSkippedRootRequires(Request $request, string $name): array
*/
private function isUpdateAllowed(BasePackage $package): bool
{
foreach ($this->updateAllowList as $pattern => $void) {
foreach ($this->updateAllowList as $pattern) {
$patternRegexp = BasePackage::packageNameToRegexp($pattern);
if (Preg::isMatch($patternRegexp, $package->getName())) {
return true;
Expand All @@ -571,7 +587,7 @@ private function isUpdateAllowed(BasePackage $package): bool

private function warnAboutNonMatchingUpdateAllowList(Request $request): void
{
foreach ($this->updateAllowList as $pattern => $void) {
foreach ($this->updateAllowList as $pattern) {
$patternRegexp = BasePackage::packageNameToRegexp($pattern);
// update pattern matches a locked package? => all good
foreach ($request->getLockedRepository()->getPackages() as $package) {
Expand Down
26 changes: 23 additions & 3 deletions src/Composer/DependencyResolver/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ class Request
protected $lockedPackages = [];
/** @var array<string, BasePackage> */
protected $fixedLockedPackages = [];
/** @var string[] */
/** @var array<string> */
protected $updateAllowList = [];
/** @var false|self::UPDATE_* */
protected $updateAllowTransitiveDependencies = false;
/** @var non-empty-list<string>|null */
private $restrictedPackages = null;

public function __construct(?LockArrayRepository $lockedRepository = null)
{
Expand Down Expand Up @@ -118,7 +120,7 @@ public function unlockPackage(BasePackage $package): void
}

/**
* @param string[] $updateAllowList
* @param array<string> $updateAllowList
* @param false|self::UPDATE_* $updateAllowTransitiveDependencies
*/
public function setUpdateAllowList(array $updateAllowList, $updateAllowTransitiveDependencies): void
Expand All @@ -128,7 +130,7 @@ public function setUpdateAllowList(array $updateAllowList, $updateAllowTransitiv
}

/**
* @return string[]
* @return array<string>
*/
public function getUpdateAllowList(): array
{
Expand Down Expand Up @@ -233,4 +235,22 @@ public function getLockedRepository(): ?LockArrayRepository
{
return $this->lockedRepository;
}

/**
* Restricts the pool builder from loading other packages than those listed here
*
* @param non-empty-list<string> $names
*/
public function restrictPackages(array $names): void
{
$this->restrictedPackages = $names;
}

/**
* @return list<string>
*/
public function getRestrictedPackages(): ?array
{
return $this->restrictedPackages;
}
}
16 changes: 10 additions & 6 deletions src/Composer/Installer.php
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class Installer
/**
* Array of package names/globs flagged for update
*
* @var string[]|null
* @var non-empty-list<string>|null
*/
protected $updateAllowList = null;
/** @var Request::UPDATE_* */
Expand Down Expand Up @@ -242,7 +242,7 @@ public function run(): int
gc_collect_cycles();
gc_disable();

if ($this->updateAllowList && $this->updateMirrors) {
if ($this->updateAllowList !== null && $this->updateMirrors) {
throw new \RuntimeException("The installer options updateMirrors and updateAllowList are mutually exclusive.");
}

Expand Down Expand Up @@ -436,15 +436,15 @@ protected function doUpdate(InstalledRepositoryInterface $localRepo, bool $doIns
$lockedRepository = $this->locker->getLockedRepository(true);
}
} catch (\Seld\JsonLint\ParsingException $e) {
if ($this->updateAllowList || $this->updateMirrors) {
if ($this->updateAllowList !== null || $this->updateMirrors) {
// in case we are doing a partial update or updating mirrors, the lock file is needed so we error
throw $e;
}
// otherwise, ignoring parse errors as the lock file will be regenerated from scratch when
// doing a full update
}

if (($this->updateAllowList || $this->updateMirrors) && !$lockedRepository) {
if (($this->updateAllowList !== null || $this->updateMirrors) && !$lockedRepository) {
$this->io->writeError('<error>Cannot update ' . ($this->updateMirrors ? 'lock file information' : 'only a partial set of packages') . ' without a lock file present. Run `composer update` to generate a lock file.</error>', true, IOInterface::QUIET);

return self::ERROR_NO_LOCK_FILE_FOR_PARTIAL_UPDATE;
Expand All @@ -467,7 +467,7 @@ protected function doUpdate(InstalledRepositoryInterface $localRepo, bool $doIns
$this->requirePackagesForUpdate($request, $lockedRepository, true);

// pass the allow list into the request, so the pool builder can apply it
if ($this->updateAllowList) {
if ($this->updateAllowList !== null) {
$request->setUpdateAllowList($this->updateAllowList, $this->updateAllowTransitiveDependencies);
}

Expand Down Expand Up @@ -1337,7 +1337,11 @@ public function setUpdateMirrors(bool $updateMirrors): self
*/
public function setUpdateAllowList(array $packages): self
{
$this->updateAllowList = array_flip(array_map('strtolower', $packages));
if (count($packages) === 0) {
$this->updateAllowList = null;
} else {
$this->updateAllowList = array_values(array_unique(array_map('strtolower', $packages)));
}

return $this;
}
Expand Down
6 changes: 6 additions & 0 deletions src/Composer/Repository/RepositorySet.php
Original file line number Diff line number Diff line change
Expand Up @@ -367,12 +367,18 @@ public function createPoolForPackages(array $packageNames, ?LockArrayRepository
{
$request = new Request($lockedRepo);

$allowedPackages = [];
foreach ($packageNames as $packageName) {
if (PlatformRepository::isPlatformPackage($packageName)) {
throw new \LogicException('createPoolForPackage(s) can not be used for platform packages, as they are never loaded by the PoolBuilder which expects them to be fixed. Use createPoolWithAllPackages or pass in a proper request with the platform packages you need fixed in it.');
}

$request->requireName($packageName);
$allowedPackages[] = strtolower($packageName);
}

if (count($allowedPackages) > 0) {
$request->restrictPackages($allowedPackages);
}

return $this->createPool($request, new NullIO());
Expand Down
2 changes: 1 addition & 1 deletion tests/Composer/Test/DependencyResolver/PoolBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public function testPoolBuilder(string $file, string $message, array $expect, ar
if (isset($requestData['allowTransitiveDeps']) && $requestData['allowTransitiveDeps']) {
$transitiveDeps = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
}
$request->setUpdateAllowList(array_flip($requestData['allowList']), $transitiveDeps);
$request->setUpdateAllowList($requestData['allowList'], $transitiveDeps);
}

foreach ($fixed as $fixedPackage) {
Expand Down

0 comments on commit 892eaac

Please sign in to comment.