Code source.
https://github.com/HackTechDev/Nethack54FreeBSD
Captures d'écran.
Analyse de compilation de NetHack 5.0.0 pour FreeBSD 15.0
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.
Contexte
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.
Étapes suivies
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
Problèmes rencontrés et corrections
-
makesystème incompatible avec les Makefiles générés./usr/bin/makesur FreeBSD est BSDmake, qui échoue sur la syntaxe GNU Make (ifdef,ifeq,endif) utilisée massivement par les Makefiles générés parsetup.sh. Il faut utilisergmake(GNU Make 4.4.1, installé séparément via les ports/pkg), disponible dansPATH. → Correction : préfixer toutes les commandesmakepargmake. -
Port Qt : binaire
mocintrouvable au chemin attendu. Le Makefile calcule le chemin du compilateur de méta-objets Qt viaMOCPATH ?= $(QTDIR)/bin/$(MOC), avecMOC ?= moc. Sur cette machine,mocn'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 : passerMOCPATH=/usr/local/bin/moc-qt5explicitement sur la ligne de commandemake. -
Édition de liens : bibliothèques X11 introuvables malgré leur présence. Le bloc
ifdef WANT_WIN_X11duMakefilegénéré contient en durLFLAGS += -L/opt/X11/lib— un chemin typique d'une installation XQuartz sur macOS, indépendant du hints file choisi. Sur FreeBSD, les bibliothèqueslibX11,libXaw7,libXmu,libXext,libXt,libXpmexistent bel et bien, mais dans/usr/local/lib, absent du chemin de recherche du linker. → Correction : passerLFLAGS=-L/usr/local/libsur 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/libcodé en dur reste présent mais n'empêche plus la résolution des bibliothèques. -
make install:sed -ien syntaxe GNU, incompatible avec lesedBSD. L'étape post-install qui désactiveGDBPATH/PANICTRACE_GDBdans lesysconfinstallé (utile quandgdbn'est pas présent) est codée en dur dans leMakefilegénéré :sed -i -e 's;...;' -e 's;...;' $(INSTDIR)/sysconf. Cette syntaxe suppose GNU sed (-isans argument obligatoire). Sur FreeBSD,sed -iexige un argument de suffixe de sauvegarde juste après (-i ''pour ne pas garder de backup) ;sedBSD interprète alors le-esuivant 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équencePOSTINSTALL(chaînée par;), son échec fait échouer toute la cibleinstall, alors que la copie du binaire et des données dansnethackdira déjà réussi avant ce point. → Correction : faire résoudresedvers 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 dePATH: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 installNotes :
bdftopcf(conversion de police pour le port X11) est également absent sur cette machine (pkg install bdftopcfle fournirait), mais son échec n'est pas bloquant — ce n'est pas la dernière commande de la chaînePOSTINSTALL, et il ne concerne que le rendu de police du port X11. -
Bug amont (indépendant de FreeBSD) :
-w Qtavec un espace casse le parsing des arguments. Danssrc/earlyarg.c,early_options()traite chaque flag reconnu en appelantconsume_arg()/consume_two_args()pour le retirer du tableauargvtransmis 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 Qtou--windowtype Qt),lopt()consomme bien"Qt"dans ses copies locales deargc/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 danssys/unix/unixmain.c, où tout argument positionnel restant après le traitement des options est interprété comme l'ancien paramètre historiquenethack 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
.gzexiste déjà danssave/.Le message apparaît en pratique à la fermeture du jeu, pas au lancement.
config_error_done()écrit ce texte viapline()/raw_print()tout au début du programme, avant l'initialisation de Qt/curses — mais cette écriture atterrit dans le buffer standard destdout, 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 (SIGTERMviatimeout) 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 —-wQtou--windowtype=Qtfonctionnent 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
gzipcodé en dur pour Linux (/bin/gzip), absent sur FreeBSD.sys/unix/hints/linux.500définitNHCFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\"— un chemin qui suppose le FHS Linux. Sur FreeBSD,gzipfait partie du système de base mais vit dans/usr/bin/gzip;/bin/gzipn'existe pas.COMPRESSétant un#definefigé à la compilation (utilisé parnh_uncompress()/docompress()danssrc/files.cviafork()+exec()avec le chemin littéral, pas une recherchePATH), 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
gmakene suffit pas ici : le hints file ajoute son propre-DCOMPRESS=...via+=après tout override qu'on passerait enNHCFLAGS/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, carmakene détecte pas qu'un#definea 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 installVérifié en recréant manuellement un
save/1001util01.gzfactice 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.
Lancer le jeu avec le support Qt
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.
Portée de ces observations
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.