Ground-Zerro / Phobos Public
Code Issues Pull requests Actions Releases View on GitHub ↗
15.4 KB bash
#!/bin/bash

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUILD_DIR="${SCRIPT_DIR}/build"
BIN_DIR="${SCRIPT_DIR}/bin"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

log_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

check_compiler() {
    local compiler=$1
    if command -v "$compiler" >/dev/null 2>&1; then
        log_success "Compiler found: $compiler ($(${compiler} --version | head -1))"
        return 0
    else
        log_warn "Compiler not found: $compiler"
        return 1
    fi
}

download_file() {
    local url=$1
    local dest=$2
    if command -v curl >/dev/null 2>&1; then
        curl -sL --max-time 60 --retry 3 -o "$dest" "$url"
    elif command -v wget >/dev/null 2>&1; then
        wget -q --timeout=60 --tries=3 -O "$dest" "$url"
    else
        log_error "Neither curl nor wget is available"
        return 1
    fi
}

download_file_with_fallback() {
    local path=$1
    local dest=$2
    local -a MIRRORS=(
        "http://archive.ubuntu.com/ubuntu/pool"
        "http://ru.archive.ubuntu.com/ubuntu/pool"
    )
    for mirror in "${MIRRORS[@]}"; do
        if download_file "${mirror}/${path}" "$dest" && [ -s "$dest" ]; then
            return 0
        fi
    done
    return 1
}

noble_repo_add() {
    echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu noble main universe" \
        | sudo tee /etc/apt/sources.list.d/noble-mips-cross.list >/dev/null
    printf 'Package: *\nPin: release n=noble\nPin-Priority: 100\n' \
        | sudo tee /etc/apt/preferences.d/noble-mips-cross >/dev/null
    sudo apt-get update -qq
}

noble_repo_remove() {
    sudo rm -f /etc/apt/sources.list.d/noble-mips-cross.list
    sudo rm -f /etc/apt/preferences.d/noble-mips-cross
    sudo apt-get update -qq
}

install_mips_from_noble() {
    log_info "Adding Ubuntu 24.04 (noble) repo for MIPS cross-compiler dependencies..."
    noble_repo_add

    local tmpdir
    tmpdir=$(mktemp -d)

    local -a PKGS=(
        "universe/g/gcc-12-cross/gcc-12-cross-base_12.3.0-17ubuntu1cross1_all.deb"
        "universe/g/gcc-12-cross-mipsen/gcc-12-cross-base-mipsen_12.3.0-17ubuntu1cross3_all.deb"
        "universe/b/binutils-mipsen/binutils-mips-linux-gnu_2.42-2ubuntu1cross5_amd64.deb"
        "universe/b/binutils-mipsen/binutils-mipsel-linux-gnu_2.42-2ubuntu1cross5_amd64.deb"
        "universe/g/gcc-12-cross-mipsen/cpp-12-mips-linux-gnu_12.3.0-17ubuntu1cross3_amd64.deb"
        "universe/g/gcc-12-cross-mipsen/cpp-12-mipsel-linux-gnu_12.3.0-17ubuntu1cross3_amd64.deb"
        "universe/g/gcc-12-cross-mipsen/gcc-12-mips-linux-gnu_12.3.0-17ubuntu1cross3_amd64.deb"
        "universe/g/gcc-12-cross-mipsen/gcc-12-mipsel-linux-gnu_12.3.0-17ubuntu1cross3_amd64.deb"
        "universe/c/cross-toolchain-base-mipsen/linux-libc-dev-mips-cross_6.8.0-25.25cross2_all.deb"
        "universe/c/cross-toolchain-base-mipsen/linux-libc-dev-mipsel-cross_6.8.0-25.25cross2_all.deb"
        "universe/c/cross-toolchain-base-mipsen/libc6-mips-cross_2.39-0ubuntu8cross2_all.deb"
        "universe/c/cross-toolchain-base-mipsen/libc6-dev-mips-cross_2.39-0ubuntu8cross2_all.deb"
        "universe/c/cross-toolchain-base-mipsen/libc6-mipsel-cross_2.39-0ubuntu8cross2_all.deb"
        "universe/c/cross-toolchain-base-mipsen/libc6-dev-mipsel-cross_2.39-0ubuntu8cross2_all.deb"
        "universe/g/gcc-12-cross-mipsen/libgcc-12-dev-mips-cross_12.3.0-17ubuntu1cross3_all.deb"
        "universe/g/gcc-12-cross-mipsen/libgcc-12-dev-mipsel-cross_12.3.0-17ubuntu1cross3_all.deb"
    )

    local failed=0
    for pkg_path in "${PKGS[@]}"; do
        local pkg_name
        pkg_name=$(basename "$pkg_path")
        log_info "Downloading ${pkg_name}..."
        if ! download_file_with_fallback "${pkg_path}" "${tmpdir}/${pkg_name}"; then
            log_error "Failed to download ${pkg_name}"
            failed=1
            break
        fi
    done

    if [ $failed -eq 0 ]; then
        log_info "Installing MIPS packages (noble repo active for dependency resolution)..."
        if sudo apt-get install -y --allow-downgrades "${tmpdir}"/*.deb; then
            for pair in "mips-linux-gnu" "mipsel-linux-gnu"; do
                if [ ! -f "/usr/bin/${pair}-gcc" ] && [ -f "/usr/bin/${pair}-gcc-12" ]; then
                    sudo ln -sf "/usr/bin/${pair}-gcc-12" "/usr/bin/${pair}-gcc"
                    log_info "Created symlink: /usr/bin/${pair}-gcc -> /usr/bin/${pair}-gcc-12"
                fi
            done
            rm -rf "$tmpdir"
            noble_repo_remove
            return 0
        fi
        log_error "apt install failed with noble repo"
        failed=1
    fi

    rm -rf "$tmpdir"
    noble_repo_remove
    return 1
}

install_mips_from_bootlin() {
    log_info "Trying Bootlin pre-built toolchains for MIPS..."
    local BOOTLIN_BASE="https://toolchains.bootlin.com/downloads/releases/toolchains"
    local INSTALL_DIR="/opt/cross"
    local tmpdir
    tmpdir=$(mktemp -d)

    mkdir -p "$INSTALL_DIR"

    local -a ARCH_LIST=(
        "mips32|mips32--uclibc--stable-2024.02-1|mips-buildroot-linux-uclibc|mips-linux-gnu"
        "mips32el|mips32el--uclibc--stable-2024.02-1|mipsel-buildroot-linux-uclibc|mipsel-linux-gnu"
    )

    for entry in "${ARCH_LIST[@]}"; do
        local arch_dir compiler_prefix tarball_base link_prefix
        arch_dir=$(echo "$entry" | cut -d'|' -f1)
        tarball_base=$(echo "$entry" | cut -d'|' -f2)
        compiler_prefix=$(echo "$entry" | cut -d'|' -f3)
        link_prefix=$(echo "$entry" | cut -d'|' -f4)
        local tarball="${tarball_base}.tar.bz2"

        log_info "Downloading Bootlin ${arch_dir} toolchain (~200 MB)..."
        if ! download_file "${BOOTLIN_BASE}/${arch_dir}/tarballs/${tarball}" "${tmpdir}/${tarball}"; then
            log_error "Failed to download Bootlin ${arch_dir} toolchain"
            rm -rf "$tmpdir"
            return 1
        fi

        log_info "Extracting ${arch_dir} toolchain..."
        tar -xjf "${tmpdir}/${tarball}" -C "$INSTALL_DIR"
        rm -f "${tmpdir}/${tarball}"

        local toolchain_dir
        toolchain_dir=$(ls -d "${INSTALL_DIR}/${tarball_base}" 2>/dev/null | head -1)
        if [ -z "$toolchain_dir" ]; then
            log_error "Extracted toolchain directory not found"
            rm -rf "$tmpdir"
            return 1
        fi

        for tool in gcc g++ ar ld strip nm objdump objcopy as ranlib; do
            local real_bin="${toolchain_dir}/bin/${compiler_prefix}-${tool}"
            [ -f "$real_bin" ] || continue
            printf '#!/bin/sh\nexec "%s" "$@"\n' "$real_bin" \
                | sudo tee "/usr/local/bin/${link_prefix}-${tool}" >/dev/null
            sudo chmod +x "/usr/local/bin/${link_prefix}-${tool}"
        done

        log_success "Bootlin ${arch_dir} toolchain installed to ${toolchain_dir}"
    done

    rm -rf "$tmpdir"
    return 0
}

install_cross_compilers() {
    log_info "Installing cross-compilers and dependencies..."

    if [ -f /etc/debian_version ]; then
        log_info "Detected Debian/Ubuntu system"
        sudo apt-get update

        local mips_ok=0
        if sudo apt-get install -y \
            gcc \
            gcc-mips-linux-gnu \
            gcc-mipsel-linux-gnu \
            gcc-aarch64-linux-gnu \
            gcc-arm-linux-gnueabihf \
            binutils-mips-linux-gnu \
            binutils-mipsel-linux-gnu \
            binutils-aarch64-linux-gnu \
            binutils-arm-linux-gnueabihf \
            libc6-dev \
            libc6-dev-mips-cross \
            libc6-dev-mipsel-cross \
            libc6-dev-arm64-cross \
            libc6-dev-armhf-cross 2>/dev/null; then
            mips_ok=1
        fi

        if [ $mips_ok -eq 0 ]; then
            log_warn "MIPS packages not found in current repos, trying Ubuntu 24.04 (noble) archives..."
            if ! install_mips_from_noble; then
                log_warn "Noble archive method failed, trying Bootlin pre-built toolchains..."
                if ! install_mips_from_bootlin; then
                    log_error "Failed to install MIPS cross-compilers from all sources"
                    log_error "Manual option: https://toolchains.bootlin.com/"
                fi
            fi

            sudo apt-get install -y \
                gcc \
                gcc-aarch64-linux-gnu \
                gcc-arm-linux-gnueabihf \
                binutils-aarch64-linux-gnu \
                binutils-arm-linux-gnueabihf \
                libc6-dev \
                libc6-dev-arm64-cross \
                libc6-dev-armhf-cross 2>/dev/null || true
        fi

        log_success "Cross-compilers installation complete"

    elif [ -f /etc/redhat-release ]; then
        log_info "Detected RedHat/CentOS/Fedora system"
        sudo yum install -y gcc gcc-mips64-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnu glibc-static || \
        sudo dnf install -y gcc gcc-mips64-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnu glibc-static
        log_success "Cross-compilers and dependencies installed"

    elif [ -f /etc/arch-release ]; then
        log_info "Detected Arch Linux system"
        sudo pacman -S --needed gcc mips-linux-gnu-gcc aarch64-linux-gnu-gcc arm-linux-gnueabihf-gcc
        log_success "Cross-compilers and dependencies installed"

    else
        log_error "Unsupported distribution. Please install cross-compilers manually."
        exit 1
    fi
}

build_for_arch() {
    local arch=$1
    local cc=$2
    local cflags=$3
    local output_name=$4
    local use_static=$5

    log_info "Building for ${arch}..."

    local build_arch_dir="${BUILD_DIR}/${arch}"
    mkdir -p "${build_arch_dir}"

    cd "${SCRIPT_DIR}"
    make clean >/dev/null 2>&1 || true

    local make_cmd="CC=\"${cc}\" EXTRA_CFLAGS=\"${cflags}\""
    if [ "$use_static" = "static" ]; then
        make_cmd="$make_cmd STATIC=1"
    fi

    if eval $make_cmd make -j$(nproc) 2>&1 | tee "${build_arch_dir}/build.log"; then
        if [ -f "${SCRIPT_DIR}/wg-obfuscator" ]; then
            mkdir -p "${BIN_DIR}"
            cp "${SCRIPT_DIR}/wg-obfuscator" "${BIN_DIR}/${output_name}"

            local size=$(ls -lh "${BIN_DIR}/${output_name}" | awk '{print $5}')
            local file_info=$(file "${BIN_DIR}/${output_name}" | cut -d: -f2)

            log_success "Build completed: ${output_name} (${size})"
            echo "           ${file_info}"

            echo "${arch}|SUCCESS|${size}|${file_info}" >> "${BUILD_DIR}/build_report.txt"
            return 0
        else
            log_error "Binary not found after build"
            echo "${arch}|FAILED|N/A|Binary not created" >> "${BUILD_DIR}/build_report.txt"
            return 1
        fi
    else
        log_error "Build failed for ${arch}"
        echo "${arch}|FAILED|N/A|Compilation error" >> "${BUILD_DIR}/build_report.txt"
        return 1
    fi
}

show_summary() {
    echo
    echo "========================================"
    echo "    BUILD SUMMARY"
    echo "========================================"
    echo

    if [ -f "${BUILD_DIR}/build_report.txt" ]; then
        printf "%-12s | %-10s | %-8s | %s\n" "ARCH" "STATUS" "SIZE" "DETAILS"
        echo "------------------------------------------------------------------------"

        while IFS='|' read -r arch status size details; do
            if [ "$status" = "SUCCESS" ]; then
                printf "${GREEN}%-12s${NC} | ${GREEN}%-10s${NC} | %-8s | %s\n" "$arch" "$status" "$size" "$details"
            else
                printf "${RED}%-12s${NC} | ${RED}%-10s${NC} | %-8s | %s\n" "$arch" "$status" "$size" "$details"
            fi
        done < "${BUILD_DIR}/build_report.txt"

        echo

        local success_count=$(grep -c "SUCCESS" "${BUILD_DIR}/build_report.txt" || echo 0)
        local total_count=$(wc -l < "${BUILD_DIR}/build_report.txt")

        echo "Results: ${success_count}/${total_count} successful builds"
        echo

        if [ -d "${BIN_DIR}" ] && [ "$(ls -A ${BIN_DIR} 2>/dev/null)" ]; then
            echo "Built binaries located in: ${BIN_DIR}"
            echo
            ls -lh "${BIN_DIR}"
        fi
    fi

    echo
    echo "Build logs: ${BUILD_DIR}/"
    echo "========================================"
}

main() {
    echo
    echo "========================================"
    echo "  WireGuard Obfuscator Multi-Arch Build"
    echo "========================================"
    echo

    log_info "Build directory: ${BUILD_DIR}"
    log_info "Binary output directory: ${BIN_DIR}"
    echo

    mkdir -p "${BUILD_DIR}"
    mkdir -p "${BIN_DIR}"
    rm -f "${BUILD_DIR}/build_report.txt"

    log_info "Checking available compilers..."
    echo

    local has_gcc=false
    local has_mips=false
    local has_mipsel=false
    local has_aarch64=false
    local has_i686=false
    local has_arm=false

    check_compiler "gcc" && has_gcc=true
    check_compiler "mips-linux-gnu-gcc" && has_mips=true
    check_compiler "mipsel-linux-gnu-gcc" && has_mipsel=true
    check_compiler "aarch64-linux-gnu-gcc" && has_aarch64=true
    check_compiler "arm-linux-gnueabihf-gcc" && has_arm=true

    echo

    if ! $has_gcc || ! $has_mips || ! $has_mipsel || ! $has_aarch64 || ! $has_arm; then
        log_warn "Some cross-compilers are missing, installing automatically..."
        install_cross_compilers
        check_compiler "gcc" && has_gcc=true
        check_compiler "mips-linux-gnu-gcc" && has_mips=true
        check_compiler "mipsel-linux-gnu-gcc" && has_mipsel=true
        check_compiler "aarch64-linux-gnu-gcc" && has_aarch64=true
        check_compiler "arm-linux-gnueabihf-gcc" && has_arm=true
        echo
    fi

    local build_count=0
    local success_count=0

    if $has_gcc || check_compiler "gcc"; then
        log_info "=== Building for x86_64 ==="
        if build_for_arch "x86_64" "gcc" "" "wg-obfuscator-x86_64" "static"; then
            success_count=$((success_count + 1))
        fi
        build_count=$((build_count + 1))
        echo
    fi

    if $has_mips || check_compiler "mips-linux-gnu-gcc"; then
        log_info "=== Building for MIPS ==="
        if build_for_arch "mips" "mips-linux-gnu-gcc" "" "wg-obfuscator-mips" "static"; then
            success_count=$((success_count + 1))
        fi
        build_count=$((build_count + 1))
        echo
    fi

    if $has_mipsel || check_compiler "mipsel-linux-gnu-gcc"; then
        log_info "=== Building for MIPSEL ==="
        if build_for_arch "mipsel" "mipsel-linux-gnu-gcc" "" "wg-obfuscator-mipsel" "static"; then
            success_count=$((success_count + 1))
        fi
        build_count=$((build_count + 1))
        echo
    fi

    if $has_aarch64 || check_compiler "aarch64-linux-gnu-gcc"; then
        log_info "=== Building for AARCH64 ==="
        if build_for_arch "aarch64" "aarch64-linux-gnu-gcc" "" "wg-obfuscator-aarch64" "static"; then
            success_count=$((success_count + 1))
        fi
        build_count=$((build_count + 1))
        echo
    fi

    if $has_arm || check_compiler "arm-linux-gnueabihf-gcc"; then
        log_info "=== Building for ARMv7 ==="
        if build_for_arch "armv7" "arm-linux-gnueabihf-gcc" "" "wg-obfuscator-armv7" "static"; then
            success_count=$((success_count + 1))
        fi
        build_count=$((build_count + 1))
        echo
    fi

    make clean >/dev/null 2>&1 || true

    show_summary

    if [ $success_count -eq $build_count ]; then
        log_success "All builds completed successfully!"
        exit 0
    else
        log_warn "Some builds failed. Check logs in ${BUILD_DIR}/"
        exit 1
    fi
}

main "$@"