Paket- und Klassenentwicklung

Öffnet man die Distribution eines LaTeX-Pakets, findet sich womöglich gar keine .sty. Stattdessen liegen dort zwei Dateien, .dtx und .inseine Umsetzung des literarischen Programmierens, bei der Code und Kommentar in einer einzigen Datei wohnen. Schickt man die .dtx von booktabs durch docstrip, meldet es Lines processed: 1053 / Comments removed: 882 / Codelines passed: 161. Von 1053 Zeilen sind nur 161 tatsächlich Code; die übrigen 85 % sind Prosa. Gibt man dieselbe Datei an pdflatex, wird aus dieser Prosa ein gesetztes PDF-Handbuch. Diese Seite führt vom Zusammenfassen der immer wieder in die Präambel kopierten Makros in eine eigene .sty über das Schreiben einer .dtx und das Testen bis zur Veröffentlichung auf CTAN.

Das .sty-Grundgerüst – ein Datum bringt Versionsprüfung

Ein Paket ist eine .sty-Datei, im eigenen Projekt oder irgendwo installiert. Die ersten beiden Zeilen dienen der Selbstauskunft: \NeedsTeXFormat{LaTeX2e} nennt das benötigte Format, \ProvidesPackage{name}[date version description] erklärt Paketname und Ausgabe. Der Name muss dem Dateibasenamen entsprechen. Der Klammerteil ist optional, bringt aber etwas Konkretes: Nutzende können dann mit \usepackage{mypackage}[2027/01/01] ein Mindestdatum verlangen, und eine ältere Fassung erzeugt 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. Das Datum gehört in die Form YYYY/MM/DD. Diese eine Zeile erspart in einigen Jahren eine Rückfrage der Art „es geht einfach nicht“.

Im Rumpf zieht man Abhängigkeiten mit \RequirePackage{...} herein – dem .sty-Gegenstück zu \usepackage. Für Farbe lädt man xcolor, zum Zeichnen tikz und so fort. Um den Nutzenden etwas mitzuteilen, dient \PackageWarning{name}{message}, und wenn es nicht weitergeht, \PackageError{name}{message}{help}. Beide nehmen den Paketnamen als erstes Argument, sodass beim Lesen des Logs sofort erkennbar ist, woher die Meldung stammt. Die Maschinerie zum Annehmen von \usepackage[option]{name}\DeclareOption mit \ProcessOptions sowie das Paar \DeclareKeys / \ProcessKeyOptions des aktuellen Kernels – teilt sich mit Klassen und ist auf der Klassenseite gesammelt. Beim Schreiben eines Pakets sind die Schritte dieselben. Das Paket kvoptions, das einem in älteren .sty-Dateien begegnet, ist eine frühere Generation derselben Idee.

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

\RequirePackage{xcolor}

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

\endinput

Eine eigene .dtx und .ins schreiben – was %<*package> bedeutet

Der Kniff hinter .dtx ist beinahe verlegen einfach: Ein % am Zeilenanfang trennt Kommentar von Code. Zeilen mit % sind Prosa, alle übrigen sind Code. Deshalb lässt sich eine Datei auf zwei Weisen lesen. tex mypackage.ins lässt docstrip die Prosa verwerfen und die .sty schreiben; pdflatex mypackage.dtx setzt die Prosa als Fließtext, während der Code mit Zeilennummern zitiert wird. Welche Abschnitte extrahiert werden, markieren Guards: %<*package> und %</package>. Die Zeile \generate{\file{mypackage.sty}{\from{mypackage.dtx}{package}}} in der .ins sagt: „Schreibe alles im Guard package nach mypackage.sty.“ Die Guard-Namen sind frei wählbar, eine .dtx kann also .sty, .cls und eine Konfigurationsdatei gemeinsam erzeugen. Was zwischen \preamble und \endpreamble steht, wird jeder erzeugten Datei als Kommentar vorangestellt – dort steht der Lizenzhinweis.

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

Die Kommentarseite braucht nur eine Handvoll Befehle. \DocInput{Datei} ist das Arbeitspferd, das die .dtx wieder einliest, und der von %<*driver>%</driver> eingefasste Abschnitt ist die kleine Dokumentkonfiguration dafür (mit ltxdoc). Die Implementierung wird in \begin{macrocode}\end{macrocode} zitiert, üblicherweise mit vier Leerzeichen Einrückung auf den Zaunzeilen. \DescribeMacro{\Befehl} hebt einen Befehl im nutzerseitigen Text hervor und trägt ihn ins Register ein. \StopEventually{} markiert die Grenze, ab der die Implementierung beginnt – wichtig, wenn eine kürzere Fassung nur für Nutzende erzeugt wird. \Finale schließlich räumt Register und Änderungshistorie auf. Führt man die obige .dtx mit einer sechszeiligen .ins aus, erscheint Lines processed: 41 / Comments removed: 24 / Codelines passed: 10, und die erzeugte .sty beginnt mit dem automatischen Hinweis %% 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

Eine .dtx altert von selbst – booktabs lässt sich nicht mehr setzen

Vor dem Schreiben lohnt eine Warnung: Die .sty kann weiter funktionieren, während die .dtx sich nicht mehr setzen lässt. Unter TeX Live 2024 wirft pdflatex booktabs.dtx 127 Fehler und erzeugt überhaupt kein PDF. Der erste lautet ! Improper alphabetic constant und tritt in der Nähe der \CharacterTable auf. Die booktabs.sty selbst arbeitet dabei tadellos. Der Grund liegt darin, dass sich der Untergrund der Dokumentation verschoben hat: doc.sty in TeX Live 2024 ist v3.0m vom 2022/11/13 – Frank Mittelbachs neu geschriebenes „V3“ –, während booktabs.dtx noch vom Januar 2020 stammt. Auch die mitgelieferte booktabs.pdf trägt eben dieses Datum vom Januar 2020 und wurde seither nie neu erzeugt. Zum Vergleich lassen sich auf demselben TeX Live 2024 multirow.dtx (30 Seiten), tabularx.dtx (12 Seiten) und array.dtx (35 Seiten) allesamt ohne Warnung setzen; es handelt sich also nicht um einen Konstruktionsfehler, sondern um ein Wartungsproblem. Die eigene .dtx bei jeder Veröffentlichung tatsächlich bauen.

Man muss nicht bei einem leeren Blatt beginnen. TeX Live liefert eine Einführung namens dtxtut mit, und dazu ein Vorlagenpaar, skeleton.dtx und skeleton.ins. Spuren von Paketen, die damit begannen, überleben an unerwarteter Stelle: Zeile 18 von skeleton.ins lautet \usedir{tex/latex/skeleton}, und dieselbe Zeile steht bis heute in Zeile 36 von booktabs.ins – das skeleton der Vorlage wurde nie umbenannt. Durchsucht man alle 1402 .ins-Dateien unter source/latex in TeX Live 2024, trägt einzig booktabs diesen Rest noch mit sich. Ein liebenswertes Detail, die Lehre aber ist klar: Wer von der Vorlage ausgeht, sucht vor der Auslieferung nach deren Namen.

In expl3 schreiben: \ProvidesExplPackage

Soll der Rumpf in expl3 entstehen, tritt an die Stelle der Identifikationszeile \ProvidesExplPackage{name}{date}{version}{description}. Neben der Aufteilung in vier Argumente hat der Befehl eine weitere wichtige Eigenschaft: Er führt zum Schluss \ExplSyntaxOn aus. Ab der Zeile nach der Deklaration steht die expl3-Syntax also bereit, ohne dass \ExplSyntaxOn je geschrieben werden müsste. Die untenstehende .sty enthält es kein einziges Mal, und dennoch funktionieren \tl_new:N wie \NewDocumentCommand genau so. Wie expl3 selbst zu lesen ist, steht auf der expl3-Seite.

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 }

Testen und die Distribution bauen: l3build

Statt jedes Mal tex mypackage.ins und pdflatex mypackage.dtx von Hand zu tippen, lässt sich die Arbeit an l3build übergeben, gepflegt vom LaTeX Project (die Fassung in TeX Live 2024 ist die Ausgabe vom 2024-02-08). Legt man im Projektwurzelverzeichnis eine build.lua mit Modulnamen und Dateiliste an, führt l3build unpack die .ins aus und legt die .sty unter build/unpacked/ ab, während l3build doc die .dtx setzt und ein PDF unter build/doc/ erzeugt. Der Vorteil: Nichts Erzeugtes verschmutzt das Arbeitsverzeichnis. l3build check ist die Testumgebung: Sie gleicht .lvt-Testdokumente gegen .tlg-Dateien mit erwarteter Logausgabe in testfiles/ ab und zeigt bei jeder Änderung einen Unterschied. Sobald Satzergebnisse maschinell statt mit dem Auge gesichert sind, verliert das Refaktorieren seinen Schrecken.

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

Auf CTAN veröffentlichen: Inhalt des Zip und Lizenz

Ein Aufruf von l3build ctan erzeugt ein einzelnes Zip, bereit zum Hochladen. Darin liegt ein nach dem Paket benanntes Verzeichnis mypackage/ mit den Quellen (.dtx und .ins) und dem gebauten PDF-Handbuch. Die .sty mitzuliefern ist hier unüblich; wer das Archiv erhält, erzeugt sie aus der .ins. Ergänzt um README und Änderungsprotokoll stimmt die Form. Zu beachten ist allerdings, dass l3build ctan die im Arbeitsverzeichnis gefundenen PDFs einsammelt, sodass herumliegende Probedrucke mit eingepackt werden – nach dem Bauen mit unzip -l den Inhalt auflisten und prüfen. Der Upload selbst läuft über das Webformular von CTAN, doch TeX Live liefert auch ctan-o-mat mit, das anhand einer Konfigurationsdatei mit Beschreibung, Kontaktangaben und Lizenz von der Kommandozeile aus prüft und hochlädt.

Eine Lizenz muss festgelegt werden. CTAN verlangt, dass die Verbreitungsbedingungen genannt werden, und die Wahl erscheint auch in der Paketinformation von TeX Live. Der De-facto-Standard dieser Welt ist die LPPL (LaTeX Project Public License); wie man wählt und worin sich die Fassungen unterscheiden – insbesondere, wo die Klausel „bei Änderung umbenennen“ tatsächlich steht – legt die Lizenzseite dar. Die gewählten Bedingungen gehören an drei Stellen: in den Meta-Kommentar am Kopf der .dtx, in die \preamble der .ins (die jeder erzeugten Datei vorangeht und so auch jene erreicht, die nur die .sty erhalten haben) und in die README. So weit gebracht, wird das eigene Paket zu etwas, das jemand in einigen Jahren mit texdoc öffnen kann.

  • Das Datum in \ProvidesPackage stets als YYYY/MM/DD schreiben. Ohne es können Nutzende keine Version verlangen.
  • Die .dtx jedes Mal bauen. Dass die .sty läuft, während die .dtx scheitert, kommt tatsächlich vor – siehe booktabs.
  • Wer von der Vorlage ausgeht, sucht nach deren Namen. Ein skeleton-Rest lebt in TeX Live 2024 noch.
  • Das Zip von l3build ctan mit unzip -l kontrollieren. Es sammelt PDFs aus dem Arbeitsverzeichnis ein.
  • Die Lizenz an drei Stellen nennen: .dtx, \preamble der .ins und README. Sie muss auch jene erreichen, die nur die .sty bekamen.