]>
git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/arcanist_util/cpp_linter/FacebookHowtoevenLinter.php
2 // Copyright 2015-present Facebook. All Rights Reserved.
3 // This source code is licensed under the BSD-style license found in the
4 // LICENSE file in the root directory of this source tree. An additional grant
5 // of patent rights can be found in the PATENTS file in the same directory.
7 final class FacebookHowtoevenLinter
extends ArcanistLinter
{
9 const VERSION
= 'fd9192f324c36d28136d14380f0b552a1385b59b';
11 private $parsedTargets = array();
13 public function getLinterName() {
17 protected function getSeverity($code) {
19 ArcanistLintSeverity
::SEVERITY_DISABLED
,
20 ArcanistLintSeverity
::SEVERITY_ADVICE
,
21 ArcanistLintSeverity
::SEVERITY_WARNING
,
22 ArcanistLintSeverity
::SEVERITY_ERROR
,
24 return idx($severities, $code, ArcanistLintSeverity
::SEVERITY_WARNING
);
27 public function willLintPaths(array $paths) {
28 // Cleanup previous runs.
29 $this->localExecx("rm -rf _build/_lint");
31 // Build compilation database.
32 $lintable_paths = $this->getLintablePaths($paths);
33 $interesting_paths = $this->getInterestingPaths($lintable_paths);
35 if (!$lintable_paths) {
42 "%C %C -p _build/dev/ %Ls",
43 $this->getBinaryPath(),
44 $this->getFilteredIssues(),
46 } catch (CommandException
$exception) {
47 PhutilConsole
::getConsole()->writeErr($exception->getMessage());
53 $this->getProjectRoot().'/_build/_lint/lint.db',
54 SQLITE3_OPEN_READONLY
))
55 ->query("SELECT * FROM raised_issues");
57 while ($issue = $result->fetchArray(SQLITE3_ASSOC
)) {
58 // Skip issues not part of the linted file.
59 if (in_array($issue['file'], $interesting_paths)) {
60 $this->addLintMessage(id(new ArcanistLintMessage())
61 ->setPath($issue['file'])
62 ->setLine($issue['line'])
63 ->setChar($issue['column'])
64 ->setCode('Howtoeven')
65 ->setSeverity($this->getSeverity($issue['severity']))
66 ->setName('Hte-'.$issue['name'])
70 ($issue['message']) ?
$issue['message'] : $issue['description'],
71 $issue['explanation']))
72 ->setOriginalText(idx($issue, 'original', ''))
73 ->setReplacementText(idx($issue, 'replacement', '')));
78 public function lintPath($path) {
82 * Get the paths that we know how to lint.
84 * The strategy is to first look whether there's an existing compilation
85 * database and use that if it's exhaustive. We generate our own only if
88 private function getLintablePaths($paths) {
89 // Replace headers with existing sources.
90 for ($i = 0; $i < count($paths); $i++
) {
91 if (preg_match("/\.h$/", $paths[$i])) {
92 $header = preg_replace("/\.h$/", ".cpp", $paths[$i]);
93 if (file_exists($header)) {
99 // Check if database exists and is exhaustive.
100 $available_paths = $this->getAvailablePaths();
101 $lintable_paths = array_intersect($paths, $available_paths);
102 if ($paths === $lintable_paths) {
103 return $lintable_paths;
106 // Generate our own database.
107 $targets = $this->getTargetsFor($paths);
109 PhutilConsole
::getConsole()->writeErr(
110 "No build targets found for %s\n",
111 implode(', ', $paths));
115 $this->localExecx("./tools/build/bin/fbconfig.par -r %Ls", $targets);
116 $this->localExecx("./tools/build/bin/fbmake.par gen_cdb");
118 $available_paths = $this->getAvailablePaths();
119 $lintable_paths = array_intersect($paths, $available_paths);
120 if ($paths != $lintable_paths) {
121 PhutilConsole
::getConsole()->writeErr(
123 implode(', ', array_diff($paths, $available_paths)));
126 // Return what we know how to lint.
127 return $lintable_paths;
131 * Get the available paths in the current compilation database.
133 private function getAvailablePaths() {
134 $database_path = $this->getProjectRoot()
135 .'/_build/dev/compile_commands.json';
136 if (!file_exists($database_path)) {
140 $entries = json_decode(file_get_contents($database_path), true);
142 foreach ($entries as $entry) {
143 $paths[] = $entry['file'];
149 * Search for the targets directories for the given files.
151 private static function getTargetsFor($paths) {
153 foreach ($paths as $path) {
154 while (($path = dirname($path)) !== '.') {
155 if (in_array('TARGETS', scandir($path))) {
156 $contents = file_get_contents($path.'/TARGETS');
157 if (strpos($contents, 'cpp_binary') !== false) {
164 return array_unique($targets);
168 * The paths that we actually want to report on.
170 private function getInterestingPaths($paths) {
172 foreach ($paths as $path) {
173 $headers[] = preg_replace("/\.cpp$/", ".h", $path);
175 return array_merge($paths, $headers);
179 * The path where the binary is located. Will return the current dewey binary
180 * unless the `HOWTOEVEN_BUILD` environment variable is set.
182 private function getBinaryPath() {
184 "/mnt/dewey/fbcode/.commits/%s/builds/howtoeven/client",
187 $build = getenv('HOWTOEVEN_BUILD');
190 "./_build/%s/tools/howtoeven/client",
192 if (!file_exists($path)) {
193 PhutilConsole
::getConsole()->writeErr(">> %s does not exist\n", $path);
202 * Execute the command in the root directory.
204 private function localExecx($command /* , ... */) {
205 $arguments = func_get_args();
206 return newv('ExecFuture', $arguments)
207 ->setCWD($this->getProjectRoot())
212 * The root of the project.
214 private function getProjectRoot() {
215 return $this->getEngine()->getWorkingCopy()->getProjectRoot();
218 private function getFilteredIssues() {
219 $issues = getenv('HOWTOEVEN_ISSUES');
220 return ($issues) ?
csprintf('-issues %s', $issues) : '';