Changeset View
Changeset View
Standalone View
Standalone View
src/lint/linter/ArcanistExternalLinter.php
| <?php | <?php | ||||
| /** | /** | ||||
| * Base class for linters which operate by invoking an external program and | * Base class for linters which operate by invoking an external program and | ||||
| * parsing results. | * parsing results. | ||||
| * | * | ||||
| * @task bin Interpreters, Binaries and Flags | * @task bin Interpreters, Binaries and Flags | ||||
| * @task parse Parsing Linter Output | * @task parse Parsing Linter Output | ||||
| * @task exec Executing the Linter | * @task exec Executing the Linter | ||||
| */ | */ | ||||
| abstract class ArcanistExternalLinter extends ArcanistFutureLinter { | abstract class ArcanistExternalLinter extends ArcanistFutureLinter { | ||||
| private $bin; | private $bin; | ||||
| private $interpreter; | private $interpreter; | ||||
| private $flags; | private $flags; | ||||
| private $minimumVersion; | |||||
| private $maximumVersion; | |||||
| /* -( Interpreters, Binaries and Flags )----------------------------------- */ | /* -( Interpreters, Binaries and Flags )----------------------------------- */ | ||||
| /** | /** | ||||
| * Return the default binary name or binary path where the external linter | * Return the default binary name or binary path where the external linter | ||||
| * lives. This can either be a binary name which is expected to be installed | * lives. This can either be a binary name which is expected to be installed | ||||
| * in PATH (like "jshint"), or a relative path from the project root | * in PATH (like "jshint"), or a relative path from the project root | ||||
| ▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | /* -( Interpreters, Binaries and Flags )----------------------------------- */ | ||||
| * @return list<string> Overridable default flags. | * @return list<string> Overridable default flags. | ||||
| * @task bin | * @task bin | ||||
| */ | */ | ||||
| protected function getDefaultFlags() { | protected function getDefaultFlags() { | ||||
| return array(); | return array(); | ||||
| } | } | ||||
| /** | /** | ||||
| * TODO | |||||
| * | |||||
| * @return string | |||||
| */ | |||||
| protected function getDefaultMinimumVersion() { | |||||
| return null; | |||||
| } | |||||
| /** | |||||
| * TODO | |||||
| * | |||||
| * @return string | |||||
| */ | |||||
| protected function getDefaultMaximumVersion() { | |||||
| return null; | |||||
| } | |||||
| /** | |||||
| * Override default flags with custom flags. If not overridden, flags provided | * Override default flags with custom flags. If not overridden, flags provided | ||||
| * by @{method:getDefaultFlags} are used. | * by @{method:getDefaultFlags} are used. | ||||
| * | * | ||||
| * @param list<string> New flags. | * @param list<string> New flags. | ||||
| * @return this | * @return this | ||||
| * @task bin | * @task bin | ||||
| */ | */ | ||||
| final public function setFlags($flags) { | final public function setFlags($flags) { | ||||
| ▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | /* -( Interpreters, Binaries and Flags )----------------------------------- */ | ||||
| * @return this | * @return this | ||||
| * @task bin | * @task bin | ||||
| */ | */ | ||||
| final public function setInterpreter($interpreter) { | final public function setInterpreter($interpreter) { | ||||
| $this->interpreter = $interpreter; | $this->interpreter = $interpreter; | ||||
| return $this; | return $this; | ||||
| } | } | ||||
| /** | |||||
| * TODO | |||||
| * | |||||
| * @return string | |||||
| */ | |||||
| final public function getMinimumVersion() { | |||||
| return coalesce($this->minimumVersion, $this->getDefaultMinimumVersion()); | |||||
| } | |||||
| /** | |||||
| * TODO | |||||
| * | |||||
| * @param string | |||||
| * @return this | |||||
| */ | |||||
| final public function setMinimumVersion($version) { | |||||
| if (!$this->hasVersion()) { | |||||
| throw new Exception('TODO'); | |||||
| } | |||||
| $this->minimumVersion = $version; | |||||
| return $this; | |||||
| } | |||||
| /** | |||||
| * TODO | |||||
| * | |||||
| * @return string | |||||
| */ | |||||
| final public function getMaximumVersion() { | |||||
| return coalesce($this->maximumVersion, $this->getDefaultMaximumVersion()); | |||||
| } | |||||
| /** | |||||
| * TODO | |||||
| * | |||||
| * @param string | |||||
| * @return this | |||||
| */ | |||||
| final public function setMaximumVersion($version) { | |||||
| if (!$this->hasVersion()) { | |||||
| throw new Exception('TODO'); | |||||
| } | |||||
| $this->maximumVersion = $version; | |||||
| return $this; | |||||
| } | |||||
| /* -( Parsing Linter Output )---------------------------------------------- */ | /* -( Parsing Linter Output )---------------------------------------------- */ | ||||
| /** | /** | ||||
| * Parse the output of the external lint program into objects of class | * Parse the output of the external lint program into objects of class | ||||
| * @{class:ArcanistLintMessage} which `arc` can consume. Generally, this | * @{class:ArcanistLintMessage} which `arc` can consume. Generally, this | ||||
| * means examining the output and converting each warning or error into a | * means examining the output and converting each warning or error into a | ||||
| * message. | * message. | ||||
| ▲ Show 20 Lines • Show All 144 Lines • ▼ Show 20 Lines | /* -( Executing the Linter )----------------------------------------------- */ | ||||
| */ | */ | ||||
| protected function getPathArgumentForLinterFuture($path) { | protected function getPathArgumentForLinterFuture($path) { | ||||
| return csprintf('%s', $path); | return csprintf('%s', $path); | ||||
| } | } | ||||
| final protected function buildFutures(array $paths) { | final protected function buildFutures(array $paths) { | ||||
| $executable = $this->getExecutableCommand(); | $executable = $this->getExecutableCommand(); | ||||
| $min_version = $this->getMinimumVersion(); | |||||
| if ($min_version) { | |||||
| if (!$this->compareVersion($min_version, '>=')) { | |||||
| // TODO: Provide a more descriptive error message. | |||||
| throw new ArcanistUsageException('Wrong version'); | |||||
| } | |||||
| } | |||||
| $max_version = $this->getMaximumVersion(); | |||||
| if ($max_version) { | |||||
| if (!$this->compareVersion($max_version, '<=')) { | |||||
| // TODO: Provide a more descriptive error message. | |||||
| throw new ArcanistUsageException('Wrong version'); | |||||
| } | |||||
| } | |||||
| $bin = csprintf('%C %Ls', $executable, $this->getCommandFlags()); | $bin = csprintf('%C %Ls', $executable, $this->getCommandFlags()); | ||||
| $futures = array(); | $futures = array(); | ||||
| foreach ($paths as $path) { | foreach ($paths as $path) { | ||||
| if ($this->supportsReadDataFromStdin()) { | if ($this->supportsReadDataFromStdin()) { | ||||
| $future = new ExecFuture( | $future = new ExecFuture( | ||||
| '%C %C', | '%C %C', | ||||
| $bin, | $bin, | ||||
| ▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | $options = array( | ||||
| 'the first one which exists will be used.'), | 'the first one which exists will be used.'), | ||||
| ), | ), | ||||
| 'flags' => array( | 'flags' => array( | ||||
| 'type' => 'optional list<string>', | 'type' => 'optional list<string>', | ||||
| 'help' => pht( | 'help' => pht( | ||||
| 'Provide a list of additional flags to pass to the linter on the '. | 'Provide a list of additional flags to pass to the linter on the '. | ||||
| 'command line.'), | 'command line.'), | ||||
| ), | ), | ||||
| 'version.min' => array( | |||||
| 'type' => 'optional string', | |||||
| 'help' => pht('TODO'), | |||||
| ), | |||||
| 'version.max' => array( | |||||
| 'type' => 'optional string', | |||||
| 'help' => pht('TODO'), | |||||
| ), | |||||
| ); | ); | ||||
| if ($this->shouldUseInterpreter()) { | if ($this->shouldUseInterpreter()) { | ||||
| $options['interpreter'] = array( | $options['interpreter'] = array( | ||||
| 'type' => 'optional string | list<string>', | 'type' => 'optional string | list<string>', | ||||
| 'help' => pht( | 'help' => pht( | ||||
| 'Specify a string (or list of strings) identifying the interpreter '. | 'Specify a string (or list of strings) identifying the interpreter '. | ||||
| 'which should be used to invoke the linter binary. If you provide '. | 'which should be used to invoke the linter binary. If you provide '. | ||||
| ▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | switch ($key) { | ||||
| if (!is_array($value)) { | if (!is_array($value)) { | ||||
| phutil_deprecated( | phutil_deprecated( | ||||
| 'String support for flags.', | 'String support for flags.', | ||||
| 'You should use list<string> instead.'); | 'You should use list<string> instead.'); | ||||
| $value = (array) $value; | $value = (array) $value; | ||||
| } | } | ||||
| $this->setFlags($value); | $this->setFlags($value); | ||||
| return; | return; | ||||
| case 'minimum-version': | |||||
| $this->minimumVersion = $value; | |||||
| return; | |||||
joshuaspence: We could check here that the linter actually implements the `getVersion` function by checking… | |||||
| case 'maximum-version': | |||||
| $this->maximumVersion = $value; | |||||
| return; | |||||
| } | } | ||||
| return parent::setLinterConfigurationValue($key, $value); | return parent::setLinterConfigurationValue($key, $value); | ||||
| } | } | ||||
| /** | /** | ||||
| * Map a configuration lint code to an `arc` lint code. Primarily, this is | * Map a configuration lint code to an `arc` lint code. Primarily, this is | ||||
| * intended for validation, but can also be used to normalize case or | * intended for validation, but can also be used to normalize case or | ||||
| Show All 12 Lines | |||||
We could check here that the linter actually implements the getVersion function by checking $this->getVersion() !== null.