#!/bin/bash # check-toolchain.sh - Verify clang installation and build requirements # Checks for clang, required tools, and platform-specific dependencies set -e # ============================================================================= # Configuration # ============================================================================= # Minimum required versions MIN_CLANG_VERSION_MAJOR=6 MIN_CLANG_VERSION_MINOR=0 # Required tools REQUIRED_TOOLS=( "clang++" "clang" "make" "bash" ) # Platform-specific tools REQUIRED_TOOLS_LINUX=( "pkg-config" ) REQUIRED_TOOLS_MACOS=( "xcrun" ) REQUIRED_TOOLS_WIN32=( "windres" # For resource compilation ) # ============================================================================= # Utility Functions # ============================================================================= # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color print_success() { echo -e "${GREEN}✓${NC} $1" } print_warning() { echo -e "${YELLOW}⚠${NC} $1" } print_error() { echo -e "${RED}✗${NC} $1" } print_info() { echo -e "ℹ $1" } # ============================================================================= # Version Checking # ============================================================================= check_clang_version() { local clang_version local major minor if ! command -v clang++ &> /dev/null; then print_error "clang++ not found in PATH" return 1 fi clang_version=$(clang++ --version | head -n1) print_info "Found: $clang_version" # Extract version numbers (handles various clang version formats) if [[ $clang_version =~ ([0-9]+)\.([0-9]+) ]]; then major=${BASH_REMATCH[1]} minor=${BASH_REMATCH[2]} if [[ $major -gt $MIN_CLANG_VERSION_MAJOR ]] || \ [[ $major -eq $MIN_CLANG_VERSION_MAJOR && $minor -ge $MIN_CLANG_VERSION_MINOR ]]; then print_success "clang++ version $major.$minor meets minimum requirement ($MIN_CLANG_VERSION_MAJOR.$MIN_CLANG_VERSION_MINOR)" return 0 else print_error "clang++ version $major.$minor is below minimum requirement ($MIN_CLANG_VERSION_MAJOR.$MIN_CLANG_VERSION_MINOR)" return 1 fi else print_warning "Could not parse clang++ version, proceeding anyway" return 0 fi } # ============================================================================= # Tool Checking # ============================================================================= check_tool() { local tool=$1 local required=${2:-true} if command -v "$tool" &> /dev/null; then local version_info case "$tool" in "clang++"|"clang") version_info=$(command "$tool" --version 2>/dev/null | head -n1 || echo "version unknown") ;; "make") version_info=$(command "$tool" --version 2>/dev/null | head -n1 || echo "version unknown") ;; "pkg-config") version_info=$(command "$tool" --version 2>/dev/null || echo "version unknown") ;; *) version_info="available" ;; esac print_success "$tool found ($version_info)" return 0 else if [[ "$required" == "true" ]]; then print_error "$tool not found in PATH" return 1 else print_warning "$tool not found (optional)" return 0 fi fi } check_basic_tools() { local all_good=true print_info "Checking basic tools..." for tool in "${REQUIRED_TOOLS[@]}"; do if ! check_tool "$tool"; then all_good=false fi done if [[ "$all_good" == "true" ]]; then return 0 else return 1 fi } check_platform_tools() { local platform=$1 local all_good=true local tools_var case "$platform" in "linux") tools_var="REQUIRED_TOOLS_LINUX[@]" ;; "macos") tools_var="REQUIRED_TOOLS_MACOS[@]" ;; "win32") tools_var="REQUIRED_TOOLS_WIN32[@]" ;; *) print_warning "Unknown platform '$platform', skipping platform-specific tool checks" return 0 ;; esac print_info "Checking platform-specific tools for $platform..." # Use indirect reference to get the array local tools_array_name="$tools_var" if [[ -n "${!tools_array_name:-}" ]]; then local tools_array=("${!tools_array_name}") for tool in "${tools_array[@]}"; do if ! check_tool "$tool"; then all_good=false fi done fi if [[ "$all_good" == "true" ]]; then return 0 else return 1 fi } # ============================================================================= # Platform-Specific Checks # ============================================================================= check_macos_requirements() { print_info "Checking macOS-specific requirements..." # Check for Xcode command line tools if ! xcode-select -p &> /dev/null; then print_error "Xcode command line tools not installed" print_info "Install with: xcode-select --install" return 1 else print_success "Xcode command line tools installed" fi # Check for macOS SDK local sdk_path sdk_path=$(xcrun --show-sdk-path 2>/dev/null || echo "") if [[ -n "$sdk_path" && -d "$sdk_path" ]]; then print_success "macOS SDK found at $sdk_path" else print_warning "macOS SDK not found or not accessible" fi # Check for required frameworks (just check if headers exist) local frameworks=("Cocoa" "OpenGL" "Metal") for framework in "${frameworks[@]}"; do if [[ -d "$sdk_path/System/Library/Frameworks/$framework.framework" ]]; then print_success "$framework framework available" else print_warning "$framework framework not found" fi done return 0 } check_linux_requirements() { print_info "Checking Linux-specific requirements..." # Check for development packages local packages=("libx11-dev" "libgl1-mesa-dev" "libfreetype6-dev" "libfontconfig1-dev") local missing_packages=() for package in "${packages[@]}"; do if dpkg -l | grep -q "$package" 2>/dev/null; then print_success "$package installed" elif rpm -q "$package" &>/dev/null; then print_success "$package installed" else print_warning "$package not found (may be required)" missing_packages+=("$package") fi done if [[ ${#missing_packages[@]} -gt 0 ]]; then print_info "Missing packages (Ubuntu/Debian): ${missing_packages[*]}" print_info "Install with: sudo apt-get install ${missing_packages[*]}" fi return 0 } check_win32_requirements() { print_info "Checking Windows-specific requirements..." # Check for Windows SDK (approximate check) if [[ -n "${WINDOWSSDKDIR:-}" ]]; then print_success "Windows SDK environment detected" else print_warning "Windows SDK environment not detected" print_info "Make sure Visual Studio or Windows SDK is installed" fi return 0 } # ============================================================================= # Library Checks # ============================================================================= check_freetype_library() { local platform=$1 local arch=$2 print_info "Checking FreeType library availability..." case "$platform" in "macos") local freetype_lib="non-source/foreign/x64/libfreetype-mac.a" if [[ -f "$freetype_lib" ]]; then print_success "FreeType library found: $freetype_lib" else print_error "FreeType library not found: $freetype_lib" return 1 fi ;; "linux") if pkg-config --exists freetype2; then local freetype_version freetype_version=$(pkg-config --modversion freetype2) print_success "FreeType2 found via pkg-config (version $freetype_version)" else print_warning "FreeType2 not found via pkg-config" fi ;; "win32") local freetype_lib="non-source/foreign/x64/freetype.lib" if [[ -f "$freetype_lib" ]]; then print_success "FreeType library found: $freetype_lib" else print_error "FreeType library not found: $freetype_lib" return 1 fi ;; esac return 0 } # ============================================================================= # Main Checking Function # ============================================================================= check_build_environment() { local platform local arch local all_good=true echo "=== 4coder Build Environment Check ===" echo # Detect platform if command -v "$(dirname "$0")/detect-platform.sh" &> /dev/null; then platform=$($(dirname "$0")/detect-platform.sh detect) arch=$($(dirname "$0")/detect-platform.sh arch) print_info "Detected platform: $platform" print_info "Detected architecture: $arch" else print_warning "Platform detection script not found, assuming current platform" platform="unknown" arch="unknown" fi echo # Check clang version if ! check_clang_version; then all_good=false fi echo # Check basic tools if ! check_basic_tools; then all_good=false fi echo # Check platform-specific tools if ! check_platform_tools "$platform"; then all_good=false fi echo # Check platform-specific requirements case "$platform" in "macos") if ! check_macos_requirements; then all_good=false fi ;; "linux") if ! check_linux_requirements; then all_good=false fi ;; "win32") if ! check_win32_requirements; then all_good=false fi ;; esac echo # Check libraries if ! check_freetype_library "$platform" "$arch"; then all_good=false fi echo # Final result if [[ "$all_good" == "true" ]]; then print_success "Build environment check passed!" print_info "System is ready for 4coder compilation" return 0 else print_error "Build environment check failed" print_info "Some requirements are missing. Please install missing components and try again." return 1 fi } # ============================================================================= # Main execution # ============================================================================= main() { local command="${1:-check}" case "$command" in "check") check_build_environment ;; "clang") check_clang_version ;; "tools") check_basic_tools ;; "help"|"-h"|"--help") echo "Usage: $0 [check|clang|tools|help]" echo "" echo "Commands:" echo " check - Run full build environment check (default)" echo " clang - Check clang version only" echo " tools - Check basic tools only" echo " help - Show this help message" ;; *) echo "Error: Unknown command '$command'" >&2 echo "Run '$0 help' for usage information" >&2 return 1 ;; esac } # Only run main if script is executed directly (not sourced) if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi