mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-06 07:11:10 +00:00
A Linux job configures without the precompiled header and runs clang-tidy over the C++ lines a pull request changes. The only check for now is misc-include-cleaner for missing includes; .clang-tidy is where further checks get enabled. scripts/run_clang_tidy.sh (Linux, macOS) and scripts/run_clang_tidy.ps1 (Windows) run the same check locally: the same configure, the clang-tidy version pinned in scripts/clang_tidy_requirements.txt, and the same comparison against OrcaSlicer's main. They offer to install what is missing, or print the command to do it by hand.
234 lines
8.3 KiB
Bash
Executable File
234 lines
8.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Runs the clang-tidy check that gates pull requests (.github/workflows/clang_tidy.yml)
|
|
# on your branch, so its findings match what CI reports. Linux and macOS.
|
|
#
|
|
# scripts/run_clang_tidy.sh check your changes against OrcaSlicer's main
|
|
# scripts/run_clang_tidy.sh --fix also add the missing includes it names
|
|
#
|
|
# It configures a separate build directory (build-tidy) without the precompiled
|
|
# header, installs the pinned clang-tidy into a virtual environment inside it, and
|
|
# runs scripts/clang_tidy_diff.py the way CI does. Uncommitted changes are checked too.
|
|
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: scripts/run_clang_tidy.sh [options]
|
|
|
|
-b, --base REV revision to compare against (default: main of the remote that
|
|
points at OrcaSlicer/OrcaSlicer, else origin/main)
|
|
--no-fetch do not fetch that remote's main first
|
|
-B, --build-dir DIR build directory for the compile database (default: build-tidy)
|
|
-d, --deps-dir DIR dependency build directory (default: deps/build on Linux,
|
|
deps/build/<arch> on macOS)
|
|
-j, --jobs N parallel clang-tidy runs (default: all cores)
|
|
--fix apply clang-tidy's fixes (adds the missing includes)
|
|
-y, --yes install missing tools without asking
|
|
-h, --help show this help
|
|
EOF
|
|
}
|
|
|
|
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
cd "$ROOT"
|
|
|
|
BASE=""
|
|
FETCH=1
|
|
BUILD_DIR=build-tidy
|
|
DEPS_DIR=""
|
|
JOBS=""
|
|
FIX=0
|
|
YES=0
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-b|--base) BASE="$2"; shift 2 ;;
|
|
--no-fetch) FETCH=0; shift ;;
|
|
-B|--build-dir) BUILD_DIR="$2"; shift 2 ;;
|
|
-d|--deps-dir) DEPS_DIR="$2"; shift 2 ;;
|
|
-j|--jobs) JOBS="$2"; shift 2 ;;
|
|
--fix) FIX=1; shift ;;
|
|
-y|--yes) YES=1; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
case "$BUILD_DIR" in
|
|
/*) ;;
|
|
*) BUILD_DIR="$ROOT/$BUILD_DIR" ;;
|
|
esac
|
|
|
|
OS=$(uname -s)
|
|
case "$OS" in
|
|
Linux) ;;
|
|
Darwin) ;;
|
|
*) echo "Unsupported system $OS. On Windows, use scripts/run_clang_tidy.ps1." >&2; exit 1 ;;
|
|
esac
|
|
|
|
# Ask before installing anything. Without a terminal, or with "no", print the
|
|
# command instead so it can be run by hand.
|
|
ask() {
|
|
[ "$YES" = 1 ] && return 0
|
|
[ -t 0 ] || return 1
|
|
local reply
|
|
read -r -p "$1 [y/N] " reply
|
|
[[ "$reply" =~ ^[Yy] ]]
|
|
}
|
|
|
|
# --- System tools -------------------------------------------------------------
|
|
|
|
missing=()
|
|
command -v git >/dev/null || missing+=(git)
|
|
command -v python3 >/dev/null || missing+=(python3)
|
|
command -v cmake >/dev/null || missing+=(cmake)
|
|
command -v ninja >/dev/null || missing+=(ninja)
|
|
if [ "$OS" = Linux ]; then
|
|
# CI builds its compile database with clang; another compiler's flags change what
|
|
# clang-tidy sees.
|
|
command -v clang >/dev/null && command -v clang++ >/dev/null || missing+=(clang)
|
|
fi
|
|
|
|
if [ ${#missing[@]} -gt 0 ]; then
|
|
echo "Missing: ${missing[*]}"
|
|
install_cmd=""
|
|
if [ "$OS" = Darwin ]; then
|
|
pkgs=()
|
|
for m in "${missing[@]}"; do
|
|
case "$m" in
|
|
git|python3|cmake|ninja) pkgs+=("${m/python3/python}") ;;
|
|
esac
|
|
done
|
|
if command -v brew >/dev/null; then
|
|
install_cmd="brew install ${pkgs[*]}"
|
|
else
|
|
echo "Install Homebrew from https://brew.sh, then run: brew install ${pkgs[*]}" >&2
|
|
exit 1
|
|
fi
|
|
elif command -v apt-get >/dev/null; then
|
|
pkgs=()
|
|
for m in "${missing[@]}"; do
|
|
case "$m" in
|
|
ninja) pkgs+=(ninja-build) ;;
|
|
python3) pkgs+=(python3 python3-venv) ;;
|
|
*) pkgs+=("$m") ;;
|
|
esac
|
|
done
|
|
install_cmd="sudo apt-get install -y ${pkgs[*]}"
|
|
elif command -v dnf >/dev/null; then
|
|
install_cmd="sudo dnf install -y ${missing[*]/ninja/ninja-build}"
|
|
elif command -v pacman >/dev/null; then
|
|
install_cmd="sudo pacman -S --needed ${missing[*]/python3/python}"
|
|
else
|
|
echo "Install ${missing[*]} with your package manager and run this again." >&2
|
|
exit 1
|
|
fi
|
|
if ask "Install them now with: $install_cmd ?"; then
|
|
$install_cmd
|
|
else
|
|
echo "To install them yourself, run:" >&2
|
|
echo " $install_cmd" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# --- clang-tidy ---------------------------------------------------------------
|
|
|
|
REQUIREMENTS="$ROOT/scripts/clang_tidy_requirements.txt"
|
|
PINNED=$(sed -n 's/^clang-tidy==//p' "$REQUIREMENTS")
|
|
VENV="$BUILD_DIR/clang-tidy-venv"
|
|
|
|
if [ -n "${CLANG_TIDY:-}" ]; then
|
|
# Set by the caller: use it as is.
|
|
:
|
|
else
|
|
CLANG_TIDY="$VENV/bin/clang-tidy"
|
|
if [ ! -x "$CLANG_TIDY" ] || ! "$CLANG_TIDY" --version | grep -q "version $PINNED"; then
|
|
if ask "clang-tidy $PINNED (the version CI uses) is not installed. Install it into $VENV?"; then
|
|
mkdir -p "$BUILD_DIR"
|
|
if ! python3 -m venv "$VENV"; then
|
|
echo "Could not create a Python virtual environment." >&2
|
|
[ "$OS" = Linux ] && echo "On Debian or Ubuntu, install it with: sudo apt-get install -y python3-venv" >&2
|
|
exit 1
|
|
fi
|
|
"$VENV/bin/pip" install --quiet --upgrade pip
|
|
"$VENV/bin/pip" install --quiet -r "$REQUIREMENTS"
|
|
else
|
|
echo "To install it yourself, run:" >&2
|
|
echo " python3 -m venv $VENV && $VENV/bin/pip install -r scripts/clang_tidy_requirements.txt" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|
|
if ! "$CLANG_TIDY" --version | grep -q "version $PINNED"; then
|
|
echo "Warning: $CLANG_TIDY is not clang-tidy $PINNED, so results may differ from CI." >&2
|
|
fi
|
|
|
|
# --- Dependencies -------------------------------------------------------------
|
|
|
|
# A configured build directory remembers where its dependencies are.
|
|
if [ -z "$DEPS_DIR" ] && [ -f "$BUILD_DIR/CMakeCache.txt" ]; then
|
|
DEPS_DIR=$(sed -n 's/^DEP_BUILD_DIR:[A-Z]*=//p' "$BUILD_DIR/CMakeCache.txt")
|
|
fi
|
|
if [ "$OS" = Darwin ]; then
|
|
ARCH=$(uname -m)
|
|
DEPS_DIR="${DEPS_DIR:-deps/build/$ARCH}"
|
|
BUILD_DEPS_CMD="./build_release_macos.sh -d -a $ARCH"
|
|
else
|
|
DEPS_DIR="${DEPS_DIR:-deps/build}"
|
|
BUILD_DEPS_CMD="./build_linux.sh -d"
|
|
fi
|
|
case "$DEPS_DIR" in
|
|
/*) ;;
|
|
*) DEPS_DIR="$ROOT/$DEPS_DIR" ;;
|
|
esac
|
|
|
|
if [ ! -d "$DEPS_DIR/OrcaSlicer_dep/usr/local" ]; then
|
|
echo "OrcaSlicer's dependencies are not built in $DEPS_DIR."
|
|
if ask "Build them now with $BUILD_DEPS_CMD? This takes a while."; then
|
|
$BUILD_DEPS_CMD
|
|
else
|
|
echo "Build them with $BUILD_DEPS_CMD, or point to an existing build with --deps-dir." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# --- Compile database ---------------------------------------------------------
|
|
|
|
# The same configure as CI. DEP_BUILD_DIR is named outright: CMake would otherwise
|
|
# derive it from the build directory's name.
|
|
cmake_args=(-S . -B "$BUILD_DIR" -G Ninja -DCMAKE_BUILD_TYPE=Release
|
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DSLIC3R_PCH=OFF -DORCA_TOOLS=ON -DBUILD_TESTS=ON
|
|
-DDEP_BUILD_DIR="$DEPS_DIR")
|
|
if [ "$OS" = Darwin ]; then
|
|
cmake_args+=(-DCMAKE_OSX_ARCHITECTURES="$ARCH" "-DCMAKE_IGNORE_PREFIX_PATH=/opt/local;/usr/local;/opt/homebrew")
|
|
else
|
|
cmake_args+=(-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++)
|
|
fi
|
|
echo "Configuring $BUILD_DIR"
|
|
mkdir -p "$BUILD_DIR"
|
|
if ! cmake "${cmake_args[@]}" >"$BUILD_DIR/configure.log" 2>&1; then
|
|
tail -n 20 "$BUILD_DIR/configure.log" >&2
|
|
echo "Configuring failed; the full log is in $BUILD_DIR/configure.log." >&2
|
|
[ "$OS" = Linux ] && echo "Missing system libraries? Install them with: ./build_linux.sh -u" >&2
|
|
exit 1
|
|
fi
|
|
cmake --build "$BUILD_DIR" --target git_commit_hash_header >/dev/null
|
|
|
|
# --- Base revision ------------------------------------------------------------
|
|
|
|
if [ -z "$BASE" ]; then
|
|
REMOTE=$(git remote -v | awk '/github\.com[:\/]OrcaSlicer\/OrcaSlicer(\.git)? \(fetch\)/ { print $1; exit }')
|
|
REMOTE="${REMOTE:-origin}"
|
|
if [ "$FETCH" = 1 ]; then
|
|
git fetch --quiet "$REMOTE" main
|
|
fi
|
|
BASE="$REMOTE/main"
|
|
fi
|
|
echo "Comparing against $BASE"
|
|
|
|
# --- Run ----------------------------------------------------------------------
|
|
|
|
args=(-p "$BUILD_DIR" --base "$BASE" --clang-tidy "$CLANG_TIDY")
|
|
[ -n "$JOBS" ] && args+=(-j "$JOBS")
|
|
[ "$FIX" = 1 ] && args+=(-- --fix)
|
|
exec python3 scripts/clang_tidy_diff.py "${args[@]}"
|