https://github.com/HackTechDev/Nethack54FreeBSD
Ce document résume la tentative de compilation du dépôt avec le fichier de
hints sys/unix/hints/linux.500, réalisée sur la machine locale. Il complète
le fichier README officiel du projet (à ne pas confondre avec celui-ci) et
les instructions de build déjà consignées dans CLAUDE.md.
La machine hôte n'est pas Linux mais FreeBSD (gcc (FreeBSD Ports Collection) 14.2.0, noyau FreeBSD 15.0). Le fichier de hints linux.500 a
tout de même été utilisé tel quel, à la demande explicite, plutôt qu'un hints
file FreeBSD dédié (qui n'existe pas dans cette distribution). Cela a permis
de mettre en évidence plusieurs incompatibilités entre les hypothèses du
système de build (pensé pour Linux/macOS) et l'environnement réel.
cd sys/unix
sh setup.sh hints/linux.500 # génère Makefile, dat/Makefile, doc/Makefile,
cd ../.. # src/Makefile, util/Makefile
gmake fetch-lua
gmake WANT_WIN_ALL=1 QT_SELECT=5 MOCPATH=/usr/local/bin/moc-qt5 \
LFLAGS=-L/usr/local/lib all
Le binaire src/nethack a fini par être produit avec succès (tty + curses +
X11 + Qt), et ./nethack --version rapporte correctement 5.0.0 avec le
hash du commit courant.
gmake install a ensuite été lancé avec les mêmes variables :
gmake WANT_WIN_ALL=1 QT_SELECT=5 MOCPATH=/usr/local/bin/moc-qt5 \
LFLAGS=-L/usr/local/lib install
make système incompatible avec les Makefiles générés.
/usr/bin/make sur FreeBSD est BSD make, qui échoue sur la syntaxe GNU
Make (ifdef, ifeq, endif) utilisée massivement par les Makefiles
générés par setup.sh. Il faut utiliser gmake (GNU Make 4.4.1, installé
séparément via les ports/pkg), disponible dans PATH.
→ Correction : préfixer toutes les commandes make par gmake.
Port Qt : binaire moc introuvable au chemin attendu.
Le Makefile calcule le chemin du compilateur de méta-objets Qt via
MOCPATH ?= $(QTDIR)/bin/$(MOC), avec MOC ?= moc. Sur cette machine,
moc n'existe pas sous ce nom ; le paquet Qt5 de FreeBSD l'installe sous
/usr/local/bin/moc-qt5 (ou /usr/local/lib/qt5/bin/moc).
→ Correction : passer MOCPATH=/usr/local/bin/moc-qt5 explicitement sur
la ligne de commande make.
Édition de liens : bibliothèques X11 introuvables malgré leur
présence.
Le bloc ifdef WANT_WIN_X11 du Makefile généré contient en dur
LFLAGS += -L/opt/X11/lib — un chemin typique d'une installation XQuartz
sur macOS, indépendant du hints file choisi. Sur FreeBSD, les bibliothèques
libX11, libXaw7, libXmu, libXext, libXt, libXpm existent bel et
bien, mais dans /usr/local/lib, absent du chemin de recherche du linker.
→ Correction : passer LFLAGS=-L/usr/local/lib sur la ligne de commande.
Comme il s'agit d'une variable positionnée en ligne de commande, les
affectations += ultérieures dans le Makefile s'ajoutent à cette valeur
au lieu de l'écraser (comportement standard de GNU Make), donc le chemin
/opt/X11/lib codé en dur reste présent mais n'empêche plus la résolution
des bibliothèques.
make install : sed -i en syntaxe GNU, incompatible avec le sed
BSD.
L'étape post-install qui désactive GDBPATH/PANICTRACE_GDB dans le
sysconf installé (utile quand gdb n'est pas présent) est codée en dur
dans le Makefile généré :
sed -i -e 's;...;' -e 's;...;' $(INSTDIR)/sysconf.
Cette syntaxe suppose GNU sed (-i sans argument obligatoire). Sur
FreeBSD, sed -i exige un argument de suffixe de sauvegarde juste après
(-i '' pour ne pas garder de backup) ; sed BSD interprète alors le
-e suivant comme ce suffixe, ce qui casse toute la commande
(sed: -e: No such file or directory). Comme c'est la dernière commande
de la séquence POSTINSTALL (chaînée par ;), son échec fait échouer
toute la cible install, alors que la copie du binaire et des données
dans nethackdir a déjà réussi avant ce point.
→ Correction : faire résoudre sed vers GNU sed (gsed, déjà présent
via les paquets FreeBSD) pour cette commande, sans modifier le dépôt ni
le système, via un lien symbolique local ajouté en tête de PATH :
mkdir -p /tmp/sedshim && ln -sf /usr/local/bin/gsed /tmp/sedshim/sed
PATH=/tmp/sedshim:$PATH gmake WANT_WIN_ALL=1 QT_SELECT=5 \
MOCPATH=/usr/local/bin/moc-qt5 LFLAGS=-L/usr/local/lib install
Notes : bdftopcf (conversion de police pour le port X11) est également
absent sur cette machine (pkg install bdftopcf le fournirait), mais son
échec n'est pas bloquant — ce n'est pas la dernière commande de la chaîne
POSTINSTALL, et il ne concerne que le rendu de police du port X11.
Bug amont (indépendant de FreeBSD) : -w Qt avec un espace casse le
parsing des arguments.
Dans src/earlyarg.c, early_options() traite chaque flag reconnu en
appelant consume_arg()/consume_two_args() pour le retirer du tableau
argv transmis au reste du programme — sauf le cas 'w' (windowtype) :
case 'w': /* windowtype: ... */
arg = lopt(arg, (ArgValRequired | ArgNamOneLetter | ArgErrComplain),
"-windowtype", origarg, &argc, &argv);
if (gc.cmdline_windowsys)
free((genericptr_t) gc.cmdline_windowsys);
gc.cmdline_windowsys = arg ? dupstr(arg) : NULL;
break; /* pas de consume_arg()/consume_two_args() ici */
Quand la valeur est donnée en token séparé (-w Qt ou --windowtype Qt),
lopt() consomme bien "Qt" dans ses copies locales de argc/argv,
mais ce résultat n'est jamais répercuté sur *argc_p/*argv_p (les
pointeurs de l'appelant). "Qt" reste donc dans le tableau d'arguments et
atterrit plus loin dans sys/unix/unixmain.c, où tout argument
positionnel restant après le traitement des options est interprété comme
l'ancien paramètre historique nethack N (nombre max de joueurs,
pré-SYSCF) :
if (argc > 1) {
int mxplyrs = atoi(argv[1]); /* atoi("Qt") == 0 */
boolean mx_ok = (mxplyrs > 0);
#ifdef SYSCF
config_error_add("%s%s%s",
mx_ok ? "MAXPLAYERS are set in sysconf file"
: "Expected MAXPLAYERS, found \"",
mx_ok ? "" : argv[1], mx_ok ? "" : "\"");
D'où le message observé au lancement :
* Expected MAXPLAYERS, found "Qt".
1 error on command line.
suivi en général d'un second message sans rapport (« Exec to uncompress
... failed », cf. point 6) si un fichier de sauvegarde .gz existe déjà
dans save/.
Le message apparaît en pratique à la fermeture du jeu, pas au
lancement. config_error_done() écrit ce texte via pline()/
raw_print() tout au début du programme, avant l'initialisation de
Qt/curses — mais cette écriture atterrit dans le buffer standard de
stdout, qui n'est pas vidé (flush) immédiatement. Le message reste
donc invisible tant que le processus tourne (masqué par la fenêtre Qt ou
l'écran alternatif de curses) et ne s'affiche dans le terminal qu'au
moment où le processus se termine normalement et vide ses buffers —
c'est-à-dire quand on quitte le jeu. Vérifié expérimentalement : en tuant
le processus brutalement (SIGTERM via timeout) avant une sortie
normale, le message n'apparaît jamais dans la capture, alors qu'il est
bien écrit en interne dès l'appel à -w Qt.
→ Correction : ne pas séparer le flag et sa valeur par un espace —
-wQt ou --windowtype=Qt fonctionnent parfaitement (vérifié, y compris
à la fermeture normale du jeu). Ce bug n'est pas spécifique à FreeBSD ; il
se reproduirait à l'identique sur Linux ou macOS.
Chemin gzip codé en dur pour Linux (/bin/gzip), absent sur
FreeBSD.
sys/unix/hints/linux.500 définit
NHCFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" — un
chemin qui suppose le FHS Linux. Sur FreeBSD, gzip fait partie du
système de base mais vit dans /usr/bin/gzip ; /bin/gzip n'existe pas.
COMPRESS étant un #define figé à la compilation (utilisé par
nh_uncompress()/docompress() dans src/files.c via fork()+exec()
avec le chemin littéral, pas une recherche PATH), le symptôme
n'apparaît qu'au moment où le jeu tente réellement de (dé)compresser un
fichier de sauvegarde (save/<uid><user>.gz) :
No such file or directory
Exec to uncompress save/1001util01 failed.
Contrairement aux points 1 à 4, un simple override de variable sur la
ligne de commande gmake ne suffit pas ici : le hints file ajoute son
propre -DCOMPRESS=... via += après tout override qu'on passerait en
NHCFLAGS/CFLAGS, et pour une macro redéfinie plusieurs fois sur la
même ligne de commande du compilateur, c'est la dernière définition qui
l'emporte — la nôtre serait donc écrasée.
→ Correction appliquée dans le dépôt, à la demande explicite (seule
modification de fichier source de cette analyse ; toutes les précédentes
n'étaient que des overrides de ligne de commande) :
- NHCFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\"
+ NHCFLAGS+=-DCOMPRESS=\"/usr/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\"
(sys/unix/hints/linux.500, ligne 168). Après modification du hints
file, il faut regénérer les Makefiles puis forcer la recompilation des
objets concernés, car make ne détecte pas qu'un #define a changé de
valeur (seules les dates de fichier sont suivies, pas le contenu des
flags) :
cd sys/unix && sh setup.sh hints/linux.500 && cd ../..
touch src/files.c src/mdlib.c util/sfctool.c # seuls fichiers référençant COMPRESS
gmake WANT_WIN_ALL=1 QT_SELECT=5 MOCPATH=/usr/local/bin/moc-qt5 \
LFLAGS=-L/usr/local/lib all
PATH=/tmp/sedshim:$PATH gmake WANT_WIN_ALL=1 QT_SELECT=5 \
MOCPATH=/usr/local/bin/moc-qt5 LFLAGS=-L/usr/local/lib install
Vérifié en recréant manuellement un save/1001util01.gz factice et en
relançant le binaire : décompression silencieuse et sans erreur.
Hormis le correctif du point 6 (modification volontaire et validée du
hints file), aucune autre modification n'a été apportée aux fichiers du
dépôt : les corrections des points 1 à 4 ont toutes été appliquées via des
variables et un PATH passés sur la ligne de commande gmake.
Le build ayant été fait avec WANT_WIN_ALL=1, le binaire installé embarque
déjà les interfaces tty, curses, X11 et Qt. Le port graphique n'est
cependant pas actif par défaut : le binaire a été compilé avec
-DDEFAULT_WINDOW_SYS=\"tty\".
Pour sélectionner Qt au lancement, coller la valeur au flag (voir bug n°5 ci-dessous pour la raison) :
/home/util01/nh/install/games/nethack -wQt
# ou
/home/util01/nh/install/games/nethack --windowtype=Qt
Le script wrapper nethack installé se contente de propager tous les
arguments ("$@") au binaire réel dans nethackdir, donc l'option
fonctionne aussi bien via le script que via l'exécutable direct.
Un lancement de test de 3 secondes avec cette option ne produit aucune
erreur et se termine proprement (le DISPLAY de la session, :0.0, est
valide). Cela confirme que l'exécutable tente bien de démarrer le port Qt
sans échouer immédiatement, mais l'affichage effectif de la fenêtre n'a pas
été vérifié visuellement depuis cet environnement.
Pour rendre Qt permanent sans repasser -w Qt à chaque lancement, ajouter
dans le fichier de configuration runtime (~/.nethackrc ou
NETHACKOPTIONS) :
OPTIONS=windowtype:Qt
L'option de ligne de commande a priorité sur cette entrée, qui elle-même a
priorité sur le défaut de compilation (tty).
Pré-requis : Qt étant une interface graphique, un serveur X accessible
(DISPLAY valide) est nécessaire — contrairement à tty ou curses qui
tournent directement dans un terminal.
Les correctifs 1 à 4 sont spécifiques à cet hôte (FreeBSD avec make
BSD, sed BSD, moc fourni par le paquet Qt5 des ports, bibliothèques X11
dans /usr/local/lib) et ne reflètent pas un défaut du hints file
linux.500 lui-même sur une vraie machine Linux. Ils ne sont donc pas
répercutés dans les instructions générales de CLAUDE.md, qui restent
focalisées sur le cas Linux/macOS standard décrit par le projet.
Le correctif 6 (/bin/gzip → /usr/bin/gzip) est, lui, spécifique à
FreeBSD mais désormais corrigé dans le dépôt (modification volontaire du
hints file, pas un simple override de commande) : un build linux.500 sur
une vraie machine Linux n'en a pas besoin, mais la modification n'y nuit
pas non plus puisque /bin/gzip reste la valeur par défaut la plus courante
sur Linux — seul FreeBSD diverge.
Le bug 5 (-w Qt avec espace) est en revanche un défaut réel de
NetHack 5.0.0, indépendant de la plateforme : il se reproduirait
identiquement sur Linux ou macOS. Il n'a pas été corrigé dans le dépôt (ce
serait un patch de src/earlyarg.c, hors du périmètre « configuration de
build » de cette analyse) ; le contournement (-wQt au lieu de -w Qt)
suffit en pratique.