Files
utun2/tools/chatgui/build.sh
T

272 lines
13 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
set -eo pipefail
LOG_FILE="build_win.log"
> "$LOG_FILE"
log() { echo "$1"; echo "$1" >> "$LOG_FILE"; }
log "================================"
log "ChatGUI Build Script (MSYS2 UCRT64)"
log "================================"
log ""
SRC_DIR="$(cd "$(dirname "$0")" && pwd)"
BUILD_DIR="$SRC_DIR/build"
RUNTIME_PREFIX="${MSYSTEM_PREFIX:-/ucrt64}"
JOBS=$(nproc 2>/dev/null || echo 4)
BUILD_INSTALLER=0
while [[ $# -gt 0 ]]; do
case "$1" in
--installer) BUILD_INSTALLER=1 ;;
-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; }
CMAKE_ARGS=()
if [[ "$BUILD_INSTALLER" = 1 ]]; then
bash "$SRC_DIR/package_win.sh" --check 2>&1 | tee -a "$LOG_FILE"
CMAKE_ARGS+=(-DCMAKE_BUILD_TYPE=Release)
fi
log "Source: $SRC_DIR"
log "Build: $BUILD_DIR"
log ""
if [ -f "$BUILD_DIR/CMakeCache.txt" ]; then
OLD_GEN=$(grep 'CMAKE_GENERATOR:' "$BUILD_DIR/CMakeCache.txt" 2>/dev/null | cut -d= -f2)
if [ "$OLD_GEN" != "Ninja" ]; then
log "Generator changed ($OLD_GEN -> Ninja), cleaning build dir..."
rm -rf "$BUILD_DIR"
fi
fi
# Старые DLL приложения в build могут мешать запуску cc1/cc1plus после обновления MSYS2.
source "$SRC_DIR/compiler_runtime.sh"
if [ "${MSYSTEM:-}" = UCRT64 ]; then
chatgui_refresh_compiler_runtime "$BUILD_DIR" "$RUNTIME_PREFIX/bin" 2>&1 | tee -a "$LOG_FILE"
log "Preparing bundled rlottie..."
cmake -S "$SRC_DIR/rlottie_src" -B "$SRC_DIR/rlottie_build" -G Ninja \
-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON 2>&1 | tee -a "$LOG_FILE" || {
log " ERROR: bundled rlottie configure failed"; exit 1;
}
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 " OK: bundled rlottie ready in $SRC_DIR/rlottie_build"
fi
log "[1/4] Configuring CMake (generator: Ninja)..."
cmake -S "$SRC_DIR" -B "$BUILD_DIR" -G Ninja "${CMAKE_ARGS[@]}" 2>&1 | tee -a "$LOG_FILE"
test ${PIPESTATUS[0]} -eq 0 || { log "CMake configure FAILED"; exit 1; }
log ""
log "[2/4] Building (parallel: $JOBS jobs)..."
cmake --build "$BUILD_DIR" -j"$JOBS" 2>&1 | tee -a "$LOG_FILE"
test ${PIPESTATUS[0]} -eq 0 || { log "Build FAILED"; exit 1; }
log ""
log "[3/4] Deploying runtime DLLs..."
RLOTTIE_DLL="$SRC_DIR/rlottie_build/librlottie.dll"
[ -f "$RLOTTIE_DLL" ] || { log " ERROR: librlottie.dll not found: $RLOTTIE_DLL"; exit 1; }
cp "$RLOTTIE_DLL" "$BUILD_DIR/librlottie.dll" || {
log " ERROR: cannot copy $RLOTTIE_DLL -> $BUILD_DIR/librlottie.dll"; exit 1;
}
log " OK: librlottie.dll (from $RLOTTIE_DLL; copied to $BUILD_DIR/librlottie.dll)"
FFMPEG_BIN="$SRC_DIR/ffmpeg/ffmpeg_build/install/bin"
QT_PACKAGE_FILE="$BUILD_DIR/chatgui_qt_package.txt"
[ -f "$QT_PACKAGE_FILE" ] || { log " ERROR: Qt package information missing: $QT_PACKAGE_FILE"; exit 1; }
QT_PACKAGE=$(tr -d '\r\n' < "$QT_PACKAGE_FILE")
case "$QT_PACKAGE" in
Qt5|Qt6) QT_MAJOR="${QT_PACKAGE#Qt}" ;;
*) log " ERROR: invalid configured Qt package: $QT_PACKAGE"; exit 1 ;;
esac
log " Runtime package: $QT_PACKAGE"
QT_COMPONENTS=(Core Gui Network Widgets)
QT_PLUGINS=(platforms/qwindows.dll imageformats/qjpeg.dll imageformats/qwebp.dll)
if [ -f "$BUILD_DIR/chatgui_capture_enabled.txt" ] && [ "$(tr -d '\r\n' < "$BUILD_DIR/chatgui_capture_enabled.txt")" = 1 ]; then
QT_COMPONENTS+=(Multimedia MultimediaWidgets)
QT_PLUGINS+=(multimedia/ffmpegmediaplugin.dll)
fi
MAIN_DLLS=(
"Qt${QT_MAJOR}Core.dll" "Qt${QT_MAJOR}Gui.dll" "Qt${QT_MAJOR}Network.dll" "Qt${QT_MAJOR}Widgets.dll"
"platforms/qwindows.dll" "imageformats/qjpeg.dll" "imageformats/qwebp.dll"
"libstdc++-6.dll" "libgcc_s_*.dll" "libwinpthread-1.dll" "librlottie.dll" "zlib1.dll" "libcrypto-*.dll"
)
if [ -d "$FFMPEG_BIN" ]; then
MAIN_DLLS+=("avcodec-*.dll" "avformat-*.dll" "avutil-*.dll" "swscale-*.dll" "swresample-*.dll")
fi
for component in "${QT_COMPONENTS[@]}"; do MAIN_DLLS+=("Qt${QT_MAJOR}${component}.dll"); done
MAIN_DLLS+=("${QT_PLUGINS[@]}")
DLLS_PRESENT=1
for d in "${MAIN_DLLS[@]}"; do
if ! compgen -G "$BUILD_DIR/$d" >/dev/null; then
log " Main DLL missing: $d; deploying runtime DLLs"
DLLS_PRESENT=0
break
fi
done
[ "$DLLS_PRESENT" -ne 1 ] || log " Main runtime DLLs present; verifying their dependencies"
# Проверяем зависимости на каждом запуске: основные DLL не гарантируют полный комплект.
{
WINDEPLOYQT=""
for candidate in "$RUNTIME_PREFIX/bin/windeployqt${QT_MAJOR}.exe" "$RUNTIME_PREFIX/bin/windeployqt-qt${QT_MAJOR}.exe" "$RUNTIME_PREFIX/bin/windeployqt.exe"; do
[ -x "$candidate" ] || continue
tool_version=$("$candidate" --version 2>&1) || { log " WARN: cannot query $candidate: $tool_version"; continue; }
if [[ "$tool_version" =~ (^|[[:space:]])${QT_MAJOR}\.[0-9]+ ]]; then WINDEPLOYQT="$candidate"; break; fi
log " WARN: ignoring $candidate ($tool_version); build uses $QT_PACKAGE"
done
if [ -x "$WINDEPLOYQT" ]; then
log " Running $WINDEPLOYQT..."
if "$WINDEPLOYQT" --no-translations --no-compiler-runtime "$BUILD_DIR/vibechat.exe" 2>&1 | tee -a "$LOG_FILE"; then
log " OK: windeployqt completed"
else
RC=${PIPESTATUS[0]}
log " WARN: windeployqt failed (exit $RC); verifying direct DLL deployment"
fi
else
log " WARN: matching $QT_PACKAGE windeployqt not found; copying dependencies directly"
fi
# Основные Qt DLL и динамические плагины всегда берём из выбранного комплекта.
for component in "${QT_COMPONENTS[@]}"; do
qt_dll="Qt${QT_MAJOR}${component}.dll"
cp "$RUNTIME_PREFIX/bin/$qt_dll" "$BUILD_DIR/" || { log " ERROR: cannot deploy $qt_dll from $RUNTIME_PREFIX/bin"; exit 1; }
done
for plugin in "${QT_PLUGINS[@]}"; do
plugin_source=""
for dir in "$RUNTIME_PREFIX/share/qt${QT_MAJOR}/plugins" "$RUNTIME_PREFIX/lib/qt${QT_MAJOR}/plugins"; do
[ -f "$dir/$plugin" ] && { plugin_source="$dir/$plugin"; break; }
done
[ -n "$plugin_source" ] || { log " ERROR: $QT_PACKAGE plugin missing: $plugin"; exit 1; }
mkdir -p "$BUILD_DIR/${plugin%/*}"
cp "$plugin_source" "$BUILD_DIR/$plugin" || { log " ERROR: cannot deploy $plugin_source"; exit 1; }
log " OK: $plugin (from $plugin_source)"
done
# Резолвим НЕ-Qt зависимости (FFmpeg, mingw-runtime): windeployqt копирует только Qt.
# FFmpeg ищем ПЕРВЫМ в $FFMPEG_BIN: exe линкован именно под кастомный билд,
# а pacman-ffmpeg той же мажорной версии в /ucrt64/bin дал бы неверный набор кодеков.
OBJDUMP="objdump"; [ -n "${MSYSTEM_PREFIX:-}" ] && OBJDUMP="$MSYSTEM_PREFIX/bin/objdump"
command -v "$OBJDUMP" >/dev/null 2>&1 || { log " ERROR: $OBJDUMP not found"; exit 1; }
shopt -s nocasematch nullglob
# Обновляем весь комплект FFmpeg, включая DLL, не импортируемые напрямую приложением.
for src in "$FFMPEG_BIN/"*.dll; do
# Общий runtime берём из установленного UCRT64, а кодеки — из собственной сборки FFmpeg.
if chatgui_is_compiler_runtime_dll "${src##*/}"; then
src="$RUNTIME_PREFIX/bin/${src##*/}"
fi
cp "$src" "$BUILD_DIR/" || { log " ERROR: failed to copy $src"; exit 1; }
log " OK: $(basename "$src") (from $src)"
done
log " Resolving non-Qt dependencies recursively..."
log " Windows system DLLs are provided by the OS and excluded from deployment"
# Проверяем также уже скопированные DLL: наличие файла не гарантирует наличие его зависимостей.
todo=("$BUILD_DIR/vibechat.exe" "$BUILD_DIR/"*.dll "$BUILD_DIR/platforms/"*.dll "$BUILD_DIR/imageformats/"*.dll "$BUILD_DIR/multimedia/"*.dll)
declare -A scanned=()
MISSING=0
# Большой вывод objdump передаём через файл, чтобы не зависеть от here-string и pipe в MSYS2.
IMPORT_DUMP=$(mktemp) || {
log " ERROR: cannot create temporary file for DLL inspection"
exit 1
}
trap 'rm -f -- "$IMPORT_DUMP" || log " WARN: cannot remove temporary import dump: $IMPORT_DUMP"' EXIT
for ((i = 0; i < ${#todo[@]}; i++)); do
src="${todo[i]}"
key="${src,,}"
[ -n "${scanned[$key]:-}" ] && continue
scanned[$key]=1
CHECK_STARTED=$SECONDS
"$OBJDUMP" -p "$src" > "$IMPORT_DUMP" 2>&1 || {
RC=$?
log " ERROR: cannot inspect $src (exit=$RC, elapsed=$((SECONDS - CHECK_STARTED))s): $(cat "$IMPORT_DUMP")"
MISSING=$((MISSING + 1))
continue
}
PARSE_STARTED=$SECONDS
import_list=$(awk '/DLL Name:/ {print $3}' "$IMPORT_DUMP" 2>&1) || {
RC=$?
log " ERROR: cannot parse imports for $src (exit=$RC, elapsed=$((SECONDS - PARSE_STARTED))s): $import_list"
MISSING=$((MISSING + 1))
continue
}
while read -r dep; do
[ -z "$dep" ] && continue
case "$dep" in
kernel32.dll|msvcrt.dll|msvcp140.dll|vcruntime140.dll|ucrtbase.dll|\
user32.dll|gdi32.dll|gdi32full.dll|shell32.dll|shlwapi.dll|shcore.dll|advapi32.dll|\
ole32.dll|oleaut32.dll|comdlg32.dll|comctl32.dll|ws2_32.dll|wsock32.dll|\
iphlpapi.dll|netapi32.dll|dnsapi.dll|winhttp.dll|winmm.dll|imm32.dll|version.dll|\
setupapi.dll|crypt32.dll|authz.dll|bcrypt.dll|bcryptprimitives.dll|ncrypt.dll|dbghelp.dll|imagehlp.dll|kernelbase.dll|\
rpcrt4.dll|psapi.dll|powrprof.dll|cfgmgr32.dll|userenv.dll|secur32.dll|\
uxtheme.dll|dwmapi.dll|d3d9.dll|d3d11.dll|d3d12.dll|dxgi.dll|opengl32.dll|glu32.dll|\
wtsapi32.dll|mpr.dll|ntdll.dll|avicap32.dll|msimg32.dll|wldap32.dll|gdiplus.dll|usp10.dll|\
dwrite.dll|winspool.drv|api-ms-win-*)
continue ;;
mf.dll|mfplat.dll|mfreadwrite.dll|mfuuid.dll|dxva2.dll|mmdevapi.dll|propsys.dll|ksuser.dll|msdmo.dll)
continue ;;
esac
if [ ! -f "$BUILD_DIR/$dep" ]; then
dep_src=""
dep_dirs=("$FFMPEG_BIN" "$RUNTIME_PREFIX/bin" "$SRC_DIR/rlottie_build")
if chatgui_is_compiler_runtime_dll "$dep"; then
dep_dirs=("$RUNTIME_PREFIX/bin")
fi
for dir in "${dep_dirs[@]}"; do
if [ -f "$dir/$dep" ]; then
dep_src="$dir/$dep"
break
fi
done
if [ -z "$dep_src" ]; then
log " ERROR: $src requires $dep; not found in FFmpeg, UCRT64 or rlottie directories"
MISSING=$((MISSING + 1))
continue
fi
cp "$dep_src" "$BUILD_DIR/" || { log " ERROR: failed to copy $dep_src"; exit 1; }
log " OK: $dep (from $dep_src; required by $src)"
fi
todo+=("$BUILD_DIR/$dep")
done <<< "$import_list"
done
shopt -u nocasematch nullglob
[ "$MISSING" -eq 0 ] || { log " ERROR: runtime DLL deployment incomplete"; exit 1; }
# Проверяем тот же основной комплект после копирования.
log " Verifying main runtime DLLs..."
MISSING=0
for d in "${MAIN_DLLS[@]}"; do
if ! compgen -G "$BUILD_DIR/$d" >/dev/null; then
log " ERROR: $d MISSING in build dir"; MISSING=1
fi
done
if [ "$MISSING" -eq 1 ]; then
log " ERROR: required runtime DLLs missing"; exit 1
fi
}
cp "$SRC_DIR/setup_firewall.bat" "$BUILD_DIR/" 2>/dev/null && log " OK: setup_firewall.bat" || log " WARN: setup_firewall.bat not found"
log ""
log "[4/4] Verifying..."
EXE="$BUILD_DIR/vibechat.exe"
if [ -f "$EXE" ]; then
SIZE=$(du -h "$EXE" | cut -f1)
log " OK: vibechat.exe ($SIZE)"
if [[ "$BUILD_INSTALLER" = 1 ]]; then
log "Creating Windows installer..."
bash "$SRC_DIR/package_win.sh" 2>&1 | tee -a "$LOG_FILE"
fi
log ""
log "================================"
log "Build SUCCESS"
log "================================"
log "Executable: $EXE"
else
log " FAIL: vibechat.exe not found"
exit 1
fi