public function lintPath($path) { $bin = $this->getLintPath(); $options = $this->getLintOptions(); $f = new ExecFuture("%C %C -", $bin, $options); $f->write($this->getData($path)); list($err, $stdout, $stderr) = $f->resolve(); if ($err === 2) { throw new Exception("cpplint failed to run correctly:\n" . $stderr); } $lines = explode("\n", $stderr); $messages = array(); foreach ($lines as $line) { $line = trim($line); $matches = null; $regex = '/^-:(\\d+):\\s*(.*)\\s*\\[(.*)\\] \\[(\\d+)\\]$/'; if (!preg_match($regex, $line, $matches)) { continue; } foreach ($matches as $key => $match) { $matches[$key] = trim($match); } $message = new ArcanistLintMessage(); $message->setPath($path); $message->setLine($matches[1]); $message->setCode($matches[3]); $message->setName($matches[3]); $message->setDescription($matches[2]); $message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING); $this->addLintMessage($message); } }
public function lintPath($path) { $working_copy = $this->getEngine()->getWorkingCopy(); $pyflakes_path = $working_copy->getConfig('lint.pyflakes.path'); $pyflakes_prefix = $working_copy->getConfig('lint.pyflakes.prefix'); // Default to just finding pyflakes in the users path $pyflakes_bin = 'pyflakes'; $python_path = ''; // If a pyflakes path was specified, then just use that as the // pyflakes binary and assume that the libraries will be imported // correctly. // // If no pyflakes path was specified and a pyflakes prefix was // specified, then use the binary from this prefix and add it to // the PYTHONPATH environment variable so that the libs are imported // correctly. This is useful when pyflakes is installed into a // non-default location. if ($pyflakes_path !== null) { $pyflakes_bin = $pyflakes_path; } else { if ($pyflakes_prefix !== null) { $pyflakes_bin = $pyflakes_prefix . '/bin/pyflakes'; $python_path = $pyflakes_prefix . '/lib/python2.6/site-packages:'; } } $options = $this->getPyFlakesOptions(); $f = new ExecFuture("/usr/bin/env PYTHONPATH=%s\$PYTHONPATH " . "{$pyflakes_bin} {$options}", $python_path); $f->write($this->getData($path)); try { list($stdout, $_) = $f->resolvex(); } catch (CommandException $e) { // PyFlakes will return an exit code of 1 if warnings/errors // are found but print nothing to stderr in this case. Therefore, // if we see any output on stderr or a return code other than 1 or 0, // pyflakes failed. if ($e->getError() !== 1 || $e->getStderr() !== '') { throw $e; } else { $stdout = $e->getStdout(); } } $lines = explode("\n", $stdout); $messages = array(); foreach ($lines as $line) { $matches = null; if (!preg_match('/^(.*?):(\\d+): (.*)$/', $line, $matches)) { continue; } foreach ($matches as $key => $match) { $matches[$key] = trim($match); } $message = new ArcanistLintMessage(); $message->setPath($path); $message->setLine($matches[2]); $message->setCode($this->getLinterName()); $message->setDescription($matches[3]); $message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING); $this->addLintMessage($message); } }
public function lintPath($path) { $rubyp = $this->getRubyPath(); $f = new ExecFuture("%s -wc", $rubyp); $f->write($this->getData($path)); list($err, $stdout, $stderr) = $f->resolve(); if ($err === 0) { return; } $lines = explode("\n", $stderr); $messages = array(); foreach ($lines as $line) { $matches = null; if (!preg_match("/(.*?):(\\d+): (.*?)\$/", $line, $matches)) { continue; } foreach ($matches as $key => $match) { $matches[$key] = trim($match); } $code = head(explode(',', $matches[3])); $message = new ArcanistLintMessage(); $message->setPath($path); $message->setLine($matches[2]); $message->setName($this->getLinterName() . " " . $code); $message->setDescription($matches[3]); $message->setSeverity($this->getMessageCodeSeverity($code)); $this->addLintMessage($message); } }
/** * @group xhpast */ function xhpast_get_parser_future($data) { if (!xhpast_is_available()) { throw new Exception(xhpast_get_build_instructions()); } $future = new ExecFuture('%s', xhpast_get_binary_path()); $future->write($data); return $future; }
/** * Construct an exec channel from a @{class:ExecFuture}. The future should * **NOT** have been started yet (e.g., with `isReady()` or `start()`), * because @{class:ExecFuture} closes stdin by default when futures start. * If stdin has been closed, you will be unable to write on the channel. * * @param ExecFuture Future to use as an underlying I/O source. * @task construct */ public function __construct(ExecFuture $future) { // Make an empty write to keep the stdin pipe open. By default, futures // close this pipe when they start. $future->write('', $keep_pipe = true); // Start the future so that reads and writes work immediately. $future->isReady(); $this->future = $future; }
private function writeAndRead($write, $read) { $future = new ExecFuture('cat'); $future->write($write); $lines = array(); foreach (new LinesOfALargeExecFuture($future) as $line) { $lines[] = $line; } $this->assertEqual($read, $lines, pht('Write: %s', id(new PhutilUTF8StringTruncator())->setMaximumGlyphs(32)->truncateString($write))); }
private function writeAndRead($write, $read) { $future = new ExecFuture('cat'); $future->write($write); $lines = array(); foreach (new LinesOfALargeExecFuture($future) as $line) { $lines[] = $line; } $this->assertEqual($read, $lines, "Write: " . phutil_utf8_shorten($write, 32)); }
private function runBootloaderTests(PhageAgentBootloader $boot) { $name = get_class($boot); $exec = new ExecFuture('%C', $boot->getBootCommand()); $exec->write($boot->getBootSequence(), $keep_open = true); $exec_channel = new PhutilExecChannel($exec); $agent = new PhutilJSONProtocolChannel($exec_channel); $agent->write(array('type' => 'EXEC', 'key' => 1, 'command' => 'echo phage')); $this->agentExpect($agent, array('type' => 'RSLV', 'key' => 1, 'err' => 0, 'stdout' => "phage\n", 'stderr' => ''), "'echo phage' for {$name}"); $agent->write(array('type' => 'EXIT')); }
public function getHighlightFuture($source) { $language = idx($this->config, 'language'); if ($language) { $language = $this->getPygmentsLexerNameFromLanguageName($language); $future = new ExecFuture('pygmentize -O stripnl=False -f html -l %s', $language); $future->write($source); return new PhutilDefaultSyntaxHighlighterEnginePygmentsFuture($future, $source); } return id(new PhutilDefaultSyntaxHighlighter())->getHighlightFuture($source); }
/** * @phutil-external-symbol function jsShrink */ public function transformResource($path, $data) { $type = self::getResourceType($path); switch ($type) { case 'css': $data = $this->replaceCSSPrintRules($path, $data); $data = $this->replaceCSSVariables($path, $data); $data = preg_replace_callback('@url\\s*\\((\\s*[\'"]?.*?)\\)@s', nonempty($this->translateURICallback, array($this, 'translateResourceURI')), $data); break; } if (!$this->minify) { return $data; } // Some resources won't survive minification (like Raphael.js), and are // marked so as not to be minified. if (strpos($data, '@' . 'do-not-minify') !== false) { return $data; } switch ($type) { case 'css': // Remove comments. $data = preg_replace('@/\\*.*?\\*/@s', '', $data); // Remove whitespace around symbols. $data = preg_replace('@\\s*([{}:;,])\\s*@', '\\1', $data); // Remove unnecessary semicolons. $data = preg_replace('@;}@', '}', $data); // Replace #rrggbb with #rgb when possible. $data = preg_replace('@#([a-f0-9])\\1([a-f0-9])\\2([a-f0-9])\\3@i', '#\\1\\2\\3', $data); $data = trim($data); break; case 'js': // If `jsxmin` is available, use it. jsxmin is the Javelin minifier and // produces the smallest output, but is complicated to build. if (Filesystem::binaryExists('jsxmin')) { $future = new ExecFuture('jsxmin __DEV__:0'); $future->write($data); list($err, $result) = $future->resolve(); if (!$err) { $data = $result; break; } } // If `jsxmin` is not available, use `JsShrink`, which doesn't compress // quite as well but is always available. $root = dirname(phutil_get_library_root('phabricator')); require_once $root . '/externals/JsShrink/jsShrink.php'; $data = jsShrink($data); break; } return $data; }
public function __construct($sockname, $repo_root, $args, array $command) { $this->command = json_encode($command); $console = PhutilConsole::getConsole(); $console->writeLog("cli query: %s\n", $this->command); $future = new ExecFuture("%C {$args} -U %s %s %s %s %s", "{$repo_root}/watchman", $sockname, '--no-pretty', '--no-spawn', '--no-local', '-j'); $cwd = getcwd(); if (!$cwd) { throw new Exception("can't figure out my cwd!?"); } $future->setCWD($cwd); $future->write($this->command . "\n"); parent::__construct($future); }
/** * Constructs an @{class:ExecFuture} for XHPAST. * * @param wild Data to pass to the future. * @return ExecFuture */ public static function getParserFuture($data) { if (!self::isAvailable()) { try { // Try to build XHPAST automatically. If we can't then just ask the // user to build it themselves. self::build(); } catch (CommandException $ex) { throw new PhutilProxyException(self::getBuildInstructions(), $ex); } } $future = new ExecFuture('%s', self::getPath()); $future->write($data); return $future; }
/** * @group xhpast */ function xhpast_get_parser_future($data) { if (!xhpast_is_available()) { try { // Try to build XHPAST automatically. If we can't then just ask the user // to build it themselves. xhpast_build(); } catch (CommandException $e) { throw new Exception(xhpast_get_build_instructions()); } } $future = new ExecFuture('%s', xhpast_get_binary_path()); $future->write($data); return $future; }
public function getHighlightFuture($source) { $language = idx($this->config, 'language'); if ($language) { $language = $this->getPygmentsLexerNameFromLanguageName($language); $future = new ExecFuture('pygmentize -O encoding=utf-8 -O stripnl=False -f html -l %s', $language); $scrub = false; if ($language == 'php' && strpos($source, '<?') === false) { $source = "<?php\n" . $source; $scrub = true; } $future->write($source); return new PhutilDefaultSyntaxHighlighterEnginePygmentsFuture($future, $source, $scrub); } return id(new PhutilDefaultSyntaxHighlighter())->getHighlightFuture($source); }
public function willParseFiles(array $file_map) { $root = dirname(phutil_get_library_root('javelin-diviner')); $bin = $root . '/jsast/jsast'; if (!Filesystem::pathExists($bin)) { throw new Exception("You must build the 'jsast' binary before you can generate " . "Javelin documentation."); } $futures = array(); foreach ($file_map as $file => $data) { $future = new ExecFuture($bin); $future->write($data); $futures[$file] = $future; } foreach (Futures($futures)->limit(8) as $file => $future) { $this->trees[$file] = $future->resolveJSON(); } }
public function transformResource($path, $data) { $type = self::getResourceType($path); switch ($type) { case 'css': $data = preg_replace_callback('@url\\s*\\((\\s*[\'"]?/rsrc/.*?)\\)@s', array($this, 'translateResourceURI'), $data); break; } if (!$this->minify) { return $data; } // Some resources won't survive minification (like Raphael.js), and are // marked so as not to be minified. if (strpos($data, '@' . 'do-not-minify') !== false) { return $data; } switch ($type) { case 'css': // Remove comments. $data = preg_replace('@/\\*.*?\\*/@s', '', $data); // Remove whitespace around symbols. $data = preg_replace('@\\s*([{}:;,])\\s*@', '\\1', $data); // Remove unnecessary semicolons. $data = preg_replace('@;}@', '}', $data); // Replace #rrggbb with #rgb when possible. $data = preg_replace('@#([a-f0-9])\\1([a-f0-9])\\2([a-f0-9])\\3@i', '#\\1\\2\\3', $data); $data = trim($data); break; case 'js': $root = dirname(phutil_get_library_root('phabricator')); $bin = $root . '/externals/javelin/support/jsxmin/jsxmin'; if (@file_exists($bin)) { $future = new ExecFuture("{$bin} __DEV__:0"); $future->write($data); list($err, $result) = $future->resolve(); if (!$err) { $data = $result; } } break; } return $data; }
public function lintPath($path) { $flake8_bin = $this->getFlake8Path(); $options = $this->getFlake8Options(); $f = new ExecFuture("%C %C -", $flake8_bin, $options); $f->write($this->getData($path)); list($err, $stdout, $stderr) = $f->resolve(); if ($err === 2) { throw new Exception("flake8 failed to run correctly:\n" . $stderr); } $lines = explode("\n", $stdout); $messages = array(); foreach ($lines as $line) { $matches = null; // stdin:2: W802 undefined name 'foo' # pyflakes // stdin:3:1: E302 expected 2 blank lines, found 1 # pep8 if (!preg_match('/^(.*?):(\\d+):(?:(\\d+):)? (\\S+) (.*)$/', $line, $matches)) { continue; } foreach ($matches as $key => $match) { $matches[$key] = trim($match); } if (substr($matches[4], 0, 1) == 'E') { $severity = ArcanistLintSeverity::SEVERITY_ERROR; } else { $severity = ArcanistLintSeverity::SEVERITY_WARNING; } $message = new ArcanistLintMessage(); $message->setPath($path); $message->setLine($matches[2]); if (!empty($matches[3])) { $message->setChar($matches[3]); } $message->setCode($matches[4]); $message->setName($this->getLinterName() . ' ' . $matches[3]); $message->setDescription($matches[5]); $message->setSeverity($severity); $this->addLintMessage($message); } }
public function getHighlightFuture($source) { $language = idx($this->config, 'language'); if (preg_match('/\\r(?!\\n)/', $source)) { // TODO: Pygments converts "\r" newlines into "\n" newlines, so we can't // use it on files with "\r" newlines. If we have "\r" not followed by // "\n" in the file, skip highlighting. $language = null; } if ($language) { $language = $this->getPygmentsLexerNameFromLanguageName($language); $future = new ExecFuture('pygmentize -O encoding=utf-8 -O stripnl=False -f html -l %s', $language); $scrub = false; if ($language == 'php' && strpos($source, '<?') === false) { $source = "<?php\n" . $source; $scrub = true; } $future->write($source); return new PhutilDefaultSyntaxHighlighterEnginePygmentsFuture($future, $source, $scrub); } return id(new PhutilDefaultSyntaxHighlighter())->getHighlightFuture($source); }
public static function rebuildPackages($root) { $packages = JavelinSyncSpec::getPackageMap(); $data = array(); foreach ($packages as $package => $items) { $content = array(); foreach ($items as $item) { if (empty($data[$item])) { $data[$item] = Filesystem::readFile($root . '/src/' . $item); } $content[] = $data[$item]; } $content = implode("\n\n", $content); echo "Writing {$package}.dev.js...\n"; Filesystem::writeFile($root . '/pkg/' . $package . '.dev.js', $content); echo "Writing {$package}.min.js...\n"; $exec = new ExecFuture($root . '/support/jsxmin/jsxmin __DEV__:0'); $exec->write($content); list($stdout) = $exec->resolvex(); Filesystem::writeFile($root . '/pkg/' . $package . '.min.js', $stdout); } }
public function lintPath($path) { $this->checkBinaryConfiguration(); $data = $this->getData($path); $future = new ExecFuture('%C', $this->getBinary()); $future->write($data); list($err, $stdout, $stderr) = $future->resolve(); if (empty($stdout) && $err) { throw new Exception(sprintf("%s\n\nSTDOUT\n%s\n\nSTDERR\n%s", pht($this->getLinterName() . ' failed to parse output!'), $stdout, $stderr)); } if ($stdout !== $data) { $lines = explode("\n", $data); $formatted_lines = explode("\n", $stdout); foreach ($lines as $line_idx => $line) { if ($line != $formatted_lines[$line_idx]) { $lines = array_slice($lines, $line_idx); $formatted_lines = array_slice($formatted_lines, $line_idx); break; } } $this->raiseLintAtLine($line_idx + 1, 1, self::LINT_GO_UNFORMATTED, pht('%s was not formatted correctly. Please setup your ' . 'editor to run gofmt on save', $path), implode("\n", $lines), implode("\n", $formatted_lines)); } }
public function markupContent($content, array $argv) { if (!Filesystem::binaryExists('cowsay')) { return $this->markupError(pht('Unable to locate the `%s` binary. Install cowsay.', 'cowsay')); } $bin = idx($argv, 'think') ? 'cowthink' : 'cowsay'; $eyes = idx($argv, 'eyes', 'oo'); $tongue = idx($argv, 'tongue', ' '); $cow = idx($argv, 'cow', 'default'); // NOTE: Strip this aggressively to prevent nonsense like // `cow=/etc/passwd`. We could build a whiltelist with `cowsay -l`. $cow = preg_replace('/[^a-z.-]+/', '', $cow); $future = new ExecFuture('%s -e %s -T %s -f %s ', $bin, $eyes, $tongue, $cow); $future->setTimeout(15); $future->write($content); list($err, $stdout, $stderr) = $future->resolve(); if ($err) { return $this->markupError(pht('Execution of `%s` failed: %s', 'cowsay', $stderr)); } if ($this->getEngine()->isTextMode()) { return $stdout; } return phutil_tag('div', array('class' => 'PhabricatorMonospaced remarkup-cowsay'), $stdout); }
private function newSymbolsFuture($path) { $javelinsymbols = $this->getSymbolsBinaryPath(); $future = new ExecFuture($javelinsymbols . ' # ' . escapeshellarg($path)); $future->write($this->getData($path)); return $future; }
/** * Sends mail using the $Sendmail program. * @param string $header The message headers * @param string $body The message body * @access protected * @return bool */ protected function SendmailSend($header, $body) { if ($this->Sender != '') { $sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); } else { $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); } if ($this->SingleTo === true) { foreach ($this->SingleToArray as $key => $val) { $mail = new ExecFuture('%C', $sendmail); $mail->write("To: {$val}\n", true); $mail->write($header . $body); $mail->resolvex(); } } else { $mail = new ExecFuture('%C', $sendmail); $mail->write($header . $body); $mail->resolvex(); } return true; }
public function testReadBuffering() { $str_len_8 = 'abcdefgh'; $str_len_4 = 'abcd'; // This is a write/read with no read buffer. $future = new ExecFuture('cat'); $future->write($str_len_8); do { $future->isReady(); list($read) = $future->read(); if (strlen($read)) { break; } } while (true); // We expect to get the entire string back in the read. $this->assertEqual($str_len_8, $read); $future->resolve(); // This is a write/read with a read buffer. $future = new ExecFuture('cat'); $future->write($str_len_8); // Set the read buffer size. $future->setReadBufferSize(4); do { $future->isReady(); list($read) = $future->read(); if (strlen($read)) { break; } } while (true); // We expect to get the entire string back in the read. $this->assertEqual($str_len_4, $read); $future->resolve(); }
private function executeDaemonLaunchCommand($command, $daemon_script_dir, array $config, $run_as_user = null) { $is_sudo = false; if ($run_as_user) { // If anything else besides sudo should be // supported then insert it here (runuser, su, ...) $command = csprintf('sudo -En -u %s -- %C', $run_as_user, $command); $is_sudo = true; } $future = new ExecFuture('exec %C', $command); // Play games to keep 'ps' looking reasonable. $future->setCWD($daemon_script_dir); $future->write(json_encode($config)); list($stdout, $stderr) = $future->resolvex(); if ($is_sudo) { // On OSX, `sudo -n` exits 0 when the user does not have permission to // switch accounts without a password. This is not consistent with // sudo on Linux, and seems buggy/broken. Check for this by string // matching the output. if (preg_match('/sudo: a password is required/', $stderr)) { throw new Exception(pht('sudo exited with a zero exit code, but emitted output ' . 'consistent with failure under OSX.')); } } }
private function uploadFile($data, $name, $desc) { $result = array('guid' => null, 'mime' => null, 'size' => null); $result['size'] = $size = strlen($data); if (!$size) { return $result; } $future = new ExecFuture('file -b --mime -'); $future->write($data); list($mime_type) = $future->resolvex(); $mime_type = trim($mime_type); $result['mime'] = $mime_type; echo "Uploading {$desc} '{$name}' ({$mime_type}, {$size} bytes)...\n"; try { $guid = $this->getConduit()->callMethodSynchronous('file.upload', array('data_base64' => base64_encode($data), 'name' => $name)); $result['guid'] = $guid; } catch (ConduitClientException $e) { $message = "Failed to upload {$desc} '{$name}'. Continue?"; if (!phutil_console_confirm($message, $default_no = false)) { throw new ArcanistUsageException('Aborted due to file upload failure.'); } } return $result; }
protected final function buildFutures(array $paths) { $executable = $this->getExecutableCommand(); $bin = csprintf('%C %Ls', $executable, $this->getCommandFlags()); $futures = array(); foreach ($paths as $path) { if ($this->supportsReadDataFromStdin()) { $future = new ExecFuture('%C %C', $bin, $this->getReadDataFromStdinFilename()); $future->write($this->getEngine()->loadData($path)); } else { // TODO: In commit hook mode, we need to do more handling here. $disk_path = $this->getEngine()->getFilePathOnDisk($path); $path_argument = $this->getPathArgumentForLinterFuture($disk_path); $future = new ExecFuture('%C %C', $bin, $path_argument); } $future->setCWD($this->getEngine()->getWorkingCopy()->getProjectRoot()); $futures[$path] = $future; } return $futures; }
$files = array('core/init.js', 'core/util.js', 'core/install.js', 'core/Event.js', 'core/Stratcom.js', 'lib/behavior.js', 'lib/Request.js', 'lib/Vector.js', 'lib/DOM.js', 'lib/JSON.js', 'lib/Mask.js', 'lib/Workflow.js', 'lib/URI.js', 'lib/control/typeahead/Typeahead.js', 'lib/control/typeahead/source/TypeaheadSource.js', 'lib/control/typeahead/source/TypeaheadPreloadedSource.js', 'lib/control/typeahead/source/TypeaheadOnDemandSource.js', 'lib/control/typeahead/normalizer/TypeaheadNormalizer.js', 'lib/control/tokenizer/Tokenizer.js', 'docs/Base.js'); $packages = array('init' => array('core/init.js'), 'javelin' => array('core/util.js', 'core/install.js', 'core/Event.js', 'core/Stratcom.js', 'lib/behavior.js', 'lib/Request.js', 'lib/Vector.js', 'lib/DOM.js', 'lib/JSON.js', 'lib/URI.js'), 'typeahead' => array('lib/control/typeahead/Typeahead.js', 'lib/control/typeahead/normalizer/TypeaheadNormalizer.js', 'lib/control/typeahead/source/TypeaheadSource.js', 'lib/control/typeahead/source/TypeaheadPreloadedSource.js', 'lib/control/typeahead/source/TypeaheadOnDemandSource.js', 'lib/control/tokenizer/Tokenizer.js'), 'workflow' => array('lib/Mask.js', 'lib/Workflow.js')); $root = Filesystem::resolvePath($argv[1]) . '/html/js/javelin/'; $data = array(); foreach ($files as $file) { echo "Reading {$file}...\n"; $content = Filesystem::readFile($root . $file); // Strip out Facebook-specific bookkeeping code. $content = preg_replace("@/\\*\\+([a-z]+)\\*/.*?/\\*-\\1\\*/([ ]*\n)*@s", "\n", $content); $data[$file] = $content; } $local = dirname(dirname(__FILE__)); foreach ($data as $file => $content) { echo "Writing {$file}...\n"; execx('mkdir -p %s', $local . '/src/' . dirname($file)); Filesystem::writeFile($local . '/src/' . $file, $content); } foreach ($packages as $package => $items) { $content = array(); foreach ($items as $item) { $content[] = $data[$item]; } $content = implode("\n\n", $content); echo "Writing {$package}.dev.js...\n"; Filesystem::writeFile($local . '/pkg/' . $package . '.dev.js', $content); echo "Writing {$package}.min.js...\n"; $exec = new ExecFuture($local . '/support/jsxmin/jsxmin __DEV__:0'); $exec->write($content); list($stdout) = $exec->resolvex(); Filesystem::writeFile($local . '/pkg/' . $package . '.min.js', $stdout); }
private function newSymbolsFuture($path) { $future = new ExecFuture('javelinsymbols # %s', $path); $future->write($this->getData($path)); return $future; }
public function run() { $this->console = PhutilConsole::getConsole(); $this->runRepositoryAPISetup(); if ($this->getArgument('no-diff')) { $this->removeScratchFile('diff-result.json'); $data = $this->runLintUnit(); $this->writeScratchJSONFile('diff-result.json', $data); return 0; } $this->runDiffSetupBasics(); $background = $this->getArgument('background', true); if ($this->isRawDiffSource() || phutil_is_windows()) { $background = false; } if ($background) { $argv = $this->getPassedArguments(); if (!PhutilConsoleFormatter::getDisableANSI()) { array_unshift($argv, '--ansi'); } $script = phutil_get_library_root('arcanist') . '/../scripts/arcanist.php'; if ($argv) { $lint_unit = new ExecFuture('php %s --recon diff --no-diff %Ls', $script, $argv); } else { $lint_unit = new ExecFuture('php %s --recon diff --no-diff', $script); } $lint_unit->write('', true); $lint_unit->start(); } $commit_message = $this->buildCommitMessage(); $this->dispatchEvent(ArcanistEventType::TYPE_DIFF_DIDBUILDMESSAGE, array('message' => $commit_message)); if (!$this->shouldOnlyCreateDiff()) { $revision = $this->buildRevisionFromCommitMessage($commit_message); } if ($background) { $server = new PhutilConsoleServer(); $server->addExecFutureClient($lint_unit); $server->setHandler(array($this, 'handleServerMessage')); $server->run(); list($err) = $lint_unit->resolve(); $data = $this->readScratchJSONFile('diff-result.json'); if ($err || !$data) { throw new Exception('Unable to read results from background linting and unit testing. ' . 'You can try running arc diff again with --background 0'); } } else { $server = $this->console->getServer(); $server->setHandler(array($this, 'handleServerMessage')); $data = $this->runLintUnit(); } $lint_result = $data['lintResult']; $this->unresolvedLint = $data['unresolvedLint']; $this->postponedLinters = $data['postponedLinters']; $unit_result = $data['unitResult']; $this->testResults = $data['testResults']; if ($this->getArgument('nolint')) { $this->excuses['lint'] = $this->getSkipExcuse('Provide explanation for skipping lint or press Enter to abort:', 'lint-excuses'); } if ($this->getArgument('nounit')) { $this->excuses['unit'] = $this->getSkipExcuse('Provide explanation for skipping unit tests or press Enter to abort:', 'unit-excuses'); } $changes = $this->generateChanges(); if (!$changes) { throw new ArcanistUsageException("There are no changes to generate a diff from!"); } $diff_spec = array('changes' => mpull($changes, 'toDictionary'), 'lintStatus' => $this->getLintStatus($lint_result), 'unitStatus' => $this->getUnitStatus($unit_result)) + $this->buildDiffSpecification(); $conduit = $this->getConduit(); $diff_info = $conduit->callMethodSynchronous('differential.creatediff', $diff_spec); $this->diffID = $diff_info['diffid']; $event = $this->dispatchEvent(ArcanistEventType::TYPE_DIFF_WASCREATED, array('diffID' => $diff_info['diffid'], 'lintResult' => $lint_result, 'unitResult' => $unit_result)); $this->updateLintDiffProperty(); $this->updateUnitDiffProperty(); $this->updateLocalDiffProperty(); $this->resolveDiffPropertyUpdates(); $output_json = $this->getArgument('json'); if ($this->shouldOnlyCreateDiff()) { if (!$output_json) { echo phutil_console_format("Created a new Differential diff:\n" . " **Diff URI:** __%s__\n\n", $diff_info['uri']); } else { $human = ob_get_clean(); echo json_encode(array('diffURI' => $diff_info['uri'], 'diffID' => $this->getDiffID(), 'human' => $human)) . "\n"; ob_start(); } } else { $revision['diffid'] = $this->getDiffID(); if ($commit_message->getRevisionID()) { $result = $conduit->callMethodSynchronous('differential.updaterevision', $revision); foreach (array('edit-messages.json', 'update-messages.json') as $file) { $messages = $this->readScratchJSONFile($file); unset($messages[$revision['id']]); $this->writeScratchJSONFile($file, $messages); } echo "Updated an existing Differential revision:\n"; } else { $revision = $this->dispatchWillCreateRevisionEvent($revision); $result = $conduit->callMethodSynchronous('differential.createrevision', $revision); $revised_message = $conduit->callMethodSynchronous('differential.getcommitmessage', array('revision_id' => $result['revisionid'])); if ($this->shouldAmend()) { $repository_api = $this->getRepositoryAPI(); if ($repository_api->supportsAmend()) { echo "Updating commit message...\n"; $repository_api->amendCommit($revised_message); } else { echo "Commit message was not amended. Amending commit message is " . "only supported in git and hg (version 2.2 or newer)"; } } echo "Created a new Differential revision:\n"; } $uri = $result['uri']; echo phutil_console_format(" **Revision URI:** __%s__\n\n", $uri); if ($this->getArgument('plan-changes')) { $conduit->callMethodSynchronous('differential.createcomment', array('revision_id' => $result['revisionid'], 'action' => 'rethink')); echo "Planned changes to the revision.\n"; } } echo "Included changes:\n"; foreach ($changes as $change) { echo ' ' . $change->renderTextSummary() . "\n"; } if ($output_json) { ob_get_clean(); } $this->removeScratchFile('create-message'); return 0; }