From a918598e476a970e0e476fcd820bf246d62de526 Mon Sep 17 00:00:00 2001 From: Chris Sarbora Date: Sun, 9 Jun 2024 03:08:08 -0500 Subject: [PATCH 1/2] Add a parallel-capable linter script (Windows) --- lint.bat | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 lint.bat diff --git a/lint.bat b/lint.bat new file mode 100644 index 000000000..820c48641 --- /dev/null +++ b/lint.bat @@ -0,0 +1,56 @@ +@echo off +setlocal enabledelayedexpansion + +rem Runs clang-format on all *.cpp/*.h/*.json files in the project, excluding builds/ +set "FILE_TYPES=*.cpp *.h *.json" +set "EXCLUDED=builds" + +rem Make sure we have a clang-format to use. +rem Prefer any clang-format already in the user's %PATH%. +set "PATH=%PATH%;%VCINSTALLDIR%\Tools\Llvm\bin\" +where /q clang-format +if %ERRORLEVEL% neq 0 ( + echo Unable to find clang-format. Are you in a Developer command prompt? + exit /b 1 +) + +rem Figure out how parallel we're gonna be +if /i "%1" == "-j" ( + shift + set /a JOBS=%1 +) else if "%1" == "" ( + set /a JOBS=%NUMBER_OF_PROCESSORS% +) else ( + echo Usage: lint.bat ^[-j ^^] + echo If -j is not specified, NUM_JOBS defaults to the number of processors ^(%NUMBER_OF_PROCESSORS%^) + exit /b 1 +) + +rem Generate a temporary filename to store the list of files +:gen_temp_file +set CPP_FILES="%TMP%\lint~%RANDOM%.tmp" +if exist %CPP_FILES% goto :gen_temp_file + +rem Find all the files, and split them up amongs %JOBS% different files, round-robin style +set /a i=1 +for /f "usebackq tokens=*" %%A in (`dir %FILE_TYPES% /s /b ^| findstr /v "%EXCLUDED%"`) do ( + rem !i! syntax here is "immediate" expansion: https://ss64.com/nt/delayedexpansion.html + echo %%A >> %CPP_FILES%-!i! + set /a i=!i! + 1 + if !i! gtr %JOBS% (set /a i=1) +) + +rem start a clang-format task for each filelist created +for /l %%A in (1,1,%JOBS%) do ( + rem Use `start /b` to asynchronously spawn a new cmd.exe session running a clang-format and then a delete + rem The clang-format cmd is stdout/stderr redirected to NUL, and the ^& is an "and then" joiner + rem In batch, `foo & bar` is a way to indicate both foo and bar on one line, one after another. The ^ here + rem is an escape, indicating that the & character should be passed to cmd, *not* start + start "" /b cmd /c clang-format -i --files %CPP_FILES%-%%A >NUL 2>&1 ^& del %CPP_FILES%-%%A +) + +rem Loop until all split filelists are deleted by their asynchronous cmds. Poor man's semaphore, file-style. +:wait_for_done +for /l %%A in (1,1,%JOBS%) do ( + if exist %CPP_FILES%-%%A goto :wait_for_done +) From 34fe23cf41502de17585ea2da1c60f565dd61591 Mon Sep 17 00:00:00 2001 From: Chris Sarbora Date: Fri, 21 Jun 2024 00:06:51 -0500 Subject: [PATCH 2/2] Add a parallel-capable linter script (*nix) --- lint.sh | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100755 lint.sh diff --git a/lint.sh b/lint.sh new file mode 100755 index 000000000..a9cb8372a --- /dev/null +++ b/lint.sh @@ -0,0 +1,74 @@ +#!/bin/sh + +# Runs clang-format on all *.cpp/*.h/*.json files in the project, excluding builds/ +FILE_TYPES="*.cpp *.h *.json" +EXCLUDED="*/builds/*" + +# Make sure we have a clang-format to use. +# Prefer any clang-format already in the user's $PATH. +# PATH=$PATH:/usr/bin/ +which -s clang-format || ( + echo Unable to find clang-format in \$PATH. + exit 1 +) + +# parse arguments +while [ "$1" != "" ]; do + case $1 in + -j | --jobs ) + shift + JOBS="$1" + ;; + * ) + echo "Usage: lint.sh [-j|--jobs ]" + echo " If -j is not specified, NUM_JOBS defaults to the number of CPU threads." + exit 1 + ;; + esac + shift +done + +# Figure out how parallel we're gonna be +# default to simple single-threaded xargs +EXECUTOR=xargs +if [ "$JOBS" != "1" ]; then + # if xargs supports parallelism, use that + if (echo foo bar | xargs -lkj -P 0$JOBS true 2>/dev/null); then + # prefix the $JOBS with 0 so that unspecified JOBS becomes "0" and specified becomes "02" which is still valid + # -n 1 so that xargs doesn't simply collect all the args into one job and break parallelism regardless + EXECUTOR="xargs -P 0$JOBS -n 1" + # if xargs doesn't support it, see if we can use GNU parallel + elif which -s parallel; then + # -X to spread the parameters evenly among jobs + EXECUTOR="parallel -X" + # if JOBS is explicitly set, add it (don't use 0-prefix trick; it triggers some buggy behavior in the "-j 0" case) + if [ "$JOBS" != "" ]; then + EXECUTOR="$EXECUTOR -j $JOBS" + fi + # if nothing supports parallelism, warn if the user appears to expect it + elif [ "$JOBS" != "" ]; then + echo "Warning: Parallel jobs specified, but installed xargs does not support -P and GNU parallel not found. Defaulting to single-threaded." >&2 + fi +fi + +# takes the FILE_TYPES and EXCLUDED vars and concats them into find parameters +function path_args() { + for i in $1; do + echo -path "$i" -or + done +} + +# finally, run the job +find . \ + -type f \ + \( \ + $(path_args "$FILE_TYPES") \ + -false \ + \) \ + -not \( \ + $(path_args "$EXCLUDED") \ + -false \ + \) \ + -print0 | +# pipe the file list into the executor (xargs or parallel) to run clang-format +$EXECUTOR --null clang-format -i