Files
af/tools/format.py
T
Anghelo Carvajal d66a21685f Initial repo setup (#1)
* git subrepo clone git@github.com:ethteck/splat.git tools/splat

subrepo:
  subdir:   "tools/splat"
  merged:   "4fec014"
upstream:
  origin:   "git@github.com:ethteck/splat.git"
  branch:   "master"
  commit:   "4fec014"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* gitignore

* decompress_baserom.py

* Initial yaml

* run flib

* more yaml stuff

* Builds an uncompressed ROM

* setup ido download

* git subrepo clone git@github.com:simonlindholm/asm-differ.git tools/asm-differ

subrepo:
  subdir:   "tools/asm-differ"
  merged:   "ae40866"
upstream:
  origin:   "git@github.com:simonlindholm/asm-differ.git"
  branch:   "main"
  commit:   "ae40866"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* git subrepo clone git@github.com:simonlindholm/asm-processor.git tools/asm-processor

subrepo:
  subdir:   "tools/asm-processor"
  merged:   "bbd86ea"
upstream:
  origin:   "git@github.com:simonlindholm/asm-processor.git"
  branch:   "main"
  commit:   "bbd86ea"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* git subrepo clone (merge) git@github.com:EllipticEllipsis/fado.git tools/fado

subrepo:
  subdir:   "tools/fado"
  merged:   "8d896ee"
upstream:
  origin:   "git@github.com:EllipticEllipsis/fado.git"
  branch:   "master"
  commit:   "8d896ee"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* setup ido in makefile

* data and rodata

* add automatic file splits

* bootstrap most symbol_addrs

* git subrepo clone --branch=irix git@github.com:hensldm/ultralib.git lib/ultralib

subrepo:
  subdir:   "lib/ultralib"
  merged:   "0d1e095"
upstream:
  origin:   "git@github.com:hensldm/ultralib.git"
  branch:   "irix"
  commit:   "0d1e095"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* setup ido building

* various tooling

* git subrepo pull tools/asm-differ

subrepo:
  subdir:   "tools/asm-differ"
  merged:   "857b398"
upstream:
  origin:   "git@github.com:simonlindholm/asm-differ.git"
  branch:   "main"
  commit:   "857b398"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* Change defines to match IDO

* review

* newlin

* update gitignore

* Windows error

* D_01000000

* yeet settings.json

* yeet

* git subrepo clone git@github.com:decompals/ultralib.git lib/ultralib

subrepo:
  subdir:   "lib/ultralib"
  merged:   "d03b2a3"
upstream:
  origin:   "git@github.com:decompals/ultralib.git"
  branch:   "main"
  commit:   "d03b2a3"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

* clangformat and clangtidy

* Setup a bunch of headers

* bootstrap boot and code functions and variables headers

* global.h

* update format.py includes

* -D_MIPS_SZLONG=32

* format.py: --verbose

* Update tools/decompress_baserom.py

Co-authored-by: Derek Hensley <hensley.derek58@gmail.com>

* Add libc to global.h

* review

* yeet saved_regs

* yeet headers

* yeet _MSC_VER

* more header cleanup

* add readme licensing note

---------

Co-authored-by: Derek Hensley <hensley.derek58@gmail.com>
2023-03-29 03:42:56 +01:00

189 lines
6.7 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import glob
import multiprocessing
import os
import re
import shutil
import subprocess
import sys
import tempfile
from functools import partial
from typing import List
# clang-format, clang-tidy and clang-apply-replacements default version
# Version 11 is used when available for more consistency between contributors
CLANG_VER = 11
# Clang-Format options (see .clang-format for rules applied)
FORMAT_OPTS = "-i -style=file"
# Clang-Tidy options (see .clang-tidy for checks enabled)
TIDY_OPTS = "-p ."
TIDY_FIX_OPTS = "--fix --fix-errors"
# Clang-Apply-Replacements options (used for multiprocessing)
APPLY_OPTS = ""
# Compiler options used with Clang-Tidy
# Normal warnings are disabled with -Wno-everything to focus only on tidying
INCLUDES = "-Iinclude -Isrc -Ibuild -I. -Ilib/ultralib/include -Ilib/ultralib/include/PR -Ibin/jp -Ibin/cn"
DEFINES = "-D_LANGUAGE_C -DNON_MATCHING -D_MIPS_SZLONG=32"
COMPILER_OPTS = f"-fno-builtin -std=gnu90 -m32 -Wno-everything {INCLUDES} {DEFINES}"
VERBOSE = False
def get_clang_executable(allowed_executables: List[str]):
for executable in allowed_executables:
try:
subprocess.check_call([executable, "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return executable
except FileNotFoundError or subprocess.CalledProcessError:
pass
return None
def get_tidy_version(tidy_executable: str):
tidy_version_run = subprocess.run([tidy_executable, "--version"], stdout=subprocess.PIPE, universal_newlines=True)
match = re.search(r"LLVM version ([0-9]+)", tidy_version_run.stdout)
return int(match.group(1))
CLANG_FORMAT = get_clang_executable([f"clang-format-{CLANG_VER}"])
if CLANG_FORMAT is None:
sys.exit(f"Error: clang-format-{CLANG_VER} not found")
CLANG_TIDY = get_clang_executable([f"clang-tidy-{CLANG_VER}", "clang-tidy"])
if CLANG_TIDY is None:
sys.exit(f"Error: neither clang-tidy-{CLANG_VER} nor clang-tidy found")
CLANG_APPLY_REPLACEMENTS = get_clang_executable([f"clang-apply-replacements-{CLANG_VER}", "clang-apply-replacements"])
# Try to detect the clang-tidy version and add --fix-notes for version 13+
# This is used to ensure all fixes are applied properly in recent versions
if get_tidy_version(CLANG_TIDY) >= 13:
TIDY_FIX_OPTS += " --fix-notes"
def list_chunks(list: List, chunk_length: int):
for i in range(0, len(list), chunk_length):
yield list[i : i + chunk_length]
def run_clang_format(files: List[str]):
exec_str = f"{CLANG_FORMAT} {FORMAT_OPTS} {' '.join(files)}"
subprocess.run(exec_str, shell=True)
def run_clang_tidy(files: List[str]):
exec_str = f"{CLANG_TIDY} {TIDY_OPTS} {TIDY_FIX_OPTS} {' '.join(files)} -- {COMPILER_OPTS}"
if VERBOSE:
subprocess.run(exec_str, shell=True)
else:
subprocess.run(exec_str, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
def run_clang_tidy_with_export(tmp_dir: str, files: List[str]):
(handle, tmp_file) = tempfile.mkstemp(suffix=".yaml", dir=tmp_dir)
os.close(handle)
exec_str = f"{CLANG_TIDY} {TIDY_OPTS} --export-fixes={tmp_file} {' '.join(files)} -- {COMPILER_OPTS}"
if VERBOSE:
subprocess.run(exec_str, shell=True)
else:
subprocess.run(exec_str, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
def run_clang_apply_replacements(tmp_dir: str):
exec_str = f"{CLANG_APPLY_REPLACEMENTS} {APPLY_OPTS} {tmp_dir}"
subprocess.run(exec_str, shell=True)
def add_final_new_line(file: str):
# https://backreference.org/2010/05/23/sanitizing-files-with-no-trailing-newline/index.html
# "gets the last character of the file pipes it into read, which will exit with a nonzero exit
# code if it encounters EOF before newline (so, if the last character of the file isn't a newline).
# If read exits nonzero, then append a newline onto the file using echo (if read exits 0,
# that satisfies the ||, so the echo command isn't run)." (https://stackoverflow.com/a/34865616)
exec_str = f"tail -c1 {file} | read -r _ || echo >> {file}"
subprocess.run(exec_str, shell=True)
def format_files(src_files: List[str], extra_files: List[str], nb_jobs: int):
if nb_jobs != 1:
print(f"Formatting files with {nb_jobs} jobs")
else:
print(f"Formatting files with a single job (consider using -j to make this faster)")
# Format files in chunks to improve performance while still utilizing jobs
file_chunks = list(list_chunks(src_files, (len(src_files) // nb_jobs) + 1))
print("Running clang-format...")
# clang-format only applies changes in the given files, so it's safe to run in parallel
with multiprocessing.get_context("fork").Pool(nb_jobs) as pool:
pool.map(run_clang_format, file_chunks)
print("Running clang-tidy...")
if nb_jobs > 1:
# clang-tidy may apply changes in #included files, so when running it in parallel we use --export-fixes
# then we call clang-apply-replacements to apply all suggested fixes at the end
tmp_dir = tempfile.mkdtemp()
try:
with multiprocessing.get_context("fork").Pool(nb_jobs) as pool:
pool.map(partial(run_clang_tidy_with_export, tmp_dir), file_chunks)
run_clang_apply_replacements(tmp_dir)
finally:
shutil.rmtree(tmp_dir)
else:
run_clang_tidy(src_files)
print("Adding missing final new lines...")
# Adding final new lines is safe to do in parallel and can be applied to all types of files
with multiprocessing.get_context("fork").Pool(nb_jobs) as pool:
pool.map(add_final_new_line, src_files + extra_files)
print("Done formatting files.")
def main():
parser = argparse.ArgumentParser(description="Format files in the codebase to enforce most style rules")
parser.add_argument("files", metavar="file", nargs="*")
parser.add_argument(
"-j",
dest="jobs",
type=int,
nargs="?",
default=1,
help="number of jobs to run (default: 1 without -j, number of cpus with -j)",
)
parser.add_argument("-v", "--verbose", action="store_true")
args = parser.parse_args()
global VERBOSE
VERBOSE = args.verbose
nb_jobs = args.jobs or multiprocessing.cpu_count()
if nb_jobs > 1:
if CLANG_APPLY_REPLACEMENTS is None:
sys.exit(
f"Error: neither clang-apply-replacements-{CLANG_VER} nor clang-apply-replacements found (required to use -j)"
)
if args.files:
files = args.files
extra_files = []
else:
files = glob.glob("src/**/*.c", recursive=True)
extra_files = glob.glob("assets/**/*.xml", recursive=True)
format_files(files, extra_files, nb_jobs)
if __name__ == "__main__":
main()