How do I create a LaTeX package?

These are three related but different questions. We have already got some good answers on this site. So perhaps let's take this place to summarize it.

This answer is community wiki, so feel free to edit it. Of course you can add more information than site links.

Starting package writing

  • Where do I start LaTeX programming?
  • Classes and packages – what's the difference?
  • Make your own .sty files
  • Style/class tutorials
  • Reference guide to begin writing a class and/or a package

Documenting packages

  • How do I document my style files?
  • Packages for documenting self written packages (sty-files)
  • What is the gold standard of package documentation?

Publishing packages

  • How does one publish/promote a new package?
  • How can I contribute to CTAN?
  • What is good practice when preparing a package for CTAN?
  • How to upload my packages or document classes to CTAN?
  • CLI tool to upload to CTAN

I've spent around a day trying to build my own package. Not impressed by the vast amount of sorry, but your princess is in another castle; I've decided to write up my findings. This is an MCVE "Hello, World!" project, and has taken some shortcuts to keep code size and answer size down.

The answer will help you understand what plugs together and how. It will also show you how to run your package locally, so you can sanity check your TeX installation and your package.

Starting point

I'll start this by showing what my TeX would look like without using a package. The aim of the package is to move the definition of \hello into a package called mypackage.

document.tex

\documentclass{article}
\newcommand{\hello}{Hello, World!}

\begin{document}
    \hello
\end{document}

I managed to achieve this goal, and by the end of this answer you too can use the following TeX.

package.tex

\documentclass{article}
\usepackage{mypackage}

\begin{document}
    \hello
\end{document}

Making a package

The package file

There are two types of packages; styles (.sty) and classes (.cls). For this we'll be making a style. When writing your own style or class I'd recommend looking at Wikibooks' Creating Packages page.

Versions in LaTeX are generally provided as dates in the ISO 8601 date format, YYYY/MM/DD. And so when you specify both the TeX and your package version you should enter both in that form.

mypackage.sty

\NeedsTeXFormat{LaTeX2e}[1994/06/01]
\ProvidesPackage{mypackage}[2020/02/28 1.0.0 Tutorial project]

\newcommand{\hello}{Hello, World!}

\endinput

The above is the style file I made.

  • \NeedsTeXFormat specifies which version of TeX is required, with the version (date) also specified.
  • \ProvidesPackage specifies what package we are defining. I have:

    • Called the package mypackage,
    • Set the TeX version to todays date,

      2020/02/28

    • Specified the version in Semantic Versioning

      1.0.0

    • Added a short description of the project.

      Tutorial project

  • \newcommand defines the wanted \hello command.

  • \endinput signals the end of the style file.

Install it locally

It's best if you sanity check the code. And so we can install the package locally. This is required to know, if you need to install your own or another persons package.

  1. Find where your TEXMF folder is.
  2. Make the folders required by the TeX Directory Structure, TDS, standard.

    Since we're making a LaTeX project, we'll place it in texmf/tex/latex. And to keep the folder clean, we'll place our package in the mypackage folder.

  3. Copy your package to the newly created folder.

In the shell it would look something like this.
Note: Ensure you change %USERPROFILE%/texmf to whatever your output is.

$ kpsewhich -var-value=TEXMFHOME
%USERPROFILE%/texmf
$ mkdir %USERPROFILE%/texmf/tex/latex/mypackage/
$ cp mypackage.sty %USERPROFILE%/texmf/tex/latex/mypackage/

If your package isn't found, because your TeX isn't TDS compliant, then you can tell TeX where it is.

$ texhash %USERPROFILE%/texmf

Testing the package

We can now run package.tex and it outputs "Hello, World!" as expected.

Documenting it

Converting from sty to dtx

The standard for documenting TeX projects is through dtx. This is some black magic that houses both the documentation and the package at the same time. To achieve this feat, the code contains a vast amount of comments and requires an installer.

The ins file extracts the information from the dtx and builds the sty or cls file.
The code is mostly boilerplate.

mypackage.ins

\input docstrip.tex
\keepsilent
\usedir{tex/latex/mypackage}
\generate{\file{mypackage.sty}{\from{mypackage.dtx}{package}}}
\Msg{Please move mypackage.sty to a TDS directory}
\endbatchfile
  • You should change all occurrences of mypackage to the name of your package.
  • You can change \Msg to whatever you want.
    You can also include additional messages if you want the message split over multiple lines.

    Think print, echo or sys.stdout.write.

Making the dtx

To build the documentation, you run TeX on the dtx file. Just like you do with any other TeX document.

mypackage.dtx

% \iffalse
%<package>\NeedsTeXFormat{LaTeX2e}[1994/06/01]
%<package>\ProvidesPackage{mypackage}[2020/02/28 1.0.0 Tutorial project]
%<*driver>
\documentclass{ltxdoc}
\usepackage{mypackage}
\EnableCrossrefs
\CodelineIndex
\RecordChanges
\begin{document}
    \DocInput{mypackage.dtx}
\end{document}
%</driver>
% \fi
%
% \begin{macro}{\hello}
% This is the function to display "Hello world!"
%    \begin{macrocode}
\newcommand{\hello}{Hello world!}
%    \end{macrocode}
% \end{macro}
\endinput

When the file is run through TeX, it evaluates the section from \documentclass through \end{document}, much like any other TeX file. If you comment out the \DocInput line then you just get an empty file.

The magic happens when \DocInput runs. This evaluates the file a second time, this time removing the comments from the file. The \iffalse and \fi cause the entire first section to not be run on the second evaluation, preventing \DocInput from running a second time, and preventing \NeedsTeXFormat from running ever.

From here, the rest of the file is simple.

  • %<package> is a special comment which includes the line, uncommented, in sty.
    This is as we specified it, package, in the ins.

    \generate{\file{mypackage.sty}{\from{mypackage.dtx}{package}}}
    

    Note: Do not put a space between % and < otherwise it won't be included.

  • \begin{macro} documents the macro with the following line as the description.

  • \newcommand{\hello}{...} is exported to the sty.
  • \endinput is exported to the sty.

Install it locally

To install the package we need to run the ins to extract the sty from the dtx.

After extracting the sty, we then just install it as we did before.
In the shell this would look something like this.

$ tex mypackage.ins
$ cp mypackage.sty %USERPROFILE%/texmf/tex/latex/mypackage/

You can also see the documentation by running the dtx.

$ pdflatex mypackage.dtx
$ ./mypackage.pdf

Publishing

Now that you know what sty, cls, ins and dtx files are, and how to install them, understanding how to publish your TeX package is really simple. And the links provided by Stefan Kottwitz's answer can better describe this.

Going forward

I'd like to restate that this is an MVCE of how to make and install your own documented package. And so I've cut corners and not covered half the tooling available.

  • Wikibooks has a more complete description on how to build a sty or cls.
  • Scott Pakin's How to Package Your LaTeX Package on how to write your own dtx and ins.
  • The links in Stefan Kottwitz's answer, as now you should have enough understanding of the entire system to understand the posts individually.