summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3build
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2014-06-28 22:01:09 +0000
committerKarl Berry <karl@freefriends.org>2014-06-28 22:01:09 +0000
commit65b048d524ef9464d4039093986e267b277dafd9 (patch)
tree9ca9621d629641ee1ece91dd7735a289bb223ec6 /Master/texmf-dist/source/latex/l3build
parent546e6790c84dde88b3abdffd431ece4e0061e0e6 (diff)
l3build
git-svn-id: svn://tug.org/texlive/trunk@34469 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/l3build')
-rw-r--r--Master/texmf-dist/source/latex/l3build/l3build.dtx1083
-rw-r--r--Master/texmf-dist/source/latex/l3build/l3build.ins40
-rw-r--r--Master/texmf-dist/source/latex/l3build/l3build.lua1025
3 files changed, 2148 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/l3build/l3build.dtx b/Master/texmf-dist/source/latex/l3build/l3build.dtx
new file mode 100644
index 00000000000..54b031c021f
--- /dev/null
+++ b/Master/texmf-dist/source/latex/l3build/l3build.dtx
@@ -0,0 +1,1083 @@
+% \iffalse
+%%
+%% File l3build.dtx (C) Copyright 2014 The LaTeX3 Project
+%%
+%% It may be distributed and/or modified under the conditions of the
+%% LaTeX Project Public License (LPPL), either version 1.3c of this
+%% license or (at your option) any later version. The latest version
+%% of this license is in the file
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3build bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% The released version of this bundle is available from CTAN.
+%%
+%% -----------------------------------------------------------------------
+%%
+%% The development version of the bundle can be found at
+%%
+%% http://www.latex-project.org/svnroot/experimental/trunk/
+%%
+%% for those people who are interested.
+%%
+%%%%%%%%%%%
+%% NOTE: %%
+%%%%%%%%%%%
+%%
+%% Snapshots taken from the repository represent work in progress and may
+%% not work or may contain conflicting material! We therefore ask
+%% people _not_ to put them into distributions, archives, etc. without
+%% prior consultation with the LaTeX Project Team.
+%%
+%% -----------------------------------------------------------------------
+%%
+%
+%
+%<*driver>
+\RequirePackage{expl3}
+\def\ExplFileName{l3build}
+\def\ExplFileDescription{L3 Regression test suite}
+\def\ExplFileDate{2014/06/15}
+\def\ExplFileVersion{5141}
+\documentclass[full]{l3doc}
+\renewcommand\partname{Part}
+\usepackage{multicol,needspace}
+
+\makeatletter
+\addtolength\columnsep{1em}
+\renewcommand\columnseprule{0.4pt}
+\def\@starttoc#1{%
+ \begin{multicols}{2}
+ \small
+ \makeatletter
+ \@input{\jobname.#1}%
+ \expandafter\newwrite\csname tf@#1\endcsname
+ \immediate\openout \csname tf@#1\endcsname \jobname.#1\relax
+ \@nobreakfalse
+ \end{multicols}}
+\makeatother
+
+\newenvironment{buildcmd}[1]{%
+ \bigskip\par\noindent\hspace{-\parindent}%
+ \fbox{\ttfamily \textcolor[gray]{0.5}{\$} texlua build.lua #1}%
+ \nopagebreak\smallskip\nopagebreak\par\nopagebreak\noindent\ignorespaces
+ }
+ {}
+\makeatletter
+\newcommand\luavar[1]{\@ifundefined{lua_#1}{\ERROR}{\texttt{\@nameuse{lua_#1}}}}
+\newcommand\luavarset[3]{\@namedef{lua_#1}{#2}}
+\newcommand\luavarseparator{}
+\makeatother
+\def\allluavars{
+\luavarset{module}{""}{The name of the module.}
+\luavarset{bundle}{""}{The name of the bundle in which the module belongs.}
+\luavarseparator
+\luavarset{maindir} {"."}{The top level directory for this module or bundle.}
+\luavarset{supportdir} {maindir .. "/support"} {Where copies of files to support check/doc compilation are stored.}
+\luavarset{testfiledir} {maindir .. "/testfiles"} {Where the tests are.}
+\luavarset{testsupdir} {testfiledir .. "/support"} {Where support files for the tests are.}
+\luavarseparator
+\luavarset{localdir} {maindir .. "/build/local"} {Generated folder where support files are placed to allow \enquote{sandboxed} \TeX{} runs.}
+\luavarset{testdir} {maindir .. "/build/test"} {Generated folder where tests are run.}
+\luavarset{typesetdir} {maindir .. "/build/doc"} {Generated folder where typesetting is run.}
+\luavarset{unpackdir} {maindir .. "/build/unpack"} {Generated folder where unpacking occurs.}
+\luavarset{distribdir} {maindir .. "/build/distrib"}{Generated folder where the archive is created.}
+\luavarset{ctandir} {distribdir .. "/ctan"} {Generated folder where files are organised for CTAN.}
+\luavarset{tdsdir} {distribdir .. "/tds"} {Generated folder where files are organised for a TDS.}
+\luavarset{tdsroot} {"latex"}{Root directory of the TDS structure for the bundle/module to be installed into.}
+\luavarseparator
+\luavarset{binaryfiles} {\{"*.pdf", "*.zip"\}}
+ {Files to be added in binary mode to zip files.}
+\luavarset{checkfiles} {\{~\}}{Extra files unpacked purely for tests}
+\luavarset{checksuppfiles} { }{Files needed for performing regression tests.}
+\luavarset{cmdchkfiles} {\{~\}}{Files need to perform command checking (\cls{l3doc}-based documentation only).}
+\luavarset{demofiles} {\{~\}}{Files which show how to use a module.}
+\luavarset{cleanfiles} {\{"*.pdf", "*.zip"\}}{Files to delete when cleaning.}
+\luavarset{excludefiles} {\{"*\string~"\}} {Files to ignore entirely (default for Emacs auto-save files).}
+\luavarset{installfiles} {\{"*.sty"\}} {Files to install to the \TeX{} tree and similar tasks.}
+\luavarset{sourcefiles} {\{"*.dtx", "*.ins"\}}{Files to copy for unpacking.}
+\luavarset{txtfiles} {\{"*.markdown"\}} {Files which should have the extension removed when sent to CTAN.}
+\luavarset{typesetfiles} {\{"*.dtx"\}} {Files to typeset for documentation.}
+\luavarset{typesetsuppfiles}{\{~\}} {Files needed to support typesetting when \enquote{sandboxed}.}
+\luavarset{unpackfiles} {\{"*.ins"\}} {Files to run to perform unpacking.}
+\luavarset{unpacksuppfiles} {\{~\}} {Files needed to support unpacking when \enquote{sandboxed}.}
+\luavarseparator
+\luavarset{lvtext} {".lvt"} {Extension of test files.}
+\luavarset{tlgext} {".tlg"} {Extension of test file output.}
+\luavarset{logext} {".log"} {Extension of checking output, before processing it into a \texttt{.tlg}.}
+\luavarseparator
+\luavarset{checkdeps} {\{~\}} {List of build unpack dependencies for checking.}
+\luavarset{typesetdeps} {\{~\}} {\dots for typesetting docs.}
+\luavarset{unpackdeps} {\{~\}} {\dots for unpacking.}
+\luavarseparator
+\luavarset{chkengines} {\{"pdftex", "xetex", "luatex"\}} {Engines to check with \texttt{checklvt} by default.}
+\luavarset{stdengine} {"pdtex"} {Engine to generate \texttt{.tlg} file from.}
+\luavarseparator
+\luavarset{typesetexe} {"pdflatex"} {Executable for compiling \texttt{doc(s)}.}
+\luavarset{unpackexe} {"tex"} {Executable for running \texttt{unpack}.}
+\luavarset{zipexe} {"zip"} {Executable for creating archive with \texttt{ctan}.}
+\luavarseparator
+\luavarset{checkopts} {"-interaction=batchmode"} {Options based to engine when running checks.}
+\luavarset{cmdchkopts} {"-interaction=batchmode"} {Options based to engine when running command checks.}
+\luavarset{typesetopts}{"-interaction=nonstopmode"}{Options based to engine when typesetting.}
+\luavarset{unpackopts} {""} {Options based to engine when unpacking.}
+\luavarset{zipopts} {"-v -r -X"} {Options based to zip program.}
+\luavarseparator
+\luavarset{checksearch} {true} {Look in \texttt{tds} dirs for checking?}
+\luavarset{typesetsearch}{true} {Look in \texttt{tds} dirs for typesetting docs?}
+\luavarset{unpacksearch} {true} {Look in \texttt{tds} dirs for unpacking?}
+\luavarseparator
+\luavarset{checkruns} {1} {How many times to run a check file before comparing the log.}
+\luavarset{scriptname} {"build.lua"} {Name of script used in dependencies.}
+\luavarset{typesetcmds} {""} {Instructions to be passed to \TeX{} when doing typesetting.}
+}
+\allluavars
+\newcommand\luavartypeset{%
+ \begingroup
+ \frenchspacing
+ \renewcommand\luavarset[3]{
+ \texttt{##1} & \texttt{##2} & ##3 \\
+ }
+ \renewcommand\luavarseparator{\midrule}
+ \setlength\LTleft{-0.21\linewidth}
+ \begin{longtable}{@{}%
+ >{\small}
+ p{0.18\linewidth}
+ @{\hspace{0.03\linewidth}}
+ >{\footnotesize\hangindent=1em}
+ p{0.34\linewidth}
+ @{\hspace{0.03\linewidth}}
+ >{\small\raggedright\arraybackslash}
+ p{0.63\linewidth}
+ @{}}
+ \toprule
+ Variable & Default & Description \\
+ \midrule\endhead
+ \allluavars
+ \bottomrule
+ \end{longtable}
+ \endgroup
+}
+
+\newcommand\code{\texttt}
+\newcommand\var{\texttt}
+
+\usepackage[procnames]{listings}
+\lstset{
+ basicstyle=\ttfamily\small,
+ numbers=left,
+ numberstyle={\tiny\color[gray]{0.4}},
+}
+
+\usepackage{shortvrb}
+\usepackage{enumitem}
+\usepackage{longtable}
+\MakeShortVerb\|
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \pkg{l3build} package\\ Checking and building packages^^A
+% \thanks{This file describes v\ExplFileVersion,
+% last revised \ExplFileDate.}^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released \ExplFileDate}
+%
+% \maketitle
+% \tableofcontents
+%
+% \begin{documentation}
+%
+% \section{The \pkg{l3build} system}
+%
+% \subsection{Introduction}
+%
+% The \pkg{l3build} system is a Lua script for building \TeX{} packages, with particular emphasis on regression testing.
+% It is written in cross-platform Lua code, so can be used by any modern \TeX{} distribution with the |texlua| interpreter.
+% A package for building with \pkg{l3build} can be written in any \TeX\ dialect; its defaults are set up for \LaTeX\ packages written in the DocStrip style. (Caveat: minimal testing has yet been performed for non-\LaTeX{} packages.)
+%
+% Test files are written as standalone \TeX{} documents using the |regression-test.tex| setup file; documentation on writing these tests is discussed in Section~\ref{sec:writing-tests}.
+%
+% The |l3build.lua| script is not designed to be executed directly; each package will define its own |build.lua| script as a driver file which both sets variables (such as the name of the package) and then calls the main |l3build.lua| script internally.
+%
+% \pagebreak[2]
+% A standard package layout might look something like the following:
+% \begin{Verbatim}[fontsize=\small]
+% abc/
+% abc.dtx
+% abc.ins
+% build.lua
+% README
+% support/
+% testfiles/
+% \end{Verbatim}
+% Most of this should look fairly self-explanatory.
+% The top level |support/| directory (optional) would contain any necessary files for compiling documentation, running regression tests, and so on.
+%
+% The \pkg{l3build} system is also capable of building and checking \emph{bundles} of packages.
+% To avoid confusion, we refer to either a standalone package or a package within a bundle as a \emph{module}.
+%
+% For example, within the \LaTeX3 project we have the \textsf{l3packages} bundle which contains the \textsf{xparse}, \textsf{xtemplate}, etc., modules.
+% These are all built and distributed as one bundle for installation, distribution \emph{via} CTAN and so forth.
+%
+% Each module in a bundle will have its own build script, and a bundle build script brings them all together.
+% A standard bundle layout would contain the following structure.
+% \begin{Verbatim}
+% mybundle/
+% build.lua
+% support/
+% yyy/ zoo/
+% build.lua build.lua
+% README README
+% testfiles/ testfiles/
+% yyy.dtx zoo.dtx
+% yyy.ins zoo.ins
+% \end{Verbatim}
+% All modules within a bundle must use the same build script name.
+%
+% The |testfiles/| folder is local to each module, and its layout consists of a series of regression tests with their outputs.
+% \begin{Verbatim}
+% testfiles/
+% test1.lvt
+% test1.tlg
+% ...
+% support/
+% my-test.cls
+% \end{Verbatim}
+% Again, the |support/| directory contains any files necessary to run some or all of these tests.
+%
+% When the build system runs, it creates a directory |build/| for various unpacking, compilation, and testing purposes.
+% For a module, this build folder can be in the main directory of the package itself, but for a bundle it should be common for the bundle itself and for all modules within that bundle.
+% A |build/| folder can be safety deleted; all material within is re-generated for each command of the \pkg{l3build} system.
+%
+% \subsection{Main build commands}
+%
+% In the working directory of a bundle or module, the following commands can be executed:
+% \begin{itemize}[noitemsep]\ttfamily
+% \item check
+% \item checklvt \meta{name} [\meta{engine}]
+% \item cmdcheck
+% \item clean
+% \item doc
+% \item install
+% \item savetlg \meta{name} [\meta{engine}]
+% \item unpack
+% \end{itemize}
+% These commands are described below.
+%
+% \begin{buildcmd}{check}
+% The |check| command runs the entire test suite.
+% This involves iterating through each \texttt{.lvt} file in the test directory (specified by the \var{testfiledir} variable), compiling each test in a \enquote{sandbox} (a directory specified by \var{testdir}), and comparing the output against each matching predefined \texttt{.tlg} file.
+%
+% If changes to the package or the typesetting environment have affected the results, the check for that file fails.
+% A |diff| of the expected to actual output should then be inspected to determine the cause of the error; it is located in the \var{testdir} directory (default \luavar{testdir}).
+%
+% The following files are moved into the \enquote{sandbox} for the |check| process:
+% \begin{itemize}
+% \item all \var{installfiles} after unpacking;
+% \item all \var{checkfiles} after unpacking;
+% \item any files in the directory \var{testsupdir};
+% \item any files that match \var{checksuppfiles} in the \var{supportdir}.
+% \end{itemize}
+% This range of possibilities allow sensible defaults but significant flexibility for defining your own test setups.
+%
+% Checking can be performed with any or all of the `engines' \texttt{pdftex}, \texttt{xetex}, and \texttt{luatex}.
+% By default, each test is executed with all three, being compared against the \texttt{.tlg} file produced from the \var{pdftex} engine (these defaults are controlled by the |chkengines| and |stdengine| variable respectively).
+% To perform the check, the engine typesets each test \var{checkruns} times.
+% More detail on this in the documentation on |savetlg|.
+% Options passed to the binary are defined in the variable \var{checkopts}.
+%
+% By default, |texmf| trees are searched for input files when checking. This can be disabled by setting \var{checksearch} to |false|: isolation provides confidence that the tests cannot accidentally be running with incorrect files installed in the main distribution or |hometexmf|.
+% \end{buildcmd}
+%
+%
+% \begin{buildcmd}{checklvt \meta{name} [\meta{engine}]}
+% Checks only the test \texttt{\meta{name}.lvt} with optionally specified \meta{engine} (one of |pdftex|, |xetex|, or |luatex|).
+% If an \meta{engine} is not specified, all three engines are checked (or those defined by the |chkengines| variable if changed from the default).
+% \end{buildcmd}
+%
+% \begin{buildcmd}{cmdcheck}
+% For \cls{l3doc}-based sources, allows checking that the commands defined in the code part (by \var{cmdchkfiles}) are documented in the description part.
+% This is performed by passing the |check| option to the \cls{l3doc} class, typesetting the file(s) to check with engine \var{stdengine} with options \var{cmdchkopts}, and checking the resultant |.cmds| file(s).
+% Dependencies are specified also with \var{checkdeps}.
+% \end{buildcmd}
+%
+%
+% \begin{buildcmd}{clean}
+% This command removes all temporary files used for package bundling and regression testing.
+% In the standard layout, these are all files within the directories defined by \var{localdir}, \var{testdir}, \var{typesetdir} and \var{unpackdir}, as well as all files defined in the \var{cleanfiles} variable in the same directory as the script.
+% The defaults are |.pdf| files from typesetting (|doc|) and |.zip| files from bundling (|ctan|).
+% \end{buildcmd}
+%
+%
+% \begin{buildcmd}{ctan}
+% Creates an archive of the package and its documentation, suitable for uploading to CTAN including a |.tds.zip| file containing the `\TeX\ Directory Structure' layout of the package or bundle.
+% The archive is compiled in \var{distribdir}, and if the results are successful the resultant |.zip| file is moved into the same directory as the build script.
+% The archive therefore contains two `views' of the package:
+% \begin{Verbatim}
+% abc.zip/
+% abc/
+% abc.dtx
+% abc.ins
+% abc.pdf
+% README
+% abc.tds.zip/
+% doc/latex/abc/
+% abc.pdf
+% README
+% source/latex/abc/
+% abc.dtx
+% abc.ins
+% tex/latex/abc/
+% abc.sty
+% \end{Verbatim}
+% The files copied into the archive are controlled by a number of variables.
+% The `root' of the TDS structure is defined by \var{tdsroot}, which is \luavar{tdsroot} by default. Plain users would redefine this to |"generic"|, for example.
+%
+% \pagebreak[2]
+% The |doc| tree is constructed from:
+% \begin{itemize}
+% \item all files matched by \var{demofiles}, and
+% \item all files matched by \var{typesetfiles} with their extension replaced with |.pdf|, and
+% \item all files matched by \var{txtfiles}, but when copied their extension is entirely removed (e.g., |README.markdown| is copied as just |README|).
+% \end{itemize}
+% The |source| tree is constructed from all files matched by \var{typesetfiles} and \var{sourcefiles}.
+% The |tex| tree from all files matched by \var{installfiles}.
+%
+% Files that should always be excluded from the archive are matched against the \var{excludefiles} variable; by default this is \luavar{excludefiles}, which match Emacs' autosave files.
+%
+% Binary files should be specified with the \var{binaryfiles} variable (default \luavar{binaryfiles}); these are added to the zip archive without normalising line endings (text files are automatically converted to Unix-style line endings).
+%
+% To create the archive, by default the binary \var{zipexe} is used (\luavar{zipexe}) with options \var{zipopts} (|-v -r -X|). The intermediate build directories \var{ctandir} and \var{tdsdir} are used to construct the archive.
+% \end{buildcmd}
+%
+%
+% \begin{buildcmd}{doc}
+% Compiles all documentation files in the \var{typesetdir} directory.
+% If the compilation is successful the |.pdf| is moved back into the main directory.
+%
+% The documentation compilation is performed with the \var{typesetexe} binary (default \texttt{pdflatex}), with options \var{typesetopts}.
+% Additional \TeX{} material defined in \var{typesetcmds} is passed to the document (e.g., for writing |\\PassOptionsToClass{l3doc}{letterpaper}|, and so on---note that backslashes need to be escaped in Lua strings).
+%
+% Files that match |typesetsuppfiles| in the |support| directory (|supportdir|) are copied into the |build/local| directory (|localdir|) for the typesetting compilation process.
+% Additional dependencies listed in the \var{typesetdeps} variable (empty by default) will also be installed.
+%
+% If \var{typesetsearch} is \code{true} (default), standard \texttt{texmf} search trees are used in the typesetting compilation. If set to false, \emph{all} necessary files for compilation must be included in the |build/local| sandbox.
+% \end{buildcmd}
+%
+%
+% \begin{buildcmd}{install}
+% Copies all package files (defined by \var{installfiles}) into the user's home \texttt{texmf} tree in the form of the \TeX\ Directory Structure.
+% \end{buildcmd}
+%
+%
+% \begin{buildcmd}{savetlg \meta{name} [\meta{engine}]}
+% This command runs through the same execution as |checklvt| for a specific test \texttt{\meta{name}.lvt} with optional \texttt{\meta{engine}}.
+% If no \meta{engine} is specific, this command saves the output of the test to a |.tlg| file.
+% This file is then used in all subsequent checks against the \texttt{\meta{name}.lvt} test.
+%
+% If the \meta{engine} is specified (one of |pdftex|, |xetex|, or |luatex|), the saved output is stored in \texttt{\meta{name}.\meta{engine}.tlg}. This is necessary if running the test through a different engine produces a different output.
+% A normalisation process is performed when checking to avoid common differences such as register allocation; full details are listed in section~\ref{sec:norm}.
+% \end{buildcmd}
+%
+%
+% \begin{buildcmd}{unpack}
+% Unpacks all files into the directory defined by \var{unpackdir}. This occurs before other build commands such as |doc|, |check|, etc., and should usually not be necessary to perform on its own (except for testing).
+%
+% The unpacking process is performed by executing the \var{unpackexe} (default \texttt{tex}) with options \var{unpackopts} on all files defined by the \var{unpackfiles} variable; by default, all files that match \luavar{unpackfiles}.
+%
+% If additional support files are required for the unpacking process, these can be enumerated in the \var{unpacksuppfiles} variable.
+% Dependencies for unpacking are defined with \var{unpackdeps}.
+%
+% By default this process allows files to be accessed in all standard |texmf| trees; this can be disabled by setting \var{unpacksearch} to |false|.
+% \end{buildcmd}
+%
+% \subsection{Example build scripts}
+%
+% An example of a standalone build script for a package that uses self-contained |.dtx| files is shown in Figure~\ref{fig:breqn}.
+% Here, the |module| only is defined, and since it doesn't use |.ins| files so the variable \var{unpackfiles} is redefined to run |tex| on the |.dtx| files instead to generate the necessary |.sty| files.
+% There are some PDFs in the repository that shouldn't be part of a CTAN submission, so they're explicitly excluded, and here unpacking is done `quietly' to minimise console output when building the package.
+% Finally, because this is a standalone package, we assume that \pkg{l3build} is installed in the main \TeX{} distribution and find the Lua script by searching for it.
+%
+% \begin{figure}[!b]
+% \begin{lstlisting}[frame=single,language={[5.2]Lua},gobble = 6]
+% #!/usr/bin/env texlua
+%
+% -- Build script for breqn
+%
+% module = "breqn"
+%
+% unpackfiles = {"*.dtx"}
+% excludefiles = {"*/breqn-abbr-test.pdf",
+% "*/eqbreaks.pdf"}
+% unpackopts = "-interaction=batchmode"
+%
+% kpse.set_program_name("kpsewhich")
+% dofile(kpse.lookup("l3build.lua"))
+% \end{lstlisting}
+% \caption{The build script for the \pkg{breqn} package.}
+% \label{fig:breqn}
+% \end{figure}
+%
+% An example of a bundle build script for \pkg{l3packages} is shown in Figure~\ref{fig:bundle}.
+% Note for \LaTeX3 we use a common file to set all build variables in one place, and the path to the |l3build.lua| script is hard-coded so we always use our own most recent version of the script.
+% An example of an accompanying module build script is shown in Figure~\ref{fig:module}.
+%
+% \begin{figure}[p]
+% \begin{lstlisting}[frame=single,language={[5.2]Lua},gobble = 6]
+% #!/usr/bin/env texlua
+%
+% -- Build script for LaTeX3 "l3packages" files
+%
+% -- Identify the bundle: there is no module as this is the "driver"
+% bundle = "l3packages"
+%
+% -- Location of main directory: use Unix-style path separators
+% maindir = ".."
+%
+% -- Load the common build code: this is the one place that a path
+% -- needs to be hard-coded
+% dofile (maindir .. "/l3build/l3build-config.lua")
+% dofile (maindir .. "/l3build/l3build.lua")
+% \end{lstlisting}
+% \caption{The build script for the \pkg{l3packages} bundle.}
+% \label{fig:bundle}
+% \end{figure}
+%
+% \begin{figure}[p]
+% \begin{lstlisting}[frame=single,language={[5.2]Lua},gobble = 6]
+% #!/usr/bin/env texlua
+%
+% -- Build script for LaTeX3 "xparse" files
+%
+% -- Identify the bundle and module:
+% bundle = "l3packages"
+% module = "xparse"
+%
+% -- Location of main directory: use Unix-style path separators
+% -- Should match that defined by the bundle.
+% maindir = "../.."
+%
+% -- Load the common build code: this is the one place that a path
+% -- needs to be hard-coded
+% dofile (maindir .. "/l3build/l3build-config.lua")
+% dofile (maindir .. "/l3build/l3build.lua")
+% \end{lstlisting}
+% \caption{The build script for the \pkg{xparse} module.}
+% \label{fig:module}
+% \end{figure}
+%
+% \begin{figure}[p]
+% \begin{lstlisting}[frame=single,gobble = 6]
+% @echo off
+% texlua build.lua %*
+% \end{lstlisting}
+% \caption{Windows batch file wrapper for running the build process.}
+% \label{fig:batch}
+% \end{figure}
+%
+% Under a Unix-like platform, you may wish to run `|chmod +x build.lua|' on these files, which allows a simpler command line use. Instead of writing\\
+% \hspace*{2\parindent}|texlua build.lua check|\\ for example, you would simply write\\
+% \hspace*{2\parindent}|./build.lua check|\\
+% instead. (Or even omit the |./| depending on your path settings.)
+% Windows users can achieve a similar effect by creating a file |build.bat| as show in Figure~\ref{fig:batch}.
+%
+% \subsection{Variables}
+%
+% This section lists all variables defined in the |l3build.lua| script that are available for customisation.
+%
+% \luavartypeset
+%
+% \subsection{Dependencies}
+%
+% If you have multiple packages that are developed separately but still interact in some way, it's often desirable to integrate them when performing regression tests.
+% For \LaTeX3, for example, when we make changes to \pkg{l3kernel} it's important to check that the tests for \pkg{l3packages} still run correctly, so it's necessary to include the \pkg{l3kernel} files in the build process for \pkg{l3packages}.
+%
+% In other words, \pkg{l3packages} is \emph{dependent} on \pkg{l3kernel}, and this is specified in \pkg{l3build} by setting appropriately the variables \texttt{checkdeps}, \texttt{typesetdeps}, and \texttt{unpackdeps}.
+% The relevant parts of the \LaTeX3 repository is structured as the following.
+% \Needspace{3\baselineskip}
+% \begin{Verbatim}
+% l3/
+% l3kernel/
+% build.lua
+% expl3.dtx
+% expl3.ins
+% ...
+% testfiles/
+% l3packages/
+% build.lua
+% xparse/
+% build.lua
+% testfiles/
+% xparse.dtx
+% xparse.ins
+% support/
+% \end{Verbatim}
+% For \LaTeX3 build files, |maindir| is defined as top level folder |l3|, so all support files are located here, and the build directories will be created there.
+% To set \pkg{l3kernel} as a dependency of \pkg{l3package}, within |l3packages/xparse/build.lua| the equivalent of the following is set:
+% \begin{Verbatim}
+% maindir = "../.."
+% checkdeps = {maindir .. "/l3kernel"}
+% \end{Verbatim}
+% This ensures that the \pkg{l3kernel} code is included in all processes involved in unpacking and checking and so on.
+% The name of the script file in the dependency is set with the |scriptname| variable; by default these are |"build.lua"|.
+%
+%
+% \subsection{Output normalisation}
+% \label{sec:norm}
+%
+% To allow test files to be used between different systems (\emph{e.g.}~when
+% multiple developers are involved in a project), the log files are normalised
+% before comparison during checking. This removes some system-dependent
+% data but also some variations due to different engines and bugs in some
+% versions of some engines. This normalisation consists of two parts: removing
+% (\enquote{ignoring}) some lines and modifying others to give consistent
+% test. Currently, the following types of line are ignored:
+% \begin{itemize}
+% \item Lines before the \cs{START}, after the \cs{END} and within
+% \cs{OMIT}/\cs{TIMO} blocks
+% \item Entirely blank lines, including those consisting only of spaces.
+% \item Lines containing file dates in the
+% \texttt{\meta{yyyy}/\meta{mm}/\meta{dd}} format.
+% \item Lines starting \cs{openin} or \cs{openout}.
+% \item \LuaTeX{}-specific lines in box output for the \cs{local\ldots}
+% concepts it introduces when these are set to their defaults.
+% \item \LuaTeX{}-specific lines in box output which record
+% \cs{discretionary}.
+% \end{itemize}
+% Modifications made in lines are:
+% \begin{itemize}
+% \item Removal of the name of the test file itself.
+% \item Removal spaces at the start of lines (deals with some issues
+% with \LuaTeX{} using a different amount of indentation to other engines).
+% \item Removal of |./| at start of file names.
+% \item Standardisation of the list of units known to \TeX{} (\pdfTeX{}
+% and \LuaTeX{} add a small number of additional units which are not
+% known to \XeTeX{}).
+% \item Standardisation of \verb*|\csname\endcsname | to |\csname\endcsname|
+% (the former is formally correct, but the latter was produced for many
+% years due to a \TeX{} bug).
+% \item Conversion of \texttt{on line \meta{number}} to \texttt{on line ...}
+% to allow flexibility in changes to test files.
+% \item Restriction of glue set in boxes to four decimal places (\LuaTeX{}
+% may vary in the last digit from the \pdfTeX{} values).
+% \item Removal of the Omega-like \texttt{direction TLT} added by
+% \LuaTeX{} to boxes running in the \enquote{normal} way.
+% \item Conversion of low chars ($1$ to $31$) to |^^| notation (\LuaTeX{}
+% does not do this).
+% \item Correction of a \LuaTeX{} error message typo (|I''m going to assume|).
+% \end{itemize}
+%
+% \section{Writing test files}
+% \label{sec:writing-tests}
+%
+% Test files are written in a \TeX{} dialect using the support file |regression-test.tex|, which should be |\input| at the very beginning of each test.
+% Additional customisations to this driver can be included in a local |regression-test.cfg| file, which will be loaded automatically if found.
+%
+% The macros loaded by |regression-test.tex| set up the test system and provide a number of commands to aid the production of a structured test suite.
+% The basis of the test suite is to output material into the |.log| file, from which a normalised test output (|.tlg|) file is produced by the build command |savetlg|.
+% A number of commands are provided for this; they are all written in uppercase to help avoid possible conflicts with other package commands.
+%
+% \subsection{Metadata and structural commands}
+%
+% Any commands that write content to the |.log| file that should be ignored can be surrounded by |\OMIT| \dots\ |\TIMO|.
+% At the appropriate location in the document where the |.log| comparisons should start (say, after |\begin{document}|), the test suite must contain the |\START| macro.
+% The test should then include \cs{AUTHOR}\marg{authors details} in case a test file fails in the future and needs to be re-analysed.
+%
+% Some additional diagnostic information can then be included as metadata for the conditions of the test.
+% The desired format can be indicated with \cs{FORMAT}\marg{format name}, and any packages or classes loaded can be indicated with
+%
+% \noindent\hspace*{2\parindent} \cs{CLASS}\oarg{options}\marg{class name, version}\par
+% \noindent\hspace*{2\parindent} \cs{PACKAGE}\oarg{options}\marg{class name, version}
+%
+% \noindent These do not provide information that is useful for automated checking; after all, packages change their version numbers frequently.
+% Rather, including this information in a test indicates the conditions under which the test was definitely known to pass at a certain time in the past.
+%
+% The |\END| command signals the end of the test (but read on).
+% Some additional diagnostic information is printed at this time to debug if the test did not complete `properly' in terms of mismatched brace groups or \cs{if}\dots\cs{fi} groups.
+%
+% In a \LaTeX{} document, |\end{document}| will implicity call |\END| at the very end of the compilation process.
+% If |\END| is used directly (replacing |\end{document}| in the test), the compilation will halt almost immediately, and various tasks that |\end{document}| usually performs will not occur (such as potentially writing to the various |.toc| files, and so on). This can be an advantage if there is additional material printed to the log file in this stage that you wish to ignore, but it is a disadvantage if the test relies on various auxiliary data for a subsequent typesetting run.
+% (See the \var{checkruns} variable for how these tests would be test up.)
+%
+% \subsection{Commands to help write tests}
+%
+% A simple command \cs{CHECKCOMMAND}\cs{\meta{macro}} is provided to check whether a particular \cs{\meta{macro}} is defined, undefined, or equivalent to \cs{relax}.
+% This is useful to flag either that internal macros are remaining local to their definitions, or that defined commands definitely are defined, or even as a reminder that commands you intend to define in a future package need to be tested once they appear.
+%
+% \cs{TYPE} is used to write material to the \texttt{.log} file, like \LaTeX's \cs{typeout}, but it allows `long' input.
+% The following commands are defined to use \cs{TYPE} to output strings to the \texttt{.log} file.
+% \begin{itemize}
+% \item
+% \cs{SEPARATOR} inserts a long line of \texttt{=} symbols to break up the log output.
+% \item
+% \cs{NEWLINE} inserts a linebreak into the log file.
+% \item
+% \cs{TRUE}, \cs{FALSE}, \cs{YES}, \cs{NO} output those strings to the log file.
+% \item
+% \cs{ERROR} is \emph{not} defined but is commonly used to indicate a code path that should never be reached.
+% \item
+% The \cs{TEST}\marg{title}\marg{contents} command surrounds its \meta{contents} with some \cs{SEPARATOR}s and a \meta{title}.
+% \item
+% \cs{TESTEXP} surrounds its contents with \cs{TYPE} and formatting to match \cs{TEST}; this can be used as a shorthand to test expandable commands.
+% \item
+% TODO: would a \cs{TESTREXP} command (based on \cs{romannumeral} expansion) be useful as well?
+% \end{itemize}
+% An example of some of these commands is shown following.
+% \begin{Verbatim}
+% \begin{TEST}{bool_set,~lazy~evaluation}
+% \bool_set:Nn \l_tmpa_bool
+% {
+% \int_compare_p:nNn 1=1
+% && \bool_if_p:n
+% {
+% \int_compare_p:nNn 2=3 ||
+% \int_compare_p:nNn 4=4 ||
+% \int_compare_p:nNn 1=\ERROR % is skipped
+% }
+% && \int_compare_p:nNn 2=2
+% }
+% \bool_if:NTF \l_tmpa_bool \TRUE \FALSE
+% \end{TEST}
+% \end{Verbatim}
+% This test will produce the following in the output.
+% \begin{Verbatim}
+% ==========================================
+% TEST 8: bool_set, lazy evaluation
+% ==========================================
+% TRUE
+% ==========================================
+% \end{Verbatim}
+% (Only if it's the eighth test in the file of course, and assuming \pkg{expl3}
+% coding conventions are active.)
+%
+% \subsection{Showing box content}
+%
+% The commands introduced above are only useful for checking algorithmic or logical correctness.
+% Many packages should be tested based on their typeset output instead; \TeX{} provides a mechanism for this by printing the contents of a box to the log file.
+% The |regression-test.tex| driver file sets up the relevant \TeX{} parameters to produce as much output as possible when showing box output.
+%
+% A plain \TeX{} example of showing box content follows.
+% \begin{Verbatim}[frame=single,fontsize=\small]
+% \input regression-test.tex\relax
+% \START
+% \setbox0=\hbox{\rm hello \it world $a=b+c$}
+% \showbox0
+% \END
+% \end{Verbatim}
+% This produces the output shown in Figure~\ref{fig:box-log} (left side).
+% It is clear that if the definitions used to typeset the material in the box changes, the log output will differ and the test will no longer pass.
+%
+% The equivalent test in \LaTeXe{} using \pkg{expl3} is similar.
+% \begin{Verbatim}[frame=single,fontsize=\small]
+% \input{regression-test.tex}
+% \documentclass{article}
+% \usepackage{expl3}
+% \START
+% \ExplSyntaxOn
+% \box_new:N \l_tmp_box
+% \hbox_set:Nn \l_tmp_box {hello~ \emph{world}~ $a=b+c$}
+% \box_show:N \l_tmp_box
+% \ExplSyntaxOff
+% \END
+% \end{Verbatim}
+% The output from this test is shown in Figure~\ref{fig:box-log} (right side).
+% There is marginal difference (mostly related to font selection and different logging settings in \LaTeX) between the plain and \pkg{expl3} versions.
+%
+% When examples are not self-contained enough to be typeset into boxes, it is possible to ask \TeX{} to output the entire contents of a page.
+% Insert \cs{showoutput} for \LaTeX{} or set \cs{tracingoutput} positive for plain \TeX{}; ensure that the test ends with \cs{newpage} or equivalent because \TeX{} waits until the entire page is finished before outputting it.
+%
+% TODO: should we add something like \cs{TRACEPAGES} to be format-agnostic here? Should this perhaps even be active by default?
+%
+% \begin{figure}
+% \hspace*{-3cm}
+% \begin{BVerbatim}[fontsize=\small]
+% > \box0=
+% \hbox(6.94444+0.83333)x90.56589
+% .\tenrm h
+% .\tenrm e
+% .\tenrm l
+% .\tenrm l
+% .\tenrm o
+% .\glue 3.33333 plus 1.66666 minus 1.11111
+% .\tenit w
+% .\tenit o
+% .\tenit r
+% .\tenit l
+% .\tenit d
+%
+% .\glue 3.57774 plus 1.53333 minus 1.0222
+% .\mathon
+% .\teni a
+% .\glue(\thickmuskip) 2.77771 plus 2.77771
+% .\tenrm =
+% .\glue(\thickmuskip) 2.77771 plus 2.77771
+% .\teni b
+% .\glue(\medmuskip) 2.22217 plus 1.11108 minus 2.22217
+% .\tenrm +
+% .\glue(\medmuskip) 2.22217 plus 1.11108 minus 2.22217
+% .\teni c
+% .\mathoff
+%
+% ! OK.
+% l.9 \showbox0
+%
+%
+% \end{BVerbatim}
+% \qquad
+% \begin{BVerbatim}[fontsize=\small]
+% > \box71=
+% \hbox(6.94444+0.83333)x91.35481
+% .\OT1/cmr/m/n/10 h
+% .\OT1/cmr/m/n/10 e
+% .\OT1/cmr/m/n/10 l
+% .\OT1/cmr/m/n/10 l
+% .\OT1/cmr/m/n/10 o
+% .\glue 3.33333 plus 1.66666 minus 1.11111
+% .\OT1/cmr/m/it/10 w
+% .\OT1/cmr/m/it/10 o
+% .\OT1/cmr/m/it/10 r
+% .\OT1/cmr/m/it/10 l
+% .\OT1/cmr/m/it/10 d
+% .\kern 1.03334
+% .\glue 3.33333 plus 1.66666 minus 1.11111
+% .\mathon
+% .\OML/cmm/m/it/10 a
+% .\glue(\thickmuskip) 2.77771 plus 2.77771
+% .\OT1/cmr/m/n/10 =
+% .\glue(\thickmuskip) 2.77771 plus 2.77771
+% .\OML/cmm/m/it/10 b
+% .\glue(\medmuskip) 2.22217 plus 1.11108 minus 2.22217
+% .\OT1/cmr/m/n/10 +
+% .\glue(\medmuskip) 2.22217 plus 1.11108 minus 2.22217
+% .\OML/cmm/m/it/10 c
+% .\mathoff
+%
+% ! OK.
+% <argument> \l_tmp_box
+%
+% l.12 \box_show:N \l_tmp_box
+% \end{BVerbatim}
+% \caption{Output from displaying the contents of a simple box to the log file, using plain \TeX{} (left) and \pkg{expl3} (right). Some blank lines have been added to the plain \TeX{} version to help with the comparison.}
+% \label{fig:box-log}
+% \end{figure}
+%
+%
+%
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \clearpage
+% \section{\texttt{regression-test.tex}}
+%
+% This section describes the code for setting up the regression test system.
+% Each test file should start with |\input regression-test.tex\relax|.
+%
+% \begin{macrocode}
+%<*package>
+% \end{macrocode}
+%
+% \subsection{Preliminaries}
+%
+% Unlike in the \LaTeXe{} regression test suite, reset catcodes: each test should set these
+% as appropriate.
+% \begin{macrocode}
+\expandafter\edef\csname\detokenize{reset@catcodes}\endcsname{%
+ \catcode`\noexpand\@=\the\catcode`\@\relax
+}
+\catcode`\@=11 %
+% \end{macrocode}
+% Put \TeX{} into scroll mode, and stop it showing the
+% implementation details of macros in error messages.
+% \begin{macrocode}
+\scrollmode
+\errorcontextlines=-1 %
+% \end{macrocode}
+%
+% Show all box details: this avoids getting variable results if boxes
+% have different numbers of lines (\LuaTeX{} adds extra information).
+% \begin{macrocode}
+\showboxbreadth=\maxdimen
+\showboxdepth=\maxdimen
+% \end{macrocode}
+%
+% Set the newline character: \LaTeXe{} does this but plain-based formats
+% do not.
+% \begin{macrocode}
+\newlinechar=`\^^J
+% \end{macrocode}
+%
+% \subsection{Commands in test files}
+%
+% A long version of |\typeout|, because tests may contain |\par| tokens.
+% Besides, with that |\TYPE|, we can do |\TYPE { ... \TRUE ... \NEWLINE ... }|.
+% \begin{macrocode}
+\long\def\LONGTYPEOUT#1{%
+ \begingroup
+ \long\def\TYPE##1{##1}%
+ \immediate\write17{#1}%
+ \endgroup
+}
+\let\TYPE\LONGTYPEOUT
+% \end{macrocode}
+%
+% Start the test, after the optional |\documentclass|
+% |\begin{document}| commands with |\START|. All lines in the |.log| file
+% before this will be ignored.
+%
+% It also prints a DocStrip-style
+% character table in the |.tlg| file.
+% Is this necessary any more? Probably too late to remove it.
+% \begin{macrocode}
+\def\STARTMESSAGE{This is a generated file for the l3build validation system.}
+\def\START{\LONGTYPEOUT{START-TEST-LOG^^J^^J%
+ \STARTMESSAGE%
+^^J^^JDon't change this file in any respect.%
+^^J^^J\CTable^^J}}
+\begingroup
+\catcode`\^^\=0
+\catcode`\^^A=\catcode`\%
+^^\catcode`^^\ =11
+^^\catcode`^^\%=11
+^^\catcode`^^\#=11
+^^\catcode`^^\~=11
+^^\endlinechar=`^^\^^J
+^^\catcode`^^\\=11^^A
+^^\gdef^^\CTable{
+%% \CharacterTable
+%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
+%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
+%% Digits \0\1\2\3\4\5\6\7\8\9
+%% Exclamation \! Double quote \" Hash (number) \#
+%% Dollar \$ Percent \% Ampersand \&
+%% Acute accent \' Left paren \( Right paren \)
+%% Asterisk \* Plus \+ Comma \,
+%% Minus \- Point \. Solidus \/
+%% Colon \: Semicolon \; Less than \<
+%% Equals \= Greater than \> Question mark \?
+%% Commercial at \@ Left bracket \[ Backslash \\
+%% Right bracket \] Circumflex \^ Underscore \_
+%% Grave accent \` Left brace \{ Vertical bar \|
+%% Right brace \} Tilde \~}
+%%
+}^^A
+^^\endgroup{}%
+% \end{macrocode}
+% The test should end with |\END| or |\end{document}|
+% \begin{macrocode}
+\ifx\@@end\@undefined
+ \let\@@@end\end
+\else
+ \let\@@@end\@@end
+\fi
+\def\END
+ {%
+ \ifnum\currentgrouplevel>0 %
+ \LONGTYPEOUT{Bad grouping: \the\currentgrouplevel!}%
+ \fi
+ \ifnum\currentiflevel>1 %
+ \LONGTYPEOUT{Bad conditionals: \the\numexpr\currentiflevel-1!}%
+ \fi
+ \LONGTYPEOUT{END-TEST-LOG}\@@@end
+ }
+\let\@@end\END
+% \end{macrocode}
+% After the |\START| should come declarations of the format and style
+% options being used.
+% \begin{macrocode}
+\def\FORMAT#1{\LONGTYPEOUT{Format: #1}%
+ \def\@tempa{#1}\ifx\@tempa\@EJ\else
+ \OMIT\TYPE{WARNING: Declared format #1,^^JActual format \@EJ}\TIMO\fi}
+% \end{macrocode}
+% The old version got this information from everyjob,
+% but that does not work with \LaTeXe\ as |\everyjob| is cleared.
+% \begin{macrocode}
+\edef\@EJ{\fmtname <\fmtversion>}
+% \end{macrocode}
+% Some author info:
+% \begin{macrocode}
+\def\AUTHOR#1{\LONGTYPEOUT{Author: #1}}
+% \end{macrocode}
+% Surround commands which produce irrelevant lines in the .log file by
+% |\OMIT|\dots|\TIMO|
+% \begin{macrocode}
+\def\OMIT{\LONGTYPEOUT{OMIT}}
+\def\TIMO{\LONGTYPEOUT{TIMO}}
+% \end{macrocode}
+% Not all packages declare themselves to the log file, and we can not
+% rely on TeX`s output as it includes full path names, and does not
+% include version numbers etc.
+%
+% If the class or package is loaded with options, you may
+% specify the options in the |\CLASS| (|\PACKAGE|) declaration. eg:
+% \begin{verbatim}
+% \CLASS[german,a4page]{article v2.0 1994/01/02}
+% \PACKAGE{ifthen v2.2 1993/11/12}
+% \PACKAGE[dvips]{graphics v 3.8 1994/02/02}
+% \end{verbatim}
+% \begin{macrocode}
+\def\CLASS{\@ifnextchar[\OPTCLASS\XCLASS}
+\def\OPTCLASS[#1]#2{%
+ \TYPE{Main Class: #2^^J\space\space\space\space Options: #1}}
+\def\XCLASS#1{%
+ \TYPE{Main Class: #1}}
+\def\PACKAGE{\@ifnextchar[\OPTPACKAGE\XPACKAGE}
+\def\OPTPACKAGE[#1]#2{%
+ \TYPE{Package: #2^^J\space\space\space\space Options: #1}}
+\def\XPACKAGE#1{%
+ \TYPE{Package: #1}}
+% \end{macrocode}
+%
+% The commands above require 2e's \verb|\@ifnextchar|, so copy that definition verbatim if necessary:
+% \begin{macrocode}
+\ifx\@ifnextchar\@undefined
+\long\def\@ifnextchar#1#2#3{%
+ \let\reserved@d=#1%
+ \def\reserved@a{#2}%
+ \def\reserved@b{#3}%
+ \futurelet\@let@token\@ifnch}
+\def\@ifnch{%
+ \ifx\@let@token\@sptoken
+ \let\reserved@c\@xifnch
+ \else
+ \ifx\@let@token\reserved@d
+ \let\reserved@c\reserved@a
+ \else
+ \let\reserved@c\reserved@b
+ \fi
+ \fi
+ \reserved@c}
+\def\:{\let\@sptoken= } \: % this makes \@sptoken a space token
+\def\:{\@xifnch} \expandafter\def\: {\futurelet\@let@token\@ifnch}
+\fi
+% \end{macrocode}
+% After the above declarations, and before the main tests, you may
+% optionally declare' all the commands in the `module' that you are
+% about to test. These commands will be registered as defined,
+% undefined or relaxed (i.e.~|\let| to |\relax|). You may wish to declare
+% commands not currently implemented, so that if they are added at a
+% later stage, the test will fail, reminding someone to document the
+% fact that the user interface has changed.
+% \begin{macrocode}
+\def\CHECKCOMMAND#1{%
+ \ifx#1\@undefined\LONGTYPEOUT{Undefined \string#1}\else
+ \ifx#1\relax\LONGTYPEOUT{Relaxed \space\space\string#1}\else
+ \LONGTYPEOUT{Defined \space\space\string#1}\fi\fi}
+% \end{macrocode}
+% To allow testing of possible changes, we allow extra code to be read
+% in before the test starts. The necessary code should be placed in a
+% file |regression-test.cfg|.
+% \begin{macrocode}
+\OMIT
+\ifx\InputIfFileExists\@undefined
+\newread\@inputcheck
+\long\def\InputIfFileExists#1#2#3{%
+ \openin\@inputcheck#1\relax
+ \ifeof\@inputcheck
+ \def\reserved@a{#3}%
+ \else
+ \def\reserved@a{#2\input #1\relax}%
+ \fi
+ \closein\@inputcheck
+ \reserved@a}
+\fi
+\InputIfFileExists{regression-test.cfg}
+ {\LONGTYPEOUT{^^J***^^Jregression-test.cfg in operation^^J***^^J}}{}
+\TIMO
+% \end{macrocode}
+%
+% \subsection{Formatting the \texttt{.log} file}
+%
+% We are not starved for space in the log file output, so let's make it as
+% verbose as is useful when reading the |.diff|'s.
+% \begin{macrocode}
+\newcount \gTESTint
+\def\SEPARATOR{%
+ \TYPE{%
+ ============================================================%
+ }%
+}
+% \end{macrocode}
+% \begin{macrocode}
+\long\def\TEST#1#2{%
+ \advance \gTESTint 1 %
+ \SEPARATOR
+ \LONGTYPEOUT{%
+ TEST \the\gTESTint: \detokenize{#1}}%
+ \SEPARATOR
+ \begingroup
+ \let\TYPE\LONGTYPEOUT
+ #2%
+ \endgroup
+ \SEPARATOR \LONGTYPEOUT{}%
+}
+\long\def\TESTEXP#1#2{%
+ \advance \gTESTint 1 %
+ \SEPARATOR
+ \LONGTYPEOUT{%
+ TEST \the\gTESTint: \detokenize{#1}}%
+ \SEPARATOR
+ \begingroup
+ \long\def\TYPE##1{##1}%
+ \LONGTYPEOUT{#2}%
+ \endgroup
+ \SEPARATOR
+ \LONGTYPEOUT{}%
+}
+
+\def \TRUE {\TYPE{TRUE}}
+\def \FALSE {\TYPE{FALSE}}
+\def \YES {\TYPE{YES}}
+\def \NO {\TYPE{NO}}
+
+\def \NEWLINE {\TYPE{^^J}}
+% \end{macrocode}
+% Finish up.
+% \begin{macrocode}
+\reset@catcodes
+% \end{macrocode}
+%
+% \begin{macrocode}
+%</package>
+% \end{macrocode}
+%
+% \newpage
+% \section{\texttt{l3build.lua}}
+%
+% This section consists of the |l3build.lua| code.
+% This code is \emph{not} generated from |l3build.dtx| since it needs to already be extracted to build \pkg{l3build} itself!
+% As Frank says, we don't want to end up with a double M\"unchhausen.
+%
+% \lstinputlisting
+% [
+% basicstyle=\ttfamily\scriptsize,
+% numbers=left,
+% numberstyle={\tiny\color[gray]{0.4}},
+% language={[5.2]Lua},
+% procnamekeys=function,
+% procnamestyle=\color{red},
+% ]
+% {l3build.lua}
+%
+% \end{implementation}
+%
+% \PrintIndex
+
+
diff --git a/Master/texmf-dist/source/latex/l3build/l3build.ins b/Master/texmf-dist/source/latex/l3build/l3build.ins
new file mode 100644
index 00000000000..852c19381d7
--- /dev/null
+++ b/Master/texmf-dist/source/latex/l3build/l3build.ins
@@ -0,0 +1,40 @@
+\iffalse meta-comment
+
+File l3build.ins Copyright (C) 2014 The LaTeX3 Project
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version. The latest version
+of this license is in the file
+
+ http://www.latex-project.org/lppl.txt
+
+This file is part of the "l3build bundle" (The Work in LPPL)
+and all files in that bundle must be distributed together.
+
+The released version of this bundle is available from CTAN.
+
+\fi
+
+\input docstrip.tex
+\askforoverwritefalse
+
+\preamble
+
+EXPERIMENTAL CODE
+
+Do not distribute this file without also distributing the
+source files specified above.
+
+Do not distribute a modified version of this file.
+
+\endpreamble
+% stop docstrip adding \endinput
+\postamble
+\endpostamble
+
+\keepsilent
+
+\generate{\file{regression-test.tex}{\from{l3build.dtx}{package}}}
+
+\endbatchfile
diff --git a/Master/texmf-dist/source/latex/l3build/l3build.lua b/Master/texmf-dist/source/latex/l3build/l3build.lua
new file mode 100644
index 00000000000..99f68bdbc77
--- /dev/null
+++ b/Master/texmf-dist/source/latex/l3build/l3build.lua
@@ -0,0 +1,1025 @@
+--[[
+
+ File l3build.lua (C) Copyright 2014 The LaTeX3 Project
+
+ It may be distributed and/or modified under the conditions of the
+ LaTeX Project Public License (LPPL), either version 1.3c of this
+ license or (at your option) any later version. The latest version
+ of this license is in the file
+
+ http://www.latex-project.org/lppl.txt
+
+ This file is part of the "l3build bundle" (The Work in LPPL)
+ and all files in that bundle must be distributed together.
+
+ The released version of this bundle is available from CTAN.
+
+--]]
+
+-- Version information: should be identical to that in l3build.dtx
+release_date = "2014/06/15"
+release_ver = "5141"
+
+-- Ensure the module and bundle exist
+module = module or ""
+bundle = bundle or ""
+
+-- Directory structure for the build system
+-- Use Unix-style path separators
+maindir = maindir or "."
+
+-- Substructure for tests and support files
+testfiledir = testfiledir or "testfiles" -- Set to "" to cancel any tests
+testsupdir = testsupdir or testfiledir .. "/support"
+supportdir = supportdir or maindir .. "/support"
+
+-- Structure within a development area
+distribdir = distribdir or maindir .. "/build/distrib"
+localdir = localdir or maindir .. "/build/local"
+testdir = testdir or maindir .. "/build/test"
+typesetdir = typesetdir or maindir .. "/build/doc"
+unpackdir = unpackdir or maindir .. "/build/unpacked"
+
+-- Substructure for CTAN release material
+ctandir = ctandir or distribdir .. "/ctan"
+tdsdir = tdsdir or distribdir .. "/tds"
+tdsroot = tdsroot or "latex"
+
+-- Location for installation on CTAN or in TEXMFHOME
+if bundle == "" then
+ moduledir = tdsroot .. "/" .. module
+else
+ moduledir = tdsroot .. "/" .. bundle .. "/" .. module
+end
+
+-- File types for various operations
+-- Use Unix-style globs
+-- All of these may be set earlier, so a initialised conditionally
+binaryfiles = binaryfiles or {"*.pdf", "*.zip"}
+checkfiles = checkfiles or { } -- Extra files unpacked purely for tests
+checksuppfiles = checksuppfiles or { }
+cmdchkfiles = cmdchkfiles or { }
+demofiles = demofiles or { }
+cleanfiles = cleanfiles or {"*.pdf", "*.zip"}
+excludefiles = excludefiles or {"*~"} -- Any Emacs stuff
+installfiles = installfiles or {"*.sty"}
+sourcefiles = sourcefiles or {"*.dtx", "*.ins"} -- Files to copy for unpacking
+txtfiles = txtfiles or {"*.markdown"}
+typesetfiles = typesetfiles or {"*.dtx"}
+typesetsuppfiles = typesetsuppfiles or { }
+unpackfiles = unpackfiles or {"*.ins"} -- Files to actually unpack
+unpacksuppfiles = unpacksuppfiles or { }
+
+-- Roots which should be unpacked to support unpacking/testing/typesetting
+checkdeps = checkdeps or { }
+typesetdeps = typesetdeps or { }
+unpackdeps = unpackdeps or { }
+
+-- Executable names plus following options
+typesetexe = typesetexe or "pdflatex"
+unpackexe = unpackexe or "tex"
+zipexe = "zip"
+
+checkopts = checkopts or "-interaction=batchmode"
+cmdchkopts = cmdchkopts or "-interaction=batchmode"
+typesetopts = typesetopts or "-interaction=nonstopmode"
+unpackopts = unpackopts or ""
+zipopts = zipopts or "-v -r -X"
+
+-- Engines for testing
+chkengines = chkengines or {"pdftex", "xetex", "luatex"}
+stdengine = stdengine or "pdftex"
+
+-- Enable access to trees outside of the repo
+-- As these may be set false, a more elaborate test than normal is needed
+-- here
+if checksearch == nil then
+ checksearch = true
+end
+if typesetsearch == nil then
+ typesetsearch = true
+end
+if unpacksearch == nil then
+ unpacksearch = true
+end
+
+-- Other required settings
+checkruns = checkruns or 1
+scriptname = scriptname or "build.lua" -- Script used in each directory
+typesetcmds = typesetcmds or ""
+
+-- Extensions for various file types: used to abstract out stuff a bit
+logext = logext or ".log"
+lvtext = lvtext or ".lvt"
+tlgext = tlgext or ".tlg"
+
+-- Convert a file glob into a pattern for use by e.g. string.gub
+-- Based on https://github.com/davidm/lua-glob-pattern
+-- Simplified substantially: "[...]" syntax not supported as is not
+-- required by the file patterns used by the team. Also note style
+-- changes to match coding approach in rest of this file.
+--
+-- License for original globtopattern
+--[[
+
+ (c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT).
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ (end license)
+
+--]]
+function glob_to_pattern (glob)
+
+ local pattern = "^" -- pattern being built
+ local i = 0 -- index in glob
+ local char -- char at index i in glob
+
+ -- escape pattern char
+ local function escape (char)
+ return string.match (char, "^%w$") and char or "%" .. char
+ end
+
+ -- Convert tokens.
+ while true do
+ i = i + 1
+ char = string.sub (glob, i, i)
+ if char == "" then
+ pattern = pattern .. "$"
+ break
+ elseif char == "?" then
+ pattern = pattern .. "."
+ elseif char == "*" then
+ pattern = pattern .. ".*"
+ elseif char == "[" then
+ -- Ignored
+ print ("[...] syntax not supported in globs!")
+ elseif char == "\\" then
+ i = i + 1
+ char = string.sub(glob, i, i)
+ if char == "" then
+ pattern = pattern .. "\\$"
+ break
+ end
+ pattern = pattern .. escape (char)
+ else
+ pattern = pattern .. escape (char)
+ end
+ end
+ return pattern
+end
+
+-- File operation support
+-- Much of this is OS-dependent as Lua offers a very limited range of file
+-- operations 'natively'.
+
+-- Detect the operating system in use
+-- See http://www.lua.org/manual/5.2/manual.html#pdf-package.config for details
+-- of the string used here to pick up the operating system (Windows or
+-- 'not-Windows'
+-- Support items are defined here for cases where a single string can cover
+-- both Windows and Unix cases: more complex situations are handled inside
+-- the support functions
+if string.sub (package.config, 1, 1) == "\\" then
+ os_concat = "&"
+ os_diffext = ".fc"
+ os_diffexe = "fc /n"
+ os_null = "nul"
+ os_pathsep = ";"
+ os_setenv = "set"
+ os_windows = true
+else
+ os_concat = ";"
+ os_diffext = ".diff"
+ os_diffexe = "diff -c"
+ os_null = "/dev/null"
+ os_pathsep = ":"
+ os_setenv = "export"
+ os_windows = false
+end
+
+-- File operations are aided by the LuaFileSystem module, which is available
+-- within texlua
+lfs = require ("lfs")
+
+-- For cleaning out a directory, which also ensures that it exists
+function cleandir (dir)
+ mkdir (dir)
+ rm (dir, "*")
+end
+
+-- Copy files 'quietly'
+function cp (glob, source, dest)
+ for _,i in ipairs (filelist (source, glob)) do
+ local source = source .. "/" .. i
+ if os_windows then
+ os.execute (
+ "copy /y " .. unix_to_win (source) .. " "
+ .. unix_to_win (dest) .. " > nul"
+ )
+ else
+ os.execute ("cp -f " .. source .. " " .. dest)
+ end
+ end
+end
+
+-- OS-dependent test for a directory
+function direxists (dir)
+ local errorlevel
+ if os_windows then
+ errorlevel =
+ os.execute ("if not exist \"" .. unix_to_win (dir) .. "\" exit 1")
+ else
+ errorlevel = os.execute ("[ -d " .. dir .. " ]")
+ end
+ if errorlevel ~= 0 then
+ return (false)
+ end
+ return (true)
+end
+
+function fileexists (file)
+ local f = io.open (file, "r")
+ if f ~= nil then
+ io.close(f)
+ return true
+ else
+ return false
+ end
+end
+
+-- Generate a table containing all file names of the given glob or all files
+-- if absent
+-- Not actually OS-dependent but in the same area
+function filelist (path, glob)
+ local files = { }
+ local pattern
+ if glob then
+ pattern = glob_to_pattern (glob)
+ end
+ if direxists (path) then
+ for entry in lfs.dir (path) do
+ if pattern then
+ if string.match (entry, pattern) then
+ table.insert (files, entry)
+ end
+ else
+ if entry ~= "." and entry ~= ".." then
+ table.insert (files, entry)
+ end
+ end
+ end
+ end
+ return files
+end
+
+function mkdir (dir)
+ if os_windows then
+ -- Windows (with the extensions) will automatically make directory trees
+ -- but issues a warning if the dir already exists: avoid by including a test
+ local dir = unix_to_win (dir)
+ os.execute ("if not exist " .. dir .. "\\nul " .. "mkdir " .. dir)
+ else
+ os.execute ("mkdir -p " .. dir)
+ end
+end
+
+-- Rename
+function ren (dir, source, dest)
+ local dir = dir .. "/"
+ if os_windows then
+ os.execute ("ren " .. unix_to_win (dir) .. source .. " " .. dest)
+ else
+ os.execute ("mv " .. dir .. source .. " " .. dir .. dest)
+ end
+end
+
+-- Remove file(s) based on a glob
+function rm (source, glob)
+ for _,i in ipairs (filelist (source, glob)) do
+ os.remove (source .. "/" .. i)
+ end
+end
+
+-- Remove a directory tree
+function rmdir (dir)
+ -- First, make sure it exists to avoid any errors
+ mkdir (dir)
+ if os_windows then
+ os.execute ("rmdir /s /q " .. unix_to_win (dir) )
+ else
+ os.execute ("rm -r " .. dir)
+ end
+end
+
+-- Run a command in a given directory
+function run (dir, cmd)
+ local errorlevel = os.execute ("cd " .. dir .. os_concat .. cmd)
+ return (errorlevel)
+end
+
+-- Deal with the fact that Windows and Unix use different path separators
+function unix_to_win (path)
+ local path = string.gsub (path, "/", "\\")
+ return path
+end
+
+--
+-- Auxiliary functions which are used by more than one main function
+--
+
+-- Do some subtarget for all modules in a bundle
+function allmodules (target)
+ local errorlevel = 0
+ for _,i in ipairs (modules) do
+ print (
+ "Running script " .. scriptname .. " with target \"" .. target .. "\" for module "
+ .. i
+ )
+ errorlevel = run (i, "texlua " .. scriptname .. " " .. target)
+ if errorlevel > 0 then
+ return (errorlevel)
+ end
+ end
+ return (errorlevel)
+end
+
+-- Set up the check system files: needed for checking one or more tests and
+-- for saving the test files
+function checkinit ()
+ cleandir (localdir)
+ cleandir (testdir)
+ cleandir (unpackdir)
+ depinstall (checkdeps)
+ -- Copy dependencies to the test directory itself: this makes the paths
+ -- a lot easier to manage, and is important for dealing with the log and
+ -- with file input/output tests
+ for _,i in ipairs (filelist (localdir)) do
+ cp (i, localdir, testdir)
+ end
+ bundleunpack ()
+ for _,i in ipairs (installfiles) do
+ cp (i, unpackdir, testdir)
+ end
+ for _,i in ipairs (checkfiles) do
+ cp (i, unpackdir, testdir)
+ end
+ if direxists (testsupdir) then
+ for _,i in ipairs (filelist (testsupdir)) do
+ cp (i, testsupdir, testdir)
+ end
+ end
+ for _,i in ipairs (checksuppfiles) do
+ cp (i, supportdir, testdir)
+ end
+end
+
+-- Unpack files needed to support testing/typesetting/unpacking
+function depinstall (deps)
+ for _,i in ipairs (deps) do
+ print ("Installing dependency: " .. i)
+ run (i, "texlua " .. scriptname .. " localinstall")
+ cleandir (unpackdir)
+ end
+end
+
+-- Convert the raw log file into one for comparison/storage: keeps only
+-- the 'business' part from the tests and removes system-dependent stuff
+function formatlog (logfile, newfile)
+ local function killcheck (line, kill)
+ local killnext = false
+ local line = line
+ -- Skip lines containing file dates
+ if string.match (line, "[^<]%d%d%d%d/%d%d/%d%d") then
+ line = ""
+ elseif
+ -- Skip \openin/\openout lines in web2c 7.x
+ -- As Lua doesn't allow "(in|out)", a slightly complex approach:
+ -- do a substitution to check the line is exactly what is required!
+ string.match (
+ string.gsub (line, "^\\openin", "\\openout"), "^\\openout%d = "
+ )
+ then
+ line = ""
+ elseif
+ -- Various things that only LuaTeX adds to boxes: at least while
+ -- we don't have bidi tests these lines can be removed
+ string.match (line, "^%.*\\whatsit$") or
+ string.match (line, "^%.*\\localinterlinepenalty=0$") or
+ string.match (line, "^%.*\\localbrokenpenalty=0$") or
+ string.match (line, "^%.*\\localleftbox=null$") or
+ string.match (line, "^%.*\\localrightbox=null$") then
+ line = ""
+ -- Pick up LuaTeX's \discretionary line and remove it and the next
+ -- line
+ elseif
+ string.match (line, "%.+\\discretionary") then
+ line = ""
+ killnext = true
+ elseif kill then
+ line = ""
+ end
+ return line, killnext
+ end
+ -- Substitutions to remove some non-useful changes
+ local function normalize (line)
+ local line = line
+ -- Remove test file name from lines
+ line = string.gsub (line, string.match (logfile, ".*/(.*)%" .. logext .. "$"), "")
+ -- Zap ./ at begin of filename
+ line = string.gsub (line, "%(%.%/", "(")
+ -- XeTeX knows only the smaller set of dimension units
+ line = string.gsub (
+ line, "cm, mm, dd, cc, bp, or sp", "cm, mm, dd, cc, nd, nc, bp, or sp"
+ )
+ -- Normalise a case where fixing a TeX bug changes the message text
+ line = string.gsub (line, "\\csname\\endcsname ", "\\csname\\endcsname")
+ -- Zap "on line <num>" and replace with "on line ..."
+ line = string.gsub (line, "on line %d*", "on line ...")
+ -- Remove spaces at the start of lines: deals with the fact that LuaTeX
+ -- uses a different number to the other engines
+ line = string.gsub (line, "^%s+", "")
+ -- LuaTeX can give slightly different glue setting to pdfTeX:
+ -- handle this by restricting to four decimal places
+ line = string.gsub (
+ line, "glue set (%d+.%d%d%d%d)%dfil", "glue set %1fil"
+ )
+ -- For the present, remove direction information on boxes
+ line = string.gsub (line, ", direction TLT", "")
+ -- LuaTeX displays low chars differently: tidy up to ^^ notation
+ for i = 1, 31, 1 do
+ line = string.gsub (line, string.char (i), "^^" .. string.char (64 + i))
+ end
+ -- Minor LuaTeX bug: it prints an extra "'" in one message: add enough
+ -- context to hopefully hit only the bug
+ line = string.gsub (
+ line, "I''m going to assume", "I'm going to assume"
+ )
+ return line
+ end
+ local newlog = ""
+ local prestart = true
+ local skipping = false
+ local killnext = false
+ for line in io.lines (logfile) do
+ if line == "START-TEST-LOG" then
+ prestart = false
+ elseif line == "END-TEST-LOG" then
+ break
+ elseif line == "OMIT" then
+ skipping = true
+ elseif line == "TIMO" then
+ skipping = false
+ elseif not prestart and not skipping then
+ line = normalize (line)
+ line, killnext = killcheck (line, killnext)
+ if not string.match (line, "^ *$") then
+ newlog = newlog .. line .. "\n"
+ end
+ end
+ end
+ local newfile = io.open (newfile, "w")
+ io.output (newfile)
+ io.write (newlog)
+ io.close (newfile)
+end
+
+-- Runs a single test: needs the name of the test rather than the .lvt file
+-- One 'test' here may apply to multiple engines
+function runcheck (name, engine, hide)
+ local chkengines = chkengines
+ if engine then
+ chkengines = {engine}
+ end
+ local errorlevel = 0
+ for _,i in ipairs (chkengines) do
+ runtest (name, i, hide)
+ local testname = name .. "." .. i
+ local difffile = testdir .. "/" .. testname .. os_diffext
+ local newfile = testdir .. "/" .. testname .. logext
+ -- Use engine-specific file if available
+ local tlgfile = testfiledir .. "/" .. name .. "." .. i .. tlgext
+ if not fileexists (tlgfile) then
+ tlgfile = testfiledir .. "/" .. name .. tlgext
+ end
+ if os_windows then
+ tlgfile = unix_to_win (tlgfile)
+ end
+ local errlevel = os.execute (
+ os_diffexe .. " " .. tlgfile .. " " .. newfile .. " > " .. difffile
+ )
+ if errlevel == 0 then
+ os.remove (difffile)
+ else
+ errorlevel = errlevel
+ end
+ end
+ return (errorlevel)
+end
+
+-- Run one of the test files: doesn't check the result so suitable for
+-- both creating and verifying .tlg files
+function runtest (name, engine, hide)
+ cp (name .. lvtext, testfiledir, testdir)
+ local engine = engine or stdengine
+ -- Engine name doesn't include the "la" for LaTeX!
+ local cmd = string.gsub (engine, "tex$", "latex")
+ local logfile = testdir .. "/" .. name .. logext
+ local lvtfile = name .. lvtext
+ local newfile = testdir .. "/" .. name .. "." .. engine .. logext
+ for i = 1, checkruns, 1 do
+ run (
+ testdir,
+ os_setenv .. " TEXINPUTS=." .. (checksearch and os_pathsep or "")
+ .. os_concat ..
+ cmd .. " " .. checkopts .. " " .. lvtfile
+ .. (hide and (" > " .. os_null) or "")
+ )
+ end
+ formatlog (logfile, newfile)
+end
+
+-- Strip the extension from a file name
+function stripext (file)
+ local name = string.gsub (file, "%..*$", "")
+ return name
+end
+
+function testexists (test)
+ if fileexists (testfiledir .. "/" .. test .. lvtext) then
+ return true
+ else
+ return false
+ end
+end
+
+-- Standard versions of the main targets for building modules
+
+-- Simply print out how to use the build system
+help = help or function ()
+ print ""
+ if testfiledir ~= "" then
+ print " build check - run automated check system "
+ end
+ if module ~= "" and testfiledir ~= "" then
+ print " build checklvt <name> - check one test file <name> for all engines"
+ print " build checklvt <name> <engine> - check one test file <name> for <engine> "
+ end
+ print " build clean - clean out directory tree "
+ if next (cmdchkfiles) ~= nil then
+ print " build cmdcheck - check commands documented are defined "
+ end
+ if module == "" or bundle == "" then
+ print " build ctan - create CTAN-ready archive "
+ end
+ print " build doc - runs all documentation files "
+ print " build install - install files in local texmf tree "
+ if module ~= "" and testfiledir ~= "" then
+ print " build savetlg <name> - save test log for <name> for all engines "
+ print " build savetlg <name> <engine> - save test log for <name> for <engine> "
+ end
+ print " build unpack - extract packages "
+ print ""
+end
+
+function check ()
+ local errorlevel = 0
+ if testfiledir ~= "" and direxists (testfiledir) then
+ checkinit ()
+ print ("Running checks on")
+ for _,i in ipairs (filelist (testfiledir, "*" .. lvtext)) do
+ local name = stripext (i)
+ print (" " .. name)
+ local errlevel = runcheck (name, nil, true)
+ if errlevel ~= 0 then
+ errorlevel = 1
+ end
+ end
+ if errorlevel ~= 0 then
+ print ("\n Check failed with difference files")
+ for _,i in ipairs (filelist (testdir, "*" .. os_diffext)) do
+ print (" - " .. testdir .. "/" .. i)
+ end
+ print ("")
+ else
+ print ("\n All checks passed\n")
+ end
+ end
+ return (errorlevel)
+end
+
+function checklvt (name, engine)
+ local engine = engine or stdengine
+ if testexists (name) then
+ checkinit ()
+ print ("Running checks on " .. name)
+ runcheck (name, engine)
+ if fileexists (testdir .. "/" .. name .. "." .. engine .. os_diffext) then
+ print (" Check fails with diff file")
+ print (
+ " " .. testdir .. os_dirsep .. name .. "." .. engine .. os_diffext)
+ else
+ print (" Check passes")
+ end
+ else
+ print ("Test \"" .. name .. "\" not set up!")
+ end
+end
+
+-- Remove all generated files
+function clean ()
+ rmdir (distribdir)
+ mkdir (distribdir)
+ cleandir (localdir)
+ cleandir (testdir)
+ cleandir (typesetdir)
+ cleandir (unpackdir)
+ for _,i in ipairs (cleanfiles) do
+ rm (".", i)
+ end
+end
+
+function bundleclean ()
+ allmodules ("clean")
+ for _,i in ipairs (cleanfiles) do
+ rm (".", i)
+ end
+ rmdir (ctandir)
+ rmdir (tdsdir)
+end
+
+-- Check commands are defined
+function cmdcheck ()
+ cleandir (localdir)
+ cleandir (testdir)
+ depinstall (checkdeps)
+ local engine = string.gsub (stdengine, "tex$", "latex")
+ print ("Checking source files")
+ for _,i in ipairs (cmdchkfiles) do
+ for _,j in ipairs (filelist (".", i)) do
+ print (" " .. stripext (j))
+ os.execute (
+ os_setenv .. " TEXINPUTS=." .. os_pathsep .. localdir ..
+ (checksearch and os_pathsep or "") .. os_concat ..
+ engine .. " " .. cmdchkopts .. " -output-directory=" .. testdir ..
+ " \"\\PassOptionsToClass{check}{l3doc} \\input " .. j .. "\""
+ .. " > " .. os_null
+ )
+ for line in io.lines (testdir .. "/" .. stripext (j) .. ".cmds") do
+ if string.match (line, "^%!") then
+ print (" - " .. string.match (line, "^%! (.*)"))
+ end
+ end
+ end
+ end
+end
+
+function ctan (standalone)
+ local function dirzip (dir, name)
+ local zipname = name .. ".zip"
+ local function tab_to_str (table)
+ local string = ""
+ for _,i in ipairs (table) do
+ string = string .. " " .. "\"" .. i .. "\""
+ end
+ return string
+ end
+ -- Convert the tables of files to quoted strings
+ local binfiles = tab_to_str (binaryfiles)
+ local exclude = tab_to_str (excludefiles)
+ -- First, zip up all of the text files
+ run (
+ dir,
+ zipexe .. " " .. zipopts .. " -ll ".. zipname .. " " .. "." .. " -x"
+ .. binfiles .. " " .. exclude
+ )
+ -- Then add the binary ones
+ run (
+ dir,
+ zipexe .. " " .. zipopts .. " -g ".. zipname .. " " .. ". -i" ..
+ binfiles .. " -x" .. exclude
+ )
+ end
+ local errorlevel
+ if standalone then
+ clean ()
+ errorlevel = check ()
+ bundle = module
+ else
+ bundleclean ()
+ errorlevel = allmodules ("bundlecheck")
+ end
+ if errorlevel == 0 then
+ mkdir (ctandir .. "/" .. bundle)
+ mkdir (tdsdir)
+ if standalone then
+ errorlevel = bundlectan ()
+ else
+ errorlevel = allmodules ("bundlectan")
+ end
+ end
+ if errorlevel == 0 then
+ for _,i in ipairs (txtfiles) do
+ for _,j in ipairs (filelist (".", i)) do
+ local function installtxt (name, dir)
+ cp (name, ".", dir)
+ ren (dir, name, stripext (name))
+ end
+ installtxt (j, ctandir .. "/" .. bundle)
+ installtxt (j, tdsdir .. "/doc/" .. tdsroot .. "/" .. bundle)
+ end
+ end
+ dirzip (tdsdir, bundle .. ".tds")
+ cp (bundle .. ".tds.zip", tdsdir, ctandir)
+ dirzip (ctandir, bundle)
+ cp (bundle .. ".zip", ctandir, ".")
+ else
+ print ("\n====================")
+ print ("Zip creation failed!")
+ print ("====================\n")
+ end
+end
+
+function bundlectan ()
+ local function install (source, dest, files, ctan)
+ local installdir = tdsdir .. "/" .. dest .. "/" .. moduledir
+ mkdir (installdir)
+ for _,i in ipairs (files) do
+ if ctan then
+ cp (i, source, ctandir .. "/" .. bundle)
+ end
+ cp (i, source, installdir)
+ end
+ end
+ unpack ()
+ install (unpackdir, "tex", installfiles, false)
+ local errorlevel = doc ()
+ if errorlevel == 0 then
+ -- Convert input names for typesetting into names of PDF files
+ local pdffiles = { }
+ for _,i in ipairs (typesetfiles) do
+ table.insert (pdffiles, (string.gsub (i, "%.%w+$", ".pdf")))
+ end
+ install (".", "doc", pdffiles, true)
+ install (".", "doc", demofiles, true)
+ install (".", "source", typesetfiles, true)
+ install (".", "source", sourcefiles, true)
+ end
+ return (errorlevel)
+end
+
+-- Typeset all required documents
+-- This function has several sub-parts, but as most are not needed anywhere
+-- else everything is done locally within the main function
+function doc ()
+ local function typeset (file)
+ local name = stripext (file)
+ -- A couple of short functions to deal with the repeated steps in a
+ -- clear way
+ local function index (name)
+ run (
+ typesetdir ,
+ "makeindex -s gind.ist -o " .. name .. ".ind " .. name .. ".idx"
+ )
+ end
+ local function typeset (file)
+ local errorlevel =
+ os.execute (
+ os_setenv .. " TEXINPUTS=" .. typesetdir ..
+ os_pathsep .. localdir .. (typesetsearch and os_pathsep or "") ..
+ os_concat ..
+ typesetexe .. " " .. typesetopts ..
+ " -output-directory=" .. typesetdir ..
+ " \"" .. typesetcmds ..
+ "\\input " .. typesetdir .. "/" .. file .. "\""
+ )
+ return errorlevel
+ end
+ os.remove (name .. ".pdf")
+ print ("Typesetting " .. name)
+ local errorlevel = typeset (file)
+ if errorlevel ~= 0 then
+ print (" ! Compilation failed")
+ return (errorlevel)
+ else
+ for i = 1, 2 do -- Do twice
+ index (name)
+ typeset (file)
+ end
+ cp (name .. ".pdf", typesetdir, ".")
+ end
+ return (errorlevel)
+ end
+ -- Set up
+ cleandir (localdir)
+ mkdir (typesetdir)
+ for _,i in ipairs (sourcefiles) do
+ cp (i, ".", typesetdir)
+ end
+ for _,i in ipairs (typesetfiles) do
+ cp (i, ".", typesetdir)
+ end
+ for _,i in ipairs (typesetsuppfiles) do
+ cp (i, supportdir, localdir)
+ end
+ depinstall (typesetdeps)
+ -- Main loop for doc creation
+ for _,i in ipairs (typesetfiles) do
+ for _,j in ipairs (filelist (".", i)) do
+ local errorlevel = typeset (j)
+ if errorlevel ~= 0 then
+ return (errorlevel)
+ end
+ end
+ end
+ return 0
+end
+
+-- Unpack then install
+function localinstall ()
+ unpack ()
+ for _,i in ipairs (installfiles) do
+ cp (i, unpackdir, localdir)
+ end
+end
+
+-- Locally install files: only deals with those extracted, not docs etc.
+function install ()
+ unpack ()
+ kpse.set_program_name ("latex")
+ local texmfhome = kpse.var_value ("TEXMFHOME")
+ local installdir = texmfhome .. "/tex/" .. moduledir
+ cleandir (installdir)
+ for _,i in ipairs (installfiles) do
+ cp (i, unpackdir, installdir)
+ end
+end
+
+function savetlg (name, engine)
+ local tlgfile = name .. (engine and ("." .. engine) or "") .. tlgext
+ local newfile = name .. "." .. (engine or stdengine) .. logext
+ if fileexists (testfiledir .. "/" .. name .. lvtext) then
+ checkinit ()
+ print ("Creating and copying " .. tlgfile)
+ runtest (name, engine, false)
+ ren (testdir, newfile, tlgfile)
+ cp (tlgfile, testdir, testfiledir)
+ else
+ print (
+ "Test input \"" .. testfiledir .. "/" .. name .. lvtext ..
+ "\" not found"
+ )
+ end
+end
+
+-- Unpack the package files using an 'isolated' system: this requires
+-- a copy of the 'basic' DocStrip program, which is used then removed
+function unpack ()
+ depinstall (unpackdeps)
+ bundleunpack ()
+end
+
+-- Split off from the main unpack so it can be used on a bundle and not
+-- leave only one modules files
+function bundleunpack ()
+ mkdir (localdir)
+ mkdir (unpackdir)
+ for _,i in ipairs (sourcefiles) do
+ cp (i, ".", unpackdir)
+ end
+ for _,i in ipairs (unpacksuppfiles) do
+ cp (i, supportdir, localdir)
+ end
+ for _,i in ipairs (unpackfiles) do
+ for _,j in ipairs (filelist (unpackdir, i)) do
+ os.execute (
+ -- Notice that os.execute is used from 'here' as this ensures that
+ -- localdir points to the correct place: running 'inside'
+ -- unpackdir would avoid the need for setting -output-directory
+ -- but at the cost of needing to correct the relative position
+ -- of localdir w.r.t. unpackdir
+ os_setenv .. " TEXINPUTS=" .. unpackdir .. os_pathsep .. localdir ..
+ (unpacksearch and os_pathsep or "") .. os_concat ..
+ unpackexe .. " " .. unpackopts .. " -output-directory=" .. unpackdir
+ .. " " .. unpackdir .. "/" .. j
+ )
+ end
+ end
+end
+
+function version ()
+ print (
+ "\nl3build Release " .. string.gsub (release_date, "/", "-") ..
+ " (SVN r" .. release_ver .. ")\n"
+ )
+end
+
+--
+-- The overall main function
+--
+
+function stdmain (target, file, engine)
+ -- If the module name is empty, the script is running in a bundle:
+ -- apart from ctan all of the targets are then just mappings
+ if module == "" then
+ -- Detect all of the modules
+ modules = { }
+ for entry in lfs.dir (".") do
+ if entry ~= "." and entry ~= ".." then
+ local attr = lfs.attributes (entry)
+ assert (type (attr) == "table")
+ if attr.mode == "directory" then
+ table.insert (modules, entry)
+ end
+ end
+ end
+ if target == "doc" then
+ allmodules ("doc")
+ elseif target == "check" then
+ local errorlevel = allmodules ("bundlecheck")
+ if errorlevel ~=0 then
+ print ("There were errors: checks halted!\n")
+ os.exit (errorlevel)
+ end
+ elseif target == "clean" then
+ bundleclean ()
+ elseif target == "cmdcheck" and next (cmdchkfiles) ~= nil then
+ allmodules ("cmdcheck")
+ elseif target == "ctan" then
+ ctan ()
+ elseif target == "install" then
+ allmodules ("install")
+ elseif target == "unpack" then
+ -- bundleunpack avoids cleaning out the dir, so do it once here
+ cleandir (unpackdir)
+ allmodules ("bundleunpack")
+ elseif target == "version" then
+ version ()
+ else
+ help ()
+ end
+ else
+ if target == "bundleunpack" then -- 'Hidden' as only needed 'higher up'
+ depinstall (unpackdeps)
+ bundleunpack ()
+ elseif target == "bundlecheck" then
+ local errorlevel = check ()
+ os.exit (errorlevel)
+ elseif target == "bundlectan" then
+ bundlectan ()
+ elseif target == "doc" then
+ doc ()
+ elseif target == "check" and testfiledir ~= "" then
+ check ()
+ elseif target == "checklvt" and testfiledir ~= "" then
+ if file then
+ checklvt (file, engine)
+ else
+ help ()
+ end
+ elseif target == "clean" then
+ clean ()
+ elseif target == "cmdcheck" and next (cmdchkfiles) ~= nil then
+ cmdcheck ()
+ elseif target == "ctan" and bundle == "" then -- Stand-alone module
+ ctan (true)
+ elseif target == "install" then
+ install ()
+ elseif target == "localinstall" then -- 'Hidden' target
+ localinstall ()
+ elseif target == "savetlg" and testfiledir ~= "" then
+ if file then
+ savetlg (file, engine)
+ else
+ help ()
+ end
+ elseif target == "unpack" then
+ unpack ()
+ elseif target == "version" then
+ version ()
+ else
+ help ()
+ end
+ end
+end
+
+-- Allow main function to be disabled 'higher up'
+main = main or stdmain
+
+-- Call the main function
+main (arg[1], arg[2], arg[3])