mongo/buildscripts/clangd_vscode.sh
Alex Sarkesian 42b30da814 SERVER-126898: Make clangd configuration user-controllable (#53884)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
GitOrigin-RevId: ddc00ad74e0ed090a7cce95812015a570edf0547
2026-05-15 23:12:38 +00:00

173 lines
5.8 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# Collect all passed arguments
ARGS=("$@")
# Source user-defined clangd environment variables
CLANGD_ENV="$PWD/.vscode/clangd.env"
if [[ -f $CLANGD_ENV ]]; then
source "$CLANGD_ENV"
fi
# Ordered list of possible clangd locations (each candidate must be a single path)
CANDIDATES=(
"$(command -v custom-clangd || true)"
"$(find -L .compiledb -path '*/compiledb-*/external/mongo_toolchain_v5/v5/bin/clangd' -type f -print -quit 2>/dev/null || true)"
"$(find -L . -path './bazel-*/external/mongo_toolchain_v5/v5/bin/clangd' -type f -print -quit 2>/dev/null || true)"
"/opt/mongodbtoolchain/v5/bin/clangd"
)
# Find the first available clangd
CLANGD=""
for CANDIDATE in "${CANDIDATES[@]}"; do
if [[ -n "$CANDIDATE" && -x "$CANDIDATE" ]]; then
CLANGD="$CANDIDATE"
echo "[INFO] Using clangd at: $CLANGD" >&2
break
fi
done
SKIP_SWAP="${SKIP_SWAP:-0}"
SKIP_LIMITS="${CLANGD_NO_LIMITS:-0}"
# Fail if no clangd was found
if [[ -z "$CLANGD" ]]; then
echo "[ERROR] clangd not found in any of the expected locations." >&2
exit 1
fi
if ! sudo -n true >/dev/null 2>&1; then
echo "[WARN] sudo would prompt; skipping swap setup." >&2
SKIP_SWAP=1
fi
if [[ -f /.dockerenv ]] || grep -qaE '(docker|kubepods|containerd)' /proc/1/cgroup 2>/dev/null; then
echo "[INFO] Container detected; skipping swapon (usually not permitted)." >&2
SKIP_SWAP=1
fi
FINAL_ARGS=(
"${ARGS[@]}"
"-j=${CLANGD_JOBS:-4}"
"--query-driver=./**/*{clang,gcc,g++}*" # allow any clang or gcc binary in the repo
"--header-insertion=never"
)
# Log the full command (optional)
echo "[INFO] Executing: $CLANGD ${FINAL_ARGS[*]}" >&2
UNIT="clangd-vscode-${PPID}-${RANDOM}"
TMPDIR="$PWD/.tmp"
mkdir -p "$TMPDIR"
export TMPDIR # ensure clangd uses this for tmp
SWAPFILE="$TMPDIR/clangd.swap"
SWAPSIZE_GB=16
have_swap() {
awk 'NR>1 {found=1} END{exit(found?0:1)}' /proc/swaps
}
sudo_noninteractive_ok() {
sudo -n true >/dev/null 2>&1
}
swapfile_already_on() {
# /proc/swaps: Filename Type Size Used Priority
awk -v sf="$SWAPFILE" 'NR>1 && $1==sf {found=1} END{exit(found?0:1)}' /proc/swaps
}
if [[ "$SKIP_SWAP" != "1" ]]; then
if ! have_swap; then
echo "[INFO] No swap enabled; attempting to create $SWAPFILE (${SWAPSIZE_GB}G)..." >&2
fstype="$(stat -f -c %T "$TMPDIR" 2>/dev/null || echo unknown)"
case "$fstype" in
nfs | cifs | smb2 | fuse.* | overlayfs | overlay | 9p | tmpfs)
echo "[WARN] Filesystem type '$fstype' under $TMPDIR may not support swapfiles. Skipping." >&2
;;
btrfs)
echo "[WARN] btrfs swapfiles require special setup; skipping auto-swapfile under $TMPDIR." >&2
;;
*)
if ! sudo_noninteractive_ok; then
echo "[WARN] sudo would prompt; skipping swap setup to avoid hang." >&2
else
# Create only if missing (or size change desired)
if [[ ! -f "$SWAPFILE" ]]; then
rm -f "$SWAPFILE"
if ! fallocate -l "${SWAPSIZE_GB}G" "$SWAPFILE" 2>/dev/null; then
dd if=/dev/zero of="$SWAPFILE" bs=1M count=$((SWAPSIZE_GB * 1024)) status=progress
fi
chmod 600 "$SWAPFILE"
mkswap "$SWAPFILE" >/dev/null 2>&1 || true
fi
# Enable swap from this file (non-interactive, never hangs)
if ! swapfile_already_on; then
if sudo -n swapon "$SWAPFILE" 2>/dev/null; then
echo "[INFO] Swap enabled from $SWAPFILE" >&2
else
echo "[WARN] Could not enable swap (disallowed/error)." >&2
fi
fi
fi
;;
esac
fi
else
echo "[INFO] Swap setup skipped (SKIP_SWAP=1)." >&2
fi
maybe_fix_user_bus_env() {
# If VS Code didn't give us a session env, try the conventional systemd user bus path.
local uid xdg bus
uid="$(id -u)"
xdg="${XDG_RUNTIME_DIR:-/run/user/$uid}"
bus="${DBUS_SESSION_BUS_ADDRESS:-unix:path=$xdg/bus}"
# Only export if the bus socket actually exists.
if [[ -d "$xdg" && -S "$xdg/bus" ]]; then
export XDG_RUNTIME_DIR="$xdg"
export DBUS_SESSION_BUS_ADDRESS="$bus"
return 0
fi
return 1
}
can_use_systemd_user() {
command -v systemd-run >/dev/null 2>&1 || return 1
maybe_fix_user_bus_env >/dev/null 2>&1 || true
# Quick non-hanging probe: if this can't connect, don't use systemd-run.
timeout 1s systemd-run --user --quiet --scope true >/dev/null 2>&1
}
if [[ "$SKIP_LIMITS" != "1" ]] && can_use_systemd_user; then
if systemd-run --user --quiet --collect --pipe \
--unit="$UNIT" \
-p "MemoryHigh=${CLANGD_MEMORY_HIGH:-4G}" \
-p "MemoryMax=${CLANGD_MEMORY_MAX:-8G}" \
-p "MemorySwapMax=${CLANGD_MEMORY_SWAP_MAX:-16G}" \
-p "IOWeight=${CLANGD_IO_WEIGHT:-10}" \
-p "IOSchedulingClass=${CLANGD_IO_SCHEDULING_CLASS:-idle}" \
-p "IOSchedulingPriority=${CLANGD_IO_SCHEDULING_PRIORITY:-7}" \
-- "$CLANGD" "${FINAL_ARGS[@]}"; then
exit 0
else
echo "[WARN] systemd-run --user failed; falling back to direct clangd." >&2
fi
elif [[ "$SKIP_LIMITS" == "1" ]]; then
echo "[INFO] Resource limits disabled (CLANGD_NO_LIMITS=1). Running clangd without systemd limits." >&2
else
echo "[WARN] systemd-run not available (common in containers). Running clangd without systemd limits." >&2
fi
# Fallback: run clangd directly (used when systemd-run is unavailable, limits are disabled, or systemd-run fails)
if command -v ionice >/dev/null 2>&1; then
exec ionice -c3 nice -n 19 "$CLANGD" "${FINAL_ARGS[@]}"
else
exec nice -n 19 "$CLANGD" "${FINAL_ARGS[@]}"
fi