diff --git a/build.sh b/build.sh index 41d7940f..39f2502f 100755 --- a/build.sh +++ b/build.sh @@ -1,5 +1,5 @@ -#!/bin/bash -# Инкрементальная сборка Linux и Windows/MSYS2; флаги задаются через configure. +#!/usr/bin/env bash +# Инкрементальная сборка Linux, FreeBSD и Windows/MSYS2; флаги задаются через configure. set -euo pipefail cd "$(dirname "$0")" @@ -48,9 +48,25 @@ while [[ $# -gt 0 ]]; do done [[ "$JOBS" =~ ^[1-9][0-9]*$ ]] || { echo "ERROR: invalid job count: $JOBS" >&2; exit 1; } -for tool in make sha256sum; do - command -v "$tool" >/dev/null || { echo "ERROR: required tool not found: $tool" >&2; exit 1; } -done +PLATFORM=$(uname -s) +MAKE_TOOL=${MAKE:-make} +if [[ "$PLATFORM" = FreeBSD ]]; then + MAKE_TOOL=${MAKE:-gmake} + export CC="${CC:-clang}" +fi +MAKE_PATH=$(command -v "$MAKE_TOOL") || { echo "ERROR: GNU make not found: $MAKE_TOOL (FreeBSD: pkg install gmake)" >&2; exit 1; } +MAKE_ID=$("$MAKE_TOOL" --version) || { echo "ERROR: GNU make is required; use MAKE=gmake on BSD" >&2; exit 1; } +[[ "$MAKE_ID" = *'GNU Make'* ]] || { echo "ERROR: GNU make is required; use MAKE=gmake on BSD" >&2; exit 1; } +export MAKE="$MAKE_TOOL" +if command -v sha256sum >/dev/null; then + HASH_COMMAND=(sha256sum) +elif command -v sha256 >/dev/null; then + HASH_COMMAND=(sha256 -r) +elif command -v shasum >/dev/null; then + HASH_COMMAND=(shasum -a 256) +else + echo "ERROR: SHA-256 utility not found: sha256sum, sha256 or shasum is required" >&2; exit 1 +fi read -r -a COMPILER <<< "${CC:-gcc}" COMPILER_PATH=$(command -v "${COMPILER[0]}") || { echo "ERROR: compiler not found: ${CC:-gcc}" >&2; exit 1; } COMPILER_ID=$("${COMPILER[@]}" --version) || { echo "ERROR: cannot identify compiler: ${CC:-gcc}" >&2; exit 1; } @@ -58,7 +74,7 @@ COMPILER_ID=$("${COMPILER[@]}" --version) || { echo "ERROR: cannot identify comp # Хеши учитывают содержимое и имена файлов, включая добавление/удаление макросов. hash_files() { local result - result=$(sha256sum "$@" | sha256sum) + result=$({ printf '%s\0' "$@"; "${HASH_COMMAND[@]}" "$@"; } | "${HASH_COMMAND[@]}") printf '%s\n' "${result%% *}" } @@ -66,9 +82,8 @@ BUILD_DIRS=(lib lib/libopus src tests) AUTOTOOLS_INPUTS=(configure.ac Makefile.am lib/Makefile.am lib/libopus/Makefile.am src/Makefile.am tests/Makefile.am) AUTOTOOLS_OUTPUTS=(configure aclocal.m4 config.h.in Makefile.in lib/Makefile.in lib/libopus/Makefile.in src/Makefile.in tests/Makefile.in) CONFIG_OUTPUTS=(config.status config.h Makefile lib/Makefile lib/libopus/Makefile src/Makefile tests/Makefile) -if [[ -d m4 ]]; then - while IFS= read -r -d '' macro; do AUTOTOOLS_INPUTS+=("$macro"); done < <(find m4 -type f -name '*.m4' -print0 | sort -z) -fi +shopt -s globstar nullglob +for macro in m4/**/*.m4; do AUTOTOOLS_INPUTS+=("$macro"); done GENERATION_INPUT=$(hash_files "${AUTOTOOLS_INPUTS[@]}") GENERATION_OUTPUT=missing CONFIG_OUTPUT=missing @@ -91,13 +106,14 @@ if command -v pkg-config >/dev/null && pkg-config --exists "${FFMPEG_PACKAGES[@] FFMPEG_LDFLAGS=$(pkg-config --libs-only-L "${FFMPEG_PACKAGES[@]}") fi request_sum=$({ - printf '%s\0' "$MODE" "${OSTYPE:-}" "${MSYSTEM:-}" "${MSYSTEM_PREFIX:-}" "${CC:-gcc}" "$COMPILER_PATH" "$COMPILER_ID" + printf '%s\0' "$MODE" "$PLATFORM" "${MSYSTEM:-}" "${MSYSTEM_PREFIX:-}" "${CC:-gcc}" "$COMPILER_PATH" "$COMPILER_ID" + printf '%s\0' "$MAKE_PATH" "$MAKE_ID" "${HASH_COMMAND[@]}" printf '%s\0' "${CFLAGS+x}" "${CFLAGS-}" "${CPPFLAGS+x}" "${CPPFLAGS-}" "${LDFLAGS+x}" "${LDFLAGS-}" printf '%s\0' "${LIBS+x}" "${LIBS-}" "${MAKEFLAGS-}" "${DEBUG_FLAGS+x}" "${DEBUG_FLAGS-}" printf '%s\0' "$FFMPEG_OPTION" "$FFMPEG_ID" "$FFMPEG_CFLAGS" "$FFMPEG_LDFLAGS" printf '%s\0' "${MAKE_SETTINGS[@]}" - sha256sum build.sh -} | sha256sum) + hash_files build.sh +} | "${HASH_COMMAND[@]}") CONFIG_REQUEST=${request_sum%% *} OLD_GENERATION_INPUT=""; OLD_GENERATION_OUTPUT=""; OLD_CONFIG_REQUEST=""; OLD_CONFIG_OUTPUT="" @@ -130,6 +146,7 @@ if [[ "$REGENERATE" = 1 || "$CONFIG_REQUEST" != "$OLD_CONFIG_REQUEST" || "$CONFI if [[ "$MODE" = asan ]]; then export CFLAGS="${CFLAGS-} -g -Og -fsanitize=address -fno-omit-frame-pointer -fno-pie" export LDFLAGS="${LDFLAGS-} -fsanitize=address -no-pie" + if [[ "$PLATFORM" = FreeBSD ]]; then export ASAN_OPTIONS="${ASAN_OPTIONS:-detect_leaks=0}"; fi elif [[ "$MODE" = hardened ]]; then CONFIG_ARGS+=(--enable-hardening) fi @@ -161,7 +178,7 @@ for directory in "${BUILD_DIRS[@]}"; do done if [[ "$RECONFIGURE" = 1 || "$CLEAN" = 1 || "$MISSING_DEPS" = 1 ]]; then echo "INFO: cleaning objects: reconfigured=$RECONFIGURE requested=$CLEAN missing_dependencies=$MISSING_DEPS" - make clean 2>&1 | tee build.log || { echo "ERROR: clean failed; see build.log" >&2; exit 1; } + "$MAKE_TOOL" clean 2>&1 | tee build.log || { echo "ERROR: clean failed; see build.log" >&2; exit 1; } else echo "INFO: configuration unchanged; incremental build (mode=$MODE)" : > build.log @@ -172,7 +189,7 @@ if [[ "$MODE" = asan ]]; then echo "INFO: AddressSanitizer: building lib, Opus and src; skipping tests" MAKE_ARGS+=("SUBDIRS=lib lib/libopus src") fi -make "-j$JOBS" "${MAKE_ARGS[@]}" 2>&1 | tee -a build.log || { echo "ERROR: build failed; see build.log" >&2; exit 1; } +"$MAKE_TOOL" "-j$JOBS" "${MAKE_ARGS[@]}" 2>&1 | tee -a build.log || { echo "ERROR: build failed; see build.log" >&2; exit 1; } # Состояние обновляется после успешной сборки ядра; ошибка GUI не инвалидирует готовые объекты. GENERATION_OUTPUT=$(hash_files "${AUTOTOOLS_OUTPUTS[@]}") diff --git a/build_bsd_full.sh b/build_bsd_full.sh index bc209ea9..8c4c9bf8 100755 --- a/build_bsd_full.sh +++ b/build_bsd_full.sh @@ -1,144 +1,32 @@ #!/bin/sh -# Build script for uTun on FreeBSD (full rebuild) -# Usage: ./build_bsd_full.sh [options] -# Options: -# -h, --help Show this help -# --asan Build with AddressSanitizer (clang) -# --hardened Build with compiler security hardening flags (clang) -# -j[N] Build with N parallel jobs (default: sysctl hw.ncpu) - +# Полная сборка FreeBSD: зависимости и общий инкрементальный скрипт. set -e - -# Parse arguments -USE_ASAN=0 -USE_HARDENED=0 -JOBS="" -MAKE_ARGS="" - -while [ $# -gt 0 ]; do - case "$1" in - -h|--help) - echo "Usage: ./build_bsd_full.sh [options]" - echo "Options:" - echo " -h, --help Show this help" - echo " --asan Build with AddressSanitizer" - echo " --hardened Build with compiler security hardening flags" - echo " -j[N] Build with N parallel jobs (default: sysctl hw.ncpu)" - exit 0 - ;; - --asan) - USE_ASAN=1 - shift - ;; - --hardened) - USE_HARDENED=1 - shift - ;; - -j*) - JOBS="$1" - shift - ;; - *) - MAKE_ARGS="$MAKE_ARGS $1" - shift - ;; - esac +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +case " ${*} " in + *" --help "*|*" -h "*) + echo "Usage: ./build_bsd_full.sh [--asan|--hardened] [-jN] [build.sh arguments]" + echo "Installs missing build tools and runs build.sh --full" + exit 0 ;; +esac + +need_install=0 +for tool in bash gmake autoreconf aclocal automake libtoolize pkg-config; do + command -v "$tool" >/dev/null 2>&1 || need_install=1 done - -if [ -z "$JOBS" ]; then - if command -v nproc >/dev/null 2>&1; then - JOBS="-j$(nproc)" - else - JOBS="-j$(sysctl -n hw.ncpu)" - fi +if [ "$need_install" -eq 1 ]; then + echo "INFO: installing missing FreeBSD build dependencies (requires root)" + pkg install -y bash gmake autoconf automake libtool pkgconf openssl || { + echo "ERROR: cannot install FreeBSD build dependencies" >&2; exit 1; + } fi -# Проверка и установка зависимостей -need_install="" - -for cmd in autoreconf aclocal automake libtoolize; do - if ! command -v "$cmd" >/dev/null 2>&1; then - need_install=1 - break - fi +jobs_found=0 +for argument do + case "$argument" in -j*) jobs_found=1 ;; esac done - -if [ -n "$need_install" ]; then - echo "Устанавливаются необходимые пакеты (нужен root)..." - pkg update - pkg install -y autoconf automake libtool pkgconf openssl -fi - -# Предпочитаем GNU make (gmake), но fallback на bmake — он собирает -# autotools-генерённые Makefile данного проекта. gmake ставится отдельно: -# pkg install gmake (нужен root) -GMAKE="$(command -v gmake || command -v make)" -echo "Используем make: $GMAKE" - -mkdir -p m4 - -echo "=== Полная пересборка uTun ===" - -# 1. Очистка -echo "[1/4] Очистка..." -$GMAKE distclean 2>/dev/null || true -rm -f config.h config.status config.log -rm -f configure.log autoreconf.log build.log - -# 2. Генерация configure -echo "[2/4] Автогенерация..." -autoreconf -fi 2>&1 | tee autoreconf.log - -# Проверяем, появился ли configure -if [ ! -f configure ]; then - echo "Ошибка: autoreconf не создал configure." - exit 1 -fi - -chmod +x configure - -# 3. Конфигурация -echo "[3/4] Конфигурация..." - -# FFmpeg (опционально) — автоопределение через pkg-config -FFMPEG_OPT="" -if command -v pkg-config >/dev/null 2>&1 && \ - pkg-config --exists libavformat libavcodec libavutil libswscale libswresample 2>/dev/null; then - FFMPEG_OPT="--with-ffmpeg" - echo "FFmpeg detected — building with video transcoding" -else - echo "FFmpeg not found — building without video transcoding" -fi - -if [ "$USE_ASAN" -eq 1 ]; then - echo "(AddressSanitizer enabled — clang, lib/ + src/ only)" - ./configure --prefix=/usr/local CC=clang \ - CFLAGS="-g -O1 -fsanitize=address -fno-omit-frame-pointer" \ - LDFLAGS="-fsanitize=address" $FFMPEG_OPT 2>&1 | tee configure.log -elif [ "$USE_HARDENED" -eq 1 ]; then - echo "(Hardening enabled — clang-safe flags)" - ./configure --prefix=/usr/local CC=clang \ - CFLAGS="-D_FORTIFY_SOURCE=2 -fstack-protector-strong -fPIE" \ - LDFLAGS="-Wl,-z,relro -Wl,-z,now -pie" $FFMPEG_OPT 2>&1 | tee configure.log -else - ./configure --prefix=/usr/local $FFMPEG_OPT 2>&1 | tee configure.log -fi - -# 4. Сборка -echo "[4/4] Сборка..." -if [ "$USE_ASAN" -eq 1 ]; then - (cd lib && $GMAKE $JOBS $MAKE_ARGS 2>&1 | tee ../build.log) - (cd lib/libopus && $GMAKE $JOBS $MAKE_ARGS 2>&1 | tee ../../build.log) - (cd src && $GMAKE $JOBS $MAKE_ARGS 2>&1 | tee ../build.log) -else - $GMAKE $JOBS $MAKE_ARGS 2>&1 | tee build.log -fi - -echo -echo "=== Готово ===" -echo "Бинарный файл: src/utun" -if [ "$USE_ASAN" -eq 1 ]; then - echo - echo "Запуск с ASAN (detect_leaks=0 обязателен на FreeBSD):" - echo " ASAN_OPTIONS=detect_leaks=0:halt_on_error=0 ./src/utun -f -p /tmp/utun.pid -l /tmp/utun.log" +if [ "$jobs_found" -eq 0 ]; then + jobs=$(sysctl -n hw.ncpu) || { echo "ERROR: cannot determine CPU count" >&2; exit 1; } + set -- "-j$jobs" "$@" fi +echo "INFO: running shared build script for FreeBSD" +exec bash "$SCRIPT_DIR/build.sh" --full "$@" diff --git a/configure.ac b/configure.ac index 1bebe33b..e7dbafd1 100644 --- a/configure.ac +++ b/configure.ac @@ -62,11 +62,24 @@ AC_ARG_ENABLE([hardening], [enable_hardening=no]) if test "x$enable_hardening" = "xyes"; then - HARDEN_CFLAGS="-D_FORTIFY_SOURCE=2 -fstack-protector-strong -fstack-clash-protection -Wformat=2 -Wformat-overflow=2 -fPIE" - HARDEN_LDFLAGS="-Wl,-z,relro -Wl,-z,now -pie" + HARDEN_CFLAGS="-D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wformat=2" + compiler_version=$($CC --version 2>&1) || AC_MSG_ERROR([Cannot identify compiler for hardening]) + case "$compiler_version" in + *clang*) ;; + *) HARDEN_CFLAGS="$HARDEN_CFLAGS -fstack-clash-protection -Wformat-overflow=2" ;; + esac + case "$host" in + *mingw* | *msys* | *cygwin*) + HARDEN_LDFLAGS="-Wl,--dynamicbase,--nxcompat,--high-entropy-va" + ;; + *) + HARDEN_CFLAGS="$HARDEN_CFLAGS -fPIE" + HARDEN_LDFLAGS="-Wl,-z,relro -Wl,-z,now -pie" + ;; + esac CFLAGS="$CFLAGS $HARDEN_CFLAGS" LDFLAGS="$LDFLAGS $HARDEN_LDFLAGS" - AC_MSG_NOTICE([Hardening flags enabled: $HARDEN_CFLAGS]) + AC_MSG_NOTICE([Hardening flags enabled: CFLAGS=$HARDEN_CFLAGS LDFLAGS=$HARDEN_LDFLAGS]) fi # ==================== НАДЁЖНЫЙ ДЕТЕКТ WINDOWS ====================