Files
Bubberstation/tools/define_sanity/check.py
san7890 018c10f9a0 Fixes Define Sanity (#77845)
## About The Pull Request

Hey there,

This was broken in an update from #74573
(3902973978), the RegEx was only catching
a fraction of the cases it was meant to be.

This is what we were finding on 74573 version of the RegEx:

![image](https://github.com/tgstation/tgstation/assets/34697715/a5040604-279f-4012-a5dd-3a1e9eea1b8e)

This is what we should be finding for all of the cases that
`define_sanity` will need to check for:

![image](https://github.com/tgstation/tgstation/assets/34697715/e33ad60e-fb36-448e-ae02-5494c916f450)

This is what was broken as a consequence:

![image](https://github.com/tgstation/tgstation/assets/34697715/567012db-a9c2-4118-aadf-f70996731af7)

As stated in the introductory PR #74333
(ccef887efe), it's not the end of the
world if we miss unmanaged local defines, but it's still useful to have
this as a maintainability tool to ensure that everything remains as
clean as it possibly can. I wish we could do the whole matching method
like the aforementioned PR supposed could happen, but it simply doesn't
appear to work the way we want it to.

## Changelog
Nothing player facing.

I tried to experiment with `UNLINT()` but I got absolutely ganked by
getting the regex to work, so the fix for the FA Icon file may not be
super duper great unfortunately. Let me know if you have a showstopper
idea, this is just a stopguard so this PR can get merged and I don't
have to keep talking about unmanaged local defines while reviewing PRs.
2023-08-26 02:25:16 +01:00

105 lines
3.6 KiB
Python

import fnmatch
import glob
import os
import re
import sys
parent_directory = "code/**/*.dm"
output_file_name = "define_sanity_output.txt"
how_to_fix_message = "Please #undef the above defines or remake them as global defines in the code/__DEFINES directory."
def green(text):
return "\033[32m" + str(text) + "\033[0m"
def red(text):
return "\033[31m" + str(text) + "\033[0m"
def blue(text):
return "\033[34m" + str(text) + "\033[0m"
def post_error(define_name, file, github_error_style):
if github_error_style:
print(f"::error file={file},title=Define Sanity::{define_name} is defined locally in {file} but not undefined locally!")
else:
print(red(f"- Failure: {define_name} is defined locally in {file} but not undefined locally!"))
# simple way to check if we're running on github actions, or on a local machine
on_github = os.getenv("GITHUB_ACTIONS") == "true"
# This files/directories are expected to have "global" defines, so they must be exempt from this check.
# Add directories as string here to automatically be exempt in case you have a non-complaint file name.
excluded_files = [
# Wildcard directories, all files are expected to be exempt.
"code/__DEFINES/*.dm",
"code/__HELPERS/*.dm",
"code/_globalvars/*.dm",
# TGS files come from another repository so lets not worry about them.
"code/modules/tgs/**/*.dm",
]
define_regex = re.compile(r"(\s+)?#define\s?([A-Z0-9_]+)\(?(.+)\)?")
files_to_scan = []
number_of_defines = 0
if not on_github:
print(blue(f"Running define sanity check outside of Github Actions.\nFor assistance, a '{output_file_name}' file will be generated at the root of your directory if any errors are detected."))
for code_file in glob.glob(parent_directory, recursive=True):
exempt_file = False
for exempt_directory in excluded_files:
if fnmatch.fnmatch(code_file, exempt_directory):
exempt_file = True
break
if exempt_file:
continue
# If the "base path" of the file starts with an underscore, it's assumed to be an encapsulated file holding references to the other files in its folder and is exempt from the checks.
if os.path.basename(code_file)[0] == "_":
continue
files_to_scan.append(code_file)
located_error_tuples = []
for applicable_file in files_to_scan:
with open(applicable_file, encoding="utf8") as file:
file_contents = file.read()
for define in define_regex.finditer(file_contents):
number_of_defines += 1
define_name = define.group(2)
if not re.search("#undef\s" + define_name, file_contents):
located_error_tuples.append((define_name, applicable_file))
if number_of_defines == 0:
print(red("No defines found! This is likely an error."))
sys.exit(1)
if number_of_defines <= 1000:
print(red(f"Only found {number_of_defines} defines! Something has likely gone wrong as the number of local defines should not be this low."))
sys.exit(1)
if len(located_error_tuples):
string_list = []
for error in located_error_tuples:
if not on_github:
post_error(error[0], error[1], False)
string_list.append(f"{error[0]} is defined locally in {error[1]} but not undefined locally!")
else:
post_error(error[0], error[1], True)
if len(string_list):
with open(output_file_name, "w") as output_file:
output_file.write("\n".join(string_list))
output_file.write("\n\n" + how_to_fix_message)
print(red(how_to_fix_message))
sys.exit(1)
else:
print(green(f"No unhandled local defines found (found {number_of_defines} defines)."))