Création de packages et de classes

Ouvrez la distribution d’un paquet LaTeX et vous n’y trouverez peut-être aucun .sty. À la place figurent deux fichiers, .dtx et .insune mise en œuvre de la programmation lettrée où le code et son commentaire habitent un seul fichier. Passez le .dtx de booktabs dans docstrip et il annonce Lines processed: 1053 / Comments removed: 882 / Codelines passed: 161. Sur 1053 lignes, 161 seulement sont du code ; les 85 % restants sont de la prose. Donnez le même fichier à pdflatex et cette prose devient un manuel PDF composé. Cette page va du rassemblement, dans votre propre .sty, des macros que vous recollez sans cesse dans le préambule, jusqu’à l’écriture d’un .dtx, aux tests et au dépôt sur CTAN.

Le squelette .sty — écrire une date active la vérification de version

Un paquet est un fichier .sty, dans votre projet ou installé quelque part. Ses deux premières lignes servent à se présenter : \NeedsTeXFormat{LaTeX2e} indique le format requis et \ProvidesPackage{name}[date version description] déclare le nom du paquet et sa version. Le nom doit correspondre au basename du fichier. La partie entre crochets est facultative, mais l’écrire rapporte quelque chose de concret : les utilisateurs peuvent alors exiger une date minimale avec \usepackage{mypackage}[2027/01/01], et une copie plus ancienne produit LaTeX Warning: You have requested, on input line 2, version '2027/01/01' of package mypackage, but only version '2026/08/17 v1.0 ...' is available. La date s’écrit sous la forme YYYY/MM/DD. Cette seule ligne supprimera, dans quelques années, un courriel du genre « ça ne marche pas ».

Dans le corps, on tire les dépendances avec \RequirePackage{...} — l’équivalent de \usepackage dans un .sty. Chargez xcolor pour la couleur, tikz pour le dessin, et ainsi de suite. Pour dire quelque chose à l’utilisateur, on emploie \PackageWarning{name}{message}, ou \PackageError{name}{message}{help} lorsqu’on ne peut plus continuer. Toutes deux prennent le nom du paquet en premier argument, de sorte que celui qui lit le journal voit d’un coup d’œil d’où vient le message. La mécanique d’acceptation de \usepackage[option]{name}\DeclareOption avec \ProcessOptions, ainsi que le couple \DeclareKeys / \ProcessKeyOptions fourni par le noyau actuel — est commune aux classes et se trouve rassemblée sur la page des classes. Les étapes sont identiques pour un paquet. Le paquet kvoptions, que l’on croise dans des .sty plus anciens, est une génération antérieure de la même idée.

mypackage.sty
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypackage}[2026/08/17 v1.0 My helpers]

\RequirePackage{xcolor}

\newcommand{\greet}[1][world]{Hello, \textcolor{blue}{#1}!}

\endinput

Écrire son propre .dtx et son .ins — ce que signifie %<*package>

Le procédé du .dtx est presque gênant de simplicité : un % en début de ligne sépare le commentaire du code. Les lignes commençant par % sont de la prose ; le reste est du code. Voilà pourquoi un même fichier se lit de deux façons. Lancez tex mypackage.ins et docstrip jette la prose et écrit le .sty ; lancez pdflatex mypackage.dtx et la prose est composée comme corps de texte tandis que le code est cité avec des numéros de ligne. Les portions à extraire sont marquées par des gardes, %<*package> et %</package>, et la ligne \generate{\file{mypackage.sty}{\from{mypackage.dtx}{package}}} du .ins dit : « écris tout ce qui se trouve dans la garde package vers mypackage.sty ». Les noms de gardes sont libres, si bien qu’un seul .dtx peut engendrer ensemble un .sty, un .cls et un fichier de configuration. Ce que vous placez entre \preamble et \endpreamble est ajouté en commentaire en tête de chaque fichier engendré — c’est là que va la mention de licence.

mypackage.dtx
% \iffalse meta-comment
% Copyright (C) 2026 Example Author
% This work may be distributed and/or modified under the conditions of
% the LaTeX Project Public License, version 1.3c or later.
% \fi
%
% \iffalse
%<*driver>
\documentclass{ltxdoc}
\usepackage{mypackage}
\EnableCrossrefs
\CodelineIndex
\begin{document}
\DocInput{mypackage.dtx}
\PrintIndex
\end{document}
%</driver>
%<package>\NeedsTeXFormat{LaTeX2e}
%<package>\ProvidesPackage{mypackage}
%<package>  [2026/08/17 v1.0 A demonstration package]
% \fi
%
% \title{The \textsf{mypackage} package}
% \author{Example Author}
% \maketitle
%
% \section{Usage}
% \DescribeMacro{\greet}
% |\greet| prints a greeting; the optional argument sets the name.
%
% \StopEventually{}
%
% \section{Implementation}
%    \begin{macrocode}
%<*package>
\RequirePackage{xcolor}
\newcommand{\greet}[1][world]{Hello, \textcolor{blue}{#1}!}
%</package>
%    \end{macrocode}
% \Finale
\endinput

Le côté commentaire ne demande qu’une poignée de commandes. \DocInput{fichier} est la bête de somme qui relit le .dtx, et la portion clôturée par %<*driver>%</driver> est la petite configuration de document qui s’en charge (avec ltxdoc). L’implémentation se cite dans \begin{macrocode}\end{macrocode}, par convention avec quatre espaces d’indentation sur les lignes de clôture. \DescribeMacro{\commande} met une commande en valeur dans le texte destiné à l’utilisateur et l’inscrit à l’index. \StopEventually{} marque la frontière où commence l’implémentation, ce qui compte lorsqu’on engendre une version abrégée réservée aux utilisateurs. Enfin, \Finale met de l’ordre dans l’index et l’historique des modifications. Exécutez le .dtx ci-dessus avec un .ins de six lignes et vous obtenez Lines processed: 41 / Comments removed: 24 / Codelines passed: 10, et le .sty engendré s’ouvre sur la note automatique %% This is file 'mypackage.sty', generated with the docstrip utility.

mypackage.ins
\input docstrip.tex
\keepsilent
\preamble
Generated from mypackage.dtx -- do not edit this file directly.
\endpreamble
\generate{\file{mypackage.sty}{\from{mypackage.dtx}{package}}}
\endbatchfile

Un .dtx se périme tout seul — booktabs ne se compose plus

Un écueil mérite d’être connu avant de s’y mettre : le .sty peut continuer de fonctionner alors que le .dtx ne se compose plus. Sous TeX Live 2024, pdflatex booktabs.dtx produit 127 erreurs et aucun PDF. La première est ! Improper alphabetic constant, et l’arrêt survient aux abords de la \CharacterTable. Pendant ce temps, booktabs.sty lui-même fonctionne parfaitement. La cause tient au déplacement du socle documentaire : doc.sty dans TeX Live 2024 est en v3.0m, datée du 2022/11/13 — la « V3 » réécrite par Frank Mittelbach — tandis que booktabs.dtx date toujours de janvier 2020. Le booktabs.pdf livré dans la distribution porte cette même date de janvier 2020 et n’a jamais été régénéré depuis. Par contraste, sur le même TeX Live 2024, multirow.dtx (30 pages), tabularx.dtx (12 pages) et array.dtx (35 pages) se composent tous sans le moindre avertissement : ce n’est donc pas un défaut du mécanisme mais un problème d’entretien. Compilez réellement votre propre .dtx à chaque version.

Nul besoin de partir d’une page blanche. TeX Live livre une introduction nommée dtxtut, accompagnée d’un couple de gabarits, skeleton.dtx et skeleton.ins. Les traces des paquets qui en sont partis subsistent à des endroits inattendus : la ligne 18 de skeleton.ins porte \usedir{tex/latex/skeleton}, et la même ligne figure encore à la ligne 36 de booktabs.ins — le skeleton du gabarit n’a jamais été renommé. En parcourant les 1402 fichiers .ins de source/latex dans TeX Live 2024, booktabs est le seul à traîner encore ce reliquat. Le détail est attendrissant, mais la leçon est nette : si vous partez du gabarit, cherchez-en le nom avant de publier.

Écrire en expl3 : \ProvidesExplPackage

Si le corps doit être en expl3, remplacez la ligne d’identification par \ProvidesExplPackage{name}{date}{version}{description}. Outre le découpage en quatre arguments, elle possède une autre propriété importante : la commande se termine en exécutant \ExplSyntaxOn. Dès la ligne qui suit la déclaration, la syntaxe expl3 est donc disponible sans que vous ayez jamais écrit \ExplSyntaxOn. Le .sty ci-dessous n’en contient pas une seule occurrence, et pourtant \tl_new:N comme \NewDocumentCommand fonctionnent tels quels. La lecture d’expl3 proprement dite figure sur la page expl3.

expldemo.sty
\NeedsTeXFormat{LaTeX2e}
% four arguments, and it turns on expl3 syntax by itself
\ProvidesExplPackage{expldemo}{2026/08/17}{1.0}{Expl demo}

\tl_new:N \l_expldemo_tl
\tl_set:Nn \l_expldemo_tl { from~expl3 }

\NewDocumentCommand \shout { } { \tl_use:N \l_expldemo_tl }

Tester et fabriquer la distribution : l3build

Plutôt que de taper tex mypackage.ins et pdflatex mypackage.dtx à la main chaque fois, on peut confier la besogne à l3build, maintenu par le LaTeX Project (la version livrée avec TeX Live 2024 est celle du 2024-02-08). Placez à la racine du projet un build.lua listant le nom du module et ses fichiers : l3build unpack exécute le .ins et dépose le .sty dans build/unpacked/, tandis que l3build doc compose le .dtx en un PDF sous build/doc/. L’avantage est qu’aucun produit engendré ne salit le répertoire de travail. l3build check est le banc d’essai : il confronte les documents de test .lvt aux fichiers .tlg de journal attendu dans testfiles/ et affiche un écart dès que la sortie change. Quand le résultat composé est gardé par la machine et non par l’œil, refactoriser cesse d’être effrayant.

build.lua
module       = "mypackage"
sourcefiles  = {"mypackage.dtx", "mypackage.ins"}
installfiles = {"mypackage.sty"}
uploadconfig = { pkg = "mypackage" }

-- l3build unpack   -> build/unpacked/mypackage.sty
-- l3build doc      -> build/doc/mypackage.pdf
-- l3build check    -> run testfiles/*.lvt against *.tlg
-- l3build ctan     -> mypackage-ctan.zip, ready to upload

Publier sur CTAN : contenu de l’archive et licence

Lancez l3build ctan et vous obtenez une unique archive zip prête à être envoyée. Elle contient un répertoire portant le nom du paquet, mypackage/, avec les sources (.dtx et .ins) et le manuel PDF compilé. Ne pas y mettre le .sty est l’usage dans ce milieu ; celui qui reçoit l’archive le régénère depuis le .ins. Ajoutez un README et un journal des modifications et la forme est correcte. Attention toutefois : l3build ctan ramasse les PDF trouvés dans le répertoire de travail, si bien que les tirages d’essai qui traînent se retrouvent empaquetés avec — listez l’archive avec unzip -l après construction et vérifiez. L’envoi lui-même passe par le formulaire web de CTAN, mais TeX Live livre aussi ctan-o-mat, qui valide et téléverse depuis la ligne de commande à partir d’un fichier de configuration contenant description, coordonnées et licence.

Il faut arrêter une licence. CTAN demande que les conditions de diffusion soient énoncées, et le choix apparaît aussi dans les informations de paquet de TeX Live. Le standard de fait de ce monde est la LPPL (LaTeX Project Public License) ; comment choisir et en quoi les versions diffèrent — notamment où se trouve réellement la clause « renommez-le si vous le modifiez » — est exposé sur la page consacrée à la licence. Inscrivez les conditions retenues à trois endroits : le méta-commentaire en tête du .dtx, le \preamble du .ins (qui coiffe chaque fichier engendré et atteint donc ceux qui n’ont reçu que le .sty), et le README. Arrivé là, votre paquet devient quelque chose qu’un inconnu pourra ouvrir avec texdoc dans quelques années.

  • Écrivez toujours la date de \ProvidesPackage sous la forme YYYY/MM/DD. Sans elle, les utilisateurs ne peuvent exiger de version.
  • Compilez le .dtx à chaque fois. Un .sty qui fonctionne encore tandis que le .dtx échoue arrive vraiment — voyez booktabs.
  • Si vous partez du gabarit, cherchez-en le nom. Un reliquat de skeleton survit encore dans TeX Live 2024.
  • Vérifiez l’archive de l3build ctan avec unzip -l. Elle ramasse les PDF qui traînent dans le répertoire de travail.
  • Indiquez la licence à trois endroits : le .dtx, le \preamble du .ins et le README. Elle doit atteindre celui qui n’a reçu que le .sty.