Files
JoinDomainGUI/packaging/build_windows_wine.sh
T
tuxgyverandClaude Opus 5 59b6c75942 fix: le .exe Windows demarrait sans plugin de plateforme Qt
Le hook PySide6 de PyInstaller importe QtCore pour localiser les plugins Qt.
Depuis Qt 6.10, Qt6Core.dll est lie a l'ICU de Windows (icuuc.dll, System32),
que Wine n'implemente pas : l'import echouait, le hook ne collectait aucun
plugin, et le binaire produit refusait de demarrer sous Windows avec
« could not initialize Qt platform plugin "windows" ».

- La build Wine epingle PySide6<6.10 (PYSIDE6_CONSTRAINT pour surcharger).
- Verification avant compilation que PySide6 s'importe reellement.
- Verification apres compilation que qwindows.dll est embarque.
- Reinstallation des dependances declenchee par empreinte de requirements.txt
  et de la contrainte, au lieu de la seule presence de PyInstaller.

Binaire verifie sous Wine : GUI (fenetre a trois onglets) et CLI OK, 45 Mo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 22:46:00 +02:00

110 lines
4.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# Fabrique le binaire Windows autonome (.exe) de JoinDomainGUI DEPUIS LINUX,
# en pilotant un Python Windows embarque dans un prefixe Wine dedie.
#
# PyInstaller ne cross-compile pas : on execute donc un vrai PyInstaller Windows
# sous Wine. Le .exe produit est un binaire Windows natif.
#
# Prerequis : wine (>= 8), curl, unzip.
set -euo pipefail
cd "$(dirname "$0")/.."
PYTHON_VERSION="${PYTHON_VERSION:-3.12.10}"
# Depuis Qt 6.10, Qt6Core.dll est lie a l'ICU fourni par Windows (icuuc.dll dans
# System32, present depuis Win10 1703). Wine ne fournit pas cette DLL : PySide6
# y devient inimportable, et le hook PySide6 de PyInstaller -- qui importe QtCore
# pour localiser les plugins Qt -- echoue alors SILENCIEUSEMENT et produit un
# .exe depourvu de plugin de plateforme ("could not initialize Qt platform
# plugin windows" au lancement). On contraint donc cette build a Qt 6.9.
PYSIDE6_CONSTRAINT="${PYSIDE6_CONSTRAINT:-PySide6<6.10}"
WINEPREFIX="${WINEPREFIX:-$PWD/.build-wine}"
export WINEPREFIX
export WINEARCH=win64
export WINEDEBUG="${WINEDEBUG:--all}"
PY_DIR="$WINEPREFIX/drive_c/python"
WPY="c:\\python\\python.exe"
CACHE=".build-wine-cache"
command -v wine >/dev/null || { echo "wine est requis (apt install wine)" >&2; exit 1; }
mkdir -p "$CACHE"
if [ ! -d "$WINEPREFIX" ]; then
echo ">> Initialisation du prefixe Wine ($WINEPREFIX)"
wineboot --init >/dev/null 2>&1
wineserver -w
fi
if [ ! -f "$PY_DIR/python.exe" ]; then
ZIP="$CACHE/python-$PYTHON_VERSION-embed-amd64.zip"
[ -f "$ZIP" ] || curl -fL --retry 3 -o "$ZIP" \
"https://www.python.org/ftp/python/$PYTHON_VERSION/python-$PYTHON_VERSION-embed-amd64.zip"
echo ">> Installation de Python $PYTHON_VERSION (Windows) dans le prefixe"
rm -rf "$PY_DIR"
mkdir -p "$PY_DIR"
unzip -q "$ZIP" -d "$PY_DIR"
# La distribution "embeddable" desactive site-packages : on le reactive
# pour que pip et les paquets installes soient importables.
sed -i 's/^#import site/import site/' "$PY_DIR"/python*._pth
fi
if [ ! -f "$PY_DIR/Scripts/pip.exe" ]; then
GETPIP="$CACHE/get-pip.py"
[ -f "$GETPIP" ] || curl -fL --retry 3 -o "$GETPIP" https://bootstrap.pypa.io/get-pip.py
echo ">> Installation de pip"
cp "$GETPIP" "$PY_DIR/get-pip.py"
wine "$WPY" c:\\python\\get-pip.py --no-warn-script-location
fi
# pip sous Wine est lent (plusieurs minutes pour le wheel PySide6) : on ne
# reinstalle que si requirements.txt ou la contrainte ont change (FORCE_DEPS=1
# pour forcer).
DEPS_STAMP="$PY_DIR/.deps-stamp"
WANT_HASH="$(cat requirements.txt <(echo "$PYSIDE6_CONSTRAINT") | sha256sum | cut -d' ' -f1)"
if [ "${FORCE_DEPS:-0}" = "1" ] || [ "$(cat "$DEPS_STAMP" 2>/dev/null)" != "$WANT_HASH" ]; then
echo ">> Installation des dependances Windows (peut prendre plusieurs minutes)"
printf '%s\n' "$PYSIDE6_CONSTRAINT" > "$PY_DIR/constraints.txt"
# Desinstallation prealable : pip ne retrograde pas toujours un paquet deja
# installe qui viole une contrainte nouvellement ajoutee.
wine "$WPY" -m pip uninstall -q -y PySide6 PySide6-Essentials PySide6-Addons shiboken6 >/dev/null 2>&1 || true
wine "$WPY" -m pip install -q --no-warn-script-location \
-c c:\\python\\constraints.txt -r requirements.txt pyinstaller
echo "$WANT_HASH" > "$DEPS_STAMP"
else
echo ">> Dependances Windows a jour (FORCE_DEPS=1 pour les reinstaller)"
fi
# Garde-fou : si PySide6 ne s'importe pas ici, le hook PyInstaller ne collectera
# aucun plugin Qt et produira un .exe casse sans le signaler. Echouer tot.
echo ">> Verification de PySide6 sous Wine"
if ! wine "$WPY" -c "import PySide6.QtWidgets" 2>&1; then
echo "ERREUR : PySide6 n'est pas importable sous Wine — le .exe produit serait" >&2
echo " depourvu de plugins Qt. Verifiez PYSIDE6_CONSTRAINT." >&2
exit 1
fi
echo ">> Compilation du .exe"
wine "$WPY" -m PyInstaller \
--noconfirm \
--onefile \
--windowed \
--name JoinDomainGUI \
--distpath dist/windows \
--workpath build/windows \
--specpath build/windows \
main.py
wineserver -w
# Garde-fou : sans qwindows.dll, l'application refuse de demarrer sous Windows.
if ! grep -qi 'qwindows.dll' build/windows/JoinDomainGUI/PKG-00.toc; then
echo "ERREUR : le plugin de plateforme qwindows.dll est absent du binaire." >&2
exit 1
fi
echo
echo "Binaire produit : dist/windows/JoinDomainGUI.exe"
echo "Lancement : clic droit -> Executer en tant qu'administrateur"