Commandes d’index et de bibliographie

Les programmes qui fabriquent index et bibliographies — makeindex, xindy, bibtex, biber — ne sont pas des macros LaTeX mais des exécutables autonomes. Chacun décide donc de la façon d’écrire ses arguments, du code de sortie qu’il renvoie et de l’endroit où va son journal. Et voici le fait qui casse silencieusement l’intégration continue : makeindex renvoie le code de sortie 0 même après avoir jeté des entrées, et bibtex renvoie 0 même quand il signale qu’une référence citée est introuvable. L’échec n’est pas consigné dans l’état de la compilation mais dans les fichiers .ilg et .blg. Cette page regarde les quatre du côté de la ligne de commande, et non du côté LaTeX ; l’écriture des entrées d’index et la conception d’une base bibliographique sont traitées ailleurs.

bibtex doc ou makeindex doc.idx : lequel prend l’extension

Il n’y a rien à déduire ici, cela s’apprend : les programmes bibliographiques ne prennent pas d’extension, les programmes d’index si. bibtex et biber reçoivent le nom de travail du document et ouvrent eux-mêmes .aux ou .bcf. Se tromper là-dessus produit un message étonnamment peu utile : bibtex doc.tex répond I couldn t open file name doc.tex.aux et sort avec 1, tandis que biber doc.tex répond ERROR - Cannot find 'doc.tex.bcf'! Tous deux se sont contentés d’ajouter une extension au nom fourni ; aucun ne dira que le .tex était l’erreur. makeindex, upmendex et texindy prennent au contraire le fichier d’entrée lui-même : on écrit donc doc.idx. -o change le nom de sortie et -s désigne un style.

terminal
bibtex   doc          # job name, no extension  -> reads doc.aux, writes doc.bbl
biber    doc          # job name, no extension  -> reads doc.bcf, writes doc.bbl
makeindex doc.idx     # the file itself         -> writes doc.ind and doc.ilg
upmendex -o doc.ind doc.idx
texindy  -C utf8 -L german-din -o doc.ind doc.idx

Les codes de sortie, et où l’intégration continue laisse passer un échec

Le tableau ci-dessous consigne des valeurs réellement observées sur cette machine sous TeX Live 2024. Il montre que la frontière entre avertissement et échec est tracée différemment par chaque programme. bibtex ne renvoie 2 que s’il a imprimé des messages d’erreur ; une clé de citation absente du .bib compte comme un avertissement, et le statut reste 0. makeindex renvoie 1 quand le fichier d’entrée manque, mais reste à 0 quel que soit le nombre d’entrées qu’il jette à l’intérieur. En ne surveillant que le statut de latexmk ou de votre tâche d’intégration continue, vous pouvez donc obtenir une coche verte sur une compilation où des entrées ont disparu de l’index et où une référence est restée vide. Pour les index et les bibliographies, la bonne défense consiste à inspecter les fichiers .ilg et .blg plutôt que le code de sortie.

SituationCode de sortie et journal (mesuré sur TeX Live 2024)
makeindex (entries rejected)0. Les entrées rejetées ne figurent que dans le .ilg ; avec -q elles disparaissent aussi de l’écran
makeindex (no input file)1, avec Input index file nosuch.idx not found. et un résumé d’usage sur une ligne
upmendex (no input file)255, en affichant Nothing written in output file. et 1 errors, written in doc.ilg.
bibtex (warnings only)0. Warning--I didn t find a database entry for "key" ne compte pas comme un échec
bibtex (error messages)2, pour une erreur de syntaxe dans le .bib ou pour I found no database files
biber0 pour de simples avertissements, 2 dès qu’il imprime ERROR - ; le bilan apparaît en fin sous la forme INFO - WARNINGS: 1

Lire le .ilg de makeindex : c’est là que sont passées les entrées rejetées

Chaque exécution de makeindex écrit à la fois un .ind — l’index qui sera composé — et un .ilg, journal du travail. Une exécution saine est laconique : Scanning input file doc.idx....done (6 entries accepted, 0 rejected)., puis Sorting entries....done (19 comparisons)., puis Generating output file doc.ind....done (20 lines written, 0 warnings). Ce n’est guère exagéré de dire que seuls les nombres entre parenthèses méritent lecture. Donnez-lui un .idx abîmé : le compte accepted baisse et les raisons s’alignent : !! Input index error (file = bad.idx, line = 4): suivi de -- Incomplete first argument (premature LFD). Le code de sortie reste pourtant 0. Vérifier que le compte accepted correspond au nombre de commandes \index écrites évite déjà la plupart des accidents. La version livrée avec TeX Live 2024 est d’ailleurs makeindex 2.17, qui se présente comme (kpathsea + Thai support).

terminal
makeindex doc.idx
# This is makeindex, version 2.17 [TeX Live 2024] (kpathsea + Thai support).
# Scanning input file doc.idx....done (6 entries accepted, 0 rejected).
# Sorting entries....done (19 comparisons).
# Generating output file doc.ind....done (20 lines written, 0 warnings).

grep -c "^\\\\indexentry" doc.idx   # compare this with "entries accepted"
grep "rejected"          doc.ilg   # the number CI should be watching

Ce programme discret a une origine surprenante. Il a été écrit par Pehong Chen, mais les remerciements de sa page de manuel consignent que « Leslie Lamport contributed significantly to the design of MakeIndex ». L’auteur de LaTeX a donc participé de près à la conception du programme d’index — d’où le fait que la syntaxe de \index semble prolonger LaTeX au lieu d’y être greffée. L’usage de @ dans \index{clé@affiché} et la question des clés de tri pour les mots accentués sont détaillés sur la page consacrée aux index eux-mêmes.

Quel programme d’index choisir : les mêmes quatre mots, triés par les trois

Il n’y a qu’un critère : la langue indexée demande-t-elle une collation ? Mettez les quatre mots Zeta, Ähre, Apfel et Öl tels quels dans un .idx et confiez-les aux trois programmes : la différence saute aux yeux. makeindex a produit Apfel, Zeta, Ähre, Öl — Ä et Ö ont en UTF-8 des valeurs d’octets supérieures à Z et tombent donc après la fin de l’alphabet. texindy -C utf8 -L german-din et upmendex ont tous deux produit Ähre, Apfel, Öl, Zeta, traitant Ä comme A et Ö comme O, exactement comme le veut la règle DIN allemande. xindy (de Joachim Schrod, release 2.5.1) y parvient par des modules de langue ; upmendex (version 1.08) par l’algorithme de collation d’ICU 74.2.

ProgrammeComment il a trié les mêmes quatre motsQuand le choisir
makeindexApfel, Zeta, Ähre, Öl — le non-ASCII atterrit après ZAnglais seul, ou si vous fournirez les clés de tri à la main
texindyÄhre, Apfel, Öl, Zeta — avec -L german-dinLangues européennes ; nommer la langue dans -L suffit
upmendexÄhre, Apfel, Öl, Zeta — collationné via ICUJaponais et écritures mêlées ; il lit toujours les styles makeindex
mendexDevine l’encodage d’entrée et affiche (guessed encoding #4: UTF-8 = utf8)Fonds pLaTeX anciens ; pour du neuf, préférez upmendex

Deux précautions pratiques. D’abord, le .ind écrit par texindy n’a pas la même structure que celui de makeindex : il utilise \lettergroup pour titrer chaque groupe alphabétique et écrit lui-même les définitions avec \providecommand. Si votre document les redéfinit, il y aura collision : regardez donc la sortie une fois lors du changement. Ensuite, xindy tourne sur Common Lisp — CLISP 2.49.93 dans cette édition —, ce qui alourdit le démarrage et peut se sentir nettement sur un index volumineux. Quand du japonais est en jeu, upmendex est à la fois plus rapide et plus docile.

L’étrange recensement à la fin d’un .blg de bibtex

Ouvrez doc.blg après avoir lancé bibtex doc : au-delà des avertissements apparaît un tableau inhabituel — if$ -- 47, while$ -- 2, swap$ -- 1, substring$ -- 6, et ainsi de suite. C’est le décompte du nombre d’exécutions de chaque instruction de la machine à pile interne de BibTeX : 237 appels au total dans cette exécution. Une telle chose existe parce qu’un fichier de style .bst n’est pas un fichier de configuration mais un programme destiné à cette machine virtuelle. Plus haut dans le même .blg figure Capacity: max_strings=200000, hash_size=200000, hash_prime=170003, chiffres hérités des budgets mémoire des années 1980 ; ce sont les plafonds derrière le cri qui commence par Sorry---you ve exceeded BibTeX s sur une bibliographie très volumineuse. L’auteur est Oren Patashnik, de Stanford, et la version présente dans TeX Live 2024 est BibTeX 0.99d.

BibTeX possède des descendants 8 bits et compatibles Unicode, tous livrés avec TeX Live 2024. bibtex8 se présente comme « 8-bit Big BibTeX version 0.99d-x4.02 » ; bibtexu est le « UTF-8 Big BibTeX » et est construit avec ICU 74.2. Pour le japonais, il y a pbibtex (famille pTeX) et upbibtex (famille upTeX, qui s’annonce comme upBibTeX 0.99d-j0.36-u1.30 (utf8.uptex)). Tous descendent de la même lignée 0.99d ; seuls le traitement des caractères et la collation diffèrent. Deux erreurs dominent en pratique. Un .bib mal formé donne Illegal end of database file---line 14 of file broken.bib et I m skipping whatever remains of this entry, avec le code de sortie 2 ; un \bibliography pointant vers un fichier inexistant donne I found no database files---while reading file doc.aux.

biber : un journal qui étiquette chaque ligne INFO, WARN ou ERROR

biber est une conception plus récente écrite en Perl, et jusqu’à la forme de son journal diffère. Il débute par INFO - This is Biber 2.19 puis rend compte, ligne étiquetée par ligne étiquetée, du .bcf lu, du nombre de clés de citation trouvées, de la locale appliquée et du .bbl écrit — chaque ligne préfixée INFO -. Quand quelque chose cloche, l’étiquette change, ainsi WARN - I didn t find a database entry for 'missingkey' (section 0), et un bilan suit à la fin : INFO - WARNINGS: 1. Cette lisibilité machine est la plus grande différence pratique avec bibtex : une vérification d’intégration continue s’écrit en comptant WARN - et ERROR - avec grep. L’accident courant est un nom d’entrée mal saisi ; biber doc.tex affiche ERROR - Cannot find 'doc.tex.bcf'! et sort avec 2. Notez enfin que biber et bibtex écrivent tous deux un fichier d’extension .blg : si vous avez essayé les deux, lisez la première ligne pour savoir lequel vous consultez.

terminal
biber doc
# INFO - This is Biber 2.19
# INFO - Found 2 citekeys in bib section 0
# INFO - Output to doc.bbl
# WARN - I didn t find a database entry for 'missingkey' (section 0)
# INFO - WARNINGS: 1

# a CI check that the exit code will not give you
grep -c "^WARN -\|^ERROR -" doc.blg
grep "rejected" doc.ilg

L’ordre d’exécution, et qui compte les passes à votre place

L’ordre est : composer, puis index et bibliographie, puis composer, puis composer encore. La première passe fait écrire à LaTeX les fichiers .idx et .aux — ou .bcf sous biblatex ; on lance ensuite ces programmes dessus pour produire .ind et .bbl ; on recompose pour les intégrer ; et si la numérotation a bougé, une fois de plus. L’ennui est que le nombre de passes n’est pas fixe, et c’est précisément pour cela qu’existent des outils comme latexmk. Taper les commandes à la main ne vaut la peine que pour isoler l’étape défaillante — et même cela suit un ordre fixe. Vérifiez d’abord si .idx ou .bcf a bien été produit : sinon, le problème est du côté de LaTeX. Lisez ensuite .ilg et .blg : s’ils existent, le problème est dans le programme. Recomposez enfin et voyez si .ind et .bbl sont vraiment arrivés dans le corps. Ces trois étapes ramènent la cause à presque un seul point.

terminal
# the classic Japanese sequence, written out
uplatex   paper          # writes paper.aux and paper.idx
upbibtex  paper          # reads paper.aux -> paper.bbl
upmendex  paper.idx      # reads paper.idx -> paper.ind
uplatex   paper          # pulls both in
uplatex   paper          # settles the numbering

# and the same thing delegated
latexmk paper.tex