LaTeX パッケージの配布物を開くと、.sty が入っていないことがあります。代わりにあるのは .dtx と .ins の 2 ファイル——コードと解説が同じ一つのファイルに同居した、文芸的プログラミングの実装です。実際に booktabs の .dtx を docstrip に通すと、Lines processed: 1053 / Comments removed: 882 / Codelines passed: 161 と表示されます。1053 行のうち実際のコードは 161 行だけで、残りの 85 % は散文。同じファイルを pdflatex にかければ、その散文が組版された PDF マニュアルになります。このページは、毎回プリアンブルに貼っているマクロを自分の .sty にまとめるところから、.dtx を書き、テストし、CTAN に出すところまでを扱います。
.sty の骨格 — 日付を書くと版のチェックが効く
パッケージは、プロジェクト内か導入済みのどこかにある .sty ファイルです。先頭の 2 行は自己申告——\NeedsTeXFormat{LaTeX2e} が必要なフォーマットを、\ProvidesPackage{名前}[日付 版 説明] がパッケージ名と版を宣言します。名前は必ずファイルの basename と一致させてください。角括弧の中身は省略できますが、書いておくと具体的な見返りがあります。利用者が \usepackage{mypackage}[2027/01/01] のように最低限必要な日付を指定できるようになり、手元の版が古ければ次の警告が出ます——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. 日付は YYYY/MM/DD 形式で書くこと。この一行が、数年後に「なぜか動かない」という問い合わせを一つ減らします。
本体では、他のパッケージへの依存を \RequirePackage{...} で読み込みます——.sty の中における \usepackage 相当です。色を使うなら xcolor、作図なら tikz を、この形で読みます。利用者に何かを伝えたいときは \PackageWarning{名前}{メッセージ}、続行できない事態なら \PackageError{名前}{メッセージ}{ヘルプ} を使います。どちらもパッケージ名を第 1 引数に取るので、ログを見た利用者がどこから来たメッセージか一目で分かります。\usepackage[オプション]{名前} の形でオプションを受け取る仕組み——\DeclareOption/\ProcessOptions と、現在のカーネルが備える \DeclareKeys/\ProcessKeyOptions——はクラスと共通なので、クラスのページにまとめてあります。パッケージを書くときもそのまま同じ手順です(古い .sty で見かける kvoptions パッケージも、同じ役目を担う先行世代の道具です)。
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypackage}[2026/08/17 v1.0 My helpers]
\RequirePackage{xcolor}
\newcommand{\greet}[1][world]{Hello, \textcolor{blue}{#1}!}
\endinput.dtx と .ins を自分で書く — %<*package> の意味
.dtx の仕掛けはごく単純で、行頭の % がコードと解説を分けるというだけです。% で始まる行は解説、そうでない行はコード。だから同じファイルが二通りに読めます。tex mypackage.ins を走らせると docstrip が解説を捨てて .sty を書き出し、pdflatex mypackage.dtx を走らせると今度は解説が本文として組まれ、コードは番号つきで引用されます。切り出す範囲は %<*package> と %</package> の ガード で囲んで示し、.ins 側の \generate{\file{mypackage.sty}{\from{mypackage.dtx}{package}}} が「package というガードの中身を mypackage.sty に書き出せ」と指示します。ガードの名前は自由なので、一つの .dtx から .sty と .cls と設定ファイルをまとめて生成することもできます。\preamble … \endpreamble に書いた文言は、生成される全ファイルの先頭にコメントとして入ります——ライセンス表示の置き場所です。
% \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解説側で使う命令はごく少数です。\DocInput{ファイル} が .dtx 自身を読み込ませる本体で、%<*driver> … %</driver> に囲まれた部分がそのための小さな文書クラス設定になります(ltxdoc を使います)。実装の引用は \begin{macrocode} … \end{macrocode} で囲み、行頭を 4 個の空白でそろえるのが約束です。\DescribeMacro{\命令} は利用者向けの説明で命令を目立たせ、索引にも登録します。\StopEventually{} は「ここから先は実装」という境目で、利用者向けだけの短い版を生成するときに効きます。最後の \Finale が索引と変更履歴の後始末をします。上の .dtx と 6 行の .ins を実際に走らせると Lines processed: 41 / Comments removed: 24 / Codelines passed: 10 と表示され、生成された .sty の先頭には %% This is file 'mypackage.sty', generated with the docstrip utility. という自動生成の断り書きが入ります。
\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.dtx は勝手に古びる — booktabs が組めなくなっている件
.dtx を書くときに覚えておくべき落とし穴があります。.sty が動き続けても、.dtx のほうが組めなくなることがあるのです。TeX Live 2024 で pdflatex booktabs.dtx を実行すると、127 個のエラーを出して PDF がまったく生成されません。最初のエラーは ! Improper alphabetic constant で、\CharacterTable のあたりで止まります。booktabs.sty 自体は完全に正常に動くのに、です。原因はドキュメント側の土台が入れ替わったことにあります——TeX Live 2024 の doc.sty は v3.0m(2022/11/13)、Frank Mittelbach による書き直し版の「V3」で、booktabs.dtx は 2020 年 1 月のままです。配布物に同梱されている booktabs.pdf も 2020 年 1 月の日付で、それ以来一度も再生成されていません。ちなみに同じ TeX Live 2024 で multirow.dtx(30 ページ)・tabularx.dtx(12 ページ)・array.dtx(35 ページ)はいずれも警告なく組めるので、これは仕組みの欠陥ではなく手入れの問題です。自分の .dtx は、リリースのたびに実際に組んでみること。
ゼロから書き起こす必要はありません。TeX Live には dtxtut という入門が同梱されていて、そこに skeleton.dtx/skeleton.ins という雛形が付いています。多くのパッケージがこれを写して出発した痕跡は、意外なところに残っています——skeleton.ins の 18 行目には \usedir{tex/latex/skeleton} という行があり、同じ行が booktabs.ins の 36 行目にもそのまま残っています。 雛形の skeleton を自分のパッケージ名に書き換え忘れたわけです。TeX Live 2024 の source/latex にある 1402 個の .ins を調べると、この取り残しを抱えているのは booktabs ただ 1 つ。ほほえましい話ですが、教訓は明快です——雛形から始めたら、雛形の名前が残っていないか検索すること。
expl3 で書くなら \ProvidesExplPackage
中身を expl3 で書くつもりなら、宣言行を \ProvidesExplPackage{名前}{日付}{版}{説明} に替えます。引数が 4 つに分かれるほか、もう一つ重要な性質があります——この命令は最後に \ExplSyntaxOn を実行します。 つまり宣言した次の行から、\ExplSyntaxOn を一度も書かずに expl3 の構文が使えます。下の .sty には \ExplSyntaxOn が 1 か所もありませんが、\tl_new:N も \NewDocumentCommand もそのまま動きます。expl3 そのものの読み方は expl3 のページにあります。
\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 }テストと配布物づくり — l3build
tex mypackage.ins と pdflatex mypackage.dtx を毎回手で打つ代わりに、LaTeX Project が保守する l3build に任せられます(TeX Live 2024 同梱版は 2024-02-08 リリース)。プロジェクトの根に build.lua を置いてモジュール名とファイルの一覧を書けば、l3build unpack が .ins を走らせて .sty を build/unpacked/ に生成し、l3build doc が .dtx を組んで build/doc/ に PDF を作ります。生成物が作業ディレクトリを汚さないのが利点です。l3build check はテスト機構で、testfiles/ に置いた .lvt(テスト文書)と .tlg(期待されるログ)を突き合わせ、出力が変わったら差分を見せてくれます。組版の結果を目視でなく機械で守れるようになると、リファクタリングが怖くなくなります。
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 uploadCTAN に出す — zip の中身とライセンス
l3build ctan を実行すると、そのままアップロードできる zip が 1 個できます。中身は mypackage/ というパッケージ名のディレクトリに、ソース(.dtx と .ins)と組み上がった PDF マニュアル が入った形です。.sty を入れないのがこの世界の作法で、受け取った側が .ins から生成します。ここに README と変更履歴を足せば体裁は整います。ただし l3build ctan は作業ディレクトリにある PDF を拾い集めるので、試し刷りの PDF が転がっていると一緒に梱包されます——固めたあとに unzip -l で中身を一覧して確認してください。アップロード自体は CTAN のウェブフォームから行いますが、TeX Live には ctan-o-mat も同梱されていて、設定ファイルに書いた説明・連絡先・ライセンスを使って検証とアップロードをコマンドラインから実行できます。
ライセンスは決めておく必要があります。 CTAN は配布条件の明示を求めますし、TeX Live のパッケージ情報にも表示されます。この世界の事実上の標準は LPPL(LaTeX Project Public License)で、選び方や版ごとの違い——とりわけ「改変したら名前を変えよ」という条項がどの版のどこにあるのか——はライセンスのページに詳しく書いてあります。決めた条件は 3 か所に書いてください。.dtx 冒頭のメタコメント、.ins の \preamble(生成される全ファイルの先頭に入るので、.sty を単体で受け取った人にも届きます)、そして README です。ここまで揃えば、あなたのパッケージは何年か先に誰かが texdoc で開けるものになります。
\ProvidesPackageの日付をYYYY/MM/DDで必ず書く。 これがないと利用者は版を要求できません。.dtxは毎回組んでみる。.styが通っても.dtxが落ちることは実際に起きています(booktabs)。- 雛形から始めたら雛形の名前を検索する。
skeletonの取り残しは TeX Live 2024 にも 1 件生きています。 l3build ctanの zip はunzip -lで中身を確認する。 作業ディレクトリの PDF を拾ってしまいます。- ライセンスを
.dtx・.insの\preamble・README の 3 か所に書く。.styだけ受け取った人にも届く必要があります。