|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +set -e |
| 4 | + |
| 5 | +REPO="cs01/llvm-project" |
| 6 | +DEFAULT_INSTALL_DIR="$HOME/.local/null-safe-clang" |
| 7 | + |
| 8 | +GREEN='\033[0;32m' |
| 9 | +NC='\033[0m' |
| 10 | + |
| 11 | +prompt_yn() { |
| 12 | + local prompt="$1" |
| 13 | + local default="$2" |
| 14 | + local response |
| 15 | + |
| 16 | + if ! [ -t 0 ] && ! [ -e /dev/tty ]; then |
| 17 | + echo "No TTY available, using default: $default" |
| 18 | + [ "$default" = "y" ] && return 0 || return 1 |
| 19 | + fi |
| 20 | + |
| 21 | + if [ "$default" = "y" ]; then |
| 22 | + prompt="$prompt [Y/n]: " |
| 23 | + else |
| 24 | + prompt="$prompt [y/N]: " |
| 25 | + fi |
| 26 | + |
| 27 | + while true; do |
| 28 | + if [ -t 0 ]; then |
| 29 | + read -p "$prompt" response |
| 30 | + else |
| 31 | + printf "%s" "$prompt" > /dev/tty |
| 32 | + read response < /dev/tty |
| 33 | + fi |
| 34 | + response=${response:-$default} |
| 35 | + case "$response" in |
| 36 | + [Yy]|[Yy][Ee][Ss]) return 0 ;; |
| 37 | + [Nn]|[Nn][Oo]) return 1 ;; |
| 38 | + *) echo "Please answer yes or no." ;; |
| 39 | + esac |
| 40 | + done |
| 41 | +} |
| 42 | + |
| 43 | +detect_shell_profile() { |
| 44 | + case "$SHELL" in |
| 45 | + */zsh) |
| 46 | + echo "$HOME/.zshrc" |
| 47 | + ;; |
| 48 | + */bash) |
| 49 | + if [ -f "$HOME/.bashrc" ]; then |
| 50 | + echo "$HOME/.bashrc" |
| 51 | + elif [ -f "$HOME/.bash_profile" ]; then |
| 52 | + echo "$HOME/.bash_profile" |
| 53 | + else |
| 54 | + echo "$HOME/.profile" |
| 55 | + fi |
| 56 | + ;; |
| 57 | + *) |
| 58 | + if [ -f "$HOME/.zshrc" ]; then |
| 59 | + echo "$HOME/.zshrc" |
| 60 | + elif [ -f "$HOME/.bashrc" ]; then |
| 61 | + echo "$HOME/.bashrc" |
| 62 | + elif [ -f "$HOME/.bash_profile" ]; then |
| 63 | + echo "$HOME/.bash_profile" |
| 64 | + else |
| 65 | + echo "$HOME/.profile" |
| 66 | + fi |
| 67 | + ;; |
| 68 | + esac |
| 69 | +} |
| 70 | + |
| 71 | +INSTALL_DIR="${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" |
| 72 | + |
| 73 | +echo "" |
| 74 | +printf "${GREEN}Welcome to Nullsafe Clang installer${NC}\n" |
| 75 | +echo "" |
| 76 | + |
| 77 | +if [ -z "$SKIP_PROMPTS" ]; then |
| 78 | + echo "This will download and install Nullsafe Clang to:" |
| 79 | + echo " $INSTALL_DIR" |
| 80 | + echo "" |
| 81 | + |
| 82 | + if ! prompt_yn "Proceed with installation?" "y"; then |
| 83 | + echo "Installation cancelled." |
| 84 | + exit 0 |
| 85 | + fi |
| 86 | + echo "" |
| 87 | +fi |
| 88 | + |
| 89 | +if [ -z "$VERSION" ]; then |
| 90 | + echo "Fetching latest release version..." |
| 91 | + VERSION=$(curl -s https://api.github.com/repos/$REPO/releases | \ |
| 92 | + jq -r '[.[] | select(.tag_name | startswith("v")) | select(.prerelease == false)] | first | .tag_name') |
| 93 | + |
| 94 | + if [ -z "$VERSION" ]; then |
| 95 | + echo "Failed to fetch latest version. Set VERSION manually:" |
| 96 | + echo " VERSION=v0.1.6 $0" |
| 97 | + exit 1 |
| 98 | + fi |
| 99 | + echo "Latest version: $VERSION" |
| 100 | +fi |
| 101 | + |
| 102 | +echo "Detecting platform..." |
| 103 | +OS=$(uname -s) |
| 104 | +ARCH=$(uname -m) |
| 105 | + |
| 106 | +if [ "$OS" = "Linux" ] && [ "$ARCH" = "x86_64" ]; then |
| 107 | + RELEASE_FILE="clang-nullsafe-linux-x86_64.tar.gz" |
| 108 | + echo "Detected: Linux x86_64" |
| 109 | +elif [ "$OS" = "Darwin" ]; then |
| 110 | + RELEASE_FILE="clang-nullsafe-macos-universal.tar.gz" |
| 111 | + echo "Detected: macOS (Universal)" |
| 112 | + |
| 113 | + if [ ! -f "/opt/homebrew/opt/zstd/lib/libzstd.1.dylib" ] && [ ! -f "/usr/local/opt/zstd/lib/libzstd.1.dylib" ]; then |
| 114 | + echo "" |
| 115 | + echo "Missing required dependency: zstd" |
| 116 | + echo "" |
| 117 | + |
| 118 | + if command -v brew >/dev/null 2>&1; then |
| 119 | + if [ -z "$SKIP_PROMPTS" ] && prompt_yn "Install zstd via Homebrew now?" "y"; then |
| 120 | + echo "Installing zstd..." |
| 121 | + brew install zstd |
| 122 | + printf "${GREEN}zstd installed successfully${NC}\n" |
| 123 | + else |
| 124 | + echo "Please install zstd manually:" |
| 125 | + echo " brew install zstd" |
| 126 | + echo "" |
| 127 | + echo "Then run this installer again." |
| 128 | + exit 1 |
| 129 | + fi |
| 130 | + else |
| 131 | + echo "Homebrew not found. Please install Homebrew first:" |
| 132 | + echo " https://brew.sh" |
| 133 | + echo "" |
| 134 | + echo "Then run this installer again." |
| 135 | + exit 1 |
| 136 | + fi |
| 137 | + fi |
| 138 | +else |
| 139 | + echo "Unsupported platform: $OS $ARCH" |
| 140 | + echo "" |
| 141 | + echo "For WebAssembly/Playground, visit:" |
| 142 | + echo " https://cs01.github.io/llvm-project/" |
| 143 | + echo "" |
| 144 | + echo "Or download manually from:" |
| 145 | + echo " https://github.com/$REPO/releases/tag/$VERSION" |
| 146 | + exit 1 |
| 147 | +fi |
| 148 | + |
| 149 | +URL="https://github.com/$REPO/releases/download/$VERSION/$RELEASE_FILE" |
| 150 | + |
| 151 | +echo "Downloading Nullsafe Clang $VERSION..." |
| 152 | +echo " $URL" |
| 153 | + |
| 154 | +mkdir -p "$INSTALL_DIR" |
| 155 | +cd "$INSTALL_DIR" |
| 156 | + |
| 157 | +if command -v curl >/dev/null 2>&1; then |
| 158 | + curl -L "$URL" | tar xz --strip-components=0 |
| 159 | +elif command -v wget >/dev/null 2>&1; then |
| 160 | + wget -O - "$URL" | tar xz --strip-components=0 |
| 161 | +else |
| 162 | + echo "Neither curl nor wget found. Please install one of them." |
| 163 | + exit 1 |
| 164 | +fi |
| 165 | + |
| 166 | +if [ "$OS" = "Darwin" ]; then |
| 167 | + echo "Removing macOS quarantine attribute..." |
| 168 | + xattr -dr com.apple.quarantine "$INSTALL_DIR/bin" 2>/dev/null || true |
| 169 | + |
| 170 | + if ! command -v xcrun >/dev/null 2>&1; then |
| 171 | + echo "" |
| 172 | + echo "Xcode Command Line Tools not found" |
| 173 | + echo "" |
| 174 | + |
| 175 | + if [ -z "$SKIP_PROMPTS" ] && prompt_yn "Install Xcode Command Line Tools now?" "y"; then |
| 176 | + echo "Opening Xcode Command Line Tools installer..." |
| 177 | + echo "Please complete the installation dialog, then press Enter to continue." |
| 178 | + xcode-select --install 2>/dev/null || true |
| 179 | + read -p "Press Enter after installation completes..." |
| 180 | + |
| 181 | + if ! command -v xcrun >/dev/null 2>&1; then |
| 182 | + echo "Xcode Command Line Tools still not found." |
| 183 | + echo "Please ensure installation completed successfully." |
| 184 | + exit 1 |
| 185 | + fi |
| 186 | + else |
| 187 | + echo "Please install Xcode Command Line Tools:" |
| 188 | + echo " xcode-select --install" |
| 189 | + echo "" |
| 190 | + echo "Then run this installer again." |
| 191 | + exit 1 |
| 192 | + fi |
| 193 | + fi |
| 194 | + |
| 195 | + SDK_PATH=$(xcrun --show-sdk-path 2>/dev/null || echo "") |
| 196 | + if [ -n "$SDK_PATH" ]; then |
| 197 | + echo "Configuring macOS SDK integration..." |
| 198 | + |
| 199 | + mv "$INSTALL_DIR/bin/clang" "$INSTALL_DIR/bin/clang.real" |
| 200 | + mv "$INSTALL_DIR/bin/clang++" "$INSTALL_DIR/bin/clang++.real" 2>/dev/null || true |
| 201 | + |
| 202 | + cat > "$INSTALL_DIR/bin/clang" << 'WRAPPER_EOF' |
| 203 | +#!/bin/bash |
| 204 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 205 | +SDK_PATH="$(xcrun --show-sdk-path 2>/dev/null || echo "")" |
| 206 | +if [ -n "$SDK_PATH" ]; then |
| 207 | + exec "$SCRIPT_DIR/clang.real" -isysroot "$SDK_PATH" "$@" |
| 208 | +else |
| 209 | + exec "$SCRIPT_DIR/clang.real" "$@" |
| 210 | +fi |
| 211 | +WRAPPER_EOF |
| 212 | + |
| 213 | + cat > "$INSTALL_DIR/bin/clang++" << 'WRAPPER_EOF' |
| 214 | +#!/bin/bash |
| 215 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 216 | +SDK_PATH="$(xcrun --show-sdk-path 2>/dev/null || echo "")" |
| 217 | +if [ -n "$SDK_PATH" ]; then |
| 218 | + exec "$SCRIPT_DIR/clang++.real" -isysroot "$SDK_PATH" "$@" |
| 219 | +else |
| 220 | + exec "$SCRIPT_DIR/clang++.real" "$@" |
| 221 | +fi |
| 222 | +WRAPPER_EOF |
| 223 | + |
| 224 | + chmod +x "$INSTALL_DIR/bin/clang" "$INSTALL_DIR/bin/clang++" |
| 225 | + fi |
| 226 | +fi |
| 227 | + |
| 228 | +echo "" |
| 229 | +printf "${GREEN}Successfully installed to: $INSTALL_DIR${NC}\n" |
| 230 | +echo "" |
| 231 | + |
| 232 | +SHELL_PROFILE=$(detect_shell_profile) |
| 233 | +PATH_EXPORT="export PATH=\"$INSTALL_DIR/bin:\$PATH\"" |
| 234 | + |
| 235 | +if [ -z "$SKIP_PROMPTS" ]; then |
| 236 | + echo "To use Nullsafe Clang, we need to add it to your PATH." |
| 237 | + echo "Detected shell profile: $SHELL_PROFILE" |
| 238 | + echo "" |
| 239 | + |
| 240 | + if grep -q "$INSTALL_DIR/bin" "$SHELL_PROFILE" 2>/dev/null; then |
| 241 | + printf "${GREEN}PATH already configured in $SHELL_PROFILE${NC}\n" |
| 242 | + echo "" |
| 243 | + echo "If you just configured it, reload your shell:" |
| 244 | + echo " source $SHELL_PROFILE" |
| 245 | + else |
| 246 | + if prompt_yn "Add Nullsafe Clang to PATH in $SHELL_PROFILE?" "y"; then |
| 247 | + echo "" >> "$SHELL_PROFILE" |
| 248 | + echo "# Nullsafe Clang" >> "$SHELL_PROFILE" |
| 249 | + echo "$PATH_EXPORT" >> "$SHELL_PROFILE" |
| 250 | + printf "${GREEN}Added to $SHELL_PROFILE${NC}\n" |
| 251 | + echo "" |
| 252 | + |
| 253 | + if prompt_yn "Activate it now in this shell?" "y"; then |
| 254 | + export PATH="$INSTALL_DIR/bin:$PATH" |
| 255 | + printf "${GREEN}Activated! You can now use 'clang' in this shell.${NC}\n" |
| 256 | + else |
| 257 | + echo "Reload your shell later to activate:" |
| 258 | + echo " source $SHELL_PROFILE" |
| 259 | + echo "" |
| 260 | + echo "Or open a new terminal window." |
| 261 | + fi |
| 262 | + else |
| 263 | + echo "" |
| 264 | + echo "Skipped PATH configuration. To add it manually, add this to $SHELL_PROFILE:" |
| 265 | + echo " $PATH_EXPORT" |
| 266 | + fi |
| 267 | + fi |
| 268 | + echo "" |
| 269 | + |
| 270 | + if prompt_yn "Verify installation now?" "y"; then |
| 271 | + echo "" |
| 272 | + echo "Testing Nullsafe Clang..." |
| 273 | + |
| 274 | + if command -v clang >/dev/null 2>&1 && clang --version 2>&1 | grep -q "cs01/llvm-project"; then |
| 275 | + PATH_ACTIVE=true |
| 276 | + else |
| 277 | + export PATH="$INSTALL_DIR/bin:$PATH" |
| 278 | + PATH_ACTIVE=false |
| 279 | + fi |
| 280 | + |
| 281 | + if "$INSTALL_DIR/bin/clang" --version >/dev/null 2>&1; then |
| 282 | + echo "" |
| 283 | + "$INSTALL_DIR/bin/clang" --version | head -n 1 |
| 284 | + echo "" |
| 285 | + printf "${GREEN}Installation verified successfully!${NC}\n" |
| 286 | + echo "" |
| 287 | + |
| 288 | + if [ "$PATH_ACTIVE" = true ]; then |
| 289 | + echo "Try it now:" |
| 290 | + echo " echo 'void f(int* _Nullable p) { *p = 42; }' | clang -x c - -fsyntax-only" |
| 291 | + else |
| 292 | + echo "Try it now (using full path):" |
| 293 | + echo " echo 'void f(int* _Nullable p) { *p = 42; }' | $INSTALL_DIR/bin/clang -x c - -fsyntax-only" |
| 294 | + echo "" |
| 295 | + echo "Or reload your shell and use just 'clang'." |
| 296 | + fi |
| 297 | + else |
| 298 | + echo "Warning: clang --version failed" |
| 299 | + echo "There may be an issue with the installation." |
| 300 | + fi |
| 301 | + fi |
| 302 | +else |
| 303 | + echo "To use Nullsafe Clang, add this to $SHELL_PROFILE:" |
| 304 | + echo " $PATH_EXPORT" |
| 305 | +fi |
| 306 | + |
| 307 | +echo "" |
| 308 | +echo "Try the interactive WebAssembly playground:" |
| 309 | +echo " https://cs01.github.io/llvm-project/" |
| 310 | +echo "" |
0 commit comments