From 5725a2db57eb49af03cce6374b794f69a930c359 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 23 Nov 2019 11:25:28 +0100 Subject: [PATCH] Allow all prepare/cleanup/install/update/uninstall operations to return promises to be executed in parallel as well --- .../Installer/InstallationManager.php | 108 ++++++++++-------- 1 file changed, 61 insertions(+), 47 deletions(-) diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index 6bb5b9d4e..c767b5a14 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -173,14 +173,14 @@ class InstallationManager $promises = array(); foreach ($operations as $operation) { - $method = $operation->getJobType(); + $jobType = $operation->getJobType(); $promise = null; - if ($method === 'install') { + if ($jobType === 'install') { $package = $operation->getPackage(); $installer = $this->getInstaller($package->getType()); $promise = $installer->download($package); - } elseif ($method === 'update') { + } elseif ($jobType === 'update') { $target = $operation->getTargetPackage(); $targetType = $target->getType(); $installer = $this->getInstaller($targetType); @@ -197,66 +197,80 @@ class InstallationManager } foreach ($operations as $operation) { - $method = $operation->getJobType(); - $event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($method); + $jobType = $operation->getJobType(); + + // ignoring alias ops as they don't need to execute anything + if (!in_array($jobType, array('update', 'install', 'uninstall'))) { + // output alias ops in debug verbosity as they have no output otherwise + if ($this->io->isDebug()) { + $this->io->writeError(' - ' . $operation->show(false)); + } + continue; + } + + if ($jobType === 'install' || $jobType === 'uninstall') { + $package = $operation->getPackage(); + $initialPackage = null; + } elseif ($jobType === 'update') { + $package = $operation->getTargetPackage(); + $initialPackage = $operation->getInitialPackage(); + } + $installer = $this->getInstaller($package->getType()); + + $event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($jobType); if (defined($event) && $runScripts && $this->eventDispatcher) { $this->eventDispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation); } - // output alias ops in debug verbosity as they have no output otherwise - if ($this->io->isDebug()) { - $this->io->writeError(' - ' . $operation->show(false)); - } + $dispatcher = $this->eventDispatcher; + $installManager = $this; + $loop = $this->loop; + $io = $this->io; - $e = null; - try { - if ($method === 'install' || $method === 'uninstall') { - $package = $operation->getPackage(); - $installer = $this->getInstaller($package->getType()); - $promise = $installer->prepare($method, $package); - } elseif ($method === 'update') { - $target = $operation->getTargetPackage(); - $targetType = $target->getType(); - $installer = $this->getInstaller($targetType); - $promise = $installer->prepare('update', $target, $operation->getInitialPackage()); + $promise = new \React\Promise\Promise(function ($resolve, $reject) use ($installer, $jobType, $package, $initialPackage) { + $promise = $installer->prepare($jobType, $package, $initialPackage); + if (null === $promise) { + $promise = new \React\Promise\Promise(function ($resolve, $reject) { $resolve(); }); } - if (!empty($promise)) { - $this->loop->wait(array($promise)); + return $promise->then($resolve, $reject); + }); + + $promise = $promise->then(function () use ($jobType, $installManager, $repo, $operation) { + $promise = $installManager->$jobType($repo, $operation); + if (null === $promise) { + $promise = new \React\Promise\Promise(function ($resolve, $reject) { $resolve(); }); } - $promise = $this->$method($repo, $operation); - if (!empty($promise)) { - $this->loop->wait(array($promise)); + return $promise; + })->then(function () use ($jobType, $installer, $package, $initialPackage) { + $promise = $installer->cleanup($jobType, $package, $initialPackage); + if (null === $promise) { + $promise = new \React\Promise\Promise(function ($resolve, $reject) { $resolve(); }); } - } catch (\Exception $e) { - } - if ($method === 'install' || $method === 'uninstall') { - $package = $operation->getPackage(); - $installer = $this->getInstaller($package->getType()); - $promise = $installer->cleanup($method, $package); - } elseif ($method === 'update') { - $target = $operation->getTargetPackage(); - $targetType = $target->getType(); - $installer = $this->getInstaller($targetType); - $promise = $installer->cleanup('update', $target, $operation->getInitialPackage()); - } + return $promise; + })->then(function () use ($jobType, $installer, $package, $initialPackage, $runScripts, $dispatcher, $installManager, $devMode, $repo, $operations, $operation) { + $repo->write($devMode, $installManager); - if (!empty($promise)) { - $this->loop->wait(array($promise)); - } + $event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($jobType); + if (defined($event) && $runScripts && $dispatcher) { + $dispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation); + } + }, function ($e) use ($jobType, $installer, $package, $initialPackage, $loop, $io) { + $this->io->writeError(' ' . ucfirst($jobType) .' of '.$package->getPrettyName().' failed'); + + $promise = $installer->cleanup($jobType, $package, $initialPackage); + $loop->wait(array($promise)); - if ($e) { throw $e; - } + }); - $repo->write($devMode, $this); + $promises[] = $promise; + } - $event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($method); - if (defined($event) && $runScripts && $this->eventDispatcher) { - $this->eventDispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation); - } + if (!empty($promises)) { + $this->loop->wait($promises); } }