More updates to stop using dev or alpha or beta versions.
[yaffs-website] / vendor / consolidation / robo / src / Task / Base / ParallelExec.php
1 <?php
2 namespace Robo\Task\Base;
3
4 use Robo\Contract\CommandInterface;
5 use Robo\Contract\PrintedInterface;
6 use Robo\Result;
7 use Robo\Task\BaseTask;
8 use Symfony\Component\Process\Exception\ProcessTimedOutException;
9 use Symfony\Component\Process\Process;
10
11 /**
12  * Class ParallelExecTask
13  *
14  * ``` php
15  * <?php
16  * $this->taskParallelExec()
17  *   ->process('php ~/demos/script.php hey')
18  *   ->process('php ~/demos/script.php hoy')
19  *   ->process('php ~/demos/script.php gou')
20  *   ->run();
21  * ?>
22  * ```
23  */
24 class ParallelExec extends BaseTask implements CommandInterface, PrintedInterface
25 {
26     use \Robo\Common\CommandReceiver;
27
28     /**
29      * @var Process[]
30      */
31     protected $processes = [];
32
33     /**
34      * @var null|int
35      */
36     protected $timeout = null;
37
38     /**
39      * @var null|int
40      */
41     protected $idleTimeout = null;
42
43     /**
44      * @var null|int
45      */
46     protected $waitInterval = 0;
47
48     /**
49      * @var bool
50      */
51     protected $isPrinted = false;
52
53     /**
54      * {@inheritdoc}
55      */
56     public function getPrinted()
57     {
58         return $this->isPrinted;
59     }
60
61     /**
62      * @param bool $isPrinted
63      *
64      * @return $this
65      */
66     public function printed($isPrinted = true)
67     {
68         $this->isPrinted = $isPrinted;
69         return $this;
70     }
71
72     /**
73      * @param string|\Robo\Contract\CommandInterface $command
74      *
75      * @return $this
76      */
77     public function process($command)
78     {
79         // TODO: Symfony 4 requires that we supply the working directory.
80         $this->processes[] = new Process($this->receiveCommand($command), getcwd());
81         return $this;
82     }
83
84     /**
85      * Stops process if it runs longer then `$timeout` (seconds).
86      *
87      * @param int $timeout
88      *
89      * @return $this
90      */
91     public function timeout($timeout)
92     {
93         $this->timeout = $timeout;
94         return $this;
95     }
96
97     /**
98      * Stops process if it does not output for time longer then `$timeout` (seconds).
99      *
100      * @param int $idleTimeout
101      *
102      * @return $this
103      */
104     public function idleTimeout($idleTimeout)
105     {
106         $this->idleTimeout = $idleTimeout;
107         return $this;
108     }
109
110     /**
111      * Parallel processing will wait `$waitInterval` seconds after launching each process and before
112      * the next one.
113      *
114      * @param int $waitInterval
115      *
116      * @return $this
117      */
118     public function waitInterval($waitInterval)
119     {
120         $this->waitInterval = $waitInterval;
121         return $this;
122     }
123
124     /**
125      * {@inheritdoc}
126      */
127     public function getCommand()
128     {
129         return implode(' && ', $this->processes);
130     }
131
132     /**
133      * @return int
134      */
135     public function progressIndicatorSteps()
136     {
137         return count($this->processes);
138     }
139
140     /**
141      * {@inheritdoc}
142      */
143     public function run()
144     {
145         $this->startProgressIndicator();
146         $running = [];
147         $queue = $this->processes;
148         $nextTime = time();
149         while (true) {
150             if (($nextTime <= time()) && !empty($queue)) {
151                 $process = array_shift($queue);
152                 $process->setIdleTimeout($this->idleTimeout);
153                 $process->setTimeout($this->timeout);
154                 $process->start();
155                 $this->printTaskInfo($process->getCommandLine());
156                 $running[] = $process;
157                 $nextTime = time() + $this->waitInterval;
158             }
159             foreach ($running as $k => $process) {
160                 try {
161                     $process->checkTimeout();
162                 } catch (ProcessTimedOutException $e) {
163                     $this->printTaskWarning("Process timed out for {command}", ['command' => $process->getCommandLine(), '_style' => ['command' => 'fg=white;bg=magenta']]);
164                 }
165                 if (!$process->isRunning()) {
166                     $this->advanceProgressIndicator();
167                     if ($this->isPrinted) {
168                         $this->printTaskInfo("Output for {command}:\n\n{output}", ['command' => $process->getCommandLine(), 'output' => $process->getOutput(), '_style' => ['command' => 'fg=white;bg=magenta']]);
169                         $errorOutput = $process->getErrorOutput();
170                         if ($errorOutput) {
171                             $this->printTaskError(rtrim($errorOutput));
172                         }
173                     }
174                     unset($running[$k]);
175                 }
176             }
177             if (empty($running) && empty($queue)) {
178                 break;
179             }
180             usleep(1000);
181         }
182         $this->stopProgressIndicator();
183
184         $errorMessage = '';
185         $exitCode = 0;
186         foreach ($this->processes as $p) {
187             if ($p->getExitCode() === 0) {
188                 continue;
189             }
190             $errorMessage .= "'" . $p->getCommandLine() . "' exited with code ". $p->getExitCode()." \n";
191             $exitCode = max($exitCode, $p->getExitCode());
192         }
193         if (!$errorMessage) {
194             $this->printTaskSuccess('{process-count} processes finished running', ['process-count' => count($this->processes)]);
195         }
196
197         return new Result($this, $exitCode, $errorMessage, ['time' => $this->getExecutionTime()]);
198     }
199 }