114

I would like to jump into the cold waters of LaTeX package creation, mostly for procrastination reasons...but I haven't a clue how to start. For example, a previous question of mine got some nice answers. I would like to take one of these answers and create a package from it.

  • How do I start?
  • If others find it useful, how should I document it?
  • How do I publish it?

Perhaps I should say, I do not want anyone to do the work for me...I would much prefer an explanation on how I could do it myself!

3

2 Answers 2

24

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.

5
  • 4
    I must say this answer was extremely helpful. I've learned about .cls and .sty files and training for a while, but never tried the "next step" into .dtx and .ins. Now I found a simple guide to follow this "not so simple" steps. Thanks (+1).
    – FHZ
    Apr 8, 2020 at 5:51
  • 1
    The converting process from sty and cis to dix seems very manual. If I have my package split into several files to keep things organized when developing, copying the contents of each one Into a dtx each time I do a release sounds horrible. I wouldn't want to develop within the dtx because that isn't runnable. Is there a solution to this issue?
    – Logan
    May 13 at 21:26
  • 1
    @Logan but that is backwards, the intention is to generate .sty from .dtx not the other way round. If you have written .sty directly you do not need a .dtx May 14 at 17:13
  • @DavidCarlisle so the standard would be to only maintain .dtx files and each time you test during development you'd generate sty files by running latex mypackage.ins?
    – Logan
    May 14 at 17:18
  • or a build system such as l3build (or make or a custom shell script or ...) which does that for you @Logan May 14 at 17:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .