diff --git a/src/lint/linter/ArcanistPEP8Linter.php b/src/lint/linter/ArcanistPEP8Linter.php index bc644c63..735cfcfd 100644 --- a/src/lint/linter/ArcanistPEP8Linter.php +++ b/src/lint/linter/ArcanistPEP8Linter.php @@ -1,119 +1,119 @@ getPEP8Path()); return $stdout.$this->getPEP8Options(); } public function getPEP8Options() { $working_copy = $this->getEngine()->getWorkingCopy(); $options = $working_copy->getConfig('lint.pep8.options'); if ($options === null) { $options = $this->getConfig('options'); } return $options; } public function getPEP8Path() { $working_copy = $this->getEngine()->getWorkingCopy(); $prefix = $working_copy->getConfig('lint.pep8.prefix'); $bin = $working_copy->getConfig('lint.pep8.bin'); if ($bin === null && $prefix === null) { - $bin = csprintf('/usr/bin/env python2.6 %s', + $bin = csprintf('/usr/bin/env python %s', phutil_get_library_root('arcanist'). '/../externals/pep8/pep8.py'); } else { if ($bin === null) { $bin = 'pep8'; } if ($prefix !== null) { if (!Filesystem::pathExists($prefix.'/'.$bin)) { throw new ArcanistUsageException( "Unable to find PEP8 binary in a specified directory. Make sure ". "that 'lint.pep8.prefix' and 'lint.pep8.bin' keys are set ". "correctly. If you'd rather use a copy of PEP8 installed ". "globally, you can just remove these keys from your .arcconfig."); } $bin = csprintf("%s/%s", $prefix, $bin); return $bin; } // Look for globally installed PEP8 list($err) = exec_manual('which %s', $bin); if ($err) { throw new ArcanistUsageException( "PEP8 does not appear to be installed on this system. Install it ". "(e.g., with 'easy_install pep8') or configure ". "'lint.pep8.prefix' in your .arcconfig to point to the directory ". "where it resides."); } } return $bin; } public function lintPath($path) { $pep8_bin = $this->getPEP8Path(); $options = $this->getPEP8Options(); list($rc, $stdout) = exec_manual( "%C %C %s", $pep8_bin, $options, $this->getEngine()->getFilePathOnDisk($path)); $lines = explode("\n", $stdout); $messages = array(); foreach ($lines as $line) { $matches = null; if (!preg_match('/^(.*?):(\d+):(\d+): (\S+) (.*)$/', $line, $matches)) { continue; } foreach ($matches as $key => $match) { $matches[$key] = trim($match); } if (!$this->isMessageEnabled($matches[4])) { continue; } $message = new ArcanistLintMessage(); $message->setPath($path); $message->setLine($matches[2]); $message->setChar($matches[3]); $message->setCode($matches[4]); $message->setName('PEP8 '.$matches[4]); $message->setDescription($matches[5]); $message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING); $this->addLintMessage($message); } } } diff --git a/src/lint/linter/ArcanistPyFlakesLinter.php b/src/lint/linter/ArcanistPyFlakesLinter.php index 7f9bb842..3ae6cdc3 100644 --- a/src/lint/linter/ArcanistPyFlakesLinter.php +++ b/src/lint/linter/ArcanistPyFlakesLinter.php @@ -1,106 +1,110 @@ 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 = ''; + $python_path = array(); // 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:'; + $python_path[] = $pyflakes_prefix.'/lib/python2.7/site-packages'; + $python_path[] = $pyflakes_prefix.'/lib/python2.7/dist-packages'; + $python_path[] = $pyflakes_prefix.'/lib/python2.6/site-packages'; + $python_path[] = $pyflakes_prefix.'/lib/python2.6/dist-packages'; } - + $python_path[] = ''; + $python_path = implode(':', $python_path); $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); } $severity = ArcanistLintSeverity::SEVERITY_WARNING; $description = $matches[3]; $error_regexp = '/(^undefined|^duplicate|before assignment$)/'; if (preg_match($error_regexp, $description)) { $severity = ArcanistLintSeverity::SEVERITY_ERROR; } $message = new ArcanistLintMessage(); $message->setPath($path); $message->setLine($matches[2]); $message->setCode($this->getLinterName()); $message->setDescription($description); $message->setSeverity($severity); $this->addLintMessage($message); } } } diff --git a/src/lint/linter/ArcanistPyLintLinter.php b/src/lint/linter/ArcanistPyLintLinter.php index 618014ad..88b45c18 100644 --- a/src/lint/linter/ArcanistPyLintLinter.php +++ b/src/lint/linter/ArcanistPyLintLinter.php @@ -1,245 +1,248 @@ getEngine()->getWorkingCopy(); $error_regexp = $working_copy->getConfig('lint.pylint.codes.error'); $warning_regexp = $working_copy->getConfig('lint.pylint.codes.warning'); $advice_regexp = $working_copy->getConfig('lint.pylint.codes.advice'); if (!$error_regexp && !$warning_regexp && !$advice_regexp) { throw new ArcanistUsageException( "You are invoking the PyLint linter but have not configured any of ". "'lint.pylint.codes.error', 'lint.pylint.codes.warning', or ". "'lint.pylint.codes.advice'. Consult the documentation for ". "ArcanistPyLintLinter."); } $code_map = array( ArcanistLintSeverity::SEVERITY_ERROR => $error_regexp, ArcanistLintSeverity::SEVERITY_WARNING => $warning_regexp, ArcanistLintSeverity::SEVERITY_ADVICE => $advice_regexp, ); foreach ($code_map as $sev => $codes) { if ($codes === null) { continue; } if (!is_array($codes)) { $codes = array($codes); } foreach ($codes as $code_re) { if (preg_match("/{$code_re}/", $code)) { return $sev; } } } // If the message code doesn't match any of the provided regex's, // then just disable it. return ArcanistLintSeverity::SEVERITY_DISABLED; } private function getPyLintPath() { $pylint_bin = "pylint"; // Use the PyLint prefix specified in the config file $working_copy = $this->getEngine()->getWorkingCopy(); $prefix = $working_copy->getConfig('lint.pylint.prefix'); if ($prefix !== null) { $pylint_bin = $prefix."/bin/".$pylint_bin; } if (!Filesystem::pathExists($pylint_bin)) { list($err) = exec_manual('which %s', $pylint_bin); if ($err) { throw new ArcanistUsageException( "PyLint does not appear to be installed on this system. Install it ". "(e.g., with 'sudo easy_install pylint') or configure ". "'lint.pylint.prefix' in your .arcconfig to point to the directory ". "where it resides."); } } return $pylint_bin; } private function getPyLintPythonPath() { // Get non-default install locations for pylint and its dependencies // libraries. $working_copy = $this->getEngine()->getWorkingCopy(); $prefixes = array( $working_copy->getConfig('lint.pylint.prefix'), $working_copy->getConfig('lint.pylint.logilab_astng.prefix'), $working_copy->getConfig('lint.pylint.logilab_common.prefix'), ); // Add the libraries to the python search path $python_path = array(); foreach ($prefixes as $prefix) { if ($prefix !== null) { + $python_path[] = $prefix.'/lib/python2.7/site-packages'; + $python_path[] = $prefix.'/lib/python2.7/dist-packages'; $python_path[] = $prefix.'/lib/python2.6/site-packages'; + $python_path[] = $prefix.'/lib/python2.6/dist-packages'; } } $config_paths = $working_copy->getConfig('lint.pylint.pythonpath'); if ($config_paths !== null) { foreach ($config_paths as $config_path) { if ($config_path !== null) { $python_path[] = Filesystem::resolvePath($config_path, $working_copy->getProjectRoot()); } } } $python_path[] = ''; return implode(":", $python_path); } private function getPyLintOptions() { // '-rn': don't print lint report/summary at end // '-iy': show message codes for lint warnings/errors $options = array('-rn', '-iy'); $working_copy = $this->getEngine()->getWorkingCopy(); // Specify an --rcfile, either absolute or relative to the project root. // Stupidly, the command line args above are overridden by rcfile, so be // careful. $rcfile = $working_copy->getConfig('lint.pylint.rcfile'); if ($rcfile !== null) { $rcfile = Filesystem::resolvePath( $rcfile, $working_copy->getProjectRoot()); $options[] = csprintf('--rcfile=%s', $rcfile); } // Add any options defined in the config file for PyLint $config_options = $working_copy->getConfig('lint.pylint.options'); if ($config_options !== null) { $options = array_merge($options, $config_options); } return implode(" ", $options); } public function willLintPaths(array $paths) { return; } public function getLinterName() { return 'PyLint'; } public function getLintSeverityMap() { return array(); } public function getLintNameMap() { return array(); } public function lintPath($path) { $pylint_bin = $this->getPyLintPath(); $python_path = $this->getPyLintPythonPath(); $options = $this->getPyLintOptions(); $path_on_disk = $this->getEngine()->getFilePathOnDisk($path); try { list($stdout, $_) = execx( "/usr/bin/env PYTHONPATH=%s\$PYTHONPATH ". "{$pylint_bin} {$options} {$path_on_disk}", $python_path); } catch (CommandException $e) { if ($e->getError() == 32) { // According to ##man pylint## the exit status of 32 means there was a // usage error. That's bad, so actually exit abnormally. throw $e; } else { // The other non-zero exit codes mean there were messages issued, // which is expected, so don't exit. $stdout = $e->getStdout(); } } $lines = explode("\n", $stdout); $messages = array(); foreach ($lines as $line) { $matches = null; if (!preg_match( '/([A-Z]\d+): *(\d+)(?:|,\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($matches[1]); $message->setName($this->getLinterName()." ".$matches[1]); $message->setDescription($matches[3]); $message->setSeverity($this->getMessageCodeSeverity($matches[1])); $this->addLintMessage($message); } } }