Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tools - Add tool to run sqfvm checks on cba #1594

Merged
merged 2 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ tools/ndocs-project/Data/*

ArmaScriptCompiler.exe
*.sqfc
sqfvm.exe
107 changes: 107 additions & 0 deletions tools/sqfvmChecker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import os
import sys
import subprocess
import concurrent.futures

addon_base_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))

files_to_ignore_lower = [
x.lower() for x in ["initSettings.sqf", "initKeybinds.sqf", "XEH_PREP.sqf", "backwards_comp.sqf", "gui_createCategory.sqf"]
]
sqfvm_exe = os.path.join(addon_base_path, "sqfvm.exe")
virtual_paths = [
# would need to add more even more to /include to use it
"P:/a3|/a3", # "{}|/a3".format(os.path.join(addon_base_path, "include", "a3")),
"P:/a3|/A3",
f"{addon_base_path}|/x/cba"
]


def get_files_to_process(basePath):
arma_files = []
for root, _dirs, files in os.walk(os.path.join(addon_base_path, "addons")):
for file in files:
if file.endswith(".sqf") or file == "config.cpp":
if file.lower() in files_to_ignore_lower:
continue
skipPreprocessing = False
addonTomlPath = os.path.join(root, "addon.toml")
if os.path.isfile(addonTomlPath):
with open(addonTomlPath, "r") as f:
tomlFile = f.read()
if "preprocess = false" in tomlFile:
print("'preprocess = false' not supported")
raise
skipPreprocessing = "[preprocess]\nenabled = false" in tomlFile
addonTomlPath = os.path.join(os.path.dirname(root), "addon.toml")
if os.path.isfile(addonTomlPath):
with open(addonTomlPath, "r") as f:
tomlFile = f.read()
if "preprocess = false" in tomlFile:
print("'preprocess = false' not supported")
raise
skipPreprocessing = "[preprocess]\nenabled = false" in tomlFile
if file == "config.cpp" and skipPreprocessing:
continue # ignore configs with __has_include
filePath = os.path.join(root, file)
arma_files.append(filePath)
return arma_files


def process_file(filePath, skipA3Warnings=True):
with open(filePath, "r", encoding="utf-8", errors="ignore") as file:
content = file.read()
if content.startswith("//pragma SKIP_COMPILE"):
return False
cmd = [sqfvm_exe, "--input", filePath, "--parse-only", "--automated"]
for v in virtual_paths:
cmd.append("-v")
cmd.append(v)
# cmd.append("-V")
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True)
try:
ret = proc.wait(12) # max wait - seconds
except Exception as _e:
print("sqfvm timed out: {}".format(filePath))
return True
# print("{} = {}".format(filePath, ret))

fileHasError = False
keepReadingLines = True
while keepReadingLines:
line = proc.stdout.readline()
if not line:
keepReadingLines = False
else:
line = line.rstrip()
if line.startswith("[ERR]"):
fileHasError = True
if not (
skipA3Warnings
and line.startswith("[WRN]")
and ("a3/" in line)
and (("Unexpected IFDEF" in line) or ("defined twice" in line))
):
print(" {}".format(line))
return fileHasError


def main():
if not os.path.isfile(sqfvm_exe):
print("Error: sqfvm.exe not found in base folder [{}]".format(sqfvm_exe))
return 1

error_count = 0
arma_files = get_files_to_process(addon_base_path)
print("Checking {} files".format(len(arma_files)))
with concurrent.futures.ThreadPoolExecutor(max_workers=12) as executor:
for fileError in executor.map(process_file, arma_files):
if fileError:
error_count += 1

print("Errors: {}".format(error_count))
return error_count


if __name__ == "__main__":
sys.exit(main())
Loading