diff options
author | Karl Berry <karl@freefriends.org> | 2014-06-28 22:01:09 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2014-06-28 22:01:09 +0000 |
commit | 65b048d524ef9464d4039093986e267b277dafd9 (patch) | |
tree | 9ca9621d629641ee1ece91dd7735a289bb223ec6 /Master/texmf-dist/source/latex/l3build | |
parent | 546e6790c84dde88b3abdffd431ece4e0061e0e6 (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.dtx | 1083 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/l3build/l3build.ins | 40 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/l3build/l3build.lua | 1025 |
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]) |