diff --git a/.clang-format b/.clang-format index f0ecd63b..0d945dd6 100644 --- a/.clang-format +++ b/.clang-format @@ -1,5 +1,6 @@ +# using [clang-formt-12 options](https://releases.llvm.org/12.0.0/tools/clang/docs/ClangFormatStyleOptions.html) RawStringFormats: - - Language: Cpp + - Language: Cpp Delimiters: - c - C @@ -13,35 +14,23 @@ RawStringFormats: - h - hpp CanonicalDelimiter: '' - BasedOnStyle: google - - Language: TextProto - Delimiters: - - pb - - PB - - proto - - PROTO - EnclosingFunctions: - - EqualsProto - - EquivToProto - - PARSE_PARTIAL_TEXT_PROTO - - PARSE_TEST_PROTO - - PARSE_TEXT_PROTO - - ParseTextOrDie - - ParseTextProtoOrDie - CanonicalDelimiter: '' - BasedOnStyle: google + BasedOnStyle: Mozilla Language: Cpp BasedOnStyle: Mozilla +# 6.1 IndentWidth: 4 +ContinuationIndentWidth: 4 +# 6.2 +TabWidth: 4 +UseTab: Never +# 6.3 +ColumnLimit: 80 +# 6.9 AlignAfterOpenBracket: Align -AllowAllArgumentsOnNextLine: false -AlignConsecutiveMacros: true -AllowShortBlocksOnASingleLine: true -AlwaysBreakAfterReturnType: All BinPackArguments: true -BinPackParameters: false -BreakBeforeBinaryOperators: NonAssignment +BinPackParameters: true +# 6.10 BreakBeforeBraces: Custom BraceWrapping: AfterCaseLabel: true @@ -60,104 +49,13 @@ BraceWrapping: SplitEmptyFunction: true SplitEmptyRecord: false SplitEmptyNamespace: true -ColumnLimit: 79 -ContinuationIndentWidth: 2 -DerivePointerAlignment: false -IncludeBlocks: Regroup -IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - - Regex: '^(<|"(gtest|gmock|isl|json)/)' - Priority: 1 - - Regex: ".*" - Priority: 3 -IndentPPDirectives: None -KeepEmptyLinesAtTheStartOfBlocks: false -NamespaceIndentation: None +# 6.27 +BreakBeforeBinaryOperators: NonAssignment + +# additional +AlignEscapedNewlines: Left +AllowAllParametersOfDeclarationOnNextLine: false +AllowAllArgumentsOnNextLine: false PointerAlignment: Right -ReflowComments: false -SortIncludes: false -Standard: Auto -StatementMacros: - - Q_UNUSED - - QT_REQUIRE_VERSION - -# AccessModifierOffset: -2 -# AlignConsecutiveAssignments: false -# AlignConsecutiveDeclarations: false -# AlignEscapedNewlines: Right -# AlignOperands: true -# AlignTrailingComments: true -# AllowAllConstructorInitializersOnNextLine: true -# AllowAllParametersOfDeclarationOnNextLine: false -# AllowShortCaseLabelsOnASingleLine: false -# AllowShortFunctionsOnASingleLine: Inline -# AllowShortLambdasOnASingleLine: All -# AllowShortIfStatementsOnASingleLine: Never -# AllowShortLoopsOnASingleLine: false -# AlwaysBreakAfterDefinitionReturnType: TopLevel -# AlwaysBreakAfterReturnType: TopLevel -# AlwaysBreakBeforeMultilineStrings: false -# AlwaysBreakTemplateDeclarations: Yes -# BreakBeforeInheritanceComma: false -# BreakInheritanceList: BeforeComma -# BreakBeforeTernaryOperators: true -# BreakConstructorInitializersBeforeComma: false -# BreakConstructorInitializers: BeforeComma -# BreakAfterJavaFieldAnnotations: false -# BreakStringLiterals: true -# CommentPragmas: '^ IWYU pragma:' -# CompactNamespaces: false -# ConstructorInitializerAllOnOneLineOrOnePerLine: false -# ConstructorInitializerIndentWidth: 2 -# Cpp11BracedListStyle: false -# DisableFormat: false -# ExperimentalAutoDetectBinPacking: false -# FixNamespaceComments: false -# ForEachMacros: -# - foreach -# - Q_FOREACH -# - BOOST_FOREACH -# IncludeIsMainRegex: '(Test)?$' -# IndentCaseLabels: true -# IndentWrappedFunctionNames: false -# JavaScriptQuotes: Leave -# JavaScriptWrapImports: true -# KeepEmptyLinesAtTheStartOfBlocks: true -# MacroBlockBegin: '' -# MacroBlockEnd: '' -# MaxEmptyLinesToKeep: 1 -# ObjCBinPackProtocolList: Auto -# ObjCBlockIndentWidth: 2 -# ObjCSpaceAfterProperty: true -# ObjCSpaceBeforeProtocolList: false -# PenaltyBreakAssignment: 2 -# PenaltyBreakBeforeFirstCallParameter: 19 -# PenaltyBreakComment: 300 -# PenaltyBreakFirstLessLess: 120 -# PenaltyBreakString: 1000 -# PenaltyBreakTemplateDeclaration: 10 -# PenaltyExcessCharacter: 1000000 -# PenaltyReturnTypeOnItsOwnLine: 200 -# SortIncludes: true -# SortUsingDeclarations: true -# SpaceAfterCStyleCast: false -# SpaceAfterLogicalNot: false -# SpaceAfterTemplateKeyword: false -# SpaceBeforeAssignmentOperators: true -# SpaceBeforeCpp11BracedList: false -# SpaceBeforeCtorInitializerColon: true -# SpaceBeforeInheritanceColon: true -# SpaceBeforeParens: ControlStatements -# SpaceBeforeRangeBasedForLoopColon: true -# SpaceInEmptyParentheses: false -# SpacesBeforeTrailingComments: 1 -# SpacesInAngles: false -# SpacesInContainerLiterals: true -# SpacesInCStyleCastParentheses: false -# SpacesInParentheses: false -# SpacesInSquareBrackets: false -# TabWidth: 4 -# UseTab: Never -# ... - +SpaceAroundPointerQualifiers: After +SortIncludes: false diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..67cd167f --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,16 @@ +# refer to https://clang.llvm.org/extra/clang-tidy/checks/list.html + +Checks: '-*, readability-identifier-naming, clang-analyzer-core.*,' +WarningsAsErrors: '-*' +HeaderFilterRegex: '' +FormatStyle: file +InheritParentConfig: false +AnalyzeTemporaryDtors: false +User: wamr +CheckOptions: + - key: readability-identifier-naming.VariableCase + value: lower_case + - key: readability-identifier-naming.ParameterCase + value: lower_case + - key: readability-identifier-naming.MacroDefinitionCase + value: UPPER_CASE diff --git a/ci/run_pre_commit_check.py b/ci/run_pre_commit_check.py new file mode 100644 index 00000000..41f907bd --- /dev/null +++ b/ci/run_pre_commit_check.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import json +import os +import pathlib +import queue +import re +import shlex +import shutil +import subprocess +import sys + +CLANG_CMD = "clang-13" +CLANG_CPP_CMD = "clang-cpp-13" +CLANG_FORMAT_CMD = "clang-format-13" +CLANG_TIDY_CMD = "clang-tidy-13" +CMAKE_CMD = "cmake" + + +# glob style patterns +EXCLUDE_PATHS = [ + "**/.git/", + "**/.github/", + "**/.vscode/", + "**/assembly-script/", + "**/build/", + "**/build-scripts/", + "**/ci/", + "**/core/deps/", + "**/doc/", + "**/samples/workload/", + "**/test-tools/", + "**/wamr-sdk/", + "**/wamr-dev/", + "**/wamr-dev-simd/", +] + +C_SUFFIXES = [".c", ".h"] + +VALID_DIR_NAME = r"([a-zA-Z0-9]+\-*)+[a-zA-Z0-9]*" +VALID_FILE_NAME = r"\.?([a-zA-Z0-9]+\_*)+[a-zA-Z0-9]*\.*\w*" + + +def locate_command(command): + if not shutil.which(command): + print(f"Command '{command}'' not found") + return False + + return True + + +def is_excluded(path): + for exclude_path in EXCLUDE_PATHS: + if path.match(exclude_path): + return True + return False + + +def pre_flight_check(root): + def check_clang_foramt(root): + if not locate_command(CLANG_FORMAT_CMD): + return False + + # Quick syntax check for .clang-format + try: + subprocess.check_call( + shlex.split(f"{CLANG_FORMAT_CMD} --dump-config"), cwd=root + ) + except subprocess.CalledProcessError: + print(f"Might have a typo in .clang-format") + return False + return True + + def check_clang_tidy(root): + if not locate_command(CLANG_TIDY_CMD): + return False + + if ( + not locate_command(CLANG_CMD) + or not locate_command(CLANG_CPP_CMD) + or not locate_command(CMAKE_CMD) + ): + return False + + # Quick syntax check for .clang-format + try: + subprocess.check_call( + shlex.split("{CLANG_TIDY_CMD} --dump-config"), cwd=root + ) + except subprocess.CalledProcessError: + print(f"Might have a typo in .clang-tidy") + return False + + # looking for compile command database + return True + + def check_aspell(root): + return True + + return check_clang_foramt(root) and check_clang_tidy(root) and check_aspell(root) + + +def run_clang_format(file_path, root): + try: + subprocess.check_call( + shlex.split( + f"{CLANG_FORMAT_CMD} --style=file --Werror --dry-run {file_path}" + ), + cwd=root, + ) + return True + except subprocess.CalledProcessError: + print(f"{file_path} failed the check of {CLANG_FORMAT_CMD}") + return False + + +def generate_compile_commands(compile_command_database, root): + CMD = f"{CMAKE_CMD} -DCMAKE_C_COMPILER={shutil.which(CLANG_CMD)} -DCMAKE_CXX_COMPILER={shutil.which(CLANG_CPP_CMD)} -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .." + + try: + linux_mini_build = root.joinpath("product-mini/platforms/linux/build").resolve() + linux_mini_build.mkdir(exist_ok=True) + if subprocess.check_call(shlex.split(CMD), cwd=linux_mini_build): + return False + + wamrc_build = root.joinpath("wamr-compiler/build").resolve() + wamrc_build.mkdir(exist_ok=True) + + if subprocess.check_call(shlex.split(CMD), cwd=wamrc_build): + return False + + with open(linux_mini_build.joinpath("compile_commands.json"), "r") as f: + iwasm_compile_commands = json.load(f) + + with open(wamrc_build.joinpath("compile_commands.json"), "r") as f: + wamrc_compile_commands = json.load(f) + + all_compile_commands = iwasm_compile_commands + wamrc_compile_commands + # TODO: duplication items ? + with open(compile_command_database, "w") as f: + json.dump(all_compile_commands, f) + + return True + except subprocess.CalledProcessError: + return False + + +def run_clang_tidy(file_path, root): + # preparatoin + compile_command_database = pathlib.Path("/tmp/compile_commands.json") + if not compile_command_database.exists() and not generate_compile_commands( + compile_command_database, root + ): + return False + + try: + if subprocess.check_call( + shlex.split(f"{CLANG_TIDY_CMD} -p={compile_command_database} {file_path}"), + cwd=root, + ): + print(f"{file_path} failed the check of {CLANG_TIDY_CMD}") + except subprocess.CalledProcessError: + print(f"{file_path} failed the check of {CLANG_TIDY_CMD}") + return False + return True + + +def run_aspell(file_path, root): + return True + + +def check_dir_name(path, root): + # since we don't want to check the path beyond root. + # we hope "-" only will be used in a dir name as separators + return all( + [ + re.match(VALID_DIR_NAME, path_part) + for path_part in path.relative_to(root).parts + ] + ) + + +def check_file_name(path): + # since we don't want to check the path beyond root. + # we hope "_" only will be used in a file name as separators + return re.match(VALID_FILE_NAME, path.name) is not None + + +def run_pre_commit_check(path, root=None): + path = path.resolve() + if path.is_dir(): + if not check_dir_name(path, root): + print(f"{path} is not a valid directory name") + return False + else: + return True + + if path.is_file(): + if not check_file_name(path): + print(f"{path} is not a valid file name") + return False + + if not path.suffix in C_SUFFIXES: + return True + + return ( + run_clang_format(path, root) + and run_clang_tidy(path, root) + and run_aspell(path, root) + ) + + print(f"{path} neither a file nor a directory") + return False + + +def main(): + wamr_root = pathlib.Path(__file__).parent.joinpath("..").resolve() + + if not pre_flight_check(wamr_root): + return False + + invalid_file, invalid_directory = 0, 0 + + # in order to skip exclude directories ASAP, + # will not yield Path. + # since we will create every object + dirs = queue.Queue() + dirs.put(wamr_root) + while not dirs.empty(): + qsize = dirs.qsize() + while qsize: + current_dir = dirs.get() + + for path in current_dir.iterdir(): + path = path.resolve() + + if path.is_symlink(): + continue + + if path.is_dir() and not is_excluded(path): + invalid_directory += ( + 0 if run_pre_commit_check(path, wamr_root) else 1 + ) + dirs.put(path) + + if not path.is_file(): + continue + + invalid_file += 0 if run_pre_commit_check(path) else 1 + + else: + qsize -= 1 + + print(f"invalid_directory={invalid_directory}, invalid_file={invalid_file}") + return True + + +if __name__ == "__main__": + main()