3

Consider this MWE, test-0.tex:

\documentclass{article}

\usepackage{filecontents} % tlmgr install filecontents
\usepackage[utf8x]{inputenc}

\begin{filecontents}{test-1.tex}
\usepackage{tikz}
\usepackage{pgfplots}
\end{filecontents}
\input{test-1.tex}

\usepackage{lipsum}
\usepackage{hologo} % tlmgr install oberdiek; http://tex.stackexchange.com/a/7564/2595

\begin{document}

\lipsum[1]

This document was typeset with \hologo{pdfLaTeX}, using packages: \texttt{filecontents}, \texttt{inputenc}, \texttt{tikz}, \texttt{pgfplots}, \texttt{lipsum}, \texttt{hologo}.
\end{document}

I would like somehow to use a macro that will determine the first-level packages used automatically, so I could write instead something like pseudo-code:

This document was typeset with \hologo{pdfLaTeX}, using packages: \typesetUsedPackages.

By first-level packages, I mean packages that are used via \usepackage{} either in the main .tex file, or in (recursively) \input- or \included .tex files - but not packages that may be included from these first-level packages; the MWE shows what output I'd want.

(Also, it would be neat if the executable logo, and the documentclass used, could be determined and typeset automatically, e.g.:

This document was typeset with \typesedUsedExecutable in document class \typesetUsedDocumentclass; using packages: \typesetUsedPackages.

)

Is there anything like this out there?

sdaau
  • 17,079

2 Answers2

1

Well, you can rely on the policy that in the preamble, one uses \usepackage, whereas in the packages and classes, it's \RequirePackage:

\documentclass[a4paper]{article}

\def\usedpackages{}
\let\xusepackage\usepackage
\renewcommand\usepackage[2][]{\edef\usedpackages{\usedpackages#2,}\xusepackage[#1]{#2}}

\usepackage[margin=0pt]{geometry}
\usepackage{pgffor}

\begin{document}

Packages:
\begin{itemize}
\foreach \x in \usedpackages{\ifx\x\empty\else \item \x \fi}
\end{itemize}

And the packages really got loaded with the options passed into it since
foreach is working and we get zero margins here.

\end{document}
yo'
  • 51,322
  • Thanks for that, @tohecz - it seems much more convenient to concatenate in memory, and then use pgf \foreach to split at the commas; cheers! – sdaau Oct 14 '14 at 14:32
0

OK, managed to do something - but, of course, a more erudite answer will be greatly appreciated. First, here is the usage example:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\documentclass{article}

  %%% start scanning
  \input{_usedpackages.tex}


    \usepackage{filecontents} % tlmgr install filecontents
    \usepackage[utf8x]{inputenc}

    \begin{filecontents*}{test-1.tex}
    \usepackage{tikz}
    \usepackage{pgfplots}
    \end{filecontents*}
    \input{test-1.tex}

    \usepackage{lipsum}
    \usepackage{hologo} % tlmgr install oberdiek; http://tex.stackexchange.com/a/7564/2595


  %%% stop scanning - \typesetUsedPackages should be ready after this:
  \stopScanUsedPackages

\begin{document}

  \lipsum[1]

  This document was typeset with \hologo{pdfLaTeX}, using packages: \typesetUsedPackages.

  \typeout{USEDPACK \the\usedpacklinecnt,\the\usedpacklcnt: \typesetUsedPackages}

\end{document}

This code starts scanning on \input{_usedpackages.tex}, and it does so by redefining \usepackage so it first outputs the name of the package in a file usedpacks.texlog, one package name per line. (Since .sty packages are supposed to use \RequirePackage, I guess this in itself ensures that only "first-level" packages are collected).

The same _usedpackages.tex file defines \stopScanUsedPackages, this basically stops the file writing, re-reads it and composes a new macro where the content of each line is formatted with the style in \usedpackstyle (here \texttt{#1}), called \typesetUsedPackages. This macro is output in terminal as:

USEDPACK 6,6: \texttt {filecontents}, \texttt {inputenc}, \texttt {tikz}, \text
tt {pgfplots}, \texttt {lipsum}, \texttt {hologo}

... and the typeset result is:

test-0.png

Of course, you'd need the _usedpackages.tex file - and here it is; simply paste this snippet before the \documentclass in the previous example, and it will be generated (or just copy the part between \begin/\end{filecontents*}):

\RequirePackage{filecontents}
\begin{filecontents*}{_usedpackages.tex}

% NB: Clearly, these three - as any other \usepackages which
% execute before this code is run - will not show in
% used packages list (\typesetUsedPackages)
% http://stackoverflow.com/a/6677461/277826
\usepackage{ifthen} % provides \ifthenelse test
\usepackage{xifthen} % provides \isempty test; tlmgr install xifthen ifmtarg
\usepackage{xstring}

% this starts the scan at point of inclusion:
\newwrite\usedpackfile
\immediate\openout\usedpackfile=usedpacks.texlog

\let\oldusepackage\usepackage
\def\tempty{}
\renewcommand{\usepackage}[2][]{%
  %\immediate\write\usedpackfile{\string\texttt{#2}} % don't style here
  \immediate\write\usedpackfile{#2} %
  \ifthenelse {\isempty{#1}} %
  { %
    %\typeout{EMPTY} %
    \oldusepackage{#2} % optional argument empty
  } %
  { % else
    %\typeout{NOT EMPTY} %
    \oldusepackage[#1]{#2} % optional argument not empty
  } %
}


\newread\usedpackmyread
\newcount\usedpacklinecnt
\newcount\usedpacklcnt
\def\typesetUsedPackages{}
\def\usedpackstyle#1{\texttt{#1}}

% this stops the scan - insert right before \begin{document}
% (cannot get it to work with \newcommand?!)
\makeatletter
\def\stopScanUsedPackages{%
  \immediate\closeout\usedpackfile
  % http://tex.stackexchange.com/a/17674/2595
  % http://tex.stackexchange.com/a/207108/2595
  %  if using:
  %  \readline: ^^M at EOL (must \StrGobbleRight), ^^M at EOF (must \ifx\tmpline\empty)
  %  \read:      "" at EOL, \par at EOF (must \if\tmpline\par)
  %
  % get number of lines
  \immediate\openin\usedpackmyread=usedpacks.texlog
  \@whilesw\unless\ifeof\usedpackmyread\fi{%
    \readline\usedpackmyread t\expandafter o\csname tmpline\endcsname % this just has to be here to have the loop run; but using it to find non-empty lines
    \StrGobbleRight{\tmpline}{1}[\tmpline] % remove the ^^M newline (last char)
    \ifx\tmpline\empty\else %
      \advance\usedpacklinecnt by \@ne %
    \fi %
  }
  \immediate\closein\usedpackmyread
  % now read lines, do formatting & concat into macro \typesetUsedPackages
  \immediate\openin\usedpackmyread=usedpacks.texlog
  \@whilesw\unless\ifeof\usedpackmyread\fi{%
    \read\usedpackmyread to\tmpline %
    \if\tmpline\par\else %
      \advance\usedpacklcnt by \@ne %
      \StrGobbleRight{\tmpline}{1}[\tmpline] % still have to run this, to get rid of a space at end
      \protected@edef\typesetUsedPackages{\typesetUsedPackages \usedpackstyle{\tmpline}} %
      \typeout{cnts: \the\usedpacklcnt=\the\usedpacklinecnt} %
      \ifnum\the\usedpacklcnt=\the\usedpacklinecnt %
        \typeout{YES} %
      \else % if not on last line
        \typeout{NO} %
        \protected@edef\typesetUsedPackages{\typesetUsedPackages,\space} %
      \fi %
    \fi %
  }
  \immediate\closein\usedpackmyread
} % end \def
\makeatother

\end{filecontents*}
%
sdaau
  • 17,079