Merge remote-tracking branch 'origin/master'
# Conflicts: # tests/Makefile.am
This commit is contained in:
commit
69a99c7860
10 files changed
+529
-354
No files matched your search
@@ -32,6 +32,7 @@ index.html
|
|||||||
*.exe
|
*.exe
|
||||||
|
|
||||||
# Autotools
|
# Autotools
|
||||||
|
/.utun-build-state*
|
||||||
Makefile
|
Makefile
|
||||||
!tools/etcpmon/Makefile
|
!tools/etcpmon/Makefile
|
||||||
Makefile.in
|
Makefile.in
|
||||||
@@ -84,6 +85,7 @@ logs/
|
|||||||
tests/test_*
|
tests/test_*
|
||||||
!tests/test_*.c
|
!tests/test_*.c
|
||||||
!tests/test_*.h
|
!tests/test_*.h
|
||||||
|
!tests/test_*.py
|
||||||
!tests/test_*.conf
|
!tests/test_*.conf
|
||||||
!tests/test_*.json
|
!tests/test_*.json
|
||||||
tests/bench_*
|
tests/bench_*
|
||||||
|
|||||||
@@ -51,9 +51,19 @@ This file contains essential information for AI coding agents working in the uTu
|
|||||||
powershell build_full.bat # запускает bash build.sh --full через MSYS2 UCRT64
|
powershell build_full.bat # запускает bash build.sh --full через MSYS2 UCRT64
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Build (FreeBSD)
|
||||||
|
```sh
|
||||||
|
# Первичная установка инструментов от root:
|
||||||
|
pkg install -y bash gmake autoconf automake libtool pkgconf openssl python3
|
||||||
|
./build.sh -j4 # общий скрипт; автоматически выбирает gmake и Clang
|
||||||
|
./build_bsd_full.sh -j4 # полная сборка через общий скрипт
|
||||||
|
```
|
||||||
|
Python 3 нужен для проверок скрипта сборки. SHA-256 берётся из базовой системы FreeBSD.
|
||||||
|
|
||||||
### Incremental Build
|
### Incremental Build
|
||||||
```bash
|
```bash
|
||||||
./build.sh -j4 # make с авто-конфигурацией если надо
|
./build.sh -j4 # инкрементальная сборка, конфигурация и очистка при необходимости
|
||||||
|
./build.sh --no-gui -j4 # только ядро и тесты, без отдельной сборки GUI
|
||||||
```
|
```
|
||||||
```powershell
|
```powershell
|
||||||
powershell -Command ".\build.bat" 2>&1 # Windows, логи: build_win.log
|
powershell -Command ".\build.bat" 2>&1 # Windows, логи: build_win.log
|
||||||
@@ -127,6 +137,7 @@ cd tests && ./test_services
|
|||||||
```
|
```
|
||||||
Использовать цель из `tests/Makefile.am`: ручная компиляция `src/*.c` пропускает подкаталоги и добавляет лишний `main`.
|
Использовать цель из `tests/Makefile.am`: ручная компиляция `src/*.c` пропускает подкаталоги и добавляет лишний `main`.
|
||||||
Уровни и категории диагностики настраиваются в самом тесте через `debug_set_level` / `debug_set_category_level`.
|
Уровни и категории диагностики настраиваются в самом тесте через `debug_set_level` / `debug_set_category_level`.
|
||||||
|
Проверки самого скрипта сборки входят в `./check.sh`; отдельно: `make -C tests check-build-script` (нужен Python 3).
|
||||||
|
|
||||||
## Code Style Guidelines
|
## Code Style Guidelines
|
||||||
|
|
||||||
@@ -614,7 +625,7 @@ DEBUG_LEVEL_DISABLED полностью отключает категорию,
|
|||||||
- Нельзя ничего восстанавливать из репозитория не спрашивая
|
- Нельзя ничего восстанавливать из репозитория не спрашивая
|
||||||
- Для отладки не printf а DEBUG_*
|
- Для отладки не printf а DEBUG_*
|
||||||
- Запрещено прямое включение arpa/inet.h, sys/time.h, unistd.h — использовать ../lib/platform_compat.h + socket_compat.h
|
- Запрещено прямое включение arpa/inet.h, sys/time.h, unistd.h — использовать ../lib/platform_compat.h + socket_compat.h
|
||||||
- Перед сборкой всегда make clean
|
- Меняй режим и флаги сборки через `./build.sh`: он отслеживает конфигурацию и очищает объекты только при необходимости.
|
||||||
- Все лишнее что менял при отладке - строго вернуть назад в состояние до вмешательства
|
- Все лишнее что менял при отладке - строго вернуть назад в состояние до вмешательства
|
||||||
|
|
||||||
## Написание кода
|
## Написание кода
|
||||||
|
|||||||
@@ -1,216 +1,212 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
# Build script for uTun (supports both Linux and Windows/MSYS2)
|
# Инкрементальная сборка Linux, FreeBSD и Windows/MSYS2; флаги задаются через configure.
|
||||||
# Usage: ./build.sh [options]
|
set -euo pipefail
|
||||||
# Options:
|
cd "$(dirname "$0")"
|
||||||
# -h, --help Show this help
|
|
||||||
# --clean Clean before build
|
|
||||||
# --full Full rebuild (autoreconf + configure + make)
|
|
||||||
# --asan Build with AddressSanitizer (debug)
|
|
||||||
# --hardened Build with compiler security hardening flags
|
|
||||||
# -j[N] Build with N parallel jobs (default: 4)
|
|
||||||
|
|
||||||
set -e # Exit on error
|
MODE=normal
|
||||||
|
|
||||||
# Detect platform
|
|
||||||
if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ -n "$MSYSTEM" ]]; then
|
|
||||||
IS_WINDOWS=1
|
|
||||||
echo "Detected Windows/MSYS2 environment"
|
|
||||||
else
|
|
||||||
IS_WINDOWS=0
|
|
||||||
echo "Detected Linux environment"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Parse arguments
|
|
||||||
CLEAN=0
|
CLEAN=0
|
||||||
FULL_REBUILD=0
|
FULL=0
|
||||||
USE_ASAN=0
|
BUILD_GUI=1
|
||||||
USE_HARDENED=0
|
JOBS=4
|
||||||
JOBS="-j4"
|
MAKE_ARGS=()
|
||||||
MAKE_ARGS=""
|
MAKE_SETTINGS=()
|
||||||
HARDEN_FLAGS=""
|
STATE=.utun-build-state
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case $1 in
|
case "$1" in
|
||||||
-h|--help)
|
-h|--help)
|
||||||
echo "Usage: ./build.sh [options]"
|
echo "Usage: ./build.sh [--clean] [--full] [--asan|--hardened] [--no-gui] [-jN] [make arguments]"
|
||||||
echo "Options:"
|
echo " --clean Rebuild objects without forcing configure"
|
||||||
echo " -h, --help Show this help"
|
echo " --full Regenerate, configure and rebuild everything"
|
||||||
echo " --clean Clean before build"
|
echo " --asan AddressSanitizer; skip tests"
|
||||||
echo " --full Full rebuild (autoreconf + configure + make)"
|
echo " --hardened Enable compiler security hardening"
|
||||||
echo " --asan Build with AddressSanitizer"
|
echo " --no-gui Skip the separately configured ChatGUI"
|
||||||
echo " --hardened Build with compiler security hardening flags"
|
echo " -jN, -j N Parallel jobs (default: 4)"
|
||||||
echo " -j[N] Build with N parallel jobs (default: 4)"
|
echo " CC/CFLAGS/CPPFLAGS/LDFLAGS/LIBS assignments are passed to configure"
|
||||||
exit 0
|
exit 0 ;;
|
||||||
;;
|
--clean) CLEAN=1 ;;
|
||||||
--clean)
|
--full) FULL=1 ;;
|
||||||
CLEAN=1
|
--no-gui) BUILD_GUI=0 ;;
|
||||||
shift
|
--asan|--hardened)
|
||||||
;;
|
selected_mode=${1#--}
|
||||||
--full)
|
if [[ "$MODE" != normal && "$MODE" != "$selected_mode" ]]; then
|
||||||
FULL_REBUILD=1
|
echo "ERROR: --asan and --hardened cannot be combined" >&2; exit 1
|
||||||
shift
|
fi
|
||||||
;;
|
MODE=$selected_mode ;;
|
||||||
--asan)
|
-j)
|
||||||
USE_ASAN=1
|
[[ $# -gt 1 ]] || { echo "ERROR: -j requires a positive job count" >&2; exit 1; }
|
||||||
shift
|
shift; JOBS=$1 ;;
|
||||||
;;
|
-j*) JOBS=${1#-j} ;;
|
||||||
--hardened)
|
CC=*|CFLAGS=*|CPPFLAGS=*|LDFLAGS=*|LIBS=*) export "$1" ;;
|
||||||
USE_HARDENED=1
|
CC+=*|CFLAGS+=*|CPPFLAGS+=*|LDFLAGS+=*|LIBS+=*)
|
||||||
shift
|
echo "ERROR: use NAME=value for configure flags; append assignments are not supported" >&2; exit 1 ;;
|
||||||
;;
|
|
||||||
-j*)
|
|
||||||
JOBS="$1"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
MAKE_ARGS="$MAKE_ARGS $1"
|
MAKE_ARGS+=("$1")
|
||||||
shift
|
[[ "$1" != *=* ]] || MAKE_SETTINGS+=("$1") ;;
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
shift
|
||||||
done
|
done
|
||||||
|
[[ "$JOBS" =~ ^[1-9][0-9]*$ ]] || { echo "ERROR: invalid job count: $JOBS" >&2; exit 1; }
|
||||||
|
|
||||||
# Check required tools
|
PLATFORM=$(uname -s)
|
||||||
if ! command -v gcc &> /dev/null; then
|
MAKE_TOOL=${MAKE:-make}
|
||||||
echo "Error: gcc not found."
|
if [[ "$PLATFORM" = FreeBSD ]]; then
|
||||||
if [ "$IS_WINDOWS" -eq 1 ]; then
|
MAKE_TOOL=${MAKE:-gmake}
|
||||||
echo "Please install: pacman -S mingw-w64-ucrt-x86_64-toolchain"
|
export CC="${CC:-clang}"
|
||||||
else
|
fi
|
||||||
echo "Please install build-essential or gcc"
|
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; }
|
||||||
|
|
||||||
|
# Хеши учитывают содержимое и имена файлов, включая добавление/удаление макросов.
|
||||||
|
hash_files() {
|
||||||
|
local result
|
||||||
|
result=$({ printf '%s\0' "$@"; "${HASH_COMMAND[@]}" "$@"; } | "${HASH_COMMAND[@]}")
|
||||||
|
printf '%s\n' "${result%% *}"
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
missing_generation=0
|
||||||
|
missing_configuration=0
|
||||||
|
for file in "${AUTOTOOLS_OUTPUTS[@]}"; do [[ -f "$file" ]] || missing_generation=1; done
|
||||||
|
for file in "${CONFIG_OUTPUTS[@]}"; do [[ -f "$file" ]] || missing_configuration=1; done
|
||||||
|
if [[ "$missing_generation" = 0 ]]; then GENERATION_OUTPUT=$(hash_files "${AUTOTOOLS_OUTPUTS[@]}"); fi
|
||||||
|
if [[ "$missing_configuration" = 0 ]]; then CONFIG_OUTPUT=$(hash_files "${CONFIG_OUTPUTS[@]}"); fi
|
||||||
|
|
||||||
|
FFMPEG_PACKAGES=(libavformat libavcodec libavutil libswscale libswresample)
|
||||||
|
FFMPEG_OPTION=--without-ffmpeg
|
||||||
|
FFMPEG_ID=unavailable
|
||||||
|
FFMPEG_CFLAGS=""
|
||||||
|
FFMPEG_LDFLAGS=""
|
||||||
|
if command -v pkg-config >/dev/null && pkg-config --exists "${FFMPEG_PACKAGES[@]}"; then
|
||||||
|
FFMPEG_OPTION=--with-ffmpeg
|
||||||
|
FFMPEG_ID=$(pkg-config --modversion "${FFMPEG_PACKAGES[@]}")
|
||||||
|
FFMPEG_CFLAGS=$(pkg-config --cflags "${FFMPEG_PACKAGES[@]}")
|
||||||
|
FFMPEG_LDFLAGS=$(pkg-config --libs-only-L "${FFMPEG_PACKAGES[@]}")
|
||||||
|
fi
|
||||||
|
request_sum=$({
|
||||||
|
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[@]}"
|
||||||
|
hash_files build.sh
|
||||||
|
} | "${HASH_COMMAND[@]}")
|
||||||
|
CONFIG_REQUEST=${request_sum%% *}
|
||||||
|
|
||||||
|
OLD_GENERATION_INPUT=""; OLD_GENERATION_OUTPUT=""; OLD_CONFIG_REQUEST=""; OLD_CONFIG_OUTPUT=""
|
||||||
|
if [[ -f "$STATE" ]]; then
|
||||||
|
if ! {
|
||||||
|
read -r OLD_GENERATION_INPUT; read -r OLD_GENERATION_OUTPUT
|
||||||
|
read -r OLD_CONFIG_REQUEST; read -r OLD_CONFIG_OUTPUT
|
||||||
|
} < "$STATE"; then
|
||||||
|
echo "WARN: incomplete build state; adopting the build with a clean rebuild"
|
||||||
|
OLD_GENERATION_INPUT=""; OLD_GENERATION_OUTPUT=""; OLD_CONFIG_REQUEST=""; OLD_CONFIG_OUTPUT=""
|
||||||
fi
|
fi
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Clean if requested
|
REGENERATE=0
|
||||||
if [ "$CLEAN" -eq 1 ]; then
|
RECONFIGURE=0
|
||||||
echo "Cleaning build..."
|
if [[ "$FULL" = 1 || "$GENERATION_INPUT" != "$OLD_GENERATION_INPUT" || "$GENERATION_OUTPUT" != "$OLD_GENERATION_OUTPUT" ]]; then
|
||||||
make clean 2>/dev/null || true
|
echo "INFO: regenerating Autotools: full=$FULL"
|
||||||
|
if [[ "$GENERATION_INPUT" != "$OLD_GENERATION_INPUT" ]]; then echo "INFO: Autotools descriptions changed or unrecorded"; fi
|
||||||
|
if [[ "$GENERATION_OUTPUT" != "$OLD_GENERATION_OUTPUT" ]]; then echo "INFO: generated Autotools files changed or missing"; fi
|
||||||
|
command -v autoreconf >/dev/null || { echo "ERROR: autoreconf is required to regenerate the build" >&2; exit 1; }
|
||||||
|
mkdir -p m4
|
||||||
|
autoreconf -fi 2>&1 | tee autoreconf.log || { echo "ERROR: autoreconf failed; see autoreconf.log" >&2; exit 1; }
|
||||||
|
REGENERATE=1
|
||||||
|
fi
|
||||||
|
if [[ "$REGENERATE" = 1 || "$CONFIG_REQUEST" != "$OLD_CONFIG_REQUEST" || "$CONFIG_OUTPUT" != "$OLD_CONFIG_OUTPUT" ]]; then
|
||||||
|
echo "INFO: configuring: mode=$MODE regenerated=$REGENERATE"
|
||||||
|
if [[ "$CONFIG_REQUEST" != "$OLD_CONFIG_REQUEST" ]]; then echo "INFO: compiler, flags or optional dependencies changed or unrecorded"; fi
|
||||||
|
if [[ "$CONFIG_OUTPUT" != "$OLD_CONFIG_OUTPUT" ]]; then echo "INFO: configuration files changed or missing"; fi
|
||||||
|
CONFIG_ARGS=("$FFMPEG_OPTION" --disable-hardening)
|
||||||
|
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
|
||||||
|
if [[ "$FFMPEG_OPTION" = --with-ffmpeg ]]; then
|
||||||
|
export CPPFLAGS="${CPPFLAGS-} $FFMPEG_CFLAGS"
|
||||||
|
export LDFLAGS="${LDFLAGS-} $FFMPEG_LDFLAGS"
|
||||||
|
fi
|
||||||
|
./configure "${CONFIG_ARGS[@]}" 2>&1 | tee configure.log || { echo "ERROR: configure failed; see configure.log" >&2; exit 1; }
|
||||||
|
RECONFIGURE=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Full rebuild: regenerate build system
|
# Проверяем только зависимости объектов, перечисленных Automake; посторонние сборки не затрагиваем.
|
||||||
if [ "$FULL_REBUILD" -eq 1 ]; then
|
MISSING_DEPS=0
|
||||||
echo ""
|
for directory in "${BUILD_DIRS[@]}"; do
|
||||||
echo "======================================"
|
while read -r keyword depfile marker; do
|
||||||
echo "FULL REBUILD: autoreconf"
|
[[ "$keyword" = include && "$marker" = '# am--include-marker' && "$depfile" = *.Po ]] || continue
|
||||||
echo "======================================"
|
depfile=${depfile//\$\(DEPDIR\)/.deps}
|
||||||
echo ""
|
object=${depfile/.deps\//}
|
||||||
|
object=${object%.Po}
|
||||||
# Check for autotools
|
[[ -f "$directory/$object.o" || -f "$directory/$object.obj" ]] || continue
|
||||||
if ! command -v autoreconf &> /dev/null; then
|
dependency_line=""
|
||||||
echo "Error: autoreconf not found."
|
if [[ -s "$directory/$depfile" ]]; then IFS= read -r dependency_line < "$directory/$depfile" || true; fi
|
||||||
if [ "$IS_WINDOWS" -eq 1 ]; then
|
if [[ "$dependency_line" != *:* ]]; then
|
||||||
echo "Install autotools: pacman -S mingw-w64-ucrt-x86_64-autotools"
|
echo "WARN: missing or empty dependencies for $directory/$object: $directory/$depfile"
|
||||||
else
|
MISSING_DEPS=1
|
||||||
echo "Install autotools: apt-get install autoconf automake libtool"
|
break
|
||||||
fi
|
fi
|
||||||
exit 1
|
done < "$directory/Makefile"
|
||||||
fi
|
done
|
||||||
|
if [[ "$RECONFIGURE" = 1 || "$CLEAN" = 1 || "$MISSING_DEPS" = 1 ]]; then
|
||||||
# Clean generated files
|
echo "INFO: cleaning objects: reconfigured=$RECONFIGURE requested=$CLEAN missing_dependencies=$MISSING_DEPS"
|
||||||
echo "Cleaning generated files..."
|
"$MAKE_TOOL" clean 2>&1 | tee build.log || { echo "ERROR: clean failed; see build.log" >&2; exit 1; }
|
||||||
make distclean 2>/dev/null || true
|
|
||||||
rm -rf build/
|
|
||||||
|
|
||||||
# Regenerate build system (root Makefile is hand-written, not generated)
|
|
||||||
echo ""
|
|
||||||
echo "Running autoreconf -fi..."
|
|
||||||
autoreconf -fi 2>&1 | tee autoreconf.log
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "======================================"
|
|
||||||
echo "Build system regenerated successfully"
|
|
||||||
echo "======================================"
|
|
||||||
echo ""
|
|
||||||
else
|
else
|
||||||
# Quick/incremental build: generate configure if needed
|
echo "INFO: configuration unchanged; incremental build (mode=$MODE)"
|
||||||
if [ ! -f ./configure ]; then
|
: > build.log
|
||||||
echo "Configure script not found. Generating..."
|
|
||||||
if ! command -v autoreconf &> /dev/null; then
|
|
||||||
echo "Error: autoreconf not found."
|
|
||||||
if [ "$IS_WINDOWS" -eq 1 ]; then
|
|
||||||
echo "Install autotools: pacman -S mingw-w64-ucrt-x86_64-autotools"
|
|
||||||
else
|
|
||||||
echo "Install autotools: apt-get install autoconf automake libtool"
|
|
||||||
fi
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
autoreconf -fiv
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Auto-detect FFmpeg (optional video transcoding). If libav libraries are
|
echo "INFO: compiling: mode=$MODE jobs=$JOBS"
|
||||||
# available, build with --with-ffmpeg; configure gracefully skips it otherwise.
|
if [[ "$MODE" = asan ]]; then
|
||||||
FFMPEG_OPT=""
|
echo "INFO: AddressSanitizer: building lib, Opus and src; skipping tests"
|
||||||
if command -v pkg-config >/dev/null 2>&1 && \
|
MAKE_ARGS+=("SUBDIRS=lib lib/libopus src")
|
||||||
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
|
fi
|
||||||
|
"$MAKE_TOOL" "-j$JOBS" "${MAKE_ARGS[@]}" 2>&1 | tee -a build.log || { echo "ERROR: build failed; see build.log" >&2; exit 1; }
|
||||||
|
|
||||||
# Generate Makefiles if needed
|
# Состояние обновляется после успешной сборки ядра; ошибка GUI не инвалидирует готовые объекты.
|
||||||
if [ ! -f Makefile ]; then
|
GENERATION_OUTPUT=$(hash_files "${AUTOTOOLS_OUTPUTS[@]}")
|
||||||
echo ""
|
CONFIG_OUTPUT=$(hash_files "${CONFIG_OUTPUTS[@]}")
|
||||||
echo "Running ./configure..."
|
state_tmp="$STATE.tmp.$$"
|
||||||
if [ "$USE_ASAN" -eq 1 ]; then
|
trap 'rm -f "$state_tmp"' EXIT
|
||||||
./configure CFLAGS="-g -Og -fsanitize=address -fno-omit-frame-pointer -fno-pie" LDFLAGS="-no-pie" $FFMPEG_OPT
|
printf '%s\n' "$GENERATION_INPUT" "$GENERATION_OUTPUT" "$CONFIG_REQUEST" "$CONFIG_OUTPUT" > "$state_tmp"
|
||||||
elif [ "$USE_HARDENED" -eq 1 ]; then
|
mv -f "$state_tmp" "$STATE"
|
||||||
./configure --enable-hardening $FFMPEG_OPT
|
|
||||||
|
if [[ "$BUILD_GUI" = 1 && -f tools/chatgui/build/CMakeCache.txt ]]; then
|
||||||
|
command -v cmake >/dev/null || { echo "ERROR: ChatGUI exists but cmake is not available" >&2; exit 1; }
|
||||||
|
echo "INFO: building ChatGUI with its CMake configuration: jobs=$JOBS"
|
||||||
|
if [[ "${MSYSTEM:-}" = UCRT64 ]]; then
|
||||||
|
(cd tools/chatgui && bash build.sh "-j$JOBS") || { echo "ERROR: ChatGUI build failed" >&2; exit 1; }
|
||||||
else
|
else
|
||||||
./configure $FFMPEG_OPT
|
cmake --build tools/chatgui/build --parallel "$JOBS" || { echo "ERROR: ChatGUI build failed" >&2; exit 1; }
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
echo "Build completed successfully!"
|
||||||
# Build (all .o files go to build/ directory, binary copied to project root)
|
echo "Binary: src/utun"
|
||||||
echo ""
|
|
||||||
echo "Compiling with $JOBS..."
|
|
||||||
BUILD_SUCCESS=0
|
|
||||||
if [ "$USE_ASAN" -eq 1 ]; then
|
|
||||||
echo "(AddressSanitizer enabled — lib/ and src/ only, skipping tests)"
|
|
||||||
AF="-fsanitize=address -fno-omit-frame-pointer -g -Og"
|
|
||||||
AL="-fsanitize=address"
|
|
||||||
(cd lib && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$AF" "LDFLAGS+=$AL" $JOBS $MAKE_ARGS 2>&1) || { echo "lib build failed"; exit 1; }
|
|
||||||
# libopus собирается без ASAN (собственный CFLAGS в Makefile, .a несовместим с PIE)
|
|
||||||
(cd lib/libopus && make clean $JOBS $MAKE_ARGS 2>&1 && make $JOBS $MAKE_ARGS 2>&1) || { echo "libopus build failed"; exit 1; }
|
|
||||||
(cd src && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$AF" "LDFLAGS+=$AL" $JOBS $MAKE_ARGS 2>&1) || { echo "src build failed"; exit 1; }
|
|
||||||
BUILD_SUCCESS=1
|
|
||||||
elif [ "$USE_HARDENED" -eq 1 ]; then
|
|
||||||
echo "(Hardening flags enabled)"
|
|
||||||
HF="-D_FORTIFY_SOURCE=2 -fstack-protector-strong -fstack-clash-protection -Wformat=2 -Wformat-overflow=2 -fPIE"
|
|
||||||
HL="-Wl,-z,relro -Wl,-z,now -pie"
|
|
||||||
(cd lib && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$HF" "LDFLAGS+=$HL" $JOBS $MAKE_ARGS 2>&1) || { echo "lib build failed"; exit 1; }
|
|
||||||
(cd src && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$HF" "LDFLAGS+=$HL" $JOBS $MAKE_ARGS 2>&1) || { echo "src build failed"; exit 1; }
|
|
||||||
(cd tests && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$HF" "LDFLAGS+=$HL" $JOBS $MAKE_ARGS 2>&1) || { echo "tests build failed"; exit 1; }
|
|
||||||
BUILD_SUCCESS=1
|
|
||||||
else
|
|
||||||
make $JOBS $MAKE_ARGS 2>&1 | tee build.log
|
|
||||||
if [ ${PIPESTATUS[0]} -eq 0 ]; then
|
|
||||||
BUILD_SUCCESS=1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $BUILD_SUCCESS -eq 1 ]; then
|
|
||||||
echo ""
|
|
||||||
echo "Build completed successfully!"
|
|
||||||
echo "Binary: utun"
|
|
||||||
|
|
||||||
# Rebuild chatgui (CMake project) if transport sources changed
|
|
||||||
if [ -f tools/chatgui/build/CMakeCache.txt ] && command -v cmake &> /dev/null; then
|
|
||||||
echo ""
|
|
||||||
echo "======================================"
|
|
||||||
echo "Rebuilding chatgui (CMake)"
|
|
||||||
echo "======================================"
|
|
||||||
(
|
|
||||||
if [ "${MSYSTEM:-}" = UCRT64 ]; then
|
|
||||||
cd tools/chatgui && bash build.sh
|
|
||||||
else
|
|
||||||
cd tools/chatgui/build && cmake --build . -j4 2>&1
|
|
||||||
fi
|
|
||||||
) || echo "WARNING: chatgui build failed"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo ""
|
|
||||||
echo "ERROR: Build failed!"
|
|
||||||
echo "Check build.log for details"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
+24
-136
@@ -1,144 +1,32 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Build script for uTun on FreeBSD (full rebuild)
|
# Полная сборка FreeBSD: зависимости и общий инкрементальный скрипт.
|
||||||
# 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)
|
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
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
|
||||||
|
|
||||||
# Parse arguments
|
need_install=0
|
||||||
USE_ASAN=0
|
for tool in bash gmake autoreconf aclocal automake libtoolize pkg-config; do
|
||||||
USE_HARDENED=0
|
command -v "$tool" >/dev/null 2>&1 || need_install=1
|
||||||
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
|
|
||||||
done
|
done
|
||||||
|
if [ "$need_install" -eq 1 ]; then
|
||||||
if [ -z "$JOBS" ]; then
|
echo "INFO: installing missing FreeBSD build dependencies (requires root)"
|
||||||
if command -v nproc >/dev/null 2>&1; then
|
pkg install -y bash gmake autoconf automake libtool pkgconf openssl || {
|
||||||
JOBS="-j$(nproc)"
|
echo "ERROR: cannot install FreeBSD build dependencies" >&2; exit 1;
|
||||||
else
|
}
|
||||||
JOBS="-j$(sysctl -n hw.ncpu)"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Проверка и установка зависимостей
|
jobs_found=0
|
||||||
need_install=""
|
for argument do
|
||||||
|
case "$argument" in -j*) jobs_found=1 ;; esac
|
||||||
for cmd in autoreconf aclocal automake libtoolize; do
|
|
||||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
|
||||||
need_install=1
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
|
if [ "$jobs_found" -eq 0 ]; then
|
||||||
if [ -n "$need_install" ]; then
|
jobs=$(sysctl -n hw.ncpu) || { echo "ERROR: cannot determine CPU count" >&2; exit 1; }
|
||||||
echo "Устанавливаются необходимые пакеты (нужен root)..."
|
set -- "-j$jobs" "$@"
|
||||||
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"
|
|
||||||
fi
|
fi
|
||||||
|
echo "INFO: running shared build script for FreeBSD"
|
||||||
|
exec bash "$SCRIPT_DIR/build.sh" --full "$@"
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
set -eo pipefail
|
set -eo pipefail
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
MAKE_TOOL=${MAKE:-$(command -v gmake || command -v make)}
|
||||||
|
TIMEOUT_TOOL=$(command -v timeout || command -v gtimeout) || {
|
||||||
|
echo "ERROR: timeout or gtimeout is required for the test runner" >&2; exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
LAST=0; [ -f .check_time ] && read -r LAST < .check_time
|
LAST=0; [ -f .check_time ] && read -r LAST < .check_time
|
||||||
TMO=$(( LAST + 30 ))
|
TMO=$(( LAST + 30 ))
|
||||||
@@ -8,7 +12,7 @@ TMO=$(( LAST + 30 ))
|
|||||||
|
|
||||||
START=$(date +%s)
|
START=$(date +%s)
|
||||||
TMP=$(mktemp)
|
TMP=$(mktemp)
|
||||||
timeout $TMO make check -j4 > "$TMP" 2>&1 || {
|
"$TIMEOUT_TOOL" "$TMO" "$MAKE_TOOL" check -j4 > "$TMP" 2>&1 || {
|
||||||
tail -40 "$TMP"; rm -f "$TMP"
|
tail -40 "$TMP"; rm -f "$TMP"
|
||||||
[ $(date +%s) -ge $((START + TMO)) ] && echo "TIMEOUT (${TMO}s)" >&2
|
[ $(date +%s) -ge $((START + TMO)) ] && echo "TIMEOUT (${TMO}s)" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@@ -18,17 +22,19 @@ rm -f "$TMP"
|
|||||||
|
|
||||||
# Интеграционный тест tcp_proxy_full (требует sudo; без passwordless sudo — пропускаем)
|
# Интеграционный тест tcp_proxy_full (требует sudo; без passwordless sudo — пропускаем)
|
||||||
echo ""
|
echo ""
|
||||||
if [ "$(id -u)" -eq 0 ]; then
|
if [ "$(uname -s)" != Linux ]; then
|
||||||
make -C tests check-proxy
|
echo "[SKIP] tcp_proxy_full integration tests require Linux networking tools"
|
||||||
make -C tests check-burst
|
elif [ "$(id -u)" -eq 0 ]; then
|
||||||
make -C tests check-load
|
"$MAKE_TOOL" -C tests check-proxy
|
||||||
|
"$MAKE_TOOL" -C tests check-burst
|
||||||
|
"$MAKE_TOOL" -C tests check-load
|
||||||
elif command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null; then
|
elif command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null; then
|
||||||
echo "=== tcp_proxy_full integration test (sudo) ==="
|
echo "=== tcp_proxy_full integration test (sudo) ==="
|
||||||
sudo -n make -C tests check-proxy
|
sudo -n "$MAKE_TOOL" -C tests check-proxy
|
||||||
echo "=== tcp_proxy_full burst test (sudo) ==="
|
echo "=== tcp_proxy_full burst test (sudo) ==="
|
||||||
sudo -n make -C tests check-burst
|
sudo -n "$MAKE_TOOL" -C tests check-burst
|
||||||
echo "=== tcp_proxy_full load test (sudo) ==="
|
echo "=== tcp_proxy_full load test (sudo) ==="
|
||||||
sudo -n make -C tests check-load
|
sudo -n "$MAKE_TOOL" -C tests check-load
|
||||||
else
|
else
|
||||||
echo "[SKIP] tcp_proxy_full integration test (requires passwordless sudo)"
|
echo "[SKIP] tcp_proxy_full integration test (requires passwordless sudo)"
|
||||||
fi
|
fi
|
||||||
|
|||||||
+16
-3
@@ -62,11 +62,24 @@ AC_ARG_ENABLE([hardening],
|
|||||||
[enable_hardening=no])
|
[enable_hardening=no])
|
||||||
|
|
||||||
if test "x$enable_hardening" = "xyes"; then
|
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_CFLAGS="-D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wformat=2"
|
||||||
HARDEN_LDFLAGS="-Wl,-z,relro -Wl,-z,now -pie"
|
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"
|
CFLAGS="$CFLAGS $HARDEN_CFLAGS"
|
||||||
LDFLAGS="$LDFLAGS $HARDEN_LDFLAGS"
|
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
|
fi
|
||||||
|
|
||||||
# ==================== НАДЁЖНЫЙ ДЕТЕКТ WINDOWS ====================
|
# ==================== НАДЁЖНЫЙ ДЕТЕКТ WINDOWS ====================
|
||||||
|
|||||||
+18
-8
@@ -1,6 +1,7 @@
|
|||||||
# Tests Makefile.am for utun - all tests with new ll_queue library
|
# Tests Makefile.am for utun - all tests with new ll_queue library
|
||||||
|
|
||||||
EXTRA_DIST = fixtures/reality_openssl_hello.hex data/silero_speech.pcm data/silero_speech.txt test_net.h run_tests.py test_run_tests.py
|
EXTRA_DIST = fixtures/reality_openssl_hello.hex data/silero_speech.pcm data/silero_speech.txt test_net.h \
|
||||||
|
run_tests.py test_run_tests.py test_build_script.py
|
||||||
|
|
||||||
# Порядок по замерам Linux: быстрые проверки раньше длительных; состав зависит от прежних configure-условий.
|
# Порядок по замерам Linux: быстрые проверки раньше длительных; состав зависит от прежних configure-условий.
|
||||||
|
|
||||||
@@ -715,23 +716,25 @@ test_media_delivery_chat_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
|||||||
all-local: copy-test-configs
|
all-local: copy-test-configs
|
||||||
|
|
||||||
copy-test-configs: $(check_PROGRAMS)
|
copy-test-configs: $(check_PROGRAMS)
|
||||||
@for conf in $(srcdir)/test_*.conf $(srcdir)/test_*.json; do \
|
@if test "$(abs_srcdir)" != "$(abs_builddir)"; then \
|
||||||
if test -f "$$conf"; then \
|
for conf in $(srcdir)/test_*.conf $(srcdir)/test_*.json; do \
|
||||||
cp -f "$$conf" "$(top_builddir)/tests/" 2>/dev/null || true; \
|
if test -f "$$conf"; then \
|
||||||
fi; \
|
cp -f "$$conf" "$(abs_builddir)/" || { echo "ERROR: cannot copy $$conf" >&2; exit 1; }; \
|
||||||
done 2>/dev/null || true
|
fi; \
|
||||||
|
done; \
|
||||||
|
fi
|
||||||
|
|
||||||
# Clean
|
# Clean
|
||||||
clean-local:
|
clean-local:
|
||||||
-rm -f $(check_PROGRAMS)
|
-rm -f $(check_PROGRAMS)
|
||||||
-rm -f $(top_builddir)/tests/test_*.conf $(top_builddir)/tests/test_*.json 2>/dev/null || true
|
@if test "$(abs_srcdir)" != "$(abs_builddir)"; then rm -f "$(abs_builddir)"/test_*.conf "$(abs_builddir)"/test_*.json; fi
|
||||||
-rm -rf $(top_builddir)/tests/logs 2>/dev/null || true
|
-rm -rf $(top_builddir)/tests/logs 2>/dev/null || true
|
||||||
|
|
||||||
# Test logs directory
|
# Test logs directory
|
||||||
TEST_LOG_DIR = $(top_builddir)/tests/logs
|
TEST_LOG_DIR = $(top_builddir)/tests/logs
|
||||||
|
|
||||||
# По умолчанию jobs = доступные CPU; TEST_JOBS=1 сохраняет последовательный запуск.
|
# По умолчанию jobs = доступные CPU; TEST_JOBS=1 сохраняет последовательный запуск.
|
||||||
check-local: check-runner $(check_PROGRAMS)
|
check-local: check-runner check-build-script $(check_PROGRAMS)
|
||||||
@python3 "$(srcdir)/run_tests.py" --log-dir "$(TEST_LOG_DIR)" $(check_PROGRAMS)
|
@python3 "$(srcdir)/run_tests.py" --log-dir "$(TEST_LOG_DIR)" $(check_PROGRAMS)
|
||||||
|
|
||||||
.PHONY: check-runner
|
.PHONY: check-runner
|
||||||
@@ -739,6 +742,13 @@ check-runner:
|
|||||||
@python3 "$(srcdir)/test_run_tests.py"
|
@python3 "$(srcdir)/test_run_tests.py"
|
||||||
@echo "[INFO] test runner regression checks passed"
|
@echo "[INFO] test runner regression checks passed"
|
||||||
|
|
||||||
|
.PHONY: check-build-script
|
||||||
|
check-build-script:
|
||||||
|
@if command -v python3 >/dev/null 2>&1; then \
|
||||||
|
python3 "$(srcdir)/test_build_script.py" "$(top_srcdir)/build.sh"; \
|
||||||
|
else echo "[SKIP] build.sh regression checks require python3"; fi
|
||||||
|
|
||||||
|
|
||||||
# Интеграционный тест tcp_proxy_full (требует sudo, TUN, iptables). Запускается из check.sh.
|
# Интеграционный тест tcp_proxy_full (требует sudo, TUN, iptables). Запускается из check.sh.
|
||||||
.PHONY: check-proxy
|
.PHONY: check-proxy
|
||||||
check-proxy:
|
check-proxy:
|
||||||
|
|||||||
@@ -0,0 +1,238 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Проверка настоящего build.sh на маленьком проекте Autotools, без изменения исходников uTun."""
|
||||||
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
import shlex
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
|
||||||
|
script = Path(sys.argv[1]).resolve()
|
||||||
|
base = Path(tempfile.mkdtemp(prefix='utun-build-tests-'))
|
||||||
|
log = base / 'commands.log'
|
||||||
|
environment = {**os.environ, 'ASAN_OPTIONS': 'detect_leaks=0'}
|
||||||
|
environment.pop('MAKEFLAGS', None)
|
||||||
|
environment.pop('MSYSTEM', None)
|
||||||
|
for name in ['CC', 'CFLAGS', 'CPPFLAGS', 'LDFLAGS', 'LIBS', 'DEBUG_FLAGS']:
|
||||||
|
environment.pop(name, None)
|
||||||
|
checks = []
|
||||||
|
default_compiler = 'clang' if sys.platform.startswith('freebsd') else 'gcc'
|
||||||
|
make_tool = environment.get('MAKE') or ('gmake' if sys.platform.startswith('freebsd') else 'make')
|
||||||
|
|
||||||
|
def write(name, content):
|
||||||
|
path = base / name
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.write_text(content)
|
||||||
|
return path
|
||||||
|
|
||||||
|
def run(args, ok=True, extra_environment=None):
|
||||||
|
if args[0] == 'make':
|
||||||
|
args = [make_tool, *args[1:]]
|
||||||
|
result = subprocess.run(args, cwd=base, env={**environment, **(extra_environment or {})}, text=True, capture_output=True)
|
||||||
|
with log.open('a') as output:
|
||||||
|
output.write(f'{args!r}\nexit={result.returncode}\n{result.stdout}{result.stderr}\n')
|
||||||
|
if ok and result.returncode:
|
||||||
|
raise AssertionError(f'{args!r}: exit={result.returncode}; see {log}\n{result.stderr[-1500:]}')
|
||||||
|
return result
|
||||||
|
|
||||||
|
def build(*args, **kwargs):
|
||||||
|
return run(['bash', 'build.sh', '--no-gui', '-j', '2', *args], **kwargs)
|
||||||
|
|
||||||
|
def check(name, condition):
|
||||||
|
if not condition:
|
||||||
|
raise AssertionError(f'{name}; see {log}')
|
||||||
|
checks.append(name)
|
||||||
|
print(f'[PASS] build.sh: {name}', flush=True)
|
||||||
|
|
||||||
|
def times():
|
||||||
|
return {str(p.relative_to(base)): p.stat().st_mtime_ns for p in base.rglob('*.o')}
|
||||||
|
|
||||||
|
def value():
|
||||||
|
binary = base / 'src/utun'
|
||||||
|
if not binary.exists():
|
||||||
|
binary = binary.with_suffix('.exe')
|
||||||
|
return run([str(binary)]).stdout.strip()
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.copyfile(script, base / 'build.sh')
|
||||||
|
actual_configuration = (script.parent / 'configure.ac').read_text()
|
||||||
|
hardening_start = actual_configuration.index('if test "x$enable_hardening" = "xyes"; then')
|
||||||
|
hardening_end = actual_configuration.index('# ==================== НАДЁЖНЫЙ ДЕТЕКТ WINDOWS', hardening_start)
|
||||||
|
hardening_rules = actual_configuration[hardening_start:hardening_end]
|
||||||
|
write('configure.ac', '''AC_INIT([build-test], [1.0])
|
||||||
|
AC_CONFIG_SRCDIR([src/main.c])
|
||||||
|
AC_CONFIG_HEADERS([config.h])
|
||||||
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
|
AC_CANONICAL_HOST
|
||||||
|
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||||
|
AC_PROG_CC
|
||||||
|
AC_PROG_RANLIB
|
||||||
|
AM_PROG_AR
|
||||||
|
AC_ARG_WITH([ffmpeg], [], [], [with_ffmpeg=no])
|
||||||
|
AC_ARG_ENABLE([hardening], [], [], [enable_hardening=no])
|
||||||
|
__HARDENING_RULES__
|
||||||
|
TEST_CONFIGURE_OPTION
|
||||||
|
AC_CONFIG_FILES([Makefile lib/Makefile lib/libopus/Makefile src/Makefile tests/Makefile])
|
||||||
|
AC_OUTPUT
|
||||||
|
'''.replace('__HARDENING_RULES__', hardening_rules))
|
||||||
|
write('m4/test.m4', 'AC_DEFUN([TEST_CONFIGURE_OPTION], [AC_DEFINE([TEST_OPTION], [1], [Test option])])\n')
|
||||||
|
write('Makefile.am', 'SUBDIRS = lib lib/libopus src tests\n')
|
||||||
|
write('lib/Makefile.am', 'noinst_LIBRARIES = libfixture.a\nlibfixture_a_SOURCES = fixture.c\n')
|
||||||
|
write('lib/fixture.c', 'int fixture(void) { return 0; }\n')
|
||||||
|
write('lib/libopus/Makefile.am', '''noinst_LIBRARIES = libopus_internal.a
|
||||||
|
libopus_internal_a_SOURCES = opus.c
|
||||||
|
CFLAGS = -g -Og -fno-omit-frame-pointer -fno-pie -fPIC
|
||||||
|
''')
|
||||||
|
write('lib/libopus/opus.c', 'int opus_fixture(void) { return 0; }\n')
|
||||||
|
write('src/Makefile.am', '''bin_PROGRAMS = utun
|
||||||
|
utun_SOURCES = main.c worker.c api.h nested.h
|
||||||
|
AM_CFLAGS = -DLOCAL_OPTION=1
|
||||||
|
utun_LDADD = ../lib/libfixture.a ../lib/libopus/libopus_internal.a
|
||||||
|
''')
|
||||||
|
write('src/main.c', '''#include <stdio.h>
|
||||||
|
#include "api.h"
|
||||||
|
int worker(void);
|
||||||
|
int fixture(void);
|
||||||
|
int opus_fixture(void);
|
||||||
|
int main(void) { printf("%d\\n", worker() + fixture() + opus_fixture()); return 0; }
|
||||||
|
''')
|
||||||
|
write('src/worker.c', '#include "api.h"\nint worker(void) { return VALUE + LOCAL_OPTION; }\n')
|
||||||
|
write('src/api.h', '#include "nested.h"\n')
|
||||||
|
write('src/nested.h', '#define VALUE 10\n')
|
||||||
|
test_rules = (script.parent / 'tests/Makefile.am').read_text()
|
||||||
|
config_rules = test_rules[test_rules.index('all-local: copy-test-configs'):test_rules.index('# Test logs directory')]
|
||||||
|
write('tests/Makefile.am', 'noinst_PROGRAMS = probe\nprobe_SOURCES = probe.c\n' + config_rules)
|
||||||
|
write('tests/probe.c', 'int main(void) { return 0; }\n')
|
||||||
|
package_tool = write('bin/pkg-config', '''#!/usr/bin/env bash
|
||||||
|
[[ "${BUILD_TEST_FFMPEG:-0}" = 1 ]] || exit 1
|
||||||
|
case "$1" in
|
||||||
|
--exists) exit 0 ;;
|
||||||
|
--modversion) echo 1.0 ;;
|
||||||
|
--cflags|--libs-only-L) exit 0 ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
''')
|
||||||
|
package_tool.chmod(0o755)
|
||||||
|
environment['PATH'] = str(base / 'bin') + os.pathsep + environment['PATH']
|
||||||
|
|
||||||
|
result = build()
|
||||||
|
check('fresh build generates, configures and builds dependencies', value() == '11' and (base / 'lib/libopus/libopus_internal.a').exists())
|
||||||
|
check('normal build includes tests', (base / 'tests/probe').exists() or (base / 'tests/probe.exe').exists())
|
||||||
|
initial = times()
|
||||||
|
result = build()
|
||||||
|
check('repeat build does not regenerate, configure or compile', initial == times() and 'INFO: configuring:' not in result.stdout and 'INFO: regenerating' not in result.stdout)
|
||||||
|
time.sleep(1.1)
|
||||||
|
write('src/nested.h', '#define VALUE 20\n')
|
||||||
|
result = build()
|
||||||
|
current = times()
|
||||||
|
check('transitive header rebuilds both dependent objects', current['src/main.o'] > initial['src/main.o'] and current['src/worker.o'] > initial['src/worker.o'])
|
||||||
|
check('header change preserves unrelated objects and configuration', current['lib/fixture.o'] == initial['lib/fixture.o'] and 'INFO: configuring:' not in result.stdout and value() == '21')
|
||||||
|
initial = times()
|
||||||
|
write('src/unused.h', '#define UNUSED 1\n')
|
||||||
|
build()
|
||||||
|
check('unrelated header does not recompile', initial == times())
|
||||||
|
|
||||||
|
write('asan_probe.c', 'int main(void) { return 0; }\n')
|
||||||
|
asan_supported = run([default_compiler, '-fsanitize=address', '-fno-pie', '-no-pie', 'asan_probe.c', '-o', 'asan_probe'], ok=False).returncode == 0
|
||||||
|
modes = ['asan', 'hardened', 'normal'] if asan_supported else ['hardened', 'normal']
|
||||||
|
if not asan_supported:
|
||||||
|
print('[SKIP] build.sh: ASAN runtime is unavailable in this compiler toolchain', flush=True)
|
||||||
|
for mode in modes:
|
||||||
|
args = [] if mode == 'normal' else ['--' + mode]
|
||||||
|
result = build(*args)
|
||||||
|
symbols = run(['nm', '-u', 'src/worker.o']).stdout
|
||||||
|
check(f'{mode}: changing mode reconfigures and cleans', 'INFO: configuring:' in result.stdout and 'INFO: cleaning objects:' in result.stdout)
|
||||||
|
check(f'{mode}: object instrumentation matches mode', ('__asan_' in symbols) == (mode == 'asan'))
|
||||||
|
if mode == 'asan':
|
||||||
|
check('ASAN skips tests and preserves unsanitized Opus', not (base / 'tests/probe').exists() and not (base / 'tests/probe.exe').exists() and '__asan_' not in run(['nm', '-u', 'lib/libopus/opus.o']).stdout)
|
||||||
|
initial = times()
|
||||||
|
result = build(*args)
|
||||||
|
check(f'{mode}: repeat build is incremental', initial == times() and 'INFO: configuring:' not in result.stdout and 'INFO: regenerating' not in result.stdout)
|
||||||
|
|
||||||
|
result = build('CFLAGS=-g -O0 -DUSER_OPTION=1')
|
||||||
|
check('quoted compiler flags are passed intact through configure', 'CFLAGS = -g -O0 -DUSER_OPTION=1' in (base / 'src/Makefile').read_text())
|
||||||
|
initial = times()
|
||||||
|
build('CFLAGS=-g -O0 -DUSER_OPTION=1')
|
||||||
|
check('repeat build with quoted flags is incremental', initial == times())
|
||||||
|
build()
|
||||||
|
compiler = shutil.which(default_compiler)
|
||||||
|
for directory in ['compiler-first', 'compiler-second']:
|
||||||
|
wrapper = write(directory + '/' + default_compiler, '#!/usr/bin/env bash\nexec ' + shlex.quote(compiler) + ' "$@"\n')
|
||||||
|
wrapper.chmod(0o755)
|
||||||
|
build(extra_environment={'PATH': str(base / 'compiler-first') + os.pathsep + environment['PATH']})
|
||||||
|
result = build(extra_environment={'PATH': str(base / 'compiler-second') + os.pathsep + environment['PATH']})
|
||||||
|
check('compiler path change is detected even with identical version', 'INFO: configuring:' in result.stdout)
|
||||||
|
build()
|
||||||
|
time.sleep(1.1)
|
||||||
|
write('src/Makefile.am', (base / 'src/Makefile.am').read_text().replace('LOCAL_OPTION=1', 'LOCAL_OPTION=2'))
|
||||||
|
result = build()
|
||||||
|
check('Makefile.am flag change rebuilds stale objects', 'INFO: regenerating' in result.stdout and value() == '22')
|
||||||
|
macro = base / 'm4/test.m4'
|
||||||
|
saved_time = macro.stat()
|
||||||
|
write('m4/test.m4', macro.read_text().replace('[1]', '[2]'))
|
||||||
|
os.utime(macro, ns=(saved_time.st_atime_ns, saved_time.st_mtime_ns))
|
||||||
|
build()
|
||||||
|
check('macro content change is detected with unchanged timestamp', '#define TEST_OPTION 2' in (base / 'config.h').read_text())
|
||||||
|
result = build(extra_environment={'BUILD_TEST_FFMPEG': '1'})
|
||||||
|
check('newly available optional dependency reconfigures', 'INFO: configuring:' in result.stdout and '--with-ffmpeg' in run(['./config.status', '--config']).stdout)
|
||||||
|
result = build()
|
||||||
|
check('disappearing optional dependency reconfigures', 'INFO: configuring:' in result.stdout and '--without-ffmpeg' in run(['./config.status', '--config']).stdout)
|
||||||
|
(base / 'src/.deps/worker.Po').unlink()
|
||||||
|
result = build()
|
||||||
|
check('missing dependencies force rebuild without configure', 'missing_dependencies=1' in result.stdout and 'INFO: configuring:' not in result.stdout)
|
||||||
|
write('src/.deps/worker.Po', '# dummy\n')
|
||||||
|
result = build()
|
||||||
|
check('dummy dependencies force rebuild', 'missing_dependencies=1' in result.stdout)
|
||||||
|
write('tests/test_preserve.conf', 'preserve configuration\n')
|
||||||
|
write('tests/test_preserve.json', '{"preserve":true}\n')
|
||||||
|
result = build('--clean')
|
||||||
|
check('explicit clean does not reconfigure', 'INFO: cleaning objects:' in result.stdout and 'INFO: configuring:' not in result.stdout)
|
||||||
|
check('in-source clean preserves configuration files', (base / 'tests/test_preserve.conf').exists() and (base / 'tests/test_preserve.json').exists())
|
||||||
|
result = build('--full')
|
||||||
|
check('explicit full regenerates, configures and rebuilds', 'INFO: regenerating' in result.stdout and 'INFO: configuring:' in result.stdout)
|
||||||
|
(base / 'src/Makefile').unlink()
|
||||||
|
result = build()
|
||||||
|
check('missing child Makefile is recreated', (base / 'src/Makefile').exists() and 'INFO: configuring:' in result.stdout)
|
||||||
|
run(['./configure', 'CFLAGS=-O0'])
|
||||||
|
result = build()
|
||||||
|
check('external configure is detected', 'INFO: configuring:' in result.stdout and 'CFLAGS = -g -O2' in (base / 'src/Makefile').read_text())
|
||||||
|
check('conflicting modes fail', build('--asan', '--hardened', ok=False).returncode != 0)
|
||||||
|
check('invalid job count fails', build('-j0', ok=False).returncode != 0)
|
||||||
|
|
||||||
|
state = (base / '.utun-build-state').read_bytes()
|
||||||
|
original = (base / 'src/worker.c').read_text()
|
||||||
|
write('src/worker.c', original + '\n#error deliberate_failure\n')
|
||||||
|
check('compiler failure propagates without committing state', build(ok=False).returncode != 0 and (base / '.utun-build-state').read_bytes() == state)
|
||||||
|
write('src/worker.c', original)
|
||||||
|
check('build recovers from compiler failure', build().returncode == 0 and value() == '22')
|
||||||
|
original = (base / 'configure.ac').read_text()
|
||||||
|
write('configure.ac', original + '\nm4_fatal([deliberate_autoconf_failure])\n')
|
||||||
|
check('autoreconf failure propagates through tee', build(ok=False).returncode != 0 and (base / '.utun-build-state').read_bytes() == state)
|
||||||
|
write('configure.ac', original)
|
||||||
|
build()
|
||||||
|
write('tools/chatgui/build/CMakeCache.txt', 'placeholder\n')
|
||||||
|
cmake = write('bin/cmake', '#!/usr/bin/env bash\nprintf "%s\\n" "$*" > gui-arguments\nexit 9\n')
|
||||||
|
cmake.chmod(0o755)
|
||||||
|
result = run(['bash', 'build.sh', '-j', '3'], ok=False)
|
||||||
|
check('GUI failure propagates and success is not printed', result.returncode != 0 and 'Build completed successfully!' not in result.stdout)
|
||||||
|
check('GUI receives selected job count', (base / 'gui-arguments').read_text().strip() == '--build tools/chatgui/build --parallel 3')
|
||||||
|
run(['make', 'distclean'])
|
||||||
|
output_dir = base / 'out'
|
||||||
|
output_dir.mkdir()
|
||||||
|
for args in [['../configure'], [make_tool, '-j2'], [make_tool, '-C', 'tests', 'clean-local']]:
|
||||||
|
result = subprocess.run(args, cwd=output_dir, env=environment, capture_output=True, text=True)
|
||||||
|
with log.open('a') as output:
|
||||||
|
output.write(f'OUT-OF-TREE {args!r}\nexit={result.returncode}\n{result.stdout}{result.stderr}\n')
|
||||||
|
if result.returncode:
|
||||||
|
raise AssertionError(f'out-of-tree {args!r}: see {log}')
|
||||||
|
if args[0] == make_tool and '-j2' in args:
|
||||||
|
check('out-of-tree build copies test configurations', (output_dir / 'tests/test_preserve.conf').exists())
|
||||||
|
check('out-of-tree clean removes copies and preserves source configurations',
|
||||||
|
not (output_dir / 'tests/test_preserve.conf').exists() and (base / 'tests/test_preserve.conf').exists())
|
||||||
|
print(f'[PASS] build.sh: {len(checks)} checks completed', flush=True)
|
||||||
|
shutil.rmtree(base)
|
||||||
|
except Exception:
|
||||||
|
print(f'[FAIL] build.sh: diagnostic files retained in {base}', file=sys.stderr)
|
||||||
|
raise
|
||||||
+13
-2
@@ -14,6 +14,18 @@ log ""
|
|||||||
SRC_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SRC_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
BUILD_DIR="$SRC_DIR/build"
|
BUILD_DIR="$SRC_DIR/build"
|
||||||
RUNTIME_PREFIX="${MSYSTEM_PREFIX:-/ucrt64}"
|
RUNTIME_PREFIX="${MSYSTEM_PREFIX:-/ucrt64}"
|
||||||
|
JOBS=$(nproc 2>/dev/null || echo 4)
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-j)
|
||||||
|
[[ $# -gt 1 ]] || { log "ERROR: -j requires a positive job count"; exit 1; }
|
||||||
|
shift; JOBS=$1 ;;
|
||||||
|
-j*) JOBS=${1#-j} ;;
|
||||||
|
*) log "ERROR: unknown argument: $1"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
[[ "$JOBS" =~ ^[1-9][0-9]*$ ]] || { log "ERROR: invalid job count: $JOBS"; exit 1; }
|
||||||
|
|
||||||
log "Source: $SRC_DIR"
|
log "Source: $SRC_DIR"
|
||||||
log "Build: $BUILD_DIR"
|
log "Build: $BUILD_DIR"
|
||||||
@@ -36,7 +48,7 @@ if [ "${MSYSTEM:-}" = UCRT64 ]; then
|
|||||||
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON 2>&1 | tee -a "$LOG_FILE" || {
|
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON 2>&1 | tee -a "$LOG_FILE" || {
|
||||||
log " ERROR: bundled rlottie configure failed"; exit 1;
|
log " ERROR: bundled rlottie configure failed"; exit 1;
|
||||||
}
|
}
|
||||||
cmake --build "$SRC_DIR/rlottie_build" --target rlottie --parallel 4 2>&1 | tee -a "$LOG_FILE" || {
|
cmake --build "$SRC_DIR/rlottie_build" --target rlottie --parallel "$JOBS" 2>&1 | tee -a "$LOG_FILE" || {
|
||||||
log " ERROR: bundled rlottie build failed"; exit 1;
|
log " ERROR: bundled rlottie build failed"; exit 1;
|
||||||
}
|
}
|
||||||
log " OK: bundled rlottie ready in $SRC_DIR/rlottie_build"
|
log " OK: bundled rlottie ready in $SRC_DIR/rlottie_build"
|
||||||
@@ -47,7 +59,6 @@ cmake -S "$SRC_DIR" -B "$BUILD_DIR" -G Ninja 2>&1 | tee -a "$LOG_FILE"
|
|||||||
test ${PIPESTATUS[0]} -eq 0 || { log "CMake configure FAILED"; exit 1; }
|
test ${PIPESTATUS[0]} -eq 0 || { log "CMake configure FAILED"; exit 1; }
|
||||||
|
|
||||||
log ""
|
log ""
|
||||||
JOBS=$(nproc 2>/dev/null || echo 4)
|
|
||||||
log "[2/4] Building (parallel: $JOBS jobs)..."
|
log "[2/4] Building (parallel: $JOBS jobs)..."
|
||||||
cmake --build "$BUILD_DIR" -j"$JOBS" 2>&1 | tee -a "$LOG_FILE"
|
cmake --build "$BUILD_DIR" -j"$JOBS" 2>&1 | tee -a "$LOG_FILE"
|
||||||
test ${PIPESTATUS[0]} -eq 0 || { log "Build FAILED"; exit 1; }
|
test ${PIPESTATUS[0]} -eq 0 || { log "Build FAILED"; exit 1; }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|||||||
Reference in new issue
Block a user