init
This commit is contained in:
@@ -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
|
||||
----
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user