Geoff Chappell - Software Analyst
A handful of options are intended to have at least some of their effect as early as possible. To learn the state of these options in advance of the ordinary processing of the command line, the compiler makes an early pass over the command line. This is not so much an early processing of the command line, done the same as later but specialised to finding just these few particular options. It is instead a cursory scan for these few options. There is a significant relaxation of syntax.
The options in question are: /Be, /clr, /nologo, /noover and /ZX. Note that /clr is ordinarily case-insensitive, but to be noticed for its early effect, it must be entirely in lower case or entirely in upper case. Each option is permitted a negative form, with a trailing hyphen. The /ZX option is allowed an argument. So too is /clr, but not if found in a command file. For both, the argument must follow immediately, i.e., without white space, but is otherwise not interpreted.
For each option, the instance that counts is in general the first among the following sources in the following order:
This is not quite the same as for the ordinary processing, in which the instance that counts is the last among these same sources but in the reverse order. Moreover, there are special cases. Within each of the contributing sources, what “first” means specifically is:
By “argument” is meant the characters, if any, that follow immediately the characters of the option, except that if there is only one such character and it is a hyphen, then it is not an argument but instead signifies that the option is negated.
In the first two cases, where the instance is found directly in the contributing source rather than in a command file, “instance” means any token that matches the option, with no allowance for whether the token would ordinarily be a continuation of some earlier option.
The early pass does recognise command files, but only to scan the first line (and only then the first 1022 characters) and with no support for recursion. If a command file cannot be opened (with read access in text mode) or its first line cannot be read, then it is ignored without complaint.
Where an option that is subject to early parsing has multiple instances on the first line of the command file, the one that counts is the last.
As for finding instances of the option on that first line of a command file, there is no formal parsing into tokens. Instead, an instance is recognised within the command file wherever the characters of the option (with or without a trailing hyphen):
The second of these conditions is skipped when scanning for the /ZX option, since it is allowed an argument of arbitrary length.
It seems unlikely that all the differences between the early and ordinary processing of command-line options are intended. The early processing is just a quick scan and has perhaps understandably not been implemented with much concern for rigour. Even so, the differences can have unexpected consequences, at least for anyone who looks carefully enough.
For simplicity, all the demonstrations sketched below assume that the CL and _CL_ environment variables are empty.
Perhaps the greatest practical implication of the differences between the early and ordinary processing of command-line options is that an option in a command file can have its intended early effect only if placed on the command file’s very first line.
To see this, prepare command files, here named “test1.txt” and “test2.txt”, the first with /nologo on the first line, the other with an empty first line and /nologo on the second line. Given an arbitrary C-language source file “test.cpp”, run the commands
cl /c @test1.txt test.cpp cl /c @test2.txt test.cpp
and see that the logo is suppressed when /nologo is on the first line of the command file but not when on the second.
To see that the early processing does not recognise /clr with an argument in command files even on the first line, first run the command
cl /c /clr:noAssembly test.cpp
to see that when this option is recognised, the compiler describes itself as “Version 13.00.9466 for .NET Framework”, this change from the usual title being perhaps the main point to recognising /clr early. Now prepare a command file, here named “test.txt”, containing just /clr:noAssembly. Run
cl /c @test.txt test.cpp
and see that the title in the logo is just the ordinary “Version 13.00.9466 for 80x86”. The /clr:noAssembly is still detected in the ordinary processing of the command line, but its early effect is missed when the option is given in a command file.
That the early parsing may accept as an option a token that would ordinarily be a continuation of some other option also is demonstrated most readily with /nologo since it is a valid option both for the compiler and linker. A feature of the compiler’s /link option is that it absorbs as its own arguments all subsequent command-line tokens, which then become command-line arguments for the linker. However, the command
cl test.cpp /link /nologo
suppresses the logo for the compiler not because that’s what the programmer actually asked for but because the /nologo intended for the linker is accepted during early parsing as applying also to the compiler.
To see that in simple cases, without complications from command files or options with arguments, an option’s first instance counts for the early effect even though the last instance ends up counting later, run the commands
cl /nologo /nologo- test.cpp cl /nologo- /nologo test.cpp
Whether the logo is suppressed depends on the first instance, even though the compiler claims (in a warning message) to have overridden the first with the second.
To demonstrate a special case involving a command file, prepare two command files, here named “clr.txt” and “noclr.txt”, containing just /clr and /clr- respectively. Run the following two sets of commands
cl /c /clr /clr:noAssembly @noclr.txt test.cpp cl /c /clr @noclr.txt /clr:noAssembly test.cpp cl /c /clr:noAssembly /clr @noclr.txt test.cpp cl /c /clr:noAssembly @noclr.txt /clr test.cpp cl /c @noclr.txt /clr /clr:noAssembly test.cpp cl /c @noclr.txt /clr:noAssembly /clr test.cpp
cl /c /clr- /clr:noAssembly @clr.txt test.cpp cl /c /clr- @clr.txt /clr:noAssembly test.cpp cl /c /clr:noAssembly /clr- @clr.txt test.cpp cl /c /clr:noAssembly @clr.txt /clr- test.cpp cl /c @clr.txt /clr- /clr:noAssembly test.cpp cl /c @clr.txt /clr:noAssembly /clr- test.cpp
The first set goes through all the combinations of /clr with and without an argument on the command line and /clr- in a command file. The second set changes to /clr- on the command line with /clr in the command file. All the commands in the first set but none in the second produce the “.NET Framework” description in the logo. For this early effect, a /clr or /clr- anywhere on the command line takes precedence over a /clr:noAssembly (which has an argument) on the command line and over any /clr variant in any command file.