diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9521810 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +* text=auto +*.sh text eol=lf +*.bash text eol=lf \ No newline at end of file diff --git a/tools/common.just b/tools/common.just index 20cbcda..b4bcc16 100644 --- a/tools/common.just +++ b/tools/common.just @@ -69,18 +69,19 @@ run-w: check-dev-tools: @echo "Checking linting tools:" - @which clang-format >/dev/null 2>&1 && echo "✓ clang-format" || echo "✗ clang-format" - @which clang-tidy >/dev/null 2>&1 && echo "✓ clang-tidy" || echo "✗ clang-tidy" - @which cppcheck >/dev/null 2>&1 && echo "✓ cppcheck" || echo "✗ cppcheck" - @which cpplint >/dev/null 2>&1 && echo "✓ cpplint" || echo "✗ cpplint" - @which cmake-format >/dev/null 2>&1 && echo "✓ cmake-format" || echo "✗ cmake-format" - @which pipx >/dev/null 2>&1 && echo "✓ pipx" || echo "✗ pipx" + @which clang-format >/dev/null 2>&1 && echo "[OK] clang-format" || echo "[X] clang-format" + @which clang-tidy >/dev/null 2>&1 && echo "[OK] clang-tidy" || echo "[X] clang-tidy" + @which cppcheck >/dev/null 2>&1 && echo "[OK] cppcheck" || echo "[X] cppcheck" + @which cpplint >/dev/null 2>&1 && echo "[OK] cpplint" || echo "[X] cpplint" + @which cmake-format >/dev/null 2>&1 && echo "[OK] cmake-format" || echo "[X] cmake-format" + @which pipx >/dev/null 2>&1 && echo "[OK] pipx" || echo "[X] pipx" install-dev-tools: chmod +x ./tools/install_dev_tools.sh ./tools/install_dev_tools.sh upgrade-python-tools: + @which pipx >/dev/null 2>&1 || (echo "[X] pipx not found. Run 'just install-dev-tools' first." && exit 1) pipx upgrade cpplint pipx upgrade cmakelang diff --git a/tools/install_dev_tools.sh b/tools/install_dev_tools.sh index efe10cf..1c8726e 100644 --- a/tools/install_dev_tools.sh +++ b/tools/install_dev_tools.sh @@ -1,70 +1,517 @@ #!/bin/bash set -e -echo "=== Installing C++ Development Tools ===" +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color -if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then +echo -e "${GREEN}=== C++ Development Tools Installer ===${NC}" +echo "" + +# ============================================================================ +# OS Detection +# ============================================================================ +echo -e "${CYAN}=== Detecting Operating System ===${NC}" +echo "OSTYPE: $OSTYPE" +echo "uname: $(uname -s 2>/dev/null || echo 'N/A')" + +IS_WINDOWS=false +IS_WSL=false +IS_LINUX=false + +# Check for WSL first (highest priority) +if grep -qEi "(Microsoft|WSL)" /proc/version 2>/dev/null; then + IS_WSL=true + echo -e "${GREEN}[OK] WSL detected - Linux running inside Windows${NC}" +# Detect Windows (Git Bash, MSYS, Cygwin) +elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then + IS_WINDOWS=true +elif [[ "$(uname -s 2>/dev/null)" == MINGW* || "$(uname -s 2>/dev/null)" == MSYS* || "$(uname -s 2>/dev/null)" == CYGWIN* ]]; then + IS_WINDOWS=true +elif [[ -n "$WINDIR" || -n "$windir" ]]; then + IS_WINDOWS=true +# Detect Linux +elif [[ "$OSTYPE" == "linux-gnu"* ]]; then + IS_LINUX=true +fi + +if [[ "$IS_WSL" == "true" ]]; then + echo -e "${GREEN}[OK] WSL environment${NC}" +elif [[ "$IS_WINDOWS" == "true" ]]; then + echo -e "${GREEN}[OK] Windows detected (Git Bash/MSYS)${NC}" + + # Check for admin rights on Windows if ! net session > /dev/null 2>&1; then - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo -e "${RED}!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" echo "ERROR: This script MUST be run as ADMINISTRATOR on Windows." - echo "Please restart Git Bash/VS Code as Administrator and try again." - echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + echo "Please restart Git Bash as Administrator and try again." + echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!${NC}" exit 1 fi + echo -e "${GREEN}[OK] Running as Administrator${NC}" +elif [[ "$IS_LINUX" == "true" ]]; then + echo -e "${GREEN}[OK] Linux detected${NC}" +else + echo -e "${RED}[X] Unknown OS: $OSTYPE${NC}" + echo "Please install dependencies manually." + exit 1 fi -echo "=== Detecting OS ===" +echo "" -# Install OS-specific dependencies (pipx, curl, build tools, LLVM, Cmake, cppcheck) -if [[ "$OSTYPE" == "linux-gnu"* ]]; then - echo "Linux detected (Ubuntu/Debian style)" - sudo apt-get update - sudo apt-get install -y pipx curl build-essential clang-format clang-tidy cppcheck cmake -elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then - echo "Windows detected (Git Bash/MSYS)" +# ============================================================================ +# Check Prerequisites +# ============================================================================ +echo -e "${CYAN}=== Checking Prerequisites ===${NC}" - if ! command -v pipx &> /dev/null; then - echo "Installing pipx via python..." - python -m pip install --user pipx +# Check Python +if ! command -v python &> /dev/null && ! command -v python3 &> /dev/null; then + echo -e "${RED}[X] Python not found!${NC}" + if [[ "$IS_WINDOWS" == "true" ]]; then + echo "Install Python from: https://www.python.org/downloads/" + echo "Or use: choco install python -y" + elif [[ "$IS_LINUX" == "true" || "$IS_WSL" == "true" ]]; then + echo "Install with: sudo apt-get install python3 python3-pip" + elif [[ "$IS_MACOS" == "true" ]]; then + echo "Install with: brew install python3" fi + exit 1 +else + PYTHON_CMD=$(command -v python3 2>/dev/null || command -v python) + echo -e "${GREEN}[OK] Python found: $($PYTHON_CMD --version)${NC}" +fi - choco install llvm -y - choco install cmake -y - choco install cppcheck -y +# Check pip module +if ! $PYTHON_CMD -m pip --version &> /dev/null; then + echo -e "${YELLOW}⚠ pip module not found, installing...${NC}" + if [[ "$IS_LINUX" == "true" || "$IS_WSL" == "true" ]]; then + if command -v apt-get &> /dev/null; then + ${SUDO_CMD:-sudo} apt-get install -y python3-pip python3-venv + elif command -v dnf &> /dev/null; then + ${SUDO_CMD:-sudo} dnf install -y python3-pip + elif command -v yum &> /dev/null; then + ${SUDO_CMD:-sudo} yum install -y python3-pip + elif command -v pacman &> /dev/null; then + ${SUDO_CMD:-sudo} pacman -S --noconfirm python-pip + elif command -v zypper &> /dev/null; then + ${SUDO_CMD:-sudo} zypper install -y python3-pip + fi + fi + + # Verify pip is now available + if ! $PYTHON_CMD -m pip --version &> /dev/null; then + echo -e "${RED}[X] Failed to install pip${NC}" + exit 1 + fi + echo -e "${GREEN}[OK] pip installed successfully${NC}" else - echo "Unknown OS: $OSTYPE. Please install dependencies manually." - exit 1 + echo -e "${GREEN}[OK] pip found${NC}" fi -if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then - PYTHON_SCRIPTS=$(python -c "import os, sysconfig; print(sysconfig.get_path('scripts',f'{os.name}_user'))") +# Check Chocolatey on Windows +if [[ "$IS_WINDOWS" == "true" ]]; then + if ! command -v choco &> /dev/null; then + echo -e "${YELLOW}⚠ Chocolatey not found${NC}" + echo "Installing Chocolatey..." + powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" + export PATH="$PATH:/c/ProgramData/chocolatey/bin" + else + echo -e "${GREEN}[OK] Chocolatey found${NC}" + fi +fi + +echo "" + +# ============================================================================ +# Install System Dependencies +# ============================================================================ +echo -e "${CYAN}=== Installing System Dependencies ===${NC}" + +if [[ "$IS_WINDOWS" == "true" ]]; then + # LLVM (clang-format, clang-tidy) + if ! command -v clang-format &> /dev/null; then + echo "Installing LLVM..." + choco install llvm -y + else + echo -e "${GREEN}[OK] LLVM already installed${NC}" + fi + + # CMake + if ! command -v cmake &> /dev/null; then + echo "Installing CMake..." + choco install cmake -y + else + echo -e "${GREEN}[OK] CMake already installed${NC}" + fi + + # cppcheck + if ! command -v cppcheck &> /dev/null; then + echo "Installing cppcheck..." + choco install cppcheck -y + else + echo -e "${GREEN}[OK] cppcheck already installed${NC}" + fi + +elif [[ "$IS_LINUX" == "true" || "$IS_WSL" == "true" ]]; then + # Detect package manager + if command -v apt-get &> /dev/null; then + echo "Detected: Debian/Ubuntu (apt-get)" + + # Check if we need sudo + SUDO_CMD="" + if [ "$EUID" -ne 0 ]; then + if command -v sudo &> /dev/null; then + SUDO_CMD="sudo" + else + echo -e "${RED}[X] This script requires root privileges or sudo${NC}" + exit 1 + fi + fi + + echo "Updating package lists..." + $SUDO_CMD apt-get update -qq + + PACKAGES_TO_INSTALL=() + + command -v curl &> /dev/null || PACKAGES_TO_INSTALL+=(curl) + command -v g++ &> /dev/null || PACKAGES_TO_INSTALL+=(build-essential) + command -v clang-format &> /dev/null || PACKAGES_TO_INSTALL+=(clang-format) + command -v clang-tidy &> /dev/null || PACKAGES_TO_INSTALL+=(clang-tidy) + command -v cppcheck &> /dev/null || PACKAGES_TO_INSTALL+=(cppcheck) + command -v cmake &> /dev/null || PACKAGES_TO_INSTALL+=(cmake) + command -v pipx &> /dev/null || PACKAGES_TO_INSTALL+=(pipx) + dpkg -l | grep -q python3-full || PACKAGES_TO_INSTALL+=(python3-full) + + if [ ${#PACKAGES_TO_INSTALL[@]} -gt 0 ]; then + echo "Installing: ${PACKAGES_TO_INSTALL[*]}" + $SUDO_CMD apt-get install -y "${PACKAGES_TO_INSTALL[@]}" + else + echo -e "${GREEN}[OK] All system packages already installed${NC}" + fi + + elif command -v dnf &> /dev/null; then + echo "Detected: Fedora/RHEL (dnf)" + + SUDO_CMD="" + if [ "$EUID" -ne 0 ]; then + if command -v sudo &> /dev/null; then + SUDO_CMD="sudo" + else + echo -e "${RED}[X] This script requires root privileges or sudo${NC}" + exit 1 + fi + fi + + PACKAGES_TO_INSTALL=() + + command -v curl &> /dev/null || PACKAGES_TO_INSTALL+=(curl) + command -v g++ &> /dev/null || PACKAGES_TO_INSTALL+=(gcc-c++ make) + command -v clang-format &> /dev/null || PACKAGES_TO_INSTALL+=(clang-tools-extra) + command -v cppcheck &> /dev/null || PACKAGES_TO_INSTALL+=(cppcheck) + command -v cmake &> /dev/null || PACKAGES_TO_INSTALL+=(cmake) + + if [ ${#PACKAGES_TO_INSTALL[@]} -gt 0 ]; then + echo "Installing: ${PACKAGES_TO_INSTALL[*]}" + $SUDO_CMD dnf install -y "${PACKAGES_TO_INSTALL[@]}" + else + echo -e "${GREEN}[OK] All system packages already installed${NC}" + fi + + elif command -v yum &> /dev/null; then + echo "Detected: CentOS/RHEL (yum)" + + SUDO_CMD="" + if [ "$EUID" -ne 0 ]; then + if command -v sudo &> /dev/null; then + SUDO_CMD="sudo" + else + echo -e "${RED}[X] This script requires root privileges or sudo${NC}" + exit 1 + fi + fi + + PACKAGES_TO_INSTALL=() + + command -v curl &> /dev/null || PACKAGES_TO_INSTALL+=(curl) + command -v g++ &> /dev/null || PACKAGES_TO_INSTALL+=(gcc-c++ make) + command -v clang-format &> /dev/null || PACKAGES_TO_INSTALL+=(clang) + command -v cppcheck &> /dev/null || PACKAGES_TO_INSTALL+=(cppcheck) + command -v cmake &> /dev/null || PACKAGES_TO_INSTALL+=(cmake) + + if [ ${#PACKAGES_TO_INSTALL[@]} -gt 0 ]; then + echo "Installing: ${PACKAGES_TO_INSTALL[*]}" + $SUDO_CMD yum install -y "${PACKAGES_TO_INSTALL[@]}" + else + echo -e "${GREEN}[OK] All system packages already installed${NC}" + fi + + elif command -v pacman &> /dev/null; then + echo "Detected: Arch Linux (pacman)" + + SUDO_CMD="" + if [ "$EUID" -ne 0 ]; then + if command -v sudo &> /dev/null; then + SUDO_CMD="sudo" + else + echo -e "${RED}[X] This script requires root privileges or sudo${NC}" + exit 1 + fi + fi + + PACKAGES_TO_INSTALL=() + + command -v curl &> /dev/null || PACKAGES_TO_INSTALL+=(curl) + command -v g++ &> /dev/null || PACKAGES_TO_INSTALL+=(base-devel) + command -v clang-format &> /dev/null || PACKAGES_TO_INSTALL+=(clang) + command -v cppcheck &> /dev/null || PACKAGES_TO_INSTALL+=(cppcheck) + command -v cmake &> /dev/null || PACKAGES_TO_INSTALL+=(cmake) + + if [ ${#PACKAGES_TO_INSTALL[@]} -gt 0 ]; then + echo "Installing: ${PACKAGES_TO_INSTALL[*]}" + $SUDO_CMD pacman -S --noconfirm "${PACKAGES_TO_INSTALL[@]}" + else + echo -e "${GREEN}[OK] All system packages already installed${NC}" + fi + + elif command -v zypper &> /dev/null; then + echo "Detected: openSUSE (zypper)" + + SUDO_CMD="" + if [ "$EUID" -ne 0 ]; then + if command -v sudo &> /dev/null; then + SUDO_CMD="sudo" + else + echo -e "${RED}[X] This script requires root privileges or sudo${NC}" + exit 1 + fi + fi + + PACKAGES_TO_INSTALL=() + + command -v curl &> /dev/null || PACKAGES_TO_INSTALL+=(curl) + command -v g++ &> /dev/null || PACKAGES_TO_INSTALL+=(-t pattern devel_C_C++) + command -v clang-format &> /dev/null || PACKAGES_TO_INSTALL+=(clang) + command -v cppcheck &> /dev/null || PACKAGES_TO_INSTALL+=(cppcheck) + command -v cmake &> /dev/null || PACKAGES_TO_INSTALL+=(cmake) + + if [ ${#PACKAGES_TO_INSTALL[@]} -gt 0 ]; then + echo "Installing: ${PACKAGES_TO_INSTALL[*]}" + $SUDO_CMD zypper install -y "${PACKAGES_TO_INSTALL[@]}" + else + echo -e "${GREEN}[OK] All system packages already installed${NC}" + fi + + else + echo -e "${YELLOW}⚠ Unknown package manager${NC}" + echo "Please install these packages manually:" + echo " - curl" + echo " - build-essential / gcc-c++ / base-devel" + echo " - clang-format" + echo " - clang-tidy" + echo " - cppcheck" + echo " - cmake" + fi +fi + +echo "" + +# ============================================================================ +# Setup Python Environment +# ============================================================================ +echo -e "${CYAN}=== Setting up Python Environment ===${NC}" + +# Ensure pipx is available and configured +if command -v pipx &> /dev/null; then + echo -e "${GREEN}[OK] pipx found${NC}" + pipx ensurepath &> /dev/null || true + export PATH="$PATH:$HOME/.local/bin" +else + echo -e "${YELLOW}⚠ pipx not found, will use pip with --user flag${NC}" +fi + +# Update PATH for user-installed Python packages +if [[ "$IS_WINDOWS" == "true" ]]; then + PYTHON_SCRIPTS=$($PYTHON_CMD -c "import os, sysconfig; print(sysconfig.get_path('scripts', f'{os.name}_user'))" 2>/dev/null || echo "$HOME/AppData/Roaming/Python/Scripts") export PATH="$PATH:$PYTHON_SCRIPTS:$HOME/.local/bin" else export PATH="$PATH:$HOME/.local/bin" fi -# Install Conan +echo "" + +# ============================================================================ +# Install Python Tools +# ============================================================================ +echo -e "${CYAN}=== Installing Python-based Tools ===${NC}" + +# Helper function to install Python packages +install_python_tool() { + local tool_name=$1 + local package_name=$2 + local check_cmd=$3 + + if eval "$check_cmd" &> /dev/null; then + echo -e "${GREEN}[OK] $tool_name already installed${NC}" + return 0 + fi + + echo "Installing $tool_name..." + + # Try pipx first (preferred for Ubuntu 24.04+) + if command -v pipx &> /dev/null; then + if pipx list 2>/dev/null | grep -q "$package_name"; then + echo -e "${GREEN}[OK] $tool_name already installed via pipx${NC}" + return 0 + fi + pipx install "$package_name" &> /dev/null && return 0 + fi + + # Fallback to pip with --user + if $PYTHON_CMD -m pip install --user "$package_name" --quiet 2>/dev/null; then + return 0 + fi + + # Last resort: try with --break-system-packages (not recommended but works) + echo -e "${YELLOW}⚠ Using --break-system-packages flag${NC}" + $PYTHON_CMD -m pip install --break-system-packages "$package_name" --quiet +} + +# Conan if ! command -v conan &> /dev/null; then echo "Installing Conan..." - pipx install conan --quiet - export PATH="$PATH:$HOME/.local/bin" - conan profile detect --force + install_python_tool "Conan" "conan" "command -v conan" + if command -v conan &> /dev/null; then + conan profile detect --force 2>/dev/null || true + fi +else + echo -e "${GREEN}[OK] Conan already installed${NC}" + conan profile detect --force 2>/dev/null || true +fi + +# cpplint +if command -v cpplint &> /dev/null || $PYTHON_CMD -c 'import cpplint' 2>/dev/null; then + echo -e "${GREEN}[OK] cpplint already installed${NC}" +else + echo "Installing cpplint..." + install_python_tool "cpplint" "cpplint" "command -v cpplint" +fi + +# cmakelang (cmake-format, cmake-lint) +if command -v cmake-format &> /dev/null || $PYTHON_CMD -c 'import cmakelang' 2>/dev/null; then + echo -e "${GREEN}[OK] cmakelang already installed${NC}" +else + echo "Installing cmakelang..." + install_python_tool "cmakelang" "cmakelang" "command -v cmake-format" +fi + +echo "" + +# ============================================================================ +# Install Just Command Runner +# ============================================================================ +echo -e "${CYAN}=== Installing Just Command Runner ===${NC}" + +if ! command -v just &> /dev/null; then + if [[ "$IS_WINDOWS" == "true" ]]; then + echo "Installing just via Chocolatey..." + choco install just -y + elif [[ "$IS_LINUX" == "true" || "$IS_WSL" == "true" ]]; then + if command -v snap &> /dev/null; then + echo "Installing just via snap..." + ${SUDO_CMD:-sudo} snap install just --classic + elif command -v cargo &> /dev/null; then + echo "Installing just via cargo..." + cargo install just + else + echo -e "${YELLOW}⚠ Neither snap nor cargo found${NC}" + echo "Installing just from GitHub releases..." + JUST_VERSION="1.36.0" + ARCH=$(uname -m) + if [[ "$ARCH" == "x86_64" ]]; then + JUST_ARCH="x86_64" + elif [[ "$ARCH" == "aarch64" ]]; then + JUST_ARCH="aarch64" + else + echo -e "${RED}[X] Unsupported architecture: $ARCH${NC}" + echo "Please install just manually from: https://github.com/casey/just" + JUST_VERSION="" + fi + + if [[ -n "$JUST_VERSION" ]]; then + JUST_URL="https://github.com/casey/just/releases/download/${JUST_VERSION}/just-${JUST_VERSION}-${JUST_ARCH}-unknown-linux-musl.tar.gz" + echo "Downloading from: $JUST_URL" + curl -L "$JUST_URL" | tar xz -C /tmp + ${SUDO_CMD:-sudo} mv /tmp/just /usr/local/bin/just + ${SUDO_CMD:-sudo} chmod +x /usr/local/bin/just + fi + fi + fi +else + echo -e "${GREEN}[OK] just already installed${NC}" fi -# Install Python-based tools via pipx -echo "Installing Python linting tools via pipx..." -pipx install cpplint --quiet || true -pipx install cmakelang --quiet || true +echo "" +# ============================================================================ +# Verify Installation +# ============================================================================ +echo -e "${CYAN}=== Verifying Installation ===${NC}" echo "" -echo "All development tools installed!" + +FAILED=0 + +check_tool() { + local tool=$1 + local check_cmd=$2 + + if eval "$check_cmd" &> /dev/null; then + echo -e "${GREEN}[OK]${NC} $tool" + else + echo -e "${RED}[X]${NC} $tool - NOT FOUND" + FAILED=1 + fi +} + +check_tool "CMake" "command -v cmake" +check_tool "Conan" "command -v conan" +check_tool "clang-format" "command -v clang-format" +check_tool "clang-tidy" "command -v clang-tidy" +check_tool "cppcheck" "command -v cppcheck" + +# Check Python tools - they might be in pipx or pip --user +if command -v cpplint &> /dev/null || $PYTHON_CMD -c 'import cpplint' 2>/dev/null; then + echo -e "${GREEN}[OK]${NC} cpplint" +else + echo -e "${RED}[X]${NC} cpplint - NOT FOUND" + FAILED=1 +fi + +if command -v cmake-format &> /dev/null || $PYTHON_CMD -c 'import cmakelang' 2>/dev/null; then + echo -e "${GREEN}[OK]${NC} cmakelang" +else + echo -e "${RED}[X]${NC} cmakelang - NOT FOUND" + FAILED=1 +fi + +check_tool "just" "command -v just" + echo "" -echo "Installed versions:" -conan --version | head -n1 -cmake --version | head -n1 -clang-format --version | head -n1 -clang-tidy --version | head -n1 -cppcheck --version | head -n1 -cpplint --version 2>&1 | head -n1 || echo "cpplint installed via pipx" -echo "Cmake-format version:" -cmake-format --version 2>&1 | head -n1 || echo "cmake-format not installed" \ No newline at end of file + +if [ $FAILED -eq 0 ]; then + echo -e "${GREEN}=== [OK] All tools installed successfully! ===${NC}" + echo "" + echo "Installed versions:" + cmake --version 2>/dev/null | head -n1 || echo "cmake: version check failed" + conan --version 2>/dev/null | head -n1 || echo "conan: version check failed" + clang-format --version 2>/dev/null | head -n1 || echo "clang-format: version check failed" + cppcheck --version 2>/dev/null | head -n1 || echo "cppcheck: version check failed" + echo "" + echo -e "${YELLOW}Note: You may need to restart your terminal for PATH changes to take effect.${NC}" +else + echo -e "${RED}=== [X] Some tools failed to install ===${NC}" + echo "Please check the errors above and try again." + exit 1 +fi \ No newline at end of file