This commit is contained in:
xsl
2025-09-04 10:54:47 +08:00
commit 6bc8f61b18
1808 changed files with 208268 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
////
- Copyright (c) 2023, Thomas Atkinson
-
- SPDX-License-Identifier: Apache-2.0
-
- 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.
-
////
= Utility Scripts
// omit in toc
A series of helpers to make life easier.
== Generate
Helps to generate new files for the project.
=== Generate Sample
All flags are optional.
Setting --name is advised.
If --output-dir is not set, the sample will be generated in the samples directory filed under the specified category.
[,bash]
----
./scripts/generate.py sample --name <SampleName> --category <category> --output-dir <output_dir>
----
Running the above line will generate the following files:
[,bash]
----
samples/category/my_sample/CMakeLists.txt
samples/category/my_sample/sample_name.cpp
samples/category/my_sample/sample_name.h
----
A new class will also be generated
[,cpp]
----
class SampleName : public VulkanSample {
...
};
----
=== Generate API Sample
API samples can be generated using the following command:
[,bash]
----
./scripts/generate.py sample_api --name <SampleName> --category <category> --output-dir <output_dir>
----
Running the above line will generate the following files:
[,bash]
----
samples/category/my_sample/CMakeLists.txt
samples/category/my_sample/sample_name.cpp
samples/category/my_sample/sample_name.h
----
A new class will also be generated
[,cpp]
----
class SampleName :public ApiVulkanSample {
...
};
----
=== Generate Android Project
[,bash]
----
./scripts/generate.py android
./scripts/generate.py android --output-dir build/<another_folder_name>
----
== Clang Format
When called from the root of the repository, this script will run clang-format on all files in the repository that have been altered in the `git diff`
[,bash]
----
./scripts/clang-format.py <brand_to_diff>
----
== Copyright Headers
When called from the root of the repository, this script will check all files in the repository that have been altered in the `git diff` to ensure they have the correct license header.
[,bash]
----
./scripts/copyright.py <branch_to_diff>
----
This is similar to the copyright CI check except when run with `--fix` this script will update the license headers in all files in the repository that have been altered in the `git diff`.
[,bash]
----
./scripts/copyright.py <branch_to_diff> --fix
----
+86
View File
@@ -0,0 +1,86 @@
#!/usr/bin/env python
# Copyright 2023, Thomas Atkinson
#
# SPDX-License-Identifier: Apache-2.0
#
# 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.
import argparse
import os
import sys
from shutil import which
from subprocess import check_output
# Get the file extension
def get_ext(file_path):
file_name = os.path.basename(file_path)
file_name, file_ext = os.path.splitext(file_name)
return file_ext
class terminal_colors:
SUCCESS = "\033[92m"
INFO = "\033[94m"
WARNING = "\033[33m"
ERROR = "\033[91m"
END = "\033[0m"
if __name__ == "__main__":
argument_parser = argparse.ArgumentParser(
description="Format C/C++ files using clang-format"
)
argument_parser.add_argument(
"branch",
type=str,
default="main",
nargs="?",
help="Branch from which to compute the diff",
)
args = argument_parser.parse_args()
if len(sys.argv) == 1:
argument_parser.print_help(sys.stderr)
sys.exit(1)
files = None
if not which("git"):
print(terminal_colors.ERROR + "Missing git" + terminal_colors.END)
sys.exit(1)
if not which("clang-format"):
print(terminal_colors.ERROR + "Missing clang-format" + terminal_colors.END)
sys.exit(1)
out = check_output(["git", "diff", args.branch, "--name-only"])
check_files = [".h", ".hpp", ".cpp"]
files = out.decode("utf-8").split("\n")
files = [f for f in files if f and get_ext(f) in check_files]
if files and len(files) > 0:
print(terminal_colors.INFO + "Formatting files:" + terminal_colors.END)
for f in files:
print(terminal_colors.INFO + " " + f + terminal_colors.END)
print()
for f in files:
if os.path.isfile(f):
check_output(["clang-format", "-i", f])
else:
print(terminal_colors.INFO + "No files to format" + terminal_colors.END)
+239
View File
@@ -0,0 +1,239 @@
#!/usr/bin/env python
# Copyright 2021-2023, Arm Limited and Contributors
#
# SPDX-License-Identifier: Apache-2.0
#
# 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.
import argparse
import os
import re
import sys
import datetime
import io
from shutil import which
from subprocess import check_output
# Patterns to search for in the files - Order is important!
COPYRIGHT_PATTERNS = [
re.compile(r"\bCopyright \(c\)[^a-zA-Z0-9]*\b\d{4}-\d{4}\b", re.IGNORECASE),
re.compile(r"\bCopyright[^a-zA-Z0-9]*\b\d{4}-\d{4}\b", re.IGNORECASE),
re.compile(r"\bCopyright \(c\)[^a-zA-Z0-9]*\b\d{4}\b", re.IGNORECASE),
re.compile(r"\bCopyright[^a-zA-Z0-9]*\b\d{4}\b", re.IGNORECASE),
]
YEAR_PATTERN = re.compile(r"\b\d{4}\b")
YEAR_RANGE_PATTERN = re.compile(r"\b\d{4}-\d{4}\b")
EXCEPTION_FILE = ".copyrightignore"
class terminal_colors:
SUCCESS = "\033[92m"
INFO = "\033[94m"
WARNING = "\033[33m"
ERROR = "\033[91m"
END = "\033[0m"
# Get the file extension
def get_ext(file_path):
file_name = os.path.basename(file_path)
file_name, file_ext = os.path.splitext(file_name)
return file_ext
# Query files for the presence of a valid year in the header
def query_files(files):
failed_queries = {}
queries = {}
for filename in files:
with io.open(filename, "r+", encoding="utf-8") as f:
queries[filename] = None
file_contents = f.read()
for query in COPYRIGHT_PATTERNS:
try:
matches = re.findall(query, file_contents)
if len(matches) > 0:
queries[filename] = matches
break # Stop searching after the first match
except re.error as error:
failed_queries[filename] = error
return queries, failed_queries
# Check the files for the presence of a valid year in the header
def check_files(check_files):
queries, failures = query_files(check_files)
current_year = datetime.datetime.now().year
missing = []
outdated = {}
for filename, tokens in queries.items():
if not tokens:
missing.append(filename)
continue
outdated[filename] = None
for token in tokens:
copyright_years = re.findall(YEAR_PATTERN, token)
most_recent_year = int(copyright_years[-1]) if copyright_years else 0
if most_recent_year != current_year:
if not outdated[filename]:
outdated[filename] = []
outdated[filename].append(token)
outdated = {k: v for k, v in outdated.items() if v is not None}
return missing, outdated, failures
def fix(file):
queries, failures = query_files([file])
current_year = datetime.datetime.now().year
for filename, tokens in queries.items():
if not tokens:
continue
for token in tokens:
year_range = YEAR_RANGE_PATTERN.search(token)
if year_range:
year_range = year_range.group(0)
start_year, end_year = year_range.split("-")
if int(end_year) != current_year:
fixed_token = token.replace(year_range, start_year + "-" + str(current_year))
with io.open(filename, "r+", encoding="utf-8") as f:
file_contents = f.read()
file_contents = file_contents.replace(token, fixed_token)
f.seek(0)
f.write(file_contents)
f.truncate(
f.tell()
)
continue
year = YEAR_PATTERN.search(token)
if year:
year = year.group(0)
if int(year) != current_year:
fixed_token = token + "-" + str(current_year)
with io.open(filename, "r+", encoding="utf-8") as f:
file_contents = f.read()
file_contents = file_contents.replace(token, fixed_token)
f.seek(0)
f.write(file_contents)
f.truncate(
f.tell()
) # Truncate the file to the current position of the file pointer
if __name__ == "__main__":
argument_parser = argparse.ArgumentParser(
description="Check that modified files include copyright headers with current year."
)
argument_parser.add_argument(
"branch",
type=str,
default="main",
nargs="?",
help="Branch from which to compute the diff",
)
argument_parser.add_argument(
"--fix",
action="store_true",
help="Fix the files that are missing the header",
default=False,
)
args = argument_parser.parse_args()
if len(sys.argv) == 1:
argument_parser.print_help(sys.stderr)
sys.exit(1)
files = None
if not which("git"):
print(terminal_colors.ERROR + "Missing git" + terminal_colors.END)
sys.exit(1)
file_exceptions = [EXCEPTION_FILE]
try:
ignored = open(EXCEPTION_FILE).readlines()
for file in ignored:
file_exceptions.append(file.strip())
except FileNotFoundError:
pass
out = check_output(["git", "diff", args.branch, "--name-only"])
files = out.decode("utf-8").split("\n")
files_to_check = list(
filter(
lambda x: os.path.isfile(x)
and os.path.basename(x) not in file_exceptions
and get_ext(x) not in file_exceptions
and len(x) > 0,
files,
)
)
if files_to_check and len(files_to_check) > 0:
missing, outdated, failures = check_files(files_to_check)
if len(failures) > 0:
print(terminal_colors.ERROR + "Failed to search:" + terminal_colors.END)
for filename, error in failures.items():
print(filename)
print()
if len(missing) > 0:
print(terminal_colors.ERROR + "Missing copyright:" + terminal_colors.END)
for filename in missing:
print(filename)
print()
if len(outdated) > 0:
print(terminal_colors.ERROR + "Outdated copyright:" + terminal_colors.END)
for filename, tokens in outdated.items():
if args.fix:
fix(filename)
print(terminal_colors.SUCCESS + "Fixed " + filename + terminal_colors.END)
else:
print(filename)
for token in tokens:
print("\t", terminal_colors.WARNING + token + terminal_colors.END)
print()
print("\n=== Files Checked ===")
for filename in files_to_check:
print(terminal_colors.INFO + filename + terminal_colors.END)
if len(outdated) > 0 or len(missing) > 0:
sys.exit(-1)
else:
sys.exit(0)
else:
print(terminal_colors.INFO + "No files found" + terminal_colors.END)
+192
View File
@@ -0,0 +1,192 @@
#!/usr/bin/env python
# Copyright 2023, Thomas Atkinson
#
# SPDX-License-Identifier: Apache-2.0
#
# 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.
import argparse
import os
from shutil import which
from subprocess import check_output
import sys
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
ROOT_DIR = os.path.join(SCRIPT_DIR, "..")
class terminal_colors:
SUCCESS = "\033[92m"
INFO = "\033[94m"
WARNING = "\033[33m"
ERROR = "\033[91m"
END = "\033[0m"
def generate_android_gradle_help(subparsers):
parser = subparsers.add_parser(
"android",
help="Generate Android Gradle files",
)
parser.set_defaults(func=generate_android_gradle)
parser.add_argument(
"--output-dir",
type=str,
help="Relative Output Directory: <project_dir>/build/android_gradle",
default=None,
)
def generate_android_gradle(args):
output_dir = (
os.path.join(ROOT_DIR, args.output_dir)
if args.output_dir
else os.path.join(ROOT_DIR, "build", "android_gradle")
)
if not which("cmake"):
print("Missing cmake")
sys.exit(1)
print(
terminal_colors.INFO
+ "Generating Android Gradle files at "
+ output_dir
+ terminal_colors.END
)
check_output(
[
"cmake",
"-DPROJECT_NAME=vulkan_samples",
"-DANDROID_API=30",
"-DARCH_ABI=arm64-v8a",
"-DANDROID_MANIFEST={}".format(
os.path.join(ROOT_DIR, "app", "android", "AndroidManifest.xml")
),
"-DJAVA_DIRS={}".format(os.path.join(ROOT_DIR, "app", "android", "java")),
"-DRES_DIRS={}".format(os.path.join(ROOT_DIR, "app", "android", "res")),
"-DOUTPUT_DIR={}".format(output_dir),
"-DASSET_DIRS=",
"-DJNI_LIBS_DIRS=",
"-DNATIVE_SCRIPT={}".format(os.path.join(ROOT_DIR, "CMakeLists.txt")),
"-P",
os.path.join(ROOT_DIR, "bldsys", "cmake", "create_gradle_project.cmake"),
]
)
def generate_sample_help(subparsers, template):
parser = subparsers.add_parser(
template,
help="Generate sample project",
)
parser.set_defaults(func=generate_sample(template))
parser.add_argument(
"--name",
type=str,
help="Name of the sample project",
default="SampleTest",
)
parser.add_argument(
"--category",
type=str,
help="Which category the sample project belongs to",
default="api",
)
parser.add_argument(
"--output-dir",
type=str,
help="Output Directory: <project_dir>/samples/<category>",
default=None,
)
def prompt_if_path_exists(output_dir, name):
path = os.path.join(output_dir, name)
if os.path.exists(path):
yes = {'yes', 'y'}
no = {'no', 'n', ''}
print(terminal_colors.WARNING
+ f"The output directory {path} is not empty. Would you like to overwrite its content with the sample template? [y/N]"
+ terminal_colors.END)
while True:
choice = input().lower()
if choice in yes:
return True
elif choice in no:
return False
else:
print("Please respond with 'y' or 'n'")
return True
def generate_sample(template):
def func(args):
output_dir = (
args.output_dir
if args.output_dir
else os.path.join(ROOT_DIR, "samples", args.category)
)
if not which("cmake"):
print("Missing cmake")
sys.exit(1)
if not prompt_if_path_exists(output_dir, args.name):
return
print(
terminal_colors.INFO
+ "Generating sample project "
+ args.name
+ terminal_colors.END
)
check_output(
[
"cmake",
"-DSAMPLE_NAME={}".format(args.name),
"-DTEMPLATE_NAME={}".format(template),
"-DOUTPUT_DIR={}".format(output_dir),
"-P",
os.path.join(ROOT_DIR, "bldsys", "cmake", "create_sample_project.cmake"),
]
)
return func
if __name__ == "__main__":
argument_parser = argparse.ArgumentParser(description="Generate utility.")
subparsers = argument_parser.add_subparsers(
help="Commands",
)
generate_android_gradle_help(subparsers)
generate_sample_help(subparsers, "sample")
generate_sample_help(subparsers, "sample_api")
args = argument_parser.parse_args()
if len(sys.argv) == 1:
argument_parser.print_help(sys.stderr)
sys.exit(1)
args.func(args)