[libcamera-devel] [PATCH] clang-format: Support multiple versions

Kieran Bingham kieran.bingham at ideasonboard.com
Fri Aug 27 16:47:53 CEST 2021


The .clang-format file can use CaseSensitive on Regex rules for Include
Categories. With this we can distinguish between QT headers which
commence with a 'Q' verses system headers including <queue>.

This requires clang-format-12 to support that rule, and clang-format is
not very amicable to .clang-format files with unsupported entries, and
will fail to run on previous versions if we add that option directly.

This causes the checkstyle utility to become unusable for users of
clang-format versions prior to 12.

Unfortunately, clang-format does not provide a way to specify a specific
file to use for the rules to apply when formatting, so we can not easily
call clang-format with a file specific to its versioned needs.

Implement a means of identifying the version of clang-format, and use
that information to search for the most recent supportable
.clang-format-$(version) file, which can then be symlinked to the fixed
file location used by clang format (.clang-format).

 .clang-format-12: is added with CaseSensitive:true for QT headers
 .clang-format-7: is moved from the current .clang-format implementation

Signed-off-by: Kieran Bingham <kieran.bingham at ideasonboard.com>

---
Note there are few things I dislike about this, hence RFC:

 - We leave our tree without a .clang-format until checkstyle runs..
 - We run the unlinking and symlinking on every invocation of checkstyle
 - The 'autogenerated' file in place of a git tracked one causes
   issues on rebase. Could be an issue for bisects...?

 .clang-format-12                 | 169 +++++++++++++++++++++++++++++++
 .clang-format => .clang-format-7 |   0
 utils/checkstyle.py              |  49 ++++++++-
 3 files changed, 217 insertions(+), 1 deletion(-)
 create mode 100644 .clang-format-12
 rename .clang-format => .clang-format-7 (100%)

diff --git a/.clang-format-12 b/.clang-format-12
new file mode 100644
index 000000000000..98e9fad472fc
--- /dev/null
+++ b/.clang-format-12
@@ -0,0 +1,169 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# clang-format configuration file. Intended for clang-format >= 12.
+#
+# For more information, see:
+#
+#   Documentation/process/clang-format.rst
+#   https://clang.llvm.org/docs/ClangFormat.html
+#   https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+#
+---
+Language: Cpp
+AccessModifierOffset: -8
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: Right
+AlignOperands: true
+AlignTrailingComments: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: InlineOnly
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: MultiLine
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+  AfterClass: true
+  AfterControlStatement: false
+  AfterEnum: false
+  AfterFunction: true
+  AfterNamespace: false
+  AfterObjCDeclaration: false
+  AfterStruct: false
+  AfterUnion: false
+  AfterExternBlock: false
+  BeforeCatch: false
+  BeforeElse: false
+  IndentBraces: false
+  SplitEmptyFunction: true
+  SplitEmptyRecord: true
+  SplitEmptyNamespace: true
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeInheritanceComma: false
+BreakInheritanceList: BeforeColon
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeColon
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: false
+ColumnLimit: 0
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 8
+ContinuationIndentWidth: 8
+Cpp11BracedListStyle: false
+DerivePointerAlignment: false
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:
+  - 'udev_list_entry_foreach'
+SortIncludes: true
+IncludeBlocks: Regroup
+IncludeCategories:
+  # Headers matching the name of the component are matched automatically.
+  # Priority 1
+  # Other library headers (explicit overrides to match before system headers)
+  - Regex:           '(<jpeglib.h>|<libudev.h>|<tiffio.h>|<xf86drm.h>|<xf86drmMode.h>|<yaml.h>)'
+    Priority:        9
+  # Qt includes (match before C++ standard library)
+  - Regex:           '<Q([A-Za-z0-9\-_])+>'
+    Priority:        9
+    CaseSensitive:   true
+  # Headers in <> with an extension. (+system libraries)
+  - Regex:           '<([A-Za-z0-9\-_])+\.h>'
+    Priority:        2
+  # System headers
+  - Regex:           '<sys/.*>'
+    Priority:        2
+  # C++ standard library includes (no extension)
+  - Regex:           '<([A-Za-z0-9\-_/])+>'
+    Priority:        2
+  # Linux headers, as a second group/subset of system headers
+  - Regex:           '<linux/.*>'
+    Priority:        3
+  # Headers for libcamera Base support
+  - Regex:           '<libcamera/base/private.h>'
+    Priority:        4
+  - Regex:           '<libcamera/base/.*\.h>'
+    Priority:        5
+  # Public API Headers for libcamera, which are not in a subdir (i.e. ipa/,internal/)
+  - Regex:           '<libcamera/([A-Za-z0-9\-_])+.h>'
+    Priority:        6
+  # IPA Interfaces
+  - Regex:           '<libcamera/ipa/.*\.h>'
+    Priority:        7
+  # libcamera Internal headers in ""
+  - Regex:           '"libcamera/internal/.*\.h"'
+    Priority:        8
+  # Other libraries headers with one group per library (.h or .hpp)
+  - Regex:           '<.*/.*\.hp*>'
+    Priority:        9
+  # local modular includes "path/file.h" (.h or .hpp)
+  - Regex:           '"(.*/)+.*\.hp*"'
+    Priority:        10
+  # Other local headers "file.h" with extension (.h or .hpp)
+  - Regex:           '".*.hp*"'
+    Priority:        11
+  # Any unmatched line, separated from the last group
+  - Regex:	     '"*"'
+    Priority:        100
+
+IncludeIsMainRegex: '(_test)?$'
+IndentCaseLabels: false
+IndentPPDirectives: None
+IndentWidth: 8
+IndentWrappedFunctionNames: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: false
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 8
+ObjCSpaceAfterProperty: true
+ObjCSpaceBeforeProtocolList: true
+
+# Taken from git's rules
+PenaltyBreakAssignment: 10
+PenaltyBreakBeforeFirstCallParameter: 30
+PenaltyBreakComment: 10
+PenaltyBreakFirstLessLess: 0
+PenaltyBreakString: 10
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 100
+PenaltyReturnTypeOnItsOwnLine: 60
+
+PointerAlignment: Right
+ReflowComments: false
+SortIncludes: true
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInContainerLiterals: false
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp11
+TabWidth: 8
+UseTab: Always
+...
diff --git a/.clang-format b/.clang-format-7
similarity index 100%
rename from .clang-format
rename to .clang-format-7
diff --git a/utils/checkstyle.py b/utils/checkstyle.py
index ececb46eaacc..279ace8346b4 100755
--- a/utils/checkstyle.py
+++ b/utils/checkstyle.py
@@ -16,6 +16,7 @@
 import argparse
 import difflib
 import fnmatch
+import glob
 import os.path
 import re
 import shutil
@@ -566,9 +567,55 @@ class Formatter(metaclass=ClassRegistry):
         return patterns
 
 
-class CLangFormatter(Formatter):
+# Identify the version of clang-format running, and match it against locally
+# available versioned configuration files of the form:
+# .clang-format-$(version)
+#
+# The highest supported local configuration file is linked as .clang-format by
+# ClangFormatLinker.relink() for use by the ClangFormatter.
+#
+class ClangFormatLinker():
+    def version():
+        version_regex = re.compile('[^0-9]*([0-9]+).*')
+        ret = subprocess.run(['clang-format', '--version'], stdout=subprocess.PIPE)
+        version = ret.stdout.decode('utf-8')
+        search = version_regex.search(version)
+        if search:
+            version = search.group(1)
+            return int(version)
+
+        # Lowest supported version
+        return 7
+
+    def supported():
+        version = ClangFormatLinker.version()
+
+        # Match the highest supported version
+        clang_files = glob.glob('.clang-format-[0-9]*')
+        clang_files = sorted(clang_files, reverse=True,
+                             key=lambda s: [int(re.sub("[^0-9]", "", s))])
+        for f in clang_files:
+            file_version = int(re.sub("[^0-9]", "", f))
+            if file_version <= version:
+                return f
+
+        return None
+
+    def relink():
+        supported = ClangFormatLinker.supported()
+        if not supported:
+            return
+
+        if os.path.exists('.clang-format'):
+            os.unlink('.clang-format')
+        os.symlink(supported, '.clang-format')
+
+
+class ClangFormatter(Formatter):
     patterns = ('*.c', '*.cpp', '*.h')
 
+    ClangFormatLinker.relink()
+
     @classmethod
     def format(cls, filename, data):
         ret = subprocess.run(['clang-format', '-style=file',
-- 
2.30.2



More information about the libcamera-devel mailing list