\NeedsTeXFormat{LaTeX2e}
\ProvidesFile{mytest.tex}
\begin{filecontents*}{myclass.cls}
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{myclass}[2010/11/07 test nonsense]
\RequirePackage{mypackage}
\newcommand*\classoption{classoption is unused}
\DeclareOption{classoption}{%
\gdef\classoption{classoption is used}%
}
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}
\ProcessOptions*\relax
\LoadClass{article}%
\endinput
\end{filecontents*}
\begin{filecontents*}{mypackage.sty}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypackage}[2010/11/07 test nonsense]
\newcommand*\packageoption{packageoption is unused}
\DeclareOption{packageoption}{%
\gdef\packageoption{packageoption is used}%
}
\ProcessOptions*\relax
\endinput
\end{filecontents*}
\documentclass[classoption, packageoption, a4paper]{myclass}
\begin{document}
\packageoption \par \classoption
\end{document}
-----------------------------------------------------------------
With the example above I get
LaTeX Warning: Unused global option(s):
[packageoption].
although the dvi-/pdf-output yields that this option
is used.
I think this is because within the cls-file
\RequirePackage is used _before_ the (global) options
have been processed.
Only when (global) options are processed from within
a .cls-file, a list of unused global options is built
(within the macro \@unusedoptionlist).
Following \usepackage / \RequirePackage lead to
removing entries of that list in case the packages
in question provide the options in question.
( \usepackage / \RequirePackage preceeding \ProcessOptions
cannot remove entries of that list as the list doesn't
exist yet.)
In some cases you can resolve the warning by having
packages loaded _after_ the (global) options
have been processed from within the .cls-file.
In other cases you cannot - e.g., if you want to load
something like the kvoptions-package from within
the .cls-file in order to have its means available for
option-processing within the .cls-file...
Question 1:
Besides avoiding warning-messages like the one above -
are there other reasons for not having loaded
packages from within a documentclass-file before
therein \ProcessOptions has taken place?
[ What about the edge/weird case that a documentclass
doesn't process options at all so that all global options
only apply to subsequent package-loading (while the
macro \@unusedoptionlist doesn't exist /is empty)?]
Question 2:
How unsafe is the following attempt of resolving this?:
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{myclass}[...]
\newcommand\mycls@alreadyusedoptionlist{}%
\newcommand\mycls@saved@list{}%
\let\mycls@saved@list\@unusedoptionlist
\let\@unusedoptionlist\@classoptionslist
\newcommand\mycls@alreadyusedoptionslist{}%
\RequirePackage{<Package 1>}
...
\RequirePackage{<Package n>}
<
Add each option which is not both in
\@unusedoptionlist and in \@classoptionslist
to the macro \mycls@alreadyusedoptionslist
>
\let\@unusedoptionlist\mycls@saved@list
\AtEndOfClass{%
\let\mycls@saved@list=\@empty
<
Add each option which is not both in
\@unusedoptionlist and in \mycls@alreadyusedoptionslist
to the macro \mycls@saved@list
>
\let\@unusedoptionlist\mycls@saved@list
}%
<...etc...>
\ProcessOptions<...etc...>
<...etc...>
\endinput
Ulrich
The approach in the catoptions package is to patch \ProcessOptions
command to save in a temporary macro a list of processed options in
each run. Then just before \begin{document} this list is reconciled
with that of \unusedoptionslist and those options already used are
filtered out of \unusedoptionslist (if they remain in
\unusedoptionslist up to that time) , so that they are not reported as
unused at \begin{document}. This requires patching \document too.
Reconciling the two lists via \AtEndOfClass, as you seem to have
proposed, will not always work. This is because the class file will
pass unknown options to, say, article class and if they are not known
to ‘article’, they will be reported as unused at \begin{document} .
Unused options are reported at \begin{document}, not at the end of
class.
> Besides avoiding warning-messages like the one above,
> are there other reasons for not having loaded
> packages from within a documentclass file before
> therein \ProcessOptions has taken place?
I don’t see why not. The processing of options may require service/
code from existing packages. Moreover, packages such as xkvltxp and
kvoptions-patch are meant to be loaded before \documentclass to
forestall premature expansion of options and their values by the latex
kernel.
> \begin{document}
> \packageoption \par \classoption
> \end{document}
>
> -----------------------------------------------------------------
>
> With the example above I get
>
> LaTeX Warning: Unused global option(s):
> [packageoption].
>
> although the dvi-/pdf-output yields that this option
> is used.
>
> I think this is because within the cls-file
> \RequirePackage is used _before_ the (global) options
> have been processed.
I think this is because:
load myclass.cls
[packageoption] => \PassOptionToClass{article}{packageoption}
\LoadClass [packageoption] {article}
|
|--> implicit
=> unused global option [packageoption]
(in article.cls)
\LoadClass{aticle}
=> \@onefilewithoptions
=> \ProcessOptions
=> \@for \CurrentOption:=\@curroptions \do{
\@ifundefined{ds@\CurrentOption}
{\def\@unusedoptionlist{packageoption}}
Finally: unsused global option, which seems to be normal, since
packageoption has been given to {article.cls} which is not defined
in this class, and in addition, {article.cls} does not declare a
\DeclareOption* statement.
Yours sincerely.
\DeclareOption* is nearly useless here: options are global !
Besides, with \DeclareOption*{\PassOptionToClass{...}{article}}
you get, at \document:
\@unusedoptionslist = {packageoption}
==> *This is the unused option after having loaded {article.cls}*
If you remove this (useless) \DeclareOption* statetement, you get
at \document :
\@unsuedoptionslist = {a4paper,packageoption}
This is the unused option after having loaded {myclass.cls} (a4paper)
+ the usused option after having loaded {article.cls} (packageoption)
because loading a class ({article}) does not remove the processed
*global* options from the list of *global options*, because this list
can be also used by a package or a class later.
Therefore, this is logical, and the only point to remove the warning is
to say in {myclass.cls}
\DeclareOption*{}
This way, the \@unusedoptionlist is only set by the article class.
> \ProcessOptions*\relax
\ProcessOptions* has no effect in a class, this is the same as
\ProcessOptions (not starred)
Well, now I can say you example *is not Minimal at all* !!!
Here is a Minimal example:
% ----------------------------------------------------
\ProvidesFile{mytest.tex}
\makeatletter
\def\test #1{\message{-> \@backslashchar #1:^^J}
\ifcsname #1\endcsname
\expandafter\show\csname #1\endcsname
\else\show\undefined
\fi}
\begin{filecontents*}{myclass.cls}
\ProvidesClass{myclass}[2010/11/07 test nonsense]
\message{Entering myclass.cls: **************** }
\test{@unusedoptionslist}
\RequirePackage{mypackage}
\message{After having loaded mypackage.sty: ****************}
\test{@unusedoptionslist}
\ProcessOptions\relax % \ProcessOption* is the same as
\ProcessOption inside classes
\message{After process options in myclass.cls: ****************}
\test{@unusedoptionslist}
\endinput
\end{filecontents*}
\begin{filecontents*}{mypackage.sty}
\ProvidesPackage{mypackage}[2010/11/07 test nonsense]
\def\packageoption{packageoption is unused}
\DeclareOption{packageoption}{%
\gdef\packageoption{packageoption is used}%
}
\ProcessOptions*\relax
\endinput
\end{filecontents*}
{\toks0\expandafter{\document}
\xdef\document{%
\message{Here at \string\document: }%
\noexpand\test{@unusedoptionlist}\the\toks0}
}
\RequirePackage{etex}\loggingall
\documentclass[packageoption]{myclass}
\tracingnone
\begin{document}
\packageoption
\end{document}
% -----------------------------------------------
What does it say ?
That LaTeX is buggy, of course... I explain: LaTeX defines
\@unusedoptionslist great ! This is the macro that contains the
"unused options", and this macro will be used at the very beginning
of \document to display the warning message.
But this is not the correct macro to be used for processing options.
LaTeX *should have defined* a macro \@usedoptionslist, and at the end
of all options processing (at the very beginning of \document), it
should have said:
\@for \CurrentOption := \@usedoptionlist \do{%
\removeelement \CurrentOption
\@classoptionlist \@unusedoptionslist
}
Therefore, \@unusedoptionslist is
{ all global+class options } - { used options }
\@unusedoptionslist is the correct macro to display the warning,
not the right one to process the options...
Finally, the filecontents environment could add automatically
\endinput, in my opinion...
Yours sincerely.
In fact, there is also another "bug", less important:
If you *do not* declare in myclass.cls
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}
then \ProcessOption in {article.cls} will check the declaredoptions
of {article.cls} against the \o...@article.cls (ie \@curroptions = the
local options list) and then execute the options that match:
options that are both 1) declared and 2) in the local list
while *it should also check against the global options*, for \LoadClass
should behave like \RequirePackage in this point: it *should execute as
well* :
options that are both 1) declared and 2) in the *global list*
Even if \DeclareOptions*{\PassOptionToClass{\CurrentOption}{article}}
is missing in {myclass.cls}
On the other hand if:
an option to {myclass.cls} [which is also a global option]
is: *not declared* in {myclass.cls}
*and* is: *not declared* in any package loaded before \document
*and* is: *not declared* in {article.cls}
Then it is *not considered to be* an "usused global option", because
the class {myclass.cls} said : \DeclareOptions*{ ... }
That means that \DeclareOption* in {myclass.cls}, whose purpose was to
give the option to {article.cls} has an undesirable side effect.
All these corrections (as usual) have two consequences :
First ) there is no more bug
Second ) the code is simpler !
Incredible but true (it's always the case with TeX...)
This is because: \@process@ptions checks:
\@expandtwoargs\in@{,\CurrentOption,}{%
,\ifx\@currext\@clsextension\else\@classoptionslist,\fi
\@curroptions,}%
\ifin@
\@use@ption
\expandafter\let\csname ds@\CurrentOption\endcsname\@empty
\fi
But inside {myclass.cls}:
\@classoptionslist = {classoption, packageoption, a4paper}
= \o...@myclass.cls
= \@curroptions inside {myclass.cls}
Therefore, the test \ifx \@clsextension\else\@classoptionslist\fi
is *totally useless* inside {myclass.cls}:
\in@ {,b,}{,a,b,c,} is the same as \in@{,b,}{,a,b,c,a,b,c,}
And now, we expect that \LoadClass{article} *will inherit* from the
class options list, for it is also the *global options list*, but with
this \ifx test, this is not the case for:
\o...@article.cls = \@empty (local options only)
Thus here is the correction for options processing (the code is simpler
than LaTeX's original):
I commented \DeclareOption* in {myclass.cls} for it becomes useless and
even harmfull, and added un 'NotAnOption' option to see the warning.
You can check that 'a4paper' is normally processed by {article.cls}
% ----------------------------------------------------------------------
\NeedsTeXFormat{LaTeX2e}
\makeatletter % LaTeX correction
\let\@usedoptionslist \@empty
\def\@use@ption{%
\@ifundefined{ds@\CurrentOption}
{}
{\xdef\@usedoptionslist{\@usedoptionslist,\CurrentOption}}%
\csname ds@\CurrentOption \endcsname}%
\def\OptionNotUsed{}%
\def\@process@ptions{%
\@for\CurrentOption:=\@declaredoptions\do{%
\ifx\CurrentOption\@empty\else
\@expandtwoargs\in@{,\CurrentOption,}
{,\@classoptionslist,\@curroptions,}
\ifin@
\@use@ption
\expandafter\let\csname ds@\CurrentOption\endcsname\@empty
\fi
\fi}%
\@process@pti@ns}
\toks0={%
\let\@unusedoptionlist \@empty
\@for \CurrentOption:=\@classoptionslist\do{%
\@expandtwoargs\in@ \CurrentOption \@usedoptionslist
\ifin@ \else
\xdef\@unusedoptionlist{\@unusedoptionlist,
\CurrentOption}%
\fi}}%
\toks1=\expandafter{\document}%
\xdef\document{\the\toks0 \the\toks1}%
\makeatletter
\ProvidesFile{mytest.tex}
\begin{filecontents*}{myclass.cls}
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{myclass}[2010/11/07 test nonsense]
\RequirePackage{mypackage}
\newcommand*\classoption{classoption is unused}
\DeclareOption{classoption}{%
\gdef\classoption{classoption is used}%
}
%%%\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}
\ProcessOptions*\relax % -> starred form useless inside a class
\LoadClass{article}%
\endinput
\end{filecontents*}
\begin{filecontents*}{mypackage.sty}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypackage}[2010/11/07 test nonsense]
\newcommand*\packageoption{packageoption is unused}
\DeclareOption{packageoption}{%
\gdef\packageoption{packageoption is used}%
}
\ProcessOptions*\relax
\endinput
\end{filecontents*}
\documentclass[classoption, packageoption, a4paper,NotAnOption]{myclass}
\begin{document}
\packageoption \par \classoption
\end{document}
% --------------------------------------------------------
F. Chervet.
Yours sincerely.