diff --git a/src/lint/linter/ArcanistCSSLintLinter.php b/src/lint/linter/ArcanistCSSLintLinter.php --- a/src/lint/linter/ArcanistCSSLintLinter.php +++ b/src/lint/linter/ArcanistCSSLintLinter.php @@ -37,9 +37,7 @@ return 'csslint'; } - public function getVersion() { - list($stdout) = execx('%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); if (preg_match('/^v(?P\d+\.\d+\.\d+)$/', $stdout, $matches)) { return $matches['version']; diff --git a/src/lint/linter/ArcanistCoffeeLintLinter.php b/src/lint/linter/ArcanistCoffeeLintLinter.php --- a/src/lint/linter/ArcanistCoffeeLintLinter.php +++ b/src/lint/linter/ArcanistCoffeeLintLinter.php @@ -30,9 +30,7 @@ return 'coffeelint'; } - public function getVersion() { - list($stdout) = execx('%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); if (preg_match('/^(?P\d+\.\d+\.\d+)$/', $stdout, $matches)) { return $matches['version']; diff --git a/src/lint/linter/ArcanistCppcheckLinter.php b/src/lint/linter/ArcanistCppcheckLinter.php --- a/src/lint/linter/ArcanistCppcheckLinter.php +++ b/src/lint/linter/ArcanistCppcheckLinter.php @@ -31,9 +31,7 @@ return 'cppcheck'; } - public function getVersion() { - list($stdout) = execx('%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); $regex = '/^Cppcheck (?P\d+\.\d+)$/'; if (preg_match($regex, $stdout, $matches)) { diff --git a/src/lint/linter/ArcanistExternalLinter.php b/src/lint/linter/ArcanistExternalLinter.php --- a/src/lint/linter/ArcanistExternalLinter.php +++ b/src/lint/linter/ArcanistExternalLinter.php @@ -379,6 +379,43 @@ nonempty($this->flags, $this->getDefaultFlags())); } + public function getVersion() { + // If the `parseVersionOutput` method is not implemented in the subclass, + // fallback to the old behavior. Ideally, all `ArcanistExternalLinters` + // subclasses should implement `parseVersionOutput` and should override + // `getVersion` if the external linter is not versioned. + $reflector = new ReflectionMethod($this, 'parseVersionOutput'); + if ($reflector->getDeclaringClass()->getName() === __CLASS__) { + return parent::getVersion(); + } + + $future = new ExecFuture( + '%C %Ls', + $this->getExecutableCommand(), + $this->getVersionFlags()); + + if ($this->shouldExpectVersionErrors()) { + list($err, $stdout, $stderr) = $future->resolve(); + } else { + list($stdout, $stderr) = $future->resolvex(); + $err = 0; + } + + return $this->parseVersionOutput($err, $stdout, $stderr); + } + + protected function getVersionFlags() { + return array('--version'); + } + + public function shouldExpectVersionErrors() { + return false; + } + + protected function parseVersionOutput($err, $stdout, $stderr) { + throw new PhutilMethodNotImplementedException(); + } + public function getCacheVersion() { try { $this->checkBinaryConfiguration(); diff --git a/src/lint/linter/ArcanistFlake8Linter.php b/src/lint/linter/ArcanistFlake8Linter.php --- a/src/lint/linter/ArcanistFlake8Linter.php +++ b/src/lint/linter/ArcanistFlake8Linter.php @@ -33,9 +33,7 @@ return 'flake8'; } - public function getVersion() { - list($stdout) = execx('%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); if (preg_match('/^(?P\d+\.\d+(?:\.\d+)?)\b/', $stdout, $matches)) { return $matches['version']; diff --git a/src/lint/linter/ArcanistHLintLinter.php b/src/lint/linter/ArcanistHLintLinter.php --- a/src/lint/linter/ArcanistHLintLinter.php +++ b/src/lint/linter/ArcanistHLintLinter.php @@ -37,10 +37,7 @@ return array('--json'); } - public function getVersion() { - list($stdout, $stderr) = execx( - '%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = null; if (preg_match('@HLint v(.*),@', $stdout, $matches)) { return $matches[1]; diff --git a/src/lint/linter/ArcanistJSHintLinter.php b/src/lint/linter/ArcanistJSHintLinter.php --- a/src/lint/linter/ArcanistJSHintLinter.php +++ b/src/lint/linter/ArcanistJSHintLinter.php @@ -49,12 +49,8 @@ return 'jshint'; } - public function getVersion() { + protected function parseVersionOutput($err, $stdout, $stderr) { // NOTE: `jshint --version` emits version information on stderr, not stdout. - list($stdout, $stderr) = execx( - '%C --version', - $this->getExecutableCommand()); - $matches = array(); $regex = '/^jshint v(?P\d+\.\d+\.\d+)$/'; if (preg_match($regex, $stderr, $matches)) { diff --git a/src/lint/linter/ArcanistJSONLintLinter.php b/src/lint/linter/ArcanistJSONLintLinter.php --- a/src/lint/linter/ArcanistJSONLintLinter.php +++ b/src/lint/linter/ArcanistJSONLintLinter.php @@ -29,12 +29,11 @@ return 'jsonlint'; } - public function getVersion() { - // NOTE: `jsonlint --version` returns a non-zero exit status. - list($err, $stdout) = exec_manual( - '%C --version', - $this->getExecutableCommand()); + public function shouldExpectVersionErrors() { + return true; + } + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); if (preg_match('/^(?P\d+\.\d+\.\d+)$/', $stdout, $matches)) { return $matches['version']; diff --git a/src/lint/linter/ArcanistJscsLinter.php b/src/lint/linter/ArcanistJscsLinter.php --- a/src/lint/linter/ArcanistJscsLinter.php +++ b/src/lint/linter/ArcanistJscsLinter.php @@ -31,9 +31,7 @@ return 'jscs'; } - public function getVersion() { - list($stdout) = execx('%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); $regex = '/^(?P\d+\.\d+\.\d+)$/'; if (preg_match($regex, $stdout, $matches)) { diff --git a/src/lint/linter/ArcanistLesscLinter.php b/src/lint/linter/ArcanistLesscLinter.php --- a/src/lint/linter/ArcanistLesscLinter.php +++ b/src/lint/linter/ArcanistLesscLinter.php @@ -87,9 +87,7 @@ return 'lessc'; } - public function getVersion() { - list($stdout) = execx('%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); $regex = '/^lessc (?P\d+\.\d+\.\d+)\b/'; if (preg_match($regex, $stdout, $matches)) { diff --git a/src/lint/linter/ArcanistPEP8Linter.php b/src/lint/linter/ArcanistPEP8Linter.php --- a/src/lint/linter/ArcanistPEP8Linter.php +++ b/src/lint/linter/ArcanistPEP8Linter.php @@ -31,9 +31,7 @@ return 'pep8'; } - public function getVersion() { - list($stdout) = execx('%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); if (preg_match('/^(?P\d+\.\d+(?:\.\d+)?)\b/', $stdout, $matches)) { return $matches['version']; diff --git a/src/lint/linter/ArcanistPhpLinter.php b/src/lint/linter/ArcanistPhpLinter.php --- a/src/lint/linter/ArcanistPhpLinter.php +++ b/src/lint/linter/ArcanistPhpLinter.php @@ -47,11 +47,11 @@ return 'php'; } - public function getVersion() { - list($stdout) = execx( - '%C --run %s', - $this->getExecutableCommand(), - 'echo phpversion();'); + protected function getVersionFlags() { + return array('--run', 'echo phpversion();'); + } + + protected function parseVersionOutput($err, $stdout, $stderr) { return $stdout; } diff --git a/src/lint/linter/ArcanistPhpcsLinter.php b/src/lint/linter/ArcanistPhpcsLinter.php --- a/src/lint/linter/ArcanistPhpcsLinter.php +++ b/src/lint/linter/ArcanistPhpcsLinter.php @@ -69,9 +69,7 @@ return 'phpcs'; } - public function getVersion() { - list($stdout) = execx('%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); $regex = '/^PHP_CodeSniffer version (?P\d+\.\d+\.\d+)\b/'; if (preg_match($regex, $stdout, $matches)) { diff --git a/src/lint/linter/ArcanistPuppetLintLinter.php b/src/lint/linter/ArcanistPuppetLintLinter.php --- a/src/lint/linter/ArcanistPuppetLintLinter.php +++ b/src/lint/linter/ArcanistPuppetLintLinter.php @@ -34,9 +34,7 @@ return 'puppet-lint'; } - public function getVersion() { - list($stdout) = execx('%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); $regex = '/^puppet-lint (?P\d+\.\d+\.\d+)$/'; if (preg_match($regex, $stdout, $matches)) { diff --git a/src/lint/linter/ArcanistPyFlakesLinter.php b/src/lint/linter/ArcanistPyFlakesLinter.php --- a/src/lint/linter/ArcanistPyFlakesLinter.php +++ b/src/lint/linter/ArcanistPyFlakesLinter.php @@ -31,11 +31,9 @@ return 'pyflakes'; } - public function getVersion() { - list($stdout) = execx('%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); - if (preg_match('/^(?P\d+\.\d+\.\d+)$/', $stdout, $matches)) { + if (preg_match('/^(?P\d+\.\d+\.\d+)\b/', $stdout, $matches)) { return $matches['version']; } else { return false; diff --git a/src/lint/linter/ArcanistPyLintLinter.php b/src/lint/linter/ArcanistPyLintLinter.php --- a/src/lint/linter/ArcanistPyLintLinter.php +++ b/src/lint/linter/ArcanistPyLintLinter.php @@ -34,9 +34,7 @@ return 'pylint'; } - public function getVersion() { - list($stdout) = execx('%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); $regex = '/^pylint (?P\d+\.\d+\.\d+)/'; if (preg_match($regex, $stdout, $matches)) { diff --git a/src/lint/linter/ArcanistRuboCopLinter.php b/src/lint/linter/ArcanistRuboCopLinter.php --- a/src/lint/linter/ArcanistRuboCopLinter.php +++ b/src/lint/linter/ArcanistRuboCopLinter.php @@ -30,9 +30,7 @@ return 'rubocop'; } - public function getVersion() { - list($stdout) = execx('%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); if (preg_match('/^(?P\d+\.\d+\.\d+)$/', $stdout, $matches)) { return $matches['version']; diff --git a/src/lint/linter/ArcanistRubyLinter.php b/src/lint/linter/ArcanistRubyLinter.php --- a/src/lint/linter/ArcanistRubyLinter.php +++ b/src/lint/linter/ArcanistRubyLinter.php @@ -31,9 +31,7 @@ return 'ruby'; } - public function getVersion() { - list($stdout) = execx('%C --version', $this->getExecutableCommand()); - + protected function parseVersionOutput($err, $stdout, $stderr) { $matches = array(); $regex = '/^ruby (?P\d+\.\d+\.\d+)+/'; if (preg_match($regex, $stdout, $matches)) {