|
| 1 | +#!/bin/bash |
| 2 | +set -e |
| 3 | + |
| 4 | +# Setup script for tree-sitter dependencies (Ubuntu/Debian) |
| 5 | +# Works for both GitHub Actions and devcontainer environments |
| 6 | +# |
| 7 | +# Dual-Environment Design: |
| 8 | +# - GitHub Actions: Runs as non-root user, auto-detects need for sudo |
| 9 | +# - Devcontainer: Can run as root (apt-install feature) or non-root (postCreateCommand) |
| 10 | +# - Auto-detection: Checks if running as root (id -u = 0), uses sudo if non-root |
| 11 | +# |
| 12 | +# Grammar building is delegated to tsdl (https://github.com/stackmystack/tsdl). |
| 13 | +# Configure grammars and versions in parsers.toml at the project root. |
| 14 | +# |
| 15 | +# Options: |
| 16 | +# --sudo: Force use of sudo (optional, auto-detected by default) |
| 17 | +# --cli: Install tree-sitter-cli via npm (optional) |
| 18 | +# --build: Build and install the tree-sitter C runtime from source when distro packages are missing (optional) |
| 19 | +# --tsdl-version VERSION: Pin tsdl release version (default: v2.0.0) |
| 20 | +# --workspace PATH: Workspace root path for informational/debugging purposes only |
| 21 | + |
| 22 | +SUDO="" |
| 23 | +INSTALL_CLI=false |
| 24 | +BUILD_FROM_SOURCE=false |
| 25 | +TSDL_VERSION="v2.0.0" |
| 26 | +WORKSPACE_ROOT="/workspaces/${PWD##*/}" |
| 27 | + |
| 28 | +while [[ $# -gt 0 ]]; do |
| 29 | + case $1 in |
| 30 | + --sudo) SUDO="sudo"; shift ;; |
| 31 | + --cli) INSTALL_CLI=true; shift ;; |
| 32 | + --build) BUILD_FROM_SOURCE=true; shift ;; |
| 33 | + --tsdl-version) TSDL_VERSION="$2"; shift 2 ;; |
| 34 | + --tsdl-version=*) TSDL_VERSION="${1#*=}"; shift ;; |
| 35 | + --workspace) WORKSPACE_ROOT="$2"; shift 2 ;; |
| 36 | + --workspace=*) WORKSPACE_ROOT="${1#*=}"; shift ;; |
| 37 | + *) echo "Unknown option: $1" >&2; shift ;; |
| 38 | + esac |
| 39 | +done |
| 40 | + |
| 41 | +# Auto-detect if we need sudo (running as non-root) |
| 42 | +if [ -z "$SUDO" ] && [ "$(id -u)" -ne 0 ]; then |
| 43 | + SUDO="sudo" |
| 44 | +fi |
| 45 | + |
| 46 | +echo "Configuration:" |
| 47 | +echo " Workspace root: $WORKSPACE_ROOT (informational only)" |
| 48 | +echo " Using sudo: $([ -n "$SUDO" ] && echo "yes" || echo "no")" |
| 49 | +echo " Install CLI: $INSTALL_CLI" |
| 50 | +echo " Build from source: $BUILD_FROM_SOURCE" |
| 51 | +echo " tsdl version: $TSDL_VERSION" |
| 52 | +echo "" |
| 53 | + |
| 54 | +have_cmd() { command -v "$1" >/dev/null 2>&1; } |
| 55 | + |
| 56 | +have_tree_sitter() { |
| 57 | + [ -f /usr/include/tree-sitter/api.h ] && return 0 |
| 58 | + [ -f /usr/local/include/tree-sitter/api.h ] && return 0 |
| 59 | + [ -f /usr/local/include/tree-sitter/lib/include/api.h ] && return 0 |
| 60 | + ldconfig -p 2>/dev/null | grep -q libtree-sitter && return 0 || return 1 |
| 61 | +} |
| 62 | + |
| 63 | +install_tree_sitter_from_source() { |
| 64 | + echo "[tree-sitter] Building runtime from source..." |
| 65 | + tmpdir=$(mktemp -d /tmp/tree-sitter-src-XXXX) |
| 66 | + trap 'rm -rf "$tmpdir"' EXIT |
| 67 | + git clone --depth 1 https://github.com/tree-sitter/tree-sitter.git "$tmpdir" || return 1 |
| 68 | + pushd "$tmpdir" >/dev/null || return 1 |
| 69 | + if ! make; then |
| 70 | + echo "[tree-sitter] ERROR: 'make' failed" >&2 |
| 71 | + popd >/dev/null |
| 72 | + return 1 |
| 73 | + fi |
| 74 | + $SUDO mkdir -p /usr/local/include/tree-sitter |
| 75 | + $SUDO cp -r lib/include/* /usr/local/include/tree-sitter/ || true |
| 76 | + $SUDO cp -a lib/libtree-sitter.* /usr/local/lib/ 2>/dev/null || true |
| 77 | + have_cmd ldconfig && $SUDO ldconfig || true |
| 78 | + popd >/dev/null |
| 79 | + echo "[tree-sitter] Runtime installed to /usr/local." |
| 80 | + return 0 |
| 81 | +} |
| 82 | + |
| 83 | +install_tsdl() { |
| 84 | + if have_cmd tsdl; then |
| 85 | + echo "[tsdl] Already installed: $(tsdl --version)" |
| 86 | + return 0 |
| 87 | + fi |
| 88 | + |
| 89 | + echo "[tsdl] Installing tsdl ${TSDL_VERSION}..." |
| 90 | + local arch |
| 91 | + arch="$(uname -m)" |
| 92 | + case "$arch" in |
| 93 | + x86_64) arch="x64" ;; |
| 94 | + aarch64) arch="arm64" ;; |
| 95 | + armv7l) arch="arm" ;; |
| 96 | + i686) arch="x86" ;; |
| 97 | + *) echo "[tsdl] ERROR: Unsupported architecture: $arch" >&2; return 1 ;; |
| 98 | + esac |
| 99 | + |
| 100 | + local os |
| 101 | + os="$(uname -s | tr '[:upper:]' '[:lower:]')" |
| 102 | + case "$os" in |
| 103 | + linux) os="linux" ;; |
| 104 | + darwin) os="macos" ;; |
| 105 | + *) echo "[tsdl] ERROR: Unsupported OS: $os" >&2; return 1 ;; |
| 106 | + esac |
| 107 | + |
| 108 | + local url="https://github.com/stackmystack/tsdl/releases/download/${TSDL_VERSION}/tsdl-${os}-${arch}.gz" |
| 109 | + local tmpbin |
| 110 | + tmpbin=$(mktemp /tmp/tsdl-XXXX) |
| 111 | + |
| 112 | + if ! wget -q "$url" -O "${tmpbin}.gz"; then |
| 113 | + echo "[tsdl] ERROR: Failed to download from $url" >&2 |
| 114 | + return 1 |
| 115 | + fi |
| 116 | + gunzip -f "${tmpbin}.gz" |
| 117 | + chmod +x "$tmpbin" |
| 118 | + $SUDO mv "$tmpbin" /usr/local/bin/tsdl |
| 119 | + echo "[tsdl] Installed: $(tsdl --version)" |
| 120 | +} |
| 121 | + |
| 122 | +# --- 1. System dependencies --- |
| 123 | +echo "Installing system dependencies..." |
| 124 | +$SUDO apt-get update -y |
| 125 | +if ! $SUDO apt-get install -y \ |
| 126 | + build-essential \ |
| 127 | + pkg-config \ |
| 128 | + $( [ "$BUILD_FROM_SOURCE" = false ] && echo "libtree-sitter-dev" ) \ |
| 129 | + wget \ |
| 130 | + gcc \ |
| 131 | + g++ \ |
| 132 | + make \ |
| 133 | + zlib1g-dev \ |
| 134 | + libssl-dev \ |
| 135 | + libreadline-dev \ |
| 136 | + libyaml-dev \ |
| 137 | + libxml2-dev \ |
| 138 | + libxslt1-dev \ |
| 139 | + libcurl4-openssl-dev \ |
| 140 | + software-properties-common \ |
| 141 | + libffi-dev; then |
| 142 | + echo "ERROR: apt-get failed to install required packages." >&2 |
| 143 | + exit 1 |
| 144 | +fi |
| 145 | + |
| 146 | +# --- 2. Tree-sitter runtime --- |
| 147 | +if [ "$BUILD_FROM_SOURCE" = true ]; then |
| 148 | + echo "[tree-sitter] --build specified; building runtime from source." |
| 149 | +fi |
| 150 | + |
| 151 | +if ! have_tree_sitter; then |
| 152 | + if [ "$BUILD_FROM_SOURCE" = true ]; then |
| 153 | + if ! install_tree_sitter_from_source; then |
| 154 | + echo "[tree-sitter] ERROR: Failed to build runtime. Aborting." >&2 |
| 155 | + exit 1 |
| 156 | + fi |
| 157 | + else |
| 158 | + echo "[tree-sitter] ERROR: Runtime (headers/libs) not found." >&2 |
| 159 | + echo "Install libtree-sitter-dev or re-run with --build." >&2 |
| 160 | + exit 1 |
| 161 | + fi |
| 162 | +fi |
| 163 | + |
| 164 | +# --- 3. tree-sitter-cli (optional) --- |
| 165 | +if [ "$INSTALL_CLI" = true ]; then |
| 166 | + echo "Installing tree-sitter-cli via npm..." |
| 167 | + $SUDO npm install -g tree-sitter-cli |
| 168 | +else |
| 169 | + echo "Skipping tree-sitter-cli (use --cli to install)" |
| 170 | +fi |
| 171 | + |
| 172 | +# --- 4. Install tsdl and build grammars --- |
| 173 | +install_tsdl |
| 174 | + |
| 175 | +echo "" |
| 176 | +echo "Building tree-sitter grammars via tsdl..." |
| 177 | +# Use parsers.toml from the project root if it exists, otherwise build defaults. |
| 178 | +# tsdl automatically reads parsers.toml in the current directory. |
| 179 | +if [ -f parsers.toml ]; then |
| 180 | + echo "[tsdl] Using parsers.toml config" |
| 181 | + $SUDO tsdl build --out-dir /usr/local/lib --progress plain |
| 182 | +else |
| 183 | + echo "[tsdl] No parsers.toml found; building default grammars: toml json bash rbs" |
| 184 | + $SUDO tsdl build toml json bash rbs --out-dir /usr/local/lib --progress plain |
| 185 | +fi |
| 186 | + |
| 187 | +$SUDO ldconfig || echo "WARNING: ldconfig failed" >&2 |
| 188 | + |
| 189 | +echo "" |
| 190 | +echo "tree-sitter setup complete!" |
| 191 | +echo "" |
| 192 | +echo "Detected library paths:" |
| 193 | + |
| 194 | +if [ -f /usr/lib/x86_64-linux-gnu/libtree-sitter.so.0 ]; then |
| 195 | + echo " TREE_SITTER_RUNTIME_LIB=/usr/lib/x86_64-linux-gnu/libtree-sitter.so.0" |
| 196 | +elif [ -f /usr/lib/x86_64-linux-gnu/libtree-sitter.so ]; then |
| 197 | + echo " TREE_SITTER_RUNTIME_LIB=/usr/lib/x86_64-linux-gnu/libtree-sitter.so" |
| 198 | +elif [ -f /usr/lib/libtree-sitter.so.0 ]; then |
| 199 | + echo " TREE_SITTER_RUNTIME_LIB=/usr/lib/libtree-sitter.so.0" |
| 200 | +elif [ -f /usr/lib/libtree-sitter.so ]; then |
| 201 | + echo " TREE_SITTER_RUNTIME_LIB=/usr/lib/libtree-sitter.so" |
| 202 | +else |
| 203 | + echo " WARNING: Could not find libtree-sitter runtime library!" |
| 204 | +fi |
| 205 | + |
| 206 | +echo "" |
| 207 | +echo "Grammar libraries:" |
| 208 | +for lib in /usr/local/lib/libtree-sitter-*.so; do |
| 209 | + [ -f "$lib" ] && echo " $lib" |
| 210 | +done |
0 commit comments