#!/usr/bin/env python3 import os import subprocess import sys import argparse from pathlib import Path from concurrent.futures import ThreadPoolExecutor, as_completed import multiprocessing import logging def main(): # Parse command-line arguments parser = argparse.ArgumentParser(description="Run Clippy on fuzzers in parallel.") parser.add_argument( "--dry-run", action="store_true", help="Show commands without executing." ) parser.add_argument( "--pedantic", action="store_true", help="Activate all clippy warnings" ) args = parser.parse_args() # Setup logging logging.basicConfig( level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s", handlers=[logging.StreamHandler(sys.stdout)], ) if args.dry_run: logging.info("Dry-run mode enabled. Commands will not be executed.") # Set directories script_dir = Path(__file__).resolve().parent libafl_dir = script_dir.parent os.chdir(libafl_dir) logging.debug(f"Changed directory to {libafl_dir}") # Initialize git submodules cmd = "git submodule update --init" logging.info(f"Running: {cmd}") if not args.dry_run: subprocess.run(cmd, shell=True, check=True) # Find fuzzer directories fuzzers = list((libafl_dir / "fuzzers").glob("*/*")) fuzzers.extend(list((libafl_dir / "fuzzers/baby/backtrace_baby_fuzzers").glob("*"))) fuzzers = [f for f in fuzzers if "nyx_" not in f.name and f.is_dir()] logging.debug(f"Found {len(fuzzers)} fuzzers.") # Function to run commands def run_clippy(fuzzer: Path): if not (fuzzer / "Cargo.toml").is_file(): logging.info(f"No Cargo.toml for {fuzzer}, skipping…") return True options = "-D clippy::pedantic" if args.pedantic else "" cmd_default = f"cargo clippy -- -D warnings {options}" for cmd in [cmd_default]: logging.info(f"[{fuzzer}] Running: {cmd}") if args.dry_run: continue result = subprocess.run( cmd, shell=True, cwd=fuzzer, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) if result.returncode != 0: logging.error( f"[{fuzzer}] Command failed: cd {fuzzer}; {cmd}\n{result.stderr}" ) return False else: logging.info(f"[{fuzzer}] Clippy passed.") return True # Run Clippy in parallel with ThreadPoolExecutor(max_workers=multiprocessing.cpu_count()) as executor: futures = {executor.submit(run_clippy, fuzzer): fuzzer for fuzzer in fuzzers} success = True for future in as_completed(futures): if not future.result(): success = False if success: logging.info("All fuzzers passed Clippy.") sys.exit(0) else: logging.error("Some fuzzers failed Clippy.") sys.exit(1) if __name__ == "__main__": main()