Customize clang-format coding styles based on Mozilla template (#770)

Customize clang-format coding styles for C source files based on Mozilla template.
To check whether the C source codes are well formatted:
``` bash
$ cd ${wamr-root}
$ clang-format --Werror --dry-run --style=file path/to/file
```
To format the C source codes in place
``` bash
$ cd ${wamr_root}
$ clang-format -i --style=file path/to/file
```

Signed-off-by: Wenyong Huang <wenyong.huang@intel.com>
This commit is contained in:
Wenyong Huang 2021-10-06 09:54:36 +08:00 committed by GitHub
parent 9ef37dd781
commit 7191ecf880
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 301 additions and 124 deletions

View File

@ -1,3 +1,4 @@
# using [clang-formt-12 options](https://releases.llvm.org/12.0.0/tools/clang/docs/ClangFormatStyleOptions.html)
RawStringFormats:
- Language: Cpp
Delimiters:
@ -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
SpaceAroundPointerQualifiers: After
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
# ...

16
.clang-tidy Normal file
View File

@ -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

263
ci/run_pre_commit_check.py Normal file
View File

@ -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()