mirror of
https://github.com/isledecomp/isle-portable.git
synced 2025-04-13 15:14:25 -04:00
Add back some stuff from decomp (#2)
* Add ncc script * add requirements.txt * Add other builds * Add with submodules * Comment out for now * Add
This commit is contained in:
parent
f3f8480834
commit
4258fb0deb
7 changed files with 775 additions and 2 deletions
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
|
@ -13,9 +13,23 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
toolchain:
|
||||
- { name: 'MSVC', shell: 'sh', setup-cmake: true, setup-ninja: true, setup-msvc: true }
|
||||
- { name: 'MSVC', shell: 'sh', setup-cmake: true, setup-ninja: true, setup-msvc: true }
|
||||
# TODO: Add back eventually, but exclude 3rdparty code from warnings as errors
|
||||
# - { name: 'msys2 mingw32', shell: 'msys2 {0}', msystem: mingw32, msys-env: mingw-w64-i686, clang-tidy: true, werror: true }
|
||||
# - { name: 'msys2 clang32', shell: 'msys2 {0}', msystem: clang32, msys-env: mingw-w64-clang-i686, clang-tidy: true, werror: true, no-dx5-libs: true }
|
||||
|
||||
steps:
|
||||
- name: Set up MSYS2
|
||||
if: ${{ !!matrix.toolchain.msystem }}
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.toolchain.msystem }}
|
||||
install: >-
|
||||
${{ matrix.toolchain.msys-env }}-cc
|
||||
${{ matrix.toolchain.msys-env }}-cmake
|
||||
${{ matrix.toolchain.msys-env }}-ninja
|
||||
${{ matrix.toolchain.msys-env }}-clang-tools-extra
|
||||
|
||||
- name: Setup cmake
|
||||
if: matrix.toolchain.setup-cmake
|
||||
uses: jwlawson/actions-setup-cmake@v1.13
|
||||
|
|
43
.github/workflows/naming.yml
vendored
Normal file
43
.github/workflows/naming.yml
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
name: Naming
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
ncc:
|
||||
name: 'C++'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install LLVM and Clang
|
||||
uses: KyleMayes/install-llvm-action@v1
|
||||
with:
|
||||
version: "16"
|
||||
|
||||
- name: Install python libraries
|
||||
run: |
|
||||
pip install -r tools/requirements.txt
|
||||
|
||||
- name: Run ncc
|
||||
run: |
|
||||
action_headers=$(find LEGO1/lego/legoomni/include/actions \
|
||||
-name '*.h' -print0 | xargs -0 echo)
|
||||
|
||||
python3 tools/ncc/ncc.py \
|
||||
--clang-lib ${{ env.LLVM_PATH }}/lib/libclang.so \
|
||||
--recurse \
|
||||
--style tools/ncc/ncc.style \
|
||||
--skip tools/ncc/skip.yml \
|
||||
--definition WINAPI FAR BOOL CALLBACK HWND__=HWND \
|
||||
--include \
|
||||
util \
|
||||
LEGO1 \
|
||||
LEGO1/omni/include \
|
||||
LEGO1/lego/legoomni/include \
|
||||
LEGO1/lego/sources \
|
||||
--exclude \
|
||||
LEGO1/omni/include/flic.h \
|
||||
LEGO1/omni/src/video/flic.cpp \
|
||||
$action_headers \
|
||||
--path LEGO1/omni LEGO1/lego/legoomni
|
|
@ -25,7 +25,7 @@ To achieve our goal of platform independence, we need to replace any Windows-onl
|
|||
| [Smacker](https://github.com/isledecomp/isle/tree/master/3rdparty/smacker) | [libsmacker](https://github.com/foxtacles/libsmacker) | ✅ | [Open issues](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable%20%22%2F%2F%20%5Blibrary%3Alibsmacker%5D%22&type=code) |
|
||||
| Filesystem | C standard library | ❌ | [Open issues](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Afilesystem%5D%22&type=code) |
|
||||
| Threads, Mutexes (Synchronization) | [SDL3](https://www.libsdl.org/) | ❌ | [Open issues](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Asynchronization%5D%22&type=code) |
|
||||
| Keyboard, Mouse, Joystick (Input) | [SDL3](https://www.libsdl.org/) | ❌ | [Open issues](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Ainput%5D%22&type=code) |
|
||||
| Keyboard, Mouse, Joystick, DirectInput (Input) | [SDL3](https://www.libsdl.org/) | ❌ | [Open issues](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Ainput%5D%22&type=code) |
|
||||
| WinMM, DirectSound (Audio) | [SDL3](https://www.libsdl.org/) | ❌ | [Open issues](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3Aaudio%5D%22&type=code) |
|
||||
| DirectDraw (2D video) | [SDL3](https://www.libsdl.org/) | ❌ | [Open issues](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3A2d%5D%22&type=code) |
|
||||
| Direct3D (3D video) | [SDL3](https://www.libsdl.org/), OpenGL ES (**TBD**) | ❌ | [Open issues](https://github.com/search?q=repo%3Aisledecomp%2Fisle-portable+%22%2F%2F+%5Blibrary%3A3d%5D%22&type=code) |
|
||||
|
|
661
tools/ncc/ncc.py
Normal file
661
tools/ncc/ncc.py
Normal file
|
@ -0,0 +1,661 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2018 Nithin Nellikunnu (nithin.nn@gmail.com)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import logging
|
||||
import argparse
|
||||
import yaml
|
||||
import re
|
||||
import sys
|
||||
import difflib
|
||||
import os
|
||||
import fnmatch
|
||||
from clang.cindex import Index
|
||||
from clang.cindex import CursorKind
|
||||
from clang.cindex import StorageClass
|
||||
from clang.cindex import TypeKind
|
||||
from clang.cindex import Config
|
||||
|
||||
|
||||
# Clang cursor kind to ncc Defined cursor map
|
||||
default_rules_db = {}
|
||||
clang_to_user_map = {}
|
||||
special_kind = {CursorKind.STRUCT_DECL: 1, CursorKind.CLASS_DECL: 1}
|
||||
file_extensions = [".c", ".cpp", ".h", ".hpp"]
|
||||
|
||||
|
||||
class Rule(object):
|
||||
def __init__(self, name, clang_kind, parent_kind=None, pattern_str='^.*$'):
|
||||
self.name = name
|
||||
self.clang_kind = clang_kind
|
||||
self.parent_kind = parent_kind
|
||||
self.pattern_str = pattern_str
|
||||
self.pattern = re.compile(pattern_str)
|
||||
self.includes = []
|
||||
self.excludes = []
|
||||
|
||||
def evaluate(self, node, scope=None):
|
||||
if not self.pattern.match(node.spelling):
|
||||
fmt = '{}:{}:{}: "{}" does not match "{}" associated with {}\n'
|
||||
msg = fmt.format(node.location.file.name, node.location.line, node.location.column,
|
||||
node.displayname, self.pattern_str, self.name)
|
||||
sys.stderr.write(msg)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class ScopePrefixRule(object):
|
||||
def __init__(self, pattern_obj):
|
||||
self.name = "ScopePrefixRule"
|
||||
self.rule_names = ["Global", "Static", "ClassMember", "StructMember"]
|
||||
self.global_prefix = ""
|
||||
self.static_prefix = ""
|
||||
self.class_member_prefix = ""
|
||||
self.struct_member_prefix = ""
|
||||
|
||||
try:
|
||||
for key, value in pattern_obj.items():
|
||||
if key == "Global":
|
||||
self.global_prefix = value
|
||||
elif key == "Static":
|
||||
self.static_prefix = value
|
||||
elif key == "ClassMember":
|
||||
self.class_member_prefix = value
|
||||
elif key == "StructMember":
|
||||
self.struct_member_prefix = value
|
||||
else:
|
||||
raise ValueError(key)
|
||||
except ValueError as e:
|
||||
sys.stderr.write('{} is not a valid rule name\n'.format(e.message))
|
||||
fixit = difflib.get_close_matches(e.message, self.rule_names, n=1, cutoff=0.8)
|
||||
if fixit:
|
||||
sys.stderr.write('Did you mean rule name: {} ?\n'.format(fixit[0]))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class DataTypePrefixRule(object):
|
||||
def __init__(self, pattern_obj):
|
||||
self.name = "DataTypePrefix"
|
||||
self.rule_names = ["String", "Integer", "Bool", "Pointer"]
|
||||
self.string_prefix = ""
|
||||
|
||||
try:
|
||||
for key, value in pattern_obj.items():
|
||||
if key == "String":
|
||||
self.string_prefix = value
|
||||
elif key == "Integer":
|
||||
self.integer_prefix = value
|
||||
elif key == "Bool":
|
||||
self.bool_prefix = value
|
||||
elif key == "Pointer":
|
||||
self.pointer_prefix = value
|
||||
else:
|
||||
raise ValueError(key)
|
||||
except ValueError as e:
|
||||
sys.stderr.write('{} is not a valid rule name\n'.format(e.message))
|
||||
fixit = difflib.get_close_matches(e.message, self.rule_names, n=1, cutoff=0.8)
|
||||
if fixit:
|
||||
sys.stderr.write('Did you mean rule name: {} ?\n'.format(fixit[0]))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class VariableNameRule(object):
|
||||
def __init__(self, pattern_obj=None):
|
||||
self.name = "VariableName"
|
||||
self.pattern_str = "^.*$"
|
||||
self.rule_names = ["ScopePrefix", "DataTypePrefix", "Pattern"]
|
||||
self.scope_prefix_rule = None
|
||||
self.datatype_prefix_rule = None
|
||||
|
||||
try:
|
||||
for key, value in pattern_obj.items():
|
||||
if key == "ScopePrefix":
|
||||
self.scope_prefix_rule = ScopePrefixRule(value)
|
||||
elif key == "DataTypePrefix":
|
||||
self.datatype_prefix_rule = DataTypePrefixRule(value)
|
||||
elif key == "Pattern":
|
||||
self.pattern_str = value
|
||||
else:
|
||||
raise ValueError(key)
|
||||
except ValueError as e:
|
||||
sys.stderr.write('{} is not a valid rule name\n'.format(e.message))
|
||||
fixit = difflib.get_close_matches(e.message, self.rule_names, n=1, cutoff=0.8)
|
||||
if fixit:
|
||||
sys.stderr.write('Did you mean rule name: {} ?\n'.format(fixit[0]))
|
||||
sys.exit(1)
|
||||
except re.error as e:
|
||||
sys.stderr.write('{} is not a valid pattern \n'.format(e.message))
|
||||
sys.exit(1)
|
||||
|
||||
def get_scope_prefix(self, node, scope=None):
|
||||
if node.storage_class == StorageClass.STATIC:
|
||||
return self.scope_prefix_rule.static_prefix
|
||||
elif (scope is None) and (node.storage_class == StorageClass.EXTERN or
|
||||
node.storage_class == StorageClass.NONE):
|
||||
return self.scope_prefix_rule.global_prefix
|
||||
elif (scope is CursorKind.CLASS_DECL) or (scope is CursorKind.CLASS_TEMPLATE):
|
||||
return self.scope_prefix_rule.class_member_prefix
|
||||
elif (scope is CursorKind.STRUCT_DECL):
|
||||
return self.scope_prefix_rule.struct_member_prefix
|
||||
return ""
|
||||
|
||||
def get_datatype_prefix(self, node):
|
||||
if node.type.kind is TypeKind.ELABORATED:
|
||||
if node.type.spelling.startswith('std::string'):
|
||||
return self.datatype_prefix_rule.string_prefix
|
||||
elif (node.type.spelling.startswith('std::unique_ptr') or
|
||||
node.type.spelling.startswith("std::shared_ptr")):
|
||||
return self.datatype_prefix_rule.pointer_prefix
|
||||
elif node.type.kind is TypeKind.POINTER:
|
||||
return self.datatype_prefix_rule.pointer_prefix
|
||||
else:
|
||||
if node.type.spelling == "int":
|
||||
return self.datatype_prefix_rule.integer_prefix
|
||||
elif node.type.spelling.startswith('bool'):
|
||||
return self.datatype_prefix_rule.bool_prefix
|
||||
return ""
|
||||
|
||||
def evaluate(self, node, scope=None):
|
||||
pattern_str = self.pattern_str
|
||||
scope_prefix = self.get_scope_prefix(node, scope)
|
||||
datatype_prefix = self.get_datatype_prefix(node)
|
||||
|
||||
pattern_str = pattern_str[0] + scope_prefix + datatype_prefix + pattern_str[1:]
|
||||
|
||||
pattern = re.compile(pattern_str)
|
||||
if not pattern.match(node.spelling):
|
||||
fmt = '{}:{}:{}: "{}" does not have the pattern {} associated with Variable name\n'
|
||||
msg = fmt.format(node.location.file.name, node.location.line, node.location.column,
|
||||
node.displayname, pattern_str)
|
||||
sys.stderr.write(msg)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# All supported rules
|
||||
default_rules_db["StructName"] = Rule("StructName", CursorKind.STRUCT_DECL)
|
||||
default_rules_db["UnionName"] = Rule("UnionName", CursorKind.UNION_DECL)
|
||||
default_rules_db["ClassName"] = Rule("ClassName", CursorKind.CLASS_DECL)
|
||||
default_rules_db["EnumName"] = Rule("EnumName", CursorKind.ENUM_DECL)
|
||||
default_rules_db["EnumConstantName"] = Rule("EnumConstantName", CursorKind.ENUM_CONSTANT_DECL)
|
||||
default_rules_db["FunctionName"] = Rule("FunctionName", CursorKind.FUNCTION_DECL)
|
||||
default_rules_db["ParameterName"] = Rule("ParameterName", CursorKind.PARM_DECL)
|
||||
default_rules_db["TypedefName"] = Rule("TypedefName", CursorKind.TYPEDEF_DECL)
|
||||
default_rules_db["CppMethod"] = Rule("CppMethod", CursorKind.CXX_METHOD)
|
||||
default_rules_db["Namespace"] = Rule("Namespace", CursorKind.NAMESPACE)
|
||||
default_rules_db["ConversionFunction"] = Rule("ConversionFunction", CursorKind.CONVERSION_FUNCTION)
|
||||
default_rules_db["TemplateTypeParameter"] = Rule(
|
||||
"TemplateTypeParameter", CursorKind.TEMPLATE_TYPE_PARAMETER)
|
||||
default_rules_db["TemplateNonTypeParameter"] = Rule(
|
||||
"TemplateNonTypeParameter", CursorKind.TEMPLATE_NON_TYPE_PARAMETER)
|
||||
default_rules_db["TemplateTemplateParameter"] = Rule(
|
||||
"TemplateTemplateParameter", CursorKind.TEMPLATE_TEMPLATE_PARAMETER)
|
||||
default_rules_db["FunctionTemplate"] = Rule("FunctionTemplate", CursorKind.FUNCTION_TEMPLATE)
|
||||
default_rules_db["ClassTemplate"] = Rule("ClassTemplate", CursorKind.CLASS_TEMPLATE)
|
||||
default_rules_db["ClassTemplatePartialSpecialization"] = Rule(
|
||||
"ClassTemplatePartialSpecialization", CursorKind.CLASS_TEMPLATE_PARTIAL_SPECIALIZATION)
|
||||
default_rules_db["NamespaceAlias"] = Rule("NamespaceAlias", CursorKind.NAMESPACE_ALIAS)
|
||||
default_rules_db["UsingDirective"] = Rule("UsingDirective", CursorKind.USING_DIRECTIVE)
|
||||
default_rules_db["UsingDeclaration"] = Rule("UsingDeclaration", CursorKind.USING_DECLARATION)
|
||||
default_rules_db["TypeAliasName"] = Rule("TypeAliasName", CursorKind.TYPE_ALIAS_DECL)
|
||||
default_rules_db["ClassAccessSpecifier"] = Rule(
|
||||
"ClassAccessSpecifier", CursorKind.CXX_ACCESS_SPEC_DECL)
|
||||
default_rules_db["TypeReference"] = Rule("TypeReference", CursorKind.TYPE_REF)
|
||||
default_rules_db["CxxBaseSpecifier"] = Rule("CxxBaseSpecifier", CursorKind.CXX_BASE_SPECIFIER)
|
||||
default_rules_db["TemplateReference"] = Rule("TemplateReference", CursorKind.TEMPLATE_REF)
|
||||
default_rules_db["NamespaceReference"] = Rule("NamespaceReference", CursorKind.NAMESPACE_REF)
|
||||
default_rules_db["MemberReference"] = Rule("MemberReference", CursorKind.MEMBER_REF)
|
||||
default_rules_db["LabelReference"] = Rule("LabelReference", CursorKind.LABEL_REF)
|
||||
default_rules_db["OverloadedDeclarationReference"] = Rule(
|
||||
"OverloadedDeclarationReference", CursorKind.OVERLOADED_DECL_REF)
|
||||
default_rules_db["VariableReference"] = Rule("VariableReference", CursorKind.VARIABLE_REF)
|
||||
default_rules_db["InvalidFile"] = Rule("InvalidFile", CursorKind.INVALID_FILE)
|
||||
default_rules_db["NoDeclarationFound"] = Rule("NoDeclarationFound", CursorKind.NO_DECL_FOUND)
|
||||
default_rules_db["NotImplemented"] = Rule("NotImplemented", CursorKind.NOT_IMPLEMENTED)
|
||||
default_rules_db["InvalidCode"] = Rule("InvalidCode", CursorKind.INVALID_CODE)
|
||||
default_rules_db["UnexposedExpression"] = Rule("UnexposedExpression", CursorKind.UNEXPOSED_EXPR)
|
||||
default_rules_db["DeclarationReferenceExpression"] = Rule(
|
||||
"DeclarationReferenceExpression", CursorKind.DECL_REF_EXPR)
|
||||
default_rules_db["MemberReferenceExpression"] = Rule(
|
||||
"MemberReferenceExpression", CursorKind.MEMBER_REF_EXPR)
|
||||
default_rules_db["CallExpression"] = Rule("CallExpression", CursorKind.CALL_EXPR)
|
||||
default_rules_db["BlockExpression"] = Rule("BlockExpression", CursorKind.BLOCK_EXPR)
|
||||
default_rules_db["IntegerLiteral"] = Rule("IntegerLiteral", CursorKind.INTEGER_LITERAL)
|
||||
default_rules_db["FloatingLiteral"] = Rule("FloatingLiteral", CursorKind.FLOATING_LITERAL)
|
||||
default_rules_db["ImaginaryLiteral"] = Rule("ImaginaryLiteral", CursorKind.IMAGINARY_LITERAL)
|
||||
default_rules_db["StringLiteral"] = Rule("StringLiteral", CursorKind.STRING_LITERAL)
|
||||
default_rules_db["CharacterLiteral"] = Rule("CharacterLiteral", CursorKind.CHARACTER_LITERAL)
|
||||
default_rules_db["ParenExpression"] = Rule("ParenExpression", CursorKind.PAREN_EXPR)
|
||||
default_rules_db["UnaryOperator"] = Rule("UnaryOperator", CursorKind.UNARY_OPERATOR)
|
||||
default_rules_db["ArraySubscriptExpression"] = Rule(
|
||||
"ArraySubscriptExpression", CursorKind.ARRAY_SUBSCRIPT_EXPR)
|
||||
default_rules_db["BinaryOperator"] = Rule("BinaryOperator", CursorKind.BINARY_OPERATOR)
|
||||
default_rules_db["CompoundAssignmentOperator"] = Rule(
|
||||
"CompoundAssignmentOperator", CursorKind.COMPOUND_ASSIGNMENT_OPERATOR)
|
||||
default_rules_db["ConditionalOperator"] = Rule(
|
||||
"ConditionalOperator", CursorKind.CONDITIONAL_OPERATOR)
|
||||
default_rules_db["CstyleCastExpression"] = Rule(
|
||||
"CstyleCastExpression", CursorKind.CSTYLE_CAST_EXPR)
|
||||
default_rules_db["CompoundLiteralExpression"] = Rule(
|
||||
"CompoundLiteralExpression", CursorKind.COMPOUND_LITERAL_EXPR)
|
||||
default_rules_db["InitListExpression"] = Rule("InitListExpression", CursorKind.INIT_LIST_EXPR)
|
||||
default_rules_db["AddrLabelExpression"] = Rule("AddrLabelExpression", CursorKind.ADDR_LABEL_EXPR)
|
||||
default_rules_db["StatementExpression"] = Rule("StatementExpression", CursorKind.StmtExpr)
|
||||
default_rules_db["GenericSelectionExpression"] = Rule(
|
||||
"GenericSelectionExpression", CursorKind.GENERIC_SELECTION_EXPR)
|
||||
default_rules_db["GnuNullExpression"] = Rule("GnuNullExpression", CursorKind.GNU_NULL_EXPR)
|
||||
default_rules_db["CxxStaticCastExpression"] = Rule(
|
||||
"CxxStaticCastExpression", CursorKind.CXX_STATIC_CAST_EXPR)
|
||||
default_rules_db["CxxDynamicCastExpression"] = Rule(
|
||||
"CxxDynamicCastExpression", CursorKind.CXX_DYNAMIC_CAST_EXPR)
|
||||
default_rules_db["CxxReinterpretCastExpression"] = Rule(
|
||||
"CxxReinterpretCastExpression", CursorKind.CXX_REINTERPRET_CAST_EXPR)
|
||||
default_rules_db["CxxConstCastExpression"] = Rule(
|
||||
"CxxConstCastExpression", CursorKind.CXX_CONST_CAST_EXPR)
|
||||
default_rules_db["CxxFunctionalCastExpression"] = Rule(
|
||||
"CxxFunctionalCastExpression", CursorKind.CXX_FUNCTIONAL_CAST_EXPR)
|
||||
default_rules_db["CxxTypeidExpression"] = Rule("CxxTypeidExpression", CursorKind.CXX_TYPEID_EXPR)
|
||||
default_rules_db["CxxBoolLiteralExpression"] = Rule(
|
||||
"CxxBoolLiteralExpression", CursorKind.CXX_BOOL_LITERAL_EXPR)
|
||||
default_rules_db["CxxNullPointerLiteralExpression"] = Rule(
|
||||
"CxxNullPointerLiteralExpression", CursorKind.CXX_NULL_PTR_LITERAL_EXPR)
|
||||
default_rules_db["CxxThisExpression"] = Rule("CxxThisExpression", CursorKind.CXX_THIS_EXPR)
|
||||
default_rules_db["CxxThrowExpression"] = Rule("CxxThrowExpression", CursorKind.CXX_THROW_EXPR)
|
||||
default_rules_db["CxxNewExpression"] = Rule("CxxNewExpression", CursorKind.CXX_NEW_EXPR)
|
||||
default_rules_db["CxxDeleteExpression"] = Rule("CxxDeleteExpression", CursorKind.CXX_DELETE_EXPR)
|
||||
default_rules_db["CxxUnaryExpression"] = Rule("CxxUnaryExpression", CursorKind.CXX_UNARY_EXPR)
|
||||
default_rules_db["PackExpansionExpression"] = Rule(
|
||||
"PackExpansionExpression", CursorKind.PACK_EXPANSION_EXPR)
|
||||
default_rules_db["SizeOfPackExpression"] = Rule(
|
||||
"SizeOfPackExpression", CursorKind.SIZE_OF_PACK_EXPR)
|
||||
default_rules_db["LambdaExpression"] = Rule("LambdaExpression", CursorKind.LAMBDA_EXPR)
|
||||
default_rules_db["ObjectBoolLiteralExpression"] = Rule(
|
||||
"ObjectBoolLiteralExpression", CursorKind.OBJ_BOOL_LITERAL_EXPR)
|
||||
default_rules_db["ObjectSelfExpression"] = Rule("ObjectSelfExpression", CursorKind.OBJ_SELF_EXPR)
|
||||
default_rules_db["UnexposedStatement"] = Rule("UnexposedStatement", CursorKind.UNEXPOSED_STMT)
|
||||
default_rules_db["LabelStatement"] = Rule("LabelStatement", CursorKind.LABEL_STMT)
|
||||
default_rules_db["CompoundStatement"] = Rule("CompoundStatement", CursorKind.COMPOUND_STMT)
|
||||
default_rules_db["CaseStatement"] = Rule("CaseStatement", CursorKind.CASE_STMT)
|
||||
default_rules_db["DefaultStatement"] = Rule("DefaultStatement", CursorKind.DEFAULT_STMT)
|
||||
default_rules_db["IfStatement"] = Rule("IfStatement", CursorKind.IF_STMT)
|
||||
default_rules_db["SwitchStatement"] = Rule("SwitchStatement", CursorKind.SWITCH_STMT)
|
||||
default_rules_db["WhileStatement"] = Rule("WhileStatement", CursorKind.WHILE_STMT)
|
||||
default_rules_db["DoStatement"] = Rule("DoStatement", CursorKind.DO_STMT)
|
||||
default_rules_db["ForStatement"] = Rule("ForStatement", CursorKind.FOR_STMT)
|
||||
default_rules_db["GotoStatement"] = Rule("GotoStatement", CursorKind.GOTO_STMT)
|
||||
default_rules_db["IndirectGotoStatement"] = Rule(
|
||||
"IndirectGotoStatement", CursorKind.INDIRECT_GOTO_STMT)
|
||||
default_rules_db["ContinueStatement"] = Rule("ContinueStatement", CursorKind.CONTINUE_STMT)
|
||||
default_rules_db["BreakStatement"] = Rule("BreakStatement", CursorKind.BREAK_STMT)
|
||||
default_rules_db["ReturnStatement"] = Rule("ReturnStatement", CursorKind.RETURN_STMT)
|
||||
default_rules_db["AsmStatement"] = Rule("AsmStatement", CursorKind.ASM_STMT)
|
||||
default_rules_db["CxxCatchStatement"] = Rule("CxxCatchStatement", CursorKind.CXX_CATCH_STMT)
|
||||
default_rules_db["CxxTryStatement"] = Rule("CxxTryStatement", CursorKind.CXX_TRY_STMT)
|
||||
default_rules_db["CxxForRangeStatement"] = Rule(
|
||||
"CxxForRangeStatement", CursorKind.CXX_FOR_RANGE_STMT)
|
||||
default_rules_db["MsAsmStatement"] = Rule("MsAsmStatement", CursorKind.MS_ASM_STMT)
|
||||
default_rules_db["NullStatement"] = Rule("NullStatement", CursorKind.NULL_STMT)
|
||||
default_rules_db["DeclarationStatement"] = Rule("DeclarationStatement", CursorKind.DECL_STMT)
|
||||
default_rules_db["TranslationUnit"] = Rule("TranslationUnit", CursorKind.TRANSLATION_UNIT)
|
||||
default_rules_db["UnexposedAttribute"] = Rule("UnexposedAttribute", CursorKind.UNEXPOSED_ATTR)
|
||||
default_rules_db["CxxFinalAttribute"] = Rule("CxxFinalAttribute", CursorKind.CXX_FINAL_ATTR)
|
||||
default_rules_db["CxxOverrideAttribute"] = Rule(
|
||||
"CxxOverrideAttribute", CursorKind.CXX_OVERRIDE_ATTR)
|
||||
default_rules_db["AnnotateAttribute"] = Rule("AnnotateAttribute", CursorKind.ANNOTATE_ATTR)
|
||||
default_rules_db["AsmLabelAttribute"] = Rule("AsmLabelAttribute", CursorKind.ASM_LABEL_ATTR)
|
||||
default_rules_db["PackedAttribute"] = Rule("PackedAttribute", CursorKind.PACKED_ATTR)
|
||||
default_rules_db["PureAttribute"] = Rule("PureAttribute", CursorKind.PURE_ATTR)
|
||||
default_rules_db["ConstAttribute"] = Rule("ConstAttribute", CursorKind.CONST_ATTR)
|
||||
default_rules_db["NoduplicateAttribute"] = Rule(
|
||||
"NoduplicateAttribute", CursorKind.NODUPLICATE_ATTR)
|
||||
default_rules_db["PreprocessingDirective"] = Rule(
|
||||
"PreprocessingDirective", CursorKind.PREPROCESSING_DIRECTIVE)
|
||||
default_rules_db["MacroDefinition"] = Rule("MacroDefinition", CursorKind.MACRO_DEFINITION)
|
||||
default_rules_db["MacroInstantiation"] = Rule("MacroInstantiation", CursorKind.MACRO_INSTANTIATION)
|
||||
default_rules_db["InclusionDirective"] = Rule("InclusionDirective", CursorKind.INCLUSION_DIRECTIVE)
|
||||
default_rules_db["TypeAliasTeplateDeclaration"] = Rule(
|
||||
"TypeAliasTeplateDeclaration", CursorKind.TYPE_ALIAS_TEMPLATE_DECL)
|
||||
|
||||
# Reverse lookup map. The parse identifies Clang cursor kinds, which must be mapped
|
||||
# to user defined types
|
||||
for key, value in default_rules_db.items():
|
||||
clang_to_user_map[value.clang_kind] = key
|
||||
default_rules_db["VariableName"] = Rule("VariableName", CursorKind.VAR_DECL)
|
||||
clang_to_user_map[CursorKind.FIELD_DECL] = "VariableName"
|
||||
clang_to_user_map[CursorKind.VAR_DECL] = "VariableName"
|
||||
|
||||
|
||||
class AstNodeStack(object):
|
||||
def __init__(self):
|
||||
self.stack = []
|
||||
|
||||
def pop(self):
|
||||
return self.stack.pop()
|
||||
|
||||
def push(self, kind):
|
||||
self.stack.append(kind)
|
||||
|
||||
def peek(self):
|
||||
if len(self.stack) > 0:
|
||||
return self.stack[-1]
|
||||
return None
|
||||
|
||||
|
||||
class Options:
|
||||
def __init__(self):
|
||||
self.args = None
|
||||
self._style_file = None
|
||||
self.file_exclusions = None
|
||||
self._skip_file = None
|
||||
|
||||
self.parser = argparse.ArgumentParser(
|
||||
prog="ncc.py",
|
||||
description="ncc is a development tool to help programmers "
|
||||
"write C/C++ code that adheres to adhere some naming conventions. It automates the "
|
||||
"process of checking C code to spare humans of this boring "
|
||||
"(but important) task. This makes it ideal for projects that want "
|
||||
"to enforce a coding standard.")
|
||||
|
||||
self.parser.add_argument('--recurse', action='store_true', dest="recurse",
|
||||
help="Read all files under each directory, recursively")
|
||||
|
||||
self.parser.add_argument('--style', dest="style_file",
|
||||
help="Read rules from the specified file. If the user does not"
|
||||
"provide a style file ncc will use all style rules. To print"
|
||||
"all style rules use --dump option")
|
||||
|
||||
self.parser.add_argument('--include', dest='include', nargs="+", help="User defined "
|
||||
"header file path, this is same as -I argument to the compiler")
|
||||
|
||||
self.parser.add_argument('--definition', dest='definition', nargs="+", help="User specified "
|
||||
"definitions, this is same as -D argument to the compiler")
|
||||
|
||||
self.parser.add_argument('--dump', dest='dump', action='store_true',
|
||||
help="Dump all available options")
|
||||
|
||||
self.parser.add_argument('--output', dest='output', help="output file name where"
|
||||
"naming convenction vialoations will be stored")
|
||||
|
||||
self.parser.add_argument('--filetype', dest='filetype', help="File extentions type"
|
||||
"that are applicable for naming convection validation")
|
||||
|
||||
self.parser.add_argument('--clang-lib', dest='clang_lib',
|
||||
help="Custom location of clang library")
|
||||
|
||||
self.parser.add_argument('--exclude', dest='exclude', nargs="+", help="Skip files "
|
||||
"matching the pattern specified from recursive searches. It "
|
||||
"matches a specified pattern according to the rules used by "
|
||||
"the Unix shell")
|
||||
|
||||
self.parser.add_argument('--skip', '-s', dest="skip_file",
|
||||
help="Read list of items to ignore during the check. "
|
||||
"User can use the skip file to specify character sequences that should "
|
||||
"be ignored by ncc")
|
||||
|
||||
# self.parser.add_argument('--exclude-dir', dest='exclude_dir', help="Skip the directories"
|
||||
# "matching the pattern specified")
|
||||
|
||||
self.parser.add_argument('--path', dest='path', nargs="+",
|
||||
help="Path of file or directory")
|
||||
|
||||
def parse_cmd_line(self):
|
||||
self.args = self.parser.parse_args()
|
||||
|
||||
if self.args.dump:
|
||||
self.dump_all_rules()
|
||||
|
||||
if self.args.style_file:
|
||||
self._style_file = self.args.style_file
|
||||
if not os.path.exists(self._style_file):
|
||||
sys.stderr.write("Style file '{}' not found!\n".format(self._style_file))
|
||||
sys.exit(1)
|
||||
|
||||
if self.args.skip_file:
|
||||
self._skip_file = self.args.skip_file
|
||||
if not os.path.exists(self._skip_file):
|
||||
sys.stderr.write("Skip file '{}' not found!\n".format(self._skip_file))
|
||||
|
||||
def dump_all_rules(self):
|
||||
print("----------------------------------------------------------")
|
||||
print("{:<35} | {}".format("Rule Name", "Pattern"))
|
||||
print("----------------------------------------------------------")
|
||||
for (key, value) in default_rules_db.items():
|
||||
print("{:<35} : {}".format(key, value.pattern_str))
|
||||
|
||||
class SkipDb(object):
|
||||
def __init__(self, skip_file=None):
|
||||
self.__skip_db = {}
|
||||
|
||||
if skip_file:
|
||||
self.build_skip_db(skip_file)
|
||||
|
||||
def build_skip_db(self, skip_file):
|
||||
with open(skip_file) as stylefile:
|
||||
style_rules = yaml.safe_load(stylefile)
|
||||
for (skip_string, skip_comment) in style_rules.items():
|
||||
self.__skip_db[skip_string] = skip_comment
|
||||
|
||||
def check_skip_db(self, input_query):
|
||||
if input_query in self.__skip_db.keys():
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
class RulesDb(object):
|
||||
def __init__(self, style_file=None):
|
||||
self.__rule_db = {}
|
||||
self.__clang_db = {}
|
||||
|
||||
if style_file:
|
||||
self.build_rules_db(style_file)
|
||||
else:
|
||||
self.__rule_db = default_rules_db
|
||||
self.__clang_db = clang_to_user_map
|
||||
|
||||
def build_rules_db(self, style_file):
|
||||
with open(style_file) as stylefile:
|
||||
style_rules = yaml.safe_load(stylefile)
|
||||
|
||||
for (rule_name, pattern_str) in style_rules.items():
|
||||
try:
|
||||
clang_kind = default_rules_db[rule_name].clang_kind
|
||||
if clang_kind:
|
||||
if rule_name == "VariableName":
|
||||
self.__rule_db[rule_name] = VariableNameRule(pattern_str)
|
||||
self.__clang_db[CursorKind.FIELD_DECL] = rule_name
|
||||
self.__clang_db[CursorKind.VAR_DECL] = rule_name
|
||||
else:
|
||||
self.__rule_db[rule_name] = default_rules_db[rule_name]
|
||||
self.__rule_db[rule_name].pattern_str = pattern_str
|
||||
self.__rule_db[rule_name].pattern = re.compile(pattern_str)
|
||||
self.__clang_db[clang_kind] = rule_name
|
||||
|
||||
except KeyError as e:
|
||||
sys.stderr.write('{} is not a valid C/C++ construct name\n'.format(e.message))
|
||||
fixit = difflib.get_close_matches(e.message, default_rules_db.keys(),
|
||||
n=1, cutoff=0.8)
|
||||
if fixit:
|
||||
sys.stderr.write('Did you mean rule name: {} ?\n'.format(fixit[0]))
|
||||
sys.exit(1)
|
||||
except re.error as e:
|
||||
sys.stderr.write('"{}" pattern {} has {} \n'.
|
||||
format(rule_name, pattern_str, e.message))
|
||||
sys.exit(1)
|
||||
|
||||
def is_rule_enabled(self, kind):
|
||||
if self.__clang_db.get(kind):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_rule_names(self, kind):
|
||||
"""
|
||||
Multiple user defined rules can be configured against one type of ClangKind
|
||||
For e.g. ClassMemberVariable, StructMemberVariable are types of FIELD_DECL
|
||||
"""
|
||||
return self.__clang_db.get(kind)
|
||||
|
||||
def get_rule(self, rule_name):
|
||||
return self.__rule_db.get(rule_name)
|
||||
|
||||
|
||||
class Validator(object):
|
||||
def __init__(self, rule_db, filename, options, skip_db=None):
|
||||
self.filename = filename
|
||||
self.rule_db = rule_db
|
||||
self.skip_db = skip_db
|
||||
self.options = options
|
||||
self.node_stack = AstNodeStack()
|
||||
|
||||
index = Index.create()
|
||||
args = []
|
||||
args.append('-x')
|
||||
args.append('c++')
|
||||
args.append('-D_GLIBCXX_USE_CXX11_ABI=0')
|
||||
if self.options.args.definition:
|
||||
for item in self.options.args.definition:
|
||||
defintion = r'-D' + item
|
||||
args.append(defintion)
|
||||
if self.options.args.include:
|
||||
for item in self.options.args.include:
|
||||
inc = r'-I' + item
|
||||
args.append(inc)
|
||||
self.cursor = index.parse(filename, args).cursor
|
||||
|
||||
def validate(self):
|
||||
return self.check(self.cursor)
|
||||
|
||||
def check(self, node):
|
||||
"""
|
||||
Recursively visit all nodes of the AST and match against the patter provided by
|
||||
the user. Return the total number of errors caught in the file
|
||||
"""
|
||||
errors = 0
|
||||
for child in node.get_children():
|
||||
if self.is_local(child, self.filename):
|
||||
|
||||
# This is the case when typedef of struct is causing double reporting of error
|
||||
# TODO: Find a better way to handle it
|
||||
parent = self.node_stack.peek()
|
||||
if (parent and parent == CursorKind.TYPEDEF_DECL and
|
||||
child.kind == CursorKind.STRUCT_DECL):
|
||||
return 0
|
||||
|
||||
errors += self.evaluate(child)
|
||||
|
||||
# Members struct, class, and unions must be treated differently.
|
||||
# So parent ast node information is pushed in to the stack.
|
||||
# Once all its children are validated pop it out of the stack
|
||||
self.node_stack.push(child.kind)
|
||||
errors += self.check(child)
|
||||
self.node_stack.pop()
|
||||
|
||||
return errors
|
||||
|
||||
def evaluate(self, node):
|
||||
"""
|
||||
get the node's rule and match the pattern. Report and error if pattern
|
||||
matching fails
|
||||
"""
|
||||
if not self.rule_db.is_rule_enabled(node.kind):
|
||||
return 0
|
||||
|
||||
# If the pattern is in the skip list, ignore it
|
||||
if self.skip_db.check_skip_db(node.displayname):
|
||||
return 0
|
||||
|
||||
rule_name = self.rule_db.get_rule_names(node.kind)
|
||||
rule = self.rule_db.get_rule(rule_name)
|
||||
if rule.evaluate(node, self.node_stack.peek()) is False:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def is_local(self, node, filename):
|
||||
""" Returns True is node belongs to the file being validated and not an include file """
|
||||
if node.location.file and node.location.file.name in filename:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def do_validate(options, filename):
|
||||
"""
|
||||
Returns true if the file should be validated
|
||||
- Check if its a c/c++ file
|
||||
- Check if the file is not excluded
|
||||
"""
|
||||
path, extension = os.path.splitext(filename)
|
||||
if extension not in file_extensions:
|
||||
return False
|
||||
|
||||
if options.args.exclude:
|
||||
for item in options.args.exclude:
|
||||
if fnmatch.fnmatch(filename, item):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s',
|
||||
filename='log.txt', filemode='w')
|
||||
|
||||
""" Parse all command line arguments and validate """
|
||||
op = Options()
|
||||
op.parse_cmd_line()
|
||||
|
||||
if op.args.path is None:
|
||||
sys.exit(1)
|
||||
|
||||
if op.args.clang_lib:
|
||||
Config.set_library_file(op.args.clang_lib)
|
||||
|
||||
""" Creating the rules database """
|
||||
rules_db = RulesDb(op._style_file)
|
||||
|
||||
""" Creating the skip database """
|
||||
skip_db = SkipDb(op._skip_file)
|
||||
|
||||
""" Check the source code against the configured rules """
|
||||
errors = 0
|
||||
for path in op.args.path:
|
||||
if os.path.isfile(path):
|
||||
if do_validate(op, path):
|
||||
v = Validator(rules_db, path, op, skip_db)
|
||||
errors += v.validate()
|
||||
elif os.path.isdir(path):
|
||||
for (root, subdirs, files) in os.walk(path):
|
||||
for filename in files:
|
||||
path = root + '/' + filename
|
||||
if do_validate(op, path):
|
||||
v = Validator(rules_db, path, op, skip_db)
|
||||
errors += v.validate()
|
||||
|
||||
if not op.args.recurse:
|
||||
break
|
||||
else:
|
||||
sys.stderr.write("File '{}' not found!\n".format(path))
|
||||
sys.exit(1)
|
||||
|
||||
if errors:
|
||||
print("Total number of errors = {}".format(errors))
|
||||
sys.exit(1)
|
21
tools/ncc/ncc.style
Normal file
21
tools/ncc/ncc.style
Normal file
|
@ -0,0 +1,21 @@
|
|||
ClassName: '^[A-Z][a-zA-Z0-9]+$'
|
||||
CppMethod: '^operator|^FUN_[a-f0-9]{8}$|^VTable0x[a-f0-9]{1,8}$|^(?!VTable)[A-Z][a-zA-Z0-9]+$'
|
||||
EnumName: '^\(unnamed|^[A-Z][a-zA-Z0-9]+$'
|
||||
EnumConstantName: '^(c_|e_)[a-z][a-zA-Z0-9]*$'
|
||||
FunctionName: '^operator|^FUN_[a-f0-9]{8}$|^VTable0x[a-f0-9]{1,8}$|^(?!VTable)[A-Z][a-zA-Z0-9]+$'
|
||||
ParameterName: '^p_(unk0x[a-f0-9]{1,8}$|(?!unk)[a-z][a-zA-Z0-9]*)$|^$'
|
||||
StructName: '^\(anon|^\(unnamed|^[A-Z][a-zA-Z0-9]+$'
|
||||
TypedefName: '^[A-Z][a-zA-Z0-9]+$'
|
||||
UnionName: '^\(anon|^[A-Z][a-zA-Z0-9]+$'
|
||||
VariableName:
|
||||
ScopePrefix:
|
||||
Global: 'g_'
|
||||
Static: 'g_'
|
||||
ClassMember: 'm_'
|
||||
StructMember: 'm_'
|
||||
DataTypePrefix:
|
||||
String: ''
|
||||
Integer: ''
|
||||
Bool: ''
|
||||
Pointer: ''
|
||||
Pattern: '^(unk0x[a-f0-9]{1,8}$|(?!unk)[a-z][a-zA-Z0-9]*|str[a-zA-Z0-9_]*)$'
|
32
tools/ncc/skip.yml
Normal file
32
tools/ncc/skip.yml
Normal file
|
@ -0,0 +1,32 @@
|
|||
configureLegoAnimationManager(MxS32): 'DLL exported function'
|
||||
configureLegoBuildingManager(MxS32): 'DLL exported function'
|
||||
configureLegoModelPresenter(MxS32): 'DLL exported function'
|
||||
configureLegoPartPresenter(MxS32, MxS32): 'DLL exported function'
|
||||
configureLegoROI(int): 'DLL exported function'
|
||||
configureLegoWorldPresenter(MxS32): 'DLL exported function'
|
||||
GetNoCD_SourceName(): 'DLL exported function'
|
||||
m_3dView: 'Allow this variable name'
|
||||
m_3dManager: 'Allow this variable name'
|
||||
m_16bitPal: 'Allow this variable name'
|
||||
m_HWDesc: 'Allow this variable name'
|
||||
m_HELDesc: 'Allow this variable name'
|
||||
p_HWDesc: 'Allow this variable name'
|
||||
p_HELDesc: 'Allow this variable name'
|
||||
e_RAMStream: 'Allow this enum constant'
|
||||
p_milliseconds: 'Probably a bug with function call'
|
||||
m_increaseAmount: "Can't currently detect member in union"
|
||||
m_increaseFactor: "Can't currently detect member in union"
|
||||
delta_rad: "Allow original naming from 1996"
|
||||
delta_pos: "Allow original naming from 1996"
|
||||
rot_mat: "Allow original naming from 1996"
|
||||
new_pos: "Allow original naming from 1996"
|
||||
new_dir: "Allow original naming from 1996"
|
||||
p_AnimTreePtr: "Allow original naming from beta"
|
||||
m_AnimTreePtr: "Allow original naming from beta"
|
||||
m_BADuration: "Allow original naming from beta"
|
||||
m_assAnimP: "Allow original naming from beta"
|
||||
m_disAnimP: "Allow original naming from beta"
|
||||
i_activity: "Allow original naming from beta"
|
||||
i_actor: "Allow original naming from beta"
|
||||
score: "Allow original naming from beta"
|
||||
c_LOCATIONS_NUM: "Allow original naming from beta"
|
2
tools/requirements.txt
Normal file
2
tools/requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
clang==16.*
|
||||
pyyaml
|
Loading…
Add table
Reference in a new issue