Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View file

@ -0,0 +1,8 @@
<rule version="1">
<pattern>\b(task_|app_)?[cmz]alloc(_check)?</pattern>
<message>
<id>applib_malloc</id>
<severity>style</severity>
<summary>For structures that are exported, we try to always use applib_malloc. Please consider using this paradigm. If you have any questions, consult Bradley Murray. He is great.</summary>
</message>
</rule>

View file

@ -0,0 +1,17 @@
<?php
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
phutil_register_library('linters', __FILE__);

View file

@ -0,0 +1,38 @@
<?php
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* This file is automatically generated. Use 'arc liberate' to rebuild it.
* @generated
* @phutil-library-version 2
*/
phutil_register_library_map(array(
'__library_version__' => 2,
'class' =>
array(
'PebbleCpplintLinter' => 'cpplint/PebbleCpplintLinter.php',
'PebbleCppcheckLinter' => 'cppcheck/PebbleCppcheckLinter.php',
),
'function' =>
array(
),
'xmap' =>
array(
'PebbleCpplintLinter' => 'ArcanistExternalLinter',
'PebbleCppcheckLinter' => 'ArcanistExternalLinter',
),
));

View file

@ -0,0 +1,134 @@
<?php
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* Uses Cppcheck to do basic checks in a C++ file.
*/
final class PebbleCppcheckLinter extends ArcanistExternalLinter {
public function getInfoName() {
return 'Cppcheck-pebble';
}
public function getInfoURI() {
return 'http://cppcheck.sourceforge.net';
}
public function getInfoDescription() {
return pht('Use `cppcheck` to perform static analysis on C/C++ code.');
}
public function getLinterName() {
return 'cppcheck-pebble';
}
public function getLinterConfigurationName() {
return 'cppcheck-pebble';
}
public function getDefaultBinary() {
return 'cppcheck';
}
public function getVersion() {
list($stdout) = execx('%C --version', $this->getExecutableCommand());
$matches = array();
$regex = '/^Cppcheck (?P<version>\d+\.\d+)$/';
if (preg_match($regex, $stdout, $matches)) {
return $matches['version'];
} else {
return false;
}
}
public function getInstallInstructions() {
return pht('Install Cppcheck using `apt-get install cppcheck` for Ubuntu'.
' or `brew install cppcheck` for Mac OS X');
}
protected function getMandatoryFlags() {
return array(
'--quiet',
'--inline-suppr',
'--xml',
'--xml-version=2',
);
}
protected function getDefaultFlags() {
return array('-j2',
'--enable=performance,style,portability,information',
'--library=tools/arc/linting/tintin.cfg,std',
'--rule-file=tools/arc/linting/tintin.rule',
'--enable=all',
'--suppress=passedByValue',
'--suppress=selfAssignment',
'--suppress=toomanyconfigs',
'--suppress=uninitStructMember',
'--suppress=unnecessaryForwardDeclaration',
'--suppress=unusedFunction',
'--suppress=variableScope',
'--suppress=unusedStructMember',
'--suppress=varFuncNullUB',
'--suppress=ConfigurationNotChecked');
}
protected function getDefaultMessageSeverity($code) {
return ArcanistLintSeverity::SEVERITY_WARNING;
}
protected function parseLinterOutput($path, $err, $stdout, $stderr) {
$dom = new DOMDocument();
$ok = @$dom->loadXML($stderr);
if (!$ok) {
return false;
}
$errors = $dom->getElementsByTagName('error');
$messages = array();
foreach ($errors as $error) {
foreach ($error->getElementsByTagName('location') as $location) {
$message = new ArcanistLintMessage();
$message->setPath($location->getAttribute('file'));
$message->setLine($location->getAttribute('line'));
$message->setCode($error->getAttribute('id'));
$message->setName($error->getAttribute('id'));
$message->setDescription($error->getAttribute('msg'));
$message->setSeverity($this->getLintMessageSeverity($error->getAttribute('id')));
$messages[] = $message;
}
}
return $messages;
}
protected function getLintCodeFromLinterConfigurationKey($code) {
if (!preg_match('@^[a-z_]+$@', $code)) {
throw new Exception(
pht(
'Unrecognized severity code "%s". Expected a valid cppcheck '.
'severity code like "%s" or "%s".',
$code,
'unreadVariable',
'memleak'));
}
return $code;
}
}

View file

@ -0,0 +1,105 @@
<?php
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* Uses Google's `cpplint.py` to check code.
*/
final class PebbleCpplintLinter extends ArcanistExternalLinter {
public function getLinterName() {
return 'cpplint-pebble';
}
public function getLinterConfigurationName() {
return 'cpplint-pebble';
}
public function getDefaultBinary() {
return "tools/arc/linting/linters/cpplint/cpplint.py";
}
public function getInstallInstructions() {
return pht('Ask Tyler Hoffman to fix it.');
}
public function shouldExpectCommandErrors() {
return true;
}
public function supportsReadDataFromStdin() {
return true;
}
public function getReadDataFromStdinFilename() {
return '-';
}
protected function getDefaultFlags() {
return array();
}
protected function getDefaultMessageSeverity($code) {
return ArcanistLintSeverity::SEVERITY_WARNING;
}
protected function parseLinterOutput($path, $err, $stdout, $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($this->getLintMessageSeverity($matches[3]));
$messages[] = $message;
}
if ($err && !$messages) {
return false;
}
return $messages;
}
protected function getLintCodeFromLinterConfigurationKey($code) {
if (!preg_match('@^[a-z_]+/[a-z_]+$@', $code)) {
throw new Exception(
pht(
'Unrecognized lint message code "%s". Expected a valid cpplint '.
'lint code like "%s" or "%s".',
$code,
'build/include_order',
'whitespace/braces'));
}
return $code;
}
}

View file

@ -0,0 +1,142 @@
#!/usr/bin/env python
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import re
# FW linters for tintin in python!
#
# Adding a new linter is simple. Simply,
# 1) Subclass FwLinter
# 2) Define found_lint_error(self, filename, line) and return True iff an error is found on the
# line passed in
# 3) Define SEVERITY, NAME, MESSAGE as documented here:
# https://secure.phabricator.com/book/phabricator/article/arcanist_lint_script_and_regex/
class FwLinter(object):
def construct_arcanist_error_string(self, severity, name, message, filename, line_num):
return '|'.join([severity, name, message, filename, line_num])
def handle_lint_error(self, filename, line, line_num):
""" Responsible for communicating the lint error to arcanist. Today, this just involves
printing the message because 'arc lint' monitors stdout """
print self.construct_arcanist_error_string(self.SEVERITY, self.NAME, self.MESSAGE,
filename, str(line_num))
def found_lint_error(self, filename, line):
""" Given a line, returns True if a lint error is found on the line and false otherwise """
raise NotImplementedError
#
# FwLinter Subclasses
#
class TodoFixmeLinter(FwLinter):
SEVERITY = "ADVICE"
NAME = "TODO/FIXME"
MESSAGE = "TODO/FIXME Found. Just letting you know"
jira_ticket_id_regex = re.compile(r'PBL-\d+', re.IGNORECASE)
def found_lint_error(self, filename, line):
line_lowercase = line.lower()
return 'todo' in line_lowercase or 'fixme' in line_lowercase
def handle_lint_error(self, filename, line, line_num):
message = self.MESSAGE
# If we find a JIRA ticket ID in the line, add the full JIRA URL to the message
jira_matches = self.jira_ticket_id_regex.findall(line)
if jira_matches:
jira_ticket_id = jira_matches[0]
jira_base_url = 'https://pebbletechnology.atlassian.net/browse/'
jira_url = jira_base_url + jira_ticket_id
message = ' '.join([message, jira_url])
print self.construct_arcanist_error_string(self.SEVERITY, self.NAME, message, filename,
str(line_num))
class UndefinedAttributeLinter(FwLinter):
SEVERITY = "ERROR"
NAME = "Undefined Attribute"
MESSAGE = "yo, you need to include util/attributes.h if you want to PACK stuff"
attribute_inc_regex = re.compile(r'(^#include\s+[<\"]util/attributes.h[>\"])')
def __init__(self):
self.include_found = False
def found_lint_error(self, filename, line):
if self.attribute_inc_regex.findall(line) or '#define PACKED' in line:
self.include_found = True
return False
elif ' PACKED ' in line and not self.include_found:
return True
class StaticFuncFormatLinter(FwLinter):
SEVERITY = "WARNING"
NAME = "Static Function Format Error"
MESSAGE = "umm, you forgot to add 'prv_' or mark this function as 'static'"
func_proto_regex = re.compile(r'^(\w+)\W?.*\W(\w+\([a-zA-Z])')
def found_lint_error(self, filename, line):
# Ignore header files
if (filename.endswith(".h")):
return False
matches = self.func_proto_regex.findall(line)
if matches and len(matches[0]) == 2:
groups = matches[0]
func_starts_with_prv = groups[1].startswith('prv_')
func_is_static = any(x in groups[0] for x in ['static', 'T_STATIC'])
return ((func_is_static and not func_starts_with_prv) or
(func_starts_with_prv and not func_is_static))
return False
class ColorFallbackDeprecatedMacroLinter(FwLinter):
SEVERITY = "WARNING"
NAME = "COLOR_FALLBACK() Deprecated Macro"
MESSAGE = "The macro `COLOR_FALLBACK()` has been deprecated for internal firmware use. " \
"Use the equivalent `PBL_IF_COLOR_ELSE()` macro instead. Unfortunately, we can't " \
"simply remove `COLOR_FALLBACK()` from the firmware because it's exported in the SDK."
def found_lint_error(self, filename, line):
return 'COLOR_FALLBACK' in line
#
# Code to run our FW linters
#
def lint(filename):
linters = [linter() for linter in FwLinter.__subclasses__()]
with open(filename) as f:
for i, line in enumerate(f.readlines()):
line_num = i + 1
for linter in linters:
if linter.found_lint_error(filename, line):
linter.handle_lint_error(filename, line, line_num)
if __name__ == '__main__':
import sys
filename = sys.argv[1]
lint(filename)

View file

@ -0,0 +1,94 @@
<?xml version="1.0"?>
<def>
<!-- pbl_malloc.h -->
<memory>
<alloc>malloc</alloc>
<dealloc>free</dealloc>
</memory>
<memory>
<alloc>kernel_malloc</alloc>
<alloc>kernel_malloc_check</alloc>
<dealloc>kernel_free</dealloc>
</memory>
<memory>
<alloc>app_malloc</alloc>
<alloc>app_malloc_check</alloc>
<dealloc>app_free</dealloc>
</memory>
<memory>
<alloc>task_malloc</alloc>
<alloc>task_malloc_check</alloc>
<dealloc>task_free</dealloc>
</memory>
<memory>
<alloc>applib_type_malloc</alloc>
<alloc>applib_malloc</alloc>
<dealloc>applib_free</dealloc>
</memory>
<!-- Layers -->
<resource>
<alloc>action_bar_layer_create</alloc>
<dealloc>action_bar_layer_destroy</dealloc>
</resource>
<resource>
<alloc>bitmap_layer_create</alloc>
<dealloc>bitmap_layer_destroy</dealloc>
</resource>
<resource>
<alloc>inverter_layer_create</alloc>
<dealloc>inverter_layer_destroy</dealloc>
</resource>
<resource>
<alloc>menu_layer_create</alloc>
<dealloc>menu_layer_destroy</dealloc>
</resource>
<resource>
<alloc>rot_bitmap_layer_create</alloc>
<dealloc>rot_bitmap_layer_destroy</dealloc>
</resource>
<resource>
<alloc>scroll_layer_create</alloc>
<dealloc>scroll_layer_destroy</dealloc>
</resource>
<resource>
<alloc>simple_menu_layer_create</alloc>
<dealloc>simple_menu_layer_destroy</dealloc>
</resource>
<resource>
<alloc>text_layer_create</alloc>
<dealloc>text_layer_destroy</dealloc>
</resource>
<!-- Animations -->
<resource>
<alloc>property_animation_create</alloc>
<dealloc>property_animation_destroy</dealloc>
</resource>
<!-- Windows -->
<resource>
<alloc>window_create</alloc>
<dealloc>window_destroy</dealloc>
</resource>
<!-- Graphics -->
<resource>
<alloc>gpath_create</alloc>
<dealloc>gpath_destroy</dealloc>
</resource>
<!-- Weather -->
<memory>
<alloc>watch_app_prefs_get_weather</alloc>
<dealloc>watch_app_prefs_destroy_weather</dealloc>
</memory>
</def>

View file

@ -0,0 +1,43 @@
<?xml version="1.0"?>
<rule version="1">
<pattern>PBL_ASSERT_OOM</pattern>
<message>
<id>pbl_assert_oom</id>
<severity>style</severity>
<summary>PBL_ASSERT_OOM is a large macro. Consider using the *_malloc_check versions or checking for NULL instead.</summary>
</message>
</rule>
<rule version="1">
<pattern>snprintf</pattern>
<message>
<id>snprintf</id>
<severity>style</severity>
<summary>The function 'snprintf' uses up a lot of our precious stack space. A simpler set of strcpy/strcat functions might be a better approach. If it is not being used deep in the stack, continue on your merry way.</summary>
</message>
</rule>
<rule version="1">
<pattern>psleep</pattern>
<message>
<id>psleep</id>
<severity>style</severity>
<summary>The function 'psleep' can delay for less than the interval
specified. In fact, psleep(1) can actually wind up taking no time at
all. (Check out sleep.h if you don't believe me!) Also note, psleep(0) forces a reschedule but will starve low priority
tasks if you do it in a while loop! So double check your delay and carry on!</summary>
</message>
</rule>
<rule version="1">
<pattern>\b(\w+_)?malloc(_check)?\b</pattern>
<message>
<id>malloc</id>
<severity>style</severity>
<summary>The function 'malloc' returns uninitialized memory while callers often (sometimes indirectly) assume that the allocated memory is filled with bytes of value zero. If in doubt, use `zalloc` instead. In most cases, the performance impact of this extra processing is negligible.</summary>
</message>
</rule>
<!-- More can be added here -->