diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..00626b3e --- /dev/null +++ b/.clang-format @@ -0,0 +1,189 @@ +# The style used for all options not specifically set in the configuration. +BasedOnStyle: LLVM + +# The extra indent or outdent of access modifiers, e.g. public:. +AccessModifierOffset: -4 + +# Aligns escaped newlines as far left as possible +AlignEscapedNewlines: Left + +# This will align the assignment operators of consecutive lines. +AlignConsecutiveAssignments: Consecutive + +# Do not align the declaration names. +AlignConsecutiveDeclarations: None + +# Align the bitfield in declarations +AlignConsecutiveBitFields: Consecutive + +# If true, aligns trailing comments. +AlignTrailingComments: true + +# Align operands when the expression is broken in multiple lines. +AlignOperands: Align + +# Allow putting all parameters of a function declaration onto the next line even if BinPackParameters is false. +AllowAllParametersOfDeclarationOnNextLine: false + +# Allows contracting simple braced statements to a single line. +AllowShortBlocksOnASingleLine: false + +# If true, short case labels will be contracted to a single line. +AllowShortCaseLabelsOnASingleLine: false + +# Dependent on the value, int f() { return 0; } can be put on a single line. Possible values: None, Inline, All. +AllowShortFunctionsOnASingleLine: None + +# If true, if (a) return; can be put on a single line. +AllowShortIfStatementsOnASingleLine: false + +# If true, while (true) continue; can be put on a single line. +AllowShortLoopsOnASingleLine: false + +# If true, always break after function definition return types. +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None + +# If true, always break before multiline string literals. +AlwaysBreakBeforeMultilineStrings: false + +# If true, always break after the template<...> of a template declaration. +AlwaysBreakTemplateDeclarations: Yes + +# If false, a function call's arguments will either be all on the same line or will have one line each. +BinPackArguments: true + +# If false, a function declaration's or function definition's parameters will either all be on the same line +# or will have one line each. +BinPackParameters: true + +# The way to wrap binary operators. Possible values: None, NonAssignment, All. +BreakBeforeBinaryOperators: None + +# The brace breaking style to use. Possible values: Attach, Linux, Stroustrup, Allman, GNU. +BreakBeforeBraces: Allman + +# If true, ternary operators will be placed after line breaks. +BreakBeforeTernaryOperators: false + +# Always break constructor initializers before commas and align the commas with the colon. +BreakConstructorInitializers: BeforeComma + +# The column limit. A column limit of 0 means that there is no column limit. +ColumnLimit: 120 + +# A regular expression that describes comments with special meaning, which should not be split into lines or otherwise changed. +CommentPragmas: "^ *" + +# If the constructor initializers don't fit on a line, put each initializer on its own line. +PackConstructorInitializers: CurrentLine + +# The number of characters to use for indentation of constructor initializer lists. +ConstructorInitializerIndentWidth: 4 + +# Indent width for line continuations. +ContinuationIndentWidth: 4 + +# If true, format braced lists as best suited for C++11 braced lists. +Cpp11BracedListStyle: true + +# Disables formatting at all. +DisableFormat: false + +# Indent case labels one level from the switch statement. +# When false, use the same indentation level as for the switch statement. +# Switch statement body is always indented one level more than case labels. +IndentCaseLabels: false + +# The number of columns to use for indentation. +IndentWidth: 4 + +# Indent if a function definition or declaration is wrapped after the type. +IndentWrappedFunctionNames: false + +# If true, empty lines at the start of blocks are kept. +KeepEmptyLinesAtTheStartOfBlocks: true + +# Language, this format style is targeted at. Possible values: None, Cpp, Java, JavaScript, Proto. +Language: Cpp + +# The maximum number of consecutive empty lines to keep. +MaxEmptyLinesToKeep: 1 + +# The indentation used for namespaces. Possible values: None, Inner, All. +NamespaceIndentation: None + +# The penalty for breaking a function call after "call(". +PenaltyBreakBeforeFirstCallParameter: 19 + +# The penalty for each line break introduced inside a comment. +PenaltyBreakComment: 300 + +# The penalty for breaking before the first <<. +PenaltyBreakFirstLessLess: 400 + +# The penalty for each line break introduced inside a string literal. +PenaltyBreakString: 1000 + +# The penalty for each character outside of the column limit. +PenaltyExcessCharacter: 1000000 + +# Penalty for putting the return type of a function onto its own line. +PenaltyReturnTypeOnItsOwnLine: 1000000000 + +# Pointer and reference alignment style. Possible values: Left, Right, Middle. +PointerAlignment: Right + +# Do not sort includes, many generated headers don't appropriately include what they rely on. +SortIncludes: Never + +# If true, a space may be inserted after C style casts. +SpaceAfterCStyleCast: false + +# If false, spaces will be removed before assignment operators. +SpaceBeforeAssignmentOperators: true + +# Defines in which cases to put a space before opening parentheses. Possible values: Never, ControlStatements, Always. +SpaceBeforeParens: ControlStatements + +# If true, spaces may be inserted into '()'. +SpaceInEmptyParentheses: false + +# The number of spaces before trailing line comments (// - comments). +SpacesBeforeTrailingComments: 1 + +# If true, spaces will be inserted after '<' and before '>' in template argument lists. +SpacesInAngles: false + +# If true, spaces may be inserted into C style casts. +SpacesInCStyleCastParentheses: false + +# If true, spaces are inserted inside container literals (e.g. ObjC and Javascript array and dict literals). +SpacesInContainerLiterals: false + +# If true, spaces will be inserted after '(' and before ')'. +SpacesInParentheses: false + +# If true, spaces will be inserted after '[' and before ']'. +SpacesInSquareBrackets: false + +# Format compatible with this standard, e.g. use A > instead of A> for LS_Cpp03. Possible values: Cpp03, Cpp11, Auto. +Standard: Cpp11 + +# The number of columns used for tab stops. +TabWidth: 4 + +# The way to use tab characters in the resulting file. Possible values: Never, ForIndentation, Always. +UseTab: Never + +# Support formatting other languages too, mostly using default configs +--- +Language: Java +IndentWidth: 4 +--- +Language: Json +IndentWidth: 4 +--- +Language: ObjC +IndentWidth: 4 +BreakBeforeBraces: Allman diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 34c47419..6d06663a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,6 +14,69 @@ env: BUILD_JOBS: 16 jobs: + check-coding-style: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Install pre-commit + run: pip install pre-commit + + - name: Install clang-format + run: | + sudo apt update + sudo apt install --assume-yes clang-format + + - name: Run pre-commit + run: | + # Determine the base reference for comparison + if [ "${{ github.event_name }}" == "pull_request" ]; then + BASE_REF="origin/${{ github.event.pull_request.base.ref }}" + else + BASE_REF="origin/main" + fi + + echo "Running pre-commit from $BASE_REF to HEAD" + pre-commit run --from-ref $BASE_REF --to-ref HEAD + + check-commit-messages: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Install gitlint + run: pip install gitlint + + - name: Run gitlint + run: | + # Determine the base reference for comparison + if [ "${{ github.event_name }}" == "pull_request" ]; then + BASE_REF="origin/${{ github.event.pull_request.base.ref }}" + else + BASE_REF="origin/main" + fi + + echo "Running gitlint from $BASE_REF to HEAD" + gitlint --commits $BASE_REF..HEAD + Vulkan-Video-Samples-linux: strategy: matrix: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..ab14dbce --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,11 @@ +repos: + - repo: local + hooks: + - id: check-code-format + name: Check C/C++ code formatting + entry: python scripts/check_code_format.py + language: system + files: \.(c|cc|cxx|cpp|h|hpp)$ + exclude: ^include/vulkan/ + pass_filenames: false + always_run: false \ No newline at end of file diff --git a/scripts/check_code_format.py b/scripts/check_code_format.py new file mode 100644 index 00000000..c0185223 --- /dev/null +++ b/scripts/check_code_format.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2017 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Script to determine if source code in Pull Request is properly formatted. +# Exits with non 0 exit code if formatting is needed. +# +# This script assumes to be invoked at the project root directory. + +import subprocess +import sys +import os +import re + +# Color codes for terminal output +class Colors: + RED = '\033[0;31m' + GREEN = '\033[0;32m' + NC = '\033[0m' # No Color + +def run_command(cmd, capture_output=True): + """Run a shell command and return the result.""" + try: + result = subprocess.run(cmd, shell=True, capture_output=capture_output, + text=True, check=False) + return result.returncode, result.stdout, result.stderr + except Exception as e: + print(f"Error running command '{cmd}': {e}") + return 1, "", str(e) + +def get_files_to_check(): + """Get list of C/C++ files to check formatting for.""" + # Get files changed compared to master + returncode, stdout, stderr = run_command("git diff --name-only master") + if returncode != 0: + print(f"Error getting git diff: {stderr}") + return [] + + files = stdout.strip().split('\n') if stdout.strip() else [] + + # Filter for C/C++ files, excluding vulkan headers + cpp_extensions = re.compile(r'.*\.(cpp|cc|c\+\+|cxx|c|h|hpp)$') + vulkan_include = re.compile(r'^include/vulkan') + + filtered_files = [] + for file in files: + if file and cpp_extensions.match(file) and not vulkan_include.match(file): + filtered_files.append(file) + + return filtered_files + +def check_formatting(files): + """Check formatting of specified files using clang-format-diff.py.""" + if not files: + return True, "" + + files_str = ' '.join(files) + cmd = f"git diff -U0 master -- {files_str} | python ./scripts/clang-format-diff.py -p1 -style=file" + + returncode, stdout, stderr = run_command(cmd) + if returncode != 0: + print(f"Error running clang-format-diff: {stderr}") + return False, stderr + + return stdout.strip() == "", stdout + +def main(): + """Main function to check code formatting.""" + # Change to script directory's parent (project root) + script_dir = os.path.dirname(os.path.abspath(__file__)) + project_root = os.path.dirname(script_dir) + os.chdir(project_root) + + files_to_check = get_files_to_check() + + if not files_to_check: + print(f"{Colors.GREEN}No source code to check for formatting.{Colors.NC}") + return 0 + + print(f"Checking formatting for files: {', '.join(files_to_check)}") + + is_formatted, format_output = check_formatting(files_to_check) + + if is_formatted: + print(f"{Colors.GREEN}All source code in PR properly formatted.{Colors.NC}") + return 0 + else: + print(f"{Colors.RED}Found formatting errors!{Colors.NC}") + print(format_output) + return 1 + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/scripts/check_code_format.sh b/scripts/check_code_format.sh deleted file mode 100644 index dde43794..00000000 --- a/scripts/check_code_format.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -# Copyright (c) 2017 Google Inc. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Script to determine if source code in Pull Request is properly formatted. -# Exits with non 0 exit code if formatting is needed. -# -# This script assumes to be invoked at the project root directory. - -RED='\033[0;31m' -GREEN='\033[0;32m' -NC='\033[0m' # No Color - -FILES_TO_CHECK=$(git diff --name-only master | grep -v -E "^include/vulkan" | grep -E ".*\.(cpp|cc|c\+\+|cxx|c|h|hpp)$") - -if [ -z "${FILES_TO_CHECK}" ]; then - echo -e "${GREEN}No source code to check for formatting.${NC}" - exit 0 -fi - -FORMAT_DIFF=$(git diff -U0 master -- ${FILES_TO_CHECK} | python ./scripts/clang-format-diff.py -p1 -style=file) - -if [ -z "${FORMAT_DIFF}" ]; then - echo -e "${GREEN}All source code in PR properly formatted.${NC}" - exit 0 -else - echo -e "${RED}Found formatting errors!${NC}" - echo "${FORMAT_DIFF}" - exit 1 -fi diff --git a/scripts/clang-format-diff.py b/scripts/clang-format-diff.py new file mode 100755 index 00000000..3e08c9c9 --- /dev/null +++ b/scripts/clang-format-diff.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +#===- clang-format-diff.py - ClangFormat Diff Utility -------*- python -*--===# +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===------------------------------------------------------------------------===# + +""" +ClangFormat Diff Utility +v1.0 + +This script reads a unified diff from stdin and reformats all the changed +lines. This is useful to reformat all the lines touched by a specific patch. +Example usage for git users: + + git diff -U0 --no-color HEAD^ | clang-format-diff.py -p1 -i + git diff -U0 --no-color -r HEAD~1 HEAD | clang-format-diff.py -p1 -i + +It should be noted that the filename contained in the diff is used unmodified +to determine the source file to update. Users calling this script directly +should be careful to ensure that the path in the diff is correct relative to the +current working directory. +""" + +import argparse +import difflib +import re +import subprocess +import sys +from io import StringIO + + +def main(): + parser = argparse.ArgumentParser(description= + 'Reformat changed lines in diff. Without -i ' + 'option just output the diff that would be ' + 'introduced.') + parser.add_argument('-i', action='store_true', default=False, + help='apply edits to files instead of displaying a diff') + parser.add_argument('-p', metavar='NUM', default=0, + help='strip the smallest prefix containing P slashes') + parser.add_argument('-regex', metavar='PATTERN', default=None, + help='custom pattern selecting file paths to reformat ' + '(case sensitive, overrides -iregex)') + parser.add_argument('-iregex', metavar='PATTERN', default= + r'.*\.(cpp|cc|c\+\+|cxx|c|h|hpp|m|mm|js|ts)$', + help='custom pattern selecting file paths to reformat ' + '(case insensitive, overridden by -regex)') + parser.add_argument('-sort-includes', action='store_true', default=False, + help='let clang-format sort include statements') + parser.add_argument('-v', '--verbose', action='store_true', + help='be more verbose, ineffective without -i') + parser.add_argument('-style', + help='formatting style to apply (LLVM, Google, Chromium, ' + 'Mozilla, WebKit, file or JSON config)') + parser.add_argument('-binary', default='clang-format', + help='location of binary to use for clang-format') + args = parser.parse_args() + + try: + p = int(args.p) + except ValueError: + print('error: invalid -p value') + return 1 + + if args.regex is not None: + file_re = re.compile(args.regex) + else: + file_re = re.compile(args.iregex, re.IGNORECASE) + + changed_files = [] + lines_by_file = {} + for line in sys.stdin: + match = re.search(r'^\+\+\+\ (.*?/){%s}(\S*)' % p, line) + if match: + filename = match.group(2) + if file_re.search(filename): + changed_files.append(filename) + + match = re.search(r'^@@.*\+(\d+)(,(\d+))?', line) + if match: + start_line = int(match.group(1)) + line_count = 1 + if match.group(3): + line_count = int(match.group(3)) + if line_count == 0: + continue + end_line = start_line + line_count - 1 + lines_by_file.setdefault(filename, []).extend( + ['-lines', str(start_line) + ':' + str(end_line)]) + + if not changed_files: + return 0 + + # Reformat files containing changes in place. + for filename in changed_files: + if args.i and args.verbose: + print('Formatting', filename) + command = [args.binary, filename] + if args.i: + command.append('-i') + if args.style: + command.extend(['-style', args.style]) + if args.sort_includes: + command.append('-sort-includes') + if filename in lines_by_file: + command.extend(lines_by_file[filename]) + if not args.i: + command.append('--output-replacements-xml') + + try: + p = subprocess.Popen(command, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE) + stdout, stderr = p.communicate() + if p.returncode != 0: + print("clang-format failed with return code", p.returncode) + print(stderr.decode()) + return p.returncode + + if not args.i: + print(stdout.decode('utf-8'), end='') + + except KeyboardInterrupt: + # Ctrl-C, print a newline so the next shell prompt is on a new line. + print('') + return 1 + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) \ No newline at end of file