[libcamera-devel] [PATCH] clang-format: Support multiple versions
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Fri Aug 27 17:43:25 CEST 2021
Hi Kieran,
Thank you for the patch.
On Fri, Aug 27, 2021 at 03:47:53PM +0100, Kieran Bingham wrote:
> The .clang-format file can use CaseSensitive on Regex rules for Include
> Categories. With this we can distinguish between QT headers which
s/QT/Qt/
> 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
Ditto.
> .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..
A bit annoying, but I think we can live with that. Maybe it's a good way
to encourage usage of checkstyle.py :-)
> - We run the unlinking and symlinking on every invocation of checkstyle
Shouldn't be too costly :-) It's slightly annoying that we can't run
checkstyle.py on a read-only directory anymore though, but I don't think
it's a common use case.
> - The 'autogenerated' file in place of a git tracked one causes
> issues on rebase. Could be an issue for bisects...?
Possibly, but I don't think we'll often bisect and run checkstyle.py at
the same time.
>
> .clang-format-12 | 169 +++++++++++++++++++++++++++++++
> .clang-format => .clang-format-7 | 0
> utils/checkstyle.py | 49 ++++++++-
The symlink should be added to .gitignore.
> 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
> +...
I trust you on this.
> 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():
No need for parentheses if you don't inherit from another class, but you
can inherit from object.
Do we need ClangFormatLinker though ? Couldn't those functions be move
to ClangFormatter ? Although relinking should probably be done from
main(), see below.
> + def version():
> + version_regex = re.compile('[^0-9]*([0-9]+).*')
> + ret = subprocess.run(['clang-format', '--version'], stdout=subprocess.PIPE)
What happens if clang-format isn't available ? We check for it in
main(), but I think this code will run before that.
> + 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
Shouldn't this cause an error instead ?
> +
> + def supported():
> + version = ClangFormatLinker.version()
That's a bit of an abuse of the Python class system. Instance methods
are supposed to be called on an instance and take a self argument. These
three functions should be declared as class method I think. This would
become
@classmethod
def supported(cls):
version = cls.version()
> +
> + # Match the highest supported version
> + clang_files = glob.glob('.clang-format-[0-9]*')
When checkstyle.py is run manually, it may be run from a subdirectory of
the git tree. You should pass the git top-level directory to relink().
This would then call for calling relink() directly from main() instead
of ClangFormatter.
> + clang_files = sorted(clang_files, reverse=True,
> + key=lambda s: [int(re.sub("[^0-9]", "", s))])
We use single quotes for strings when possible.
> + 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')
Could we avoid relinking if the symlink points to the right file already
?
> +
> +
> +class ClangFormatter(Formatter):
CLangFormatter became ClangFormatter. Is it intentional ?
> patterns = ('*.c', '*.cpp', '*.h')
>
> + ClangFormatLinker.relink()
> +
> @classmethod
> def format(cls, filename, data):
> ret = subprocess.run(['clang-format', '-style=file',
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list