Page MenuHomePhabricator

D19703.id47094.diff
No OneTemporary

D19703.id47094.diff

Index: src/__phutil_library_map__.php
===================================================================
--- src/__phutil_library_map__.php
+++ src/__phutil_library_map__.php
@@ -718,7 +718,6 @@
'PhutilExecPassthru' => 'future/exec/PhutilExecPassthru.php',
'PhutilExecutableFuture' => 'future/exec/PhutilExecutableFuture.php',
'PhutilExecutionEnvironment' => 'utils/PhutilExecutionEnvironment.php',
- 'PhutilExtensionsTestCase' => 'moduleutils/__tests__/PhutilExtensionsTestCase.php',
'PhutilFacebookAuthAdapter' => 'auth/PhutilFacebookAuthAdapter.php',
'PhutilFatalDaemon' => 'daemon/torture/PhutilFatalDaemon.php',
'PhutilFileLock' => 'filesystem/PhutilFileLock.php',
@@ -1841,7 +1840,6 @@
'PhutilExecPassthru' => 'PhutilExecutableFuture',
'PhutilExecutableFuture' => 'Future',
'PhutilExecutionEnvironment' => 'Phobject',
- 'PhutilExtensionsTestCase' => 'PhutilTestCase',
'PhutilFacebookAuthAdapter' => 'PhutilOAuthAuthAdapter',
'PhutilFatalDaemon' => 'PhutilTortureTestDaemon',
'PhutilFileLock' => 'PhutilLock',
Index: src/toolset/ArcanistWorkflow.php
===================================================================
--- src/toolset/ArcanistWorkflow.php
+++ src/toolset/ArcanistWorkflow.php
@@ -129,9 +129,13 @@
}
final public function executeWorkflow(PhutilArgumentParser $args) {
+ $runtime = $this->getRuntime();
+
$this->arguments = $args;
$caught = null;
+ $runtime->pushWorkflow($this);
+
try {
$err = $this->runWorkflow($args);
} catch (Exception $ex) {
@@ -144,6 +148,8 @@
phlog($ex);
}
+ $runtime->popWorkflow();
+
if ($caught) {
throw $caught;
}
@@ -189,4 +195,12 @@
return $this->getRuntime()->getLogEngine();
}
+ public function canHandleInterrupt() {
+ return false;
+ }
+
+ public function handleInterrupt() {
+ throw new PhutilMethodNotImplementedException();
+ }
+
}
Index: src/workflow/ArcanistWeldWorkflow.php
===================================================================
--- src/workflow/ArcanistWeldWorkflow.php
+++ src/workflow/ArcanistWeldWorkflow.php
@@ -1,36 +1,38 @@
<?php
-final class ArcanistWeldWorkflow extends ArcanistWorkflow {
+final class ArcanistWeldWorkflow
+ extends ArcanistWorkflow {
public function getWorkflowName() {
return 'weld';
}
- public function getCommandSynopses() {
- return phutil_console_format(<<<EOTEXT
- **weld** [options] __file__ __file__ ...
+ public function getWorkflowInformation() {
+ $help = pht(<<<EOTEXT
+Robustly fuse two or more files together. The resulting joint is much stronger
+than the one created by tools like __cat__.
EOTEXT
- );
- }
+);
- public function getCommandHelp() {
- return phutil_console_format(<<<EOTEXT
- Robustly fuse two or more files together. The resulting joint is
- much stronger than the one created by tools like __cat__.
-EOTEXT
- );
+ return $this->newWorkflowInformation()
+ ->addExample(pht('**weld** [__options__] __file__ __file__ ...'))
+ ->setHelp($help);
}
- public function getArguments() {
+ public function getWorkflowArguments() {
return array(
- '*' => 'files',
+ $this->newWorkflowArgument('files')
+ ->setIsPathArgument(true)
+ ->setWildcard(true),
);
}
- public function run() {
+
+ public function runWorkflow() {
$files = $this->getArgument('files');
+
if (count($files) < 2) {
- throw new ArcanistUsageException(
+ throw new PhutilArgumentUsageException(
pht('Specify two or more files to weld together.'));
}
Index: support/ArcanistRuntime.php
===================================================================
--- support/ArcanistRuntime.php
+++ support/ArcanistRuntime.php
@@ -4,6 +4,9 @@
private $workflows;
private $logEngine;
+ private $lastInterruptTime;
+
+ private $stack = array();
public function execute(array $argv) {
@@ -68,6 +71,12 @@
$log->writeTrace(pht('ARGV'), csprintf('%Ls', $argv));
+ // We're installing the signal handler after parsing "--trace" so that it
+ // can emit debugging messages. This means there's a very small window at
+ // startup where signals have no special handling, but we couldn't really
+ // route them or do anything interesting with them anyway.
+ $this->installSignalHandler();
+
$args->parsePartial($config_args, true);
$config_engine = $this->loadConfiguration($args);
@@ -514,4 +523,103 @@
return $argv;
}
+ private function installSignalHandler() {
+ $log = $this->getLogEngine();
+
+ if (!function_exists('pcntl_signal')) {
+ $log->writeTrace(
+ pht('PCNTL'),
+ pht(
+ 'Unable to install signal handler, pcntl_signal() unavailable. '.
+ 'Continuing without signal handling.'));
+ return;
+ }
+
+ // NOTE: SIGHUP, SIGTERM and SIGWINCH are handled by "PhutilSignalRouter".
+ // This logic is largely similar to the logic there, but more specific to
+ // Arcanist workflows.
+
+ pcntl_signal(SIGINT, array($this, 'routeSignal'));
+ }
+
+ public function routeSignal($signo) {
+ switch ($signo) {
+ case SIGINT:
+ $this->routeInterruptSignal($signo);
+ break;
+ }
+ }
+
+ private function routeInterruptSignal($signo) {
+ $log = $this->getLogEngine();
+
+ $last_interrupt = $this->lastInterruptTime;
+ $now = microtime(true);
+ $this->lastInterruptTime = $now;
+
+ $should_exit = false;
+
+ // If we received another SIGINT recently, always exit. This implements
+ // "press ^C twice in quick succession to exit" regardless of what the
+ // workflow may decide to do.
+ $interval = 2;
+ if ($last_interrupt !== null) {
+ if ($now - $last_interrupt < $interval) {
+ $should_exit = true;
+ }
+ }
+
+ $handler = null;
+ if (!$should_exit) {
+
+ // Look for an interrupt handler in the current workflow stack.
+
+ $stack = $this->getWorkflowStack();
+ foreach ($stack as $workflow) {
+ if ($workflow->canHandleInterrupt()) {
+ $handler = $workflow;
+ break;
+ }
+ }
+
+ // If no workflow in the current execution stack can handle an interrupt
+ // signal, just exit on the first interrupt.
+
+ if (!$handler) {
+ $should_exit = true;
+ }
+ }
+
+ if ($should_exit) {
+ $log->writeHint(
+ pht('INTERRUPT'),
+ pht('Interrupted by SIGINT (^C).'));
+ exit(128 + $signo);
+ }
+
+ $log->writeHint(
+ pht('INTERRUPT'),
+ pht('Press ^C again to exit.'));
+
+ $handler->handleInterrupt();
+ }
+
+ public function pushWorkflow(ArcanistWorkflow $workflow) {
+ $this->stack[] = $workflow;
+ return $this;
+ }
+
+ public function popWorkflow() {
+ if (!$this->stack) {
+ throw new Exception(pht('Trying to pop an empty workflow stack!'));
+ }
+
+ return array_pop($this->stack);
+ }
+
+ public function getWorkflowStack() {
+ return $this->stack;
+ }
+
+
}

File Metadata

Mime Type
text/plain
Expires
Wed, May 8, 10:53 PM (3 w, 5 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6273394
Default Alt Text
D19703.id47094.diff (6 KB)

Event Timeline