import os import unittest from concurrent.futures import ThreadPoolExecutor from ci.paths import PROJECT_ROOT SRC_ROOT = PROJECT_ROOT / "src" NUM_WORKERS = (os.cpu_count() or 1) * 4 # Files that are allowed to not have #pragma once EXCLUDED_FILES = [ # Add any exceptions here ] EXCLUDED_DIRS = [ "third_party", "platforms", ] class TestMissingPragmaOnce(unittest.TestCase): def check_file(self, file_path: str) -> list[str]: """Check if a header file has #pragma once directive or if a cpp file incorrectly has it.""" failings: list[str] = [] with open(file_path, "r", encoding="utf-8", errors="ignore") as f: content = f.read() if file_path.endswith(".h"): # For header files, check if #pragma once is missing if "#pragma once" not in content: failings.append(f"Missing #pragma once in {file_path}") elif file_path.endswith(".cpp"): # For cpp files, check if #pragma once is incorrectly present if "#pragma once" in content: failings.append(f"Incorrect #pragma once in cpp file: {file_path}") return failings def test_pragma_once_usage(self) -> None: """ Searches through files to: 1. Check for missing #pragma once in header files 2. Check for incorrect #pragma once in cpp files """ files_to_check = [] current_dir = None # Collect files to check for root, dirs, files in os.walk(SRC_ROOT): # Log when we enter a new directory rel_path = os.path.relpath(root, SRC_ROOT) if current_dir != rel_path: current_dir = rel_path print(f"Traversing directory: {rel_path}") if rel_path in EXCLUDED_DIRS: print(f" Skipping excluded directory: {rel_path}") dirs[:] = [] # Skip this directory and its subdirectories continue # Check if this directory should be excluded # if any(os.path.normpath(root).startswith(os.path.normpath(excluded_dir)) # for excluded_dir in EXCLUDED_DIRS): # print(f" Skipping excluded directory: {rel_path}") # continue for excluded_dir in EXCLUDED_DIRS: npath = os.path.normpath(root) npath_excluded = os.path.normpath(excluded_dir) print(f"Checking {npath} against excluded {npath_excluded}") if npath.startswith(npath_excluded): print(f" Skipping excluded directory: {rel_path}") break for file in files: if file.endswith((".h", ".cpp")): # Check both header and cpp files file_path = os.path.join(root, file) # Check if file is excluded # if any(file_path.endswith(excluded) for excluded in EXCLUDED_FILES): # print(f" Skipping excluded file: {file}") # continue for excluded in EXCLUDED_FILES: # print(f"Checking {file_path} against excluded {excluded}") if file_path.endswith(excluded): print(f" Skipping excluded file: {file}") break files_to_check.append(file_path) print(f"Found {len(files_to_check)} files to check") # Process files in parallel all_failings = [] with ThreadPoolExecutor(max_workers=NUM_WORKERS) as executor: futures = [ executor.submit(self.check_file, file_path) for file_path in files_to_check ] for future in futures: all_failings.extend(future.result()) # Report results if all_failings: msg = f"Found {len(all_failings)} pragma once issues: \n" + "\n".join( all_failings ) for failing in all_failings: print(failing) self.fail(msg) else: print("All files have proper pragma once usage.") print(f"Pragma once check completed. Processed {len(files_to_check)} files.") if __name__ == "__main__": unittest.main()