diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index 925a528be..ebe38f26a 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -14,6 +14,7 @@ namespace Composer\IO; use Composer\Question\StrictConfirmationQuestion; use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -253,6 +254,15 @@ class ConsoleIO extends BaseIO } } + /** + * @param int $max + * @return ProgressBar + */ + public function getProgressBar($max = 0) + { + return new ProgressBar($this->getErrorOutput(), $max); + } + /** * {@inheritDoc} */ diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index 9e55c4ac7..47b3e2914 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -13,6 +13,7 @@ namespace Composer\Installer; use Composer\IO\IOInterface; +use Composer\IO\ConsoleIO; use Composer\Package\PackageInterface; use Composer\Package\AliasPackage; use Composer\Repository\RepositoryInterface; @@ -330,7 +331,14 @@ class InstallationManager // execute all prepare => installs/updates/removes => cleanup steps if (!empty($promises)) { - $this->loop->wait($promises); + $progress = null; + if ($io instanceof ConsoleIO && !$io->isDebug()) { + $progress = $io->getProgressBar(); + } + $this->loop->wait($promises, $progress); + if ($progress) { + $progress->clear(); + } } } catch (\Exception $e) { $runCleanup(); diff --git a/src/Composer/Util/HttpDownloader.php b/src/Composer/Util/HttpDownloader.php index 6fe53390e..889fae07e 100644 --- a/src/Composer/Util/HttpDownloader.php +++ b/src/Composer/Util/HttpDownloader.php @@ -267,21 +267,12 @@ class HttpDownloader public function markJobDone() { $this->runningJobs--; - - foreach ($this->jobs as $job) { - if ($job['status'] === self::STATUS_QUEUED) { - $this->startJob($job['id']); - if ($this->runningJobs >= $this->maxJobs) { - return; - } - } - } } public function wait($index = null) { while (true) { - if (!$this->hasActiveJob($index)) { + if (!$this->countActiveJobs($index)) { return; } @@ -299,26 +290,37 @@ class HttpDownloader /** * @internal + * + * @return int number of active (queued or started) jobs */ - public function hasActiveJob($index = null) + public function countActiveJobs($index = null) { + if ($this->runningJobs < $this->maxJobs) { + foreach ($this->jobs as $job) { + if ($job['status'] === self::STATUS_QUEUED && $this->runningJobs < $this->maxJobs) { + $this->startJob($job['id']); + } + } + } + if ($this->curl) { $this->curl->tick(); } if (null !== $index) { - return $this->jobs[$index]['status'] < self::STATUS_COMPLETED; + return $this->jobs[$index]['status'] < self::STATUS_COMPLETED ? 1 : 0; } + $active = 0; foreach ($this->jobs as $job) { if ($job['status'] < self::STATUS_COMPLETED) { - return true; + $active++; } elseif (!$job['sync']) { unset($this->jobs[$job['id']]); } } - return false; + return $active; } private function getResponse($index) diff --git a/src/Composer/Util/Loop.php b/src/Composer/Util/Loop.php index b7382f1ed..00159d562 100644 --- a/src/Composer/Util/Loop.php +++ b/src/Composer/Util/Loop.php @@ -14,6 +14,7 @@ namespace Composer\Util; use Composer\Util\HttpDownloader; use React\Promise\Promise; +use Symfony\Component\Console\Helper\ProgressBar; /** * @author Jordi Boggiano @@ -36,7 +37,7 @@ class Loop } } - public function wait(array $promises) + public function wait(array $promises, ProgressBar $progress = null) { /** @var \Exception|null */ $uncaught = null; @@ -50,21 +51,32 @@ class Loop $this->currentPromises = $promises; + if ($progress) { + $totalJobs = 0; + if ($this->httpDownloader) { + $totalJobs += $this->httpDownloader->countActiveJobs(); + } + if ($this->processExecutor) { + $totalJobs += $this->processExecutor->countActiveJobs(); + } + $progress->start($totalJobs); + } + while (true) { - $hasActiveJob = false; + $activeJobs = 0; if ($this->httpDownloader) { - if ($this->httpDownloader->hasActiveJob()) { - $hasActiveJob = true; - } + $activeJobs += $this->httpDownloader->countActiveJobs(); } if ($this->processExecutor) { - if ($this->processExecutor->hasActiveJob()) { - $hasActiveJob = true; - } + $activeJobs += $this->processExecutor->countActiveJobs(); + } + + if ($progress) { + $progress->setProgress($progress->getMaxSteps() - $activeJobs); } - if (!$hasActiveJob) { + if (!$activeJobs) { break; } diff --git a/src/Composer/Util/ProcessExecutor.php b/src/Composer/Util/ProcessExecutor.php index b59bced70..96b9235c8 100644 --- a/src/Composer/Util/ProcessExecutor.php +++ b/src/Composer/Util/ProcessExecutor.php @@ -250,7 +250,7 @@ class ProcessExecutor public function wait($index = null) { while (true) { - if (!$this->hasActiveJob($index)) { + if (!$this->countActiveJobs($index)) { return; } @@ -268,8 +268,10 @@ class ProcessExecutor /** * @internal + * + * @return int number of active (queued or started) jobs */ - public function hasActiveJob($index = null) + public function countActiveJobs($index = null) { // tick foreach ($this->jobs as $job) { @@ -278,21 +280,28 @@ class ProcessExecutor call_user_func($job['resolve'], $job['process']); } } + + if ($this->runningJobs < $this->maxJobs) { + if ($job['status'] === self::STATUS_QUEUED) { + $this->startJob($job['id']); + } + } } if (null !== $index) { - return $this->jobs[$index]['status'] < self::STATUS_COMPLETED; + return $this->jobs[$index]['status'] < self::STATUS_COMPLETED ? 1 : 0; } + $active = 0; foreach ($this->jobs as $job) { if ($job['status'] < self::STATUS_COMPLETED) { - return true; + $active++; } else { unset($this->jobs[$job['id']]); } } - return false; + return $active; } /** @@ -301,15 +310,6 @@ class ProcessExecutor public function markJobDone() { $this->runningJobs--; - - foreach ($this->jobs as $job) { - if ($job['status'] === self::STATUS_QUEUED) { - $this->startJob($job['id']); - if ($this->runningJobs >= $this->maxJobs) { - return; - } - } - } } public function splitLines($output)