|
| 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 | +# This script installs ALL tree-sitter grammars for integration testing |
| 13 | +# |
| 14 | +# Options: |
| 15 | +# --sudo: Force use of sudo (optional, auto-detected by default) |
| 16 | +# --cli: Install tree-sitter-cli via npm (optional) |
| 17 | +# --build: Build and install the tree-sitter C runtime from source when distro packages are missing (optional) |
| 18 | +# --workspace PATH: Workspace root path for informational/debugging purposes only (defaults to /workspaces/tree_haver) |
| 19 | + |
| 20 | +SUDO="" |
| 21 | +INSTALL_CLI=false |
| 22 | +BUILD_FROM_SOURCE=false |
| 23 | +WORKSPACE_ROOT="/workspaces/markly-merge" |
| 24 | + |
| 25 | +# Parse arguments properly using while loop |
| 26 | +while [[ $# -gt 0 ]]; do |
| 27 | + case $1 in |
| 28 | + --sudo) |
| 29 | + SUDO="sudo" |
| 30 | + shift |
| 31 | + ;; |
| 32 | + --cli) |
| 33 | + INSTALL_CLI=true |
| 34 | + shift |
| 35 | + ;; |
| 36 | + --build) |
| 37 | + BUILD_FROM_SOURCE=true |
| 38 | + shift |
| 39 | + ;; |
| 40 | + --workspace) |
| 41 | + WORKSPACE_ROOT="$2" |
| 42 | + shift 2 |
| 43 | + ;; |
| 44 | + --workspace=*) |
| 45 | + WORKSPACE_ROOT="${1#*=}" |
| 46 | + shift |
| 47 | + ;; |
| 48 | + *) |
| 49 | + echo "Unknown option: $1" >&2 |
| 50 | + shift |
| 51 | + ;; |
| 52 | + esac |
| 53 | +done |
| 54 | + |
| 55 | +# Auto-detect if we need sudo (running as non-root) |
| 56 | +if [ -z "$SUDO" ] && [ "$(id -u)" -ne 0 ]; then |
| 57 | + SUDO="sudo" |
| 58 | +fi |
| 59 | + |
| 60 | +echo "Configuration:" |
| 61 | +echo " Workspace root: $WORKSPACE_ROOT (informational only)" |
| 62 | +echo " Using sudo: $([ -n "$SUDO" ] && echo "yes" || echo "no")" |
| 63 | +echo " Install CLI: $INSTALL_CLI" |
| 64 | +echo " Build from source: $BUILD_FROM_SOURCE" |
| 65 | +echo "" |
| 66 | + |
| 67 | +have_cmd() { command -v "$1" >/dev/null 2>&1; } |
| 68 | + |
| 69 | +have_tree_sitter() { |
| 70 | + [ -f /usr/include/tree-sitter/api.h ] && return 0 |
| 71 | + [ -f /usr/local/include/tree-sitter/api.h ] && return 0 |
| 72 | + [ -f /usr/local/include/tree-sitter/lib/include/api.h ] && return 0 |
| 73 | + ldconfig -p 2>/dev/null | grep -q libtree-sitter && return 0 || return 1 |
| 74 | +} |
| 75 | + |
| 76 | +install_tree_sitter_from_source() { |
| 77 | + echo "[ubuntu] Attempting to build and install tree-sitter from source..." |
| 78 | + tmpdir=$(mktemp -d /tmp/tree-sitter-src-XXXX) |
| 79 | + trap 'rm -rf "$tmpdir"' EXIT |
| 80 | + git clone --depth 1 https://github.com/tree-sitter/tree-sitter.git "$tmpdir" || return 1 |
| 81 | + pushd "$tmpdir" >/dev/null || return 1 |
| 82 | + if ! make; then |
| 83 | + echo "[ubuntu] ERROR: 'make' failed while building tree-sitter" >&2 |
| 84 | + popd >/dev/null |
| 85 | + return 1 |
| 86 | + fi |
| 87 | + |
| 88 | + $SUDO mkdir -p /usr/local/include/tree-sitter |
| 89 | + $SUDO cp -r lib/include/* /usr/local/include/tree-sitter/ || true |
| 90 | + $SUDO cp -a lib/libtree-sitter.* /usr/local/lib/ 2>/dev/null || true |
| 91 | + if have_cmd ldconfig; then |
| 92 | + $SUDO ldconfig || true |
| 93 | + fi |
| 94 | + |
| 95 | + popd >/dev/null |
| 96 | + echo "[ubuntu] tree-sitter built and installed to /usr/local (headers + libs)." |
| 97 | + return 0 |
| 98 | +} |
| 99 | + |
| 100 | +echo "Installing tree-sitter system library and dependencies..." |
| 101 | +$SUDO apt-get update -y |
| 102 | +# libtree-sitter-dev is optional when building from source via --build |
| 103 | +if ! $SUDO apt-get install -y \ |
| 104 | + build-essential \ |
| 105 | + pkg-config \ |
| 106 | + $( [ "$BUILD_FROM_SOURCE" = false ] && echo "libtree-sitter-dev" ) \ |
| 107 | + wget \ |
| 108 | + gcc \ |
| 109 | + g++ \ |
| 110 | + make \ |
| 111 | + zlib1g-dev \ |
| 112 | + libssl-dev \ |
| 113 | + libreadline-dev \ |
| 114 | + libyaml-dev \ |
| 115 | + libxml2-dev \ |
| 116 | + libxslt1-dev \ |
| 117 | + libcurl4-openssl-dev \ |
| 118 | + software-properties-common \ |
| 119 | + libffi-dev; then |
| 120 | + echo "ERROR: apt-get failed to install required packages." |
| 121 | + echo "Please check your network, package sources, and re-run this script." |
| 122 | + exit 1 |
| 123 | +fi |
| 124 | + |
| 125 | +# If the user requested a source-build, skip installing libtree-sitter-dev |
| 126 | +if [ "$BUILD_FROM_SOURCE" = true ]; then |
| 127 | + echo "[ubuntu] --build specified; skipping distro package 'libtree-sitter-dev' and building tree-sitter from source." |
| 128 | +fi |
| 129 | + |
| 130 | +# Ensure tree-sitter is available; if not, attempt to build from source |
| 131 | +if ! have_tree_sitter; then |
| 132 | + if [ "$BUILD_FROM_SOURCE" = true ]; then |
| 133 | + echo "[ubuntu] tree-sitter not found in system paths; attempting to build from source as requested (--build)." |
| 134 | + if ! install_tree_sitter_from_source; then |
| 135 | + echo "[ubuntu] ERROR: Failed to provide tree-sitter runtime/library. Aborting." >&2 |
| 136 | + exit 1 |
| 137 | + fi |
| 138 | + else |
| 139 | + echo "[ubuntu] ERROR: tree-sitter runtime (headers/libs) not found." |
| 140 | + echo "Install the appropriate distro package (e.g., libtree-sitter-dev) or re-run this script with --build to compile from source." |
| 141 | + exit 1 |
| 142 | + fi |
| 143 | +fi |
| 144 | + |
| 145 | +# Install tree-sitter CLI via npm (optional) |
| 146 | +if [ "$INSTALL_CLI" = true ]; then |
| 147 | + echo "Installing tree-sitter-cli via npm..." |
| 148 | + $SUDO npm install -g tree-sitter-cli |
| 149 | +else |
| 150 | + echo "Skipping tree-sitter-cli installation (use --cli flag to install)" |
| 151 | +fi |
| 152 | + |
| 153 | +# Install all tree-sitter grammars for integration testing |
| 154 | +GRAMMARS=("toml" "json" "jsonc" "bash") |
| 155 | + |
| 156 | +TMPDIR=$(mktemp -d) |
| 157 | +trap "rm -rf $TMPDIR" EXIT |
| 158 | + |
| 159 | +for grammar in "${GRAMMARS[@]}"; do |
| 160 | + echo "Building and installing tree-sitter-${grammar}..." |
| 161 | + cd "$TMPDIR" |
| 162 | + |
| 163 | + if ! wget -q "https://github.com/tree-sitter-grammars/tree-sitter-${grammar}/archive/refs/heads/master.zip" -O "${grammar}.zip"; then |
| 164 | + echo "ERROR: Failed to download tree-sitter-${grammar}" >&2 |
| 165 | + exit 1 |
| 166 | + fi |
| 167 | + |
| 168 | + if ! unzip -q "${grammar}.zip"; then |
| 169 | + echo "ERROR: Failed to unzip tree-sitter-${grammar}" >&2 |
| 170 | + exit 1 |
| 171 | + fi |
| 172 | + |
| 173 | + cd "tree-sitter-${grammar}-master" |
| 174 | + |
| 175 | + # Compile parser.c |
| 176 | + if ! gcc -fPIC -I./src -c src/parser.c -o parser.o; then |
| 177 | + echo "ERROR: Failed to compile parser.c for ${grammar}" >&2 |
| 178 | + exit 1 |
| 179 | + fi |
| 180 | + |
| 181 | + # Check if scanner exists (not all grammars have scanners) |
| 182 | + if [ -f src/scanner.c ]; then |
| 183 | + if ! gcc -fPIC -I./src -c src/scanner.c -o scanner.o; then |
| 184 | + echo "ERROR: Failed to compile scanner.c for ${grammar}" >&2 |
| 185 | + exit 1 |
| 186 | + fi |
| 187 | + OBJECTS="parser.o scanner.o" |
| 188 | + else |
| 189 | + OBJECTS="parser.o" |
| 190 | + fi |
| 191 | + |
| 192 | + # Link object files into shared library |
| 193 | + if ! gcc -shared -o "libtree-sitter-${grammar}.so" $OBJECTS; then |
| 194 | + echo "ERROR: Failed to link libtree-sitter-${grammar}.so" >&2 |
| 195 | + exit 1 |
| 196 | + fi |
| 197 | + |
| 198 | + # Install to system |
| 199 | + if ! $SUDO cp "libtree-sitter-${grammar}.so" /usr/local/lib/; then |
| 200 | + echo "ERROR: Failed to copy libtree-sitter-${grammar}.so to /usr/local/lib/" >&2 |
| 201 | + exit 1 |
| 202 | + fi |
| 203 | + |
| 204 | + echo " ✓ Installed tree-sitter-${grammar}" |
| 205 | +done |
| 206 | + |
| 207 | +if ! $SUDO ldconfig; then |
| 208 | + echo "WARNING: ldconfig failed, libraries may not be immediately available" >&2 |
| 209 | +fi |
| 210 | + |
| 211 | +echo "" |
| 212 | +echo "tree-sitter setup complete!" |
| 213 | +echo "" |
| 214 | +echo "Detected library paths:" |
| 215 | + |
| 216 | +# Detect and report tree-sitter runtime library location |
| 217 | +if [ -f /usr/lib/x86_64-linux-gnu/libtree-sitter.so.0 ]; then |
| 218 | + echo " TREE_SITTER_RUNTIME_LIB=/usr/lib/x86_64-linux-gnu/libtree-sitter.so.0" |
| 219 | +elif [ -f /usr/lib/x86_64-linux-gnu/libtree-sitter.so ]; then |
| 220 | + echo " TREE_SITTER_RUNTIME_LIB=/usr/lib/x86_64-linux-gnu/libtree-sitter.so" |
| 221 | +elif [ -f /usr/lib/libtree-sitter.so.0 ]; then |
| 222 | + echo " TREE_SITTER_RUNTIME_LIB=/usr/lib/libtree-sitter.so.0" |
| 223 | +elif [ -f /usr/lib/libtree-sitter.so ]; then |
| 224 | + echo " TREE_SITTER_RUNTIME_LIB=/usr/lib/libtree-sitter.so" |
| 225 | +else |
| 226 | + echo " WARNING: Could not find libtree-sitter runtime library!" |
| 227 | +fi |
| 228 | + |
| 229 | +echo "" |
| 230 | +echo "Grammar libraries:" |
| 231 | +for grammar in "${GRAMMARS[@]}"; do |
| 232 | + echo " TREE_SITTER_${grammar^^}_PATH=/usr/local/lib/libtree-sitter-${grammar}.so" |
| 233 | +done |
| 234 | + |
0 commit comments