MSVC’s cl.exe supports several new command-line flags for C++ module interface and implementation files, detailed here. There are several straightforward equivalents between the two compiler front-ends:
cl.exe
clang.exe
/interface
--precompile
/ifcSearchDir
-fprebuilt-module-path
/reference
-fmodule-file
/ifcOutput
-fmodule-output
/exportHeader
-fmodule-header
Some options in cl.exe don’t appear to have clang.exe equivalents (to my knowledge), such as /internalPartition and /ifcOnly, or they may be combinations of the above options.
I understand that Windows users can use the clang.exe compiler (i.e. with the GNU-style front-end) and use the instructions in Standard C++ Modules — Clang 17.0.0git documentation more or less as-is, but it would be easier to switch compilers (for CI, performance, conformance, etc) without having to change a full set of compiler command-line options. Hence, it would be a good idea to add support for these options to clang-cl.exe.
Furthermore, cl.exe supports P1689R5 with the /scanDependencies switch, whereas the Clang/LLVM ecosystem uses a different binary altogether, with clang-scan-deps.exe. How is this intended to be handled with clang-cl.exe?
On the one hand, we don’t have contributors developing in windows for C++20 modules. So it will be harder to test. On the other hand, the semantics of these options are similar instead of being equal to each other. e.g., the term ifc refers to a BMI format used by Microsoft. And it looks not good to use the term ifc when we don’t produce or consume ifc format actually.
On the one hand, we don’t have contributors developing in windows for C++20 modules
I am willing and able to contribute; I have vested interest in trying to achieve clang-cl.exe parity with cl.exe.
And it looks not good to use the term ifc when we don’t produce or consume ifc format actually.
Fair point, but I feel this could be alleviated, given several points:
Modules are very new to the entire C++ ecosystem as a whole, and the feature is still a moving target. This generally requires users to read a fair bit of documentation, and therefore:
We could document that these /*ifc* options are aliases to the clang.exe-fmodule-* options, note that Clang’s .pcm format is different to Microsoft’s .ifc format, and they cannot be mixed freely.
Of course, the best long-term solution would be to standardise the built module interface format as a C++ paper, and tweak either compiler’s implementation to fit this standard. But this is rather orthogonal to the matter.
That is not going to happen. Clang writes various binary files, e.g. pcm, to the disk. The version of the file is tied to the git hash. If you have two Clang’s built from different git hashes, the file format may differ. They could have added a new AST node, refactored the AST, or improved compactnesss of the output format.
I forgot for a moment that BMIs are binaries (ergo, built).
However, I still think the command-line flag aliases are a decent idea, as long as they are documented clearly enough (perhaps at the above-mentioned link), and users are aware of the semantic differences. It’s not like clang.exe and cl.exe generated BMIs have interop now, anyway.
I don’t feel the direction is good. While we don’t have any intention to implement IFC format in clang now, we can’t assume we won’t do that some other day. Then it will become a severe breaking change.
In my opinion, we’d better to mitigate this in tools like build systems. For example, the output of P1689 format is completely a by-product of scanning. So it is the job of cmake (or other build systems) to handle the difference. And I think this is the same for other similar options.
If C++ modules with CMake on Windows does not work out of the box, I would open a ticket with them. CMake should handle the differences between Windows and Unix-like.
Unless I’m missing something, my point is that C++ module compilation is straight-up missing in the clang-cl driver while it is present in the clang driver, and can be demonstrated by experimenting with a very simple hello-world example from the command-line:
> clang-cl.exe /std:c++20 Hello.cppm --precompile -o Hello.pcm
clang-cl: warning: unknown argument ignored in clang-cl: '--precompile' [-Wunknown-argument]
clang-cl: warning: argument unused during compilation: '/std:c++20' [-Wunused-command-line-argument]
LINK : fatal error LNK1561: entry point must be defined
clang-cl: error: linker command failed with exit code 1561 (use -v to see invocation)
Therefore, I suppose users are meant to use /clang: as a work-around. If this is the expectation, then yes, I can let the CMake authors know this (because there, too, clang-cl isn’t working for C++ modules).
My comment about P1689R5 was to discuss unifying the user-facing behaviour (i.e. the compiler flags) for the two compiler front-ends, cl.exe and clang-cl.exe.
Oh, no. It ls incorrect. The form -fmodule-file=<BMI> is deprecated. We prefer -fmodule-file=<Module-name>=<BMI>. And I don’t know why it works since we should need to compile Hello.pcm and link it into the binary. I guess something surprising happens in clang-cl or in windows.
Okay, I’m so sorry; it’s actually not working at all. I was getting very confused.
If I use -fmodule-file=, I cannot link, and the linker complains that it cannot find hello(). If I use fprebuilt-module-path and supply the precompiled module file, the compiler then complains that module Hello cannot be found:
> clang-cl /std:c++20 use.cpp /clang:"-fmodule-file=Hello=.\Hello.pcm" /Fo.\Hello.exe
Hello.exe : error LNK2019: unresolved external symbol "void __cdecl hello(void)" (?hello@@YAXXZ) referenced in function main
use.exe : fatal error LNK1120: 1 unresolved externals
clang-cl: error: linker command failed with exit code 1120 (use -v to see invocation)
clang-cl /std:c++20 use.cpp /clang:"-fmodule-file=.\Hello.pcm" /Fo.\Hello.exe
Hello.exe : error LNK2019: unresolved external symbol "void __cdecl hello(void)" (?hello@@YAXXZ) referenced in function main
use.exe : fatal error LNK1120: 1 unresolved externals
clang-cl: error: linker command failed with exit code 1120 (use -v to see invocation)
> clang-cl /std:c++20 use.cpp /clang:"-fprebuilt-module-path=. .\Hello.pcm" /Fo.\Hello.exe
use.cpp(1,8): fatal error: module 'Hello' not found
import Hello;
~~~~~~~^~~~~
1 error generated.
The module file itself can be compiled fine with clang-cl.exe and /clang:--precompile:
> clang-cl.exe /std:c++20 Hello.cppm /clang:--precompile /Fo.\Hello.pcm
> clang++.exe -module-file-info .\Hello.pcm
Information for module file '.\Hello.pcm':
Module format: raw
====== C++20 Module structure ======
Interface Unit 'Hello' is the Primary Module at index #2
Sub Modules:
Global Module Fragment '<global>' is at index #1
====== ======
Generated by this Clang:
Module name: Hello
Language options:
C99: No
C11: No
C17: No
C2x: No
Microsoft Visual C++ full compatibility mode: Yes
Kernel mode: No
Microsoft C++ extensions: Yes
Microsoft inline asm blocks: Yes
Borland extensions: No
C++: Yes
C++11: Yes
C++14: Yes
C++17: Yes
C++20: Yes
...
The documentation needs to be clearer on clang-cl C++ module compatibility. All of this works perfectly fine with the GNU-like clang.exe driver:
I’m not sure that these flags correspond with each other. -interface is the flag that says “makes a BMI and exports the module/partition”; there is also -internalPartition for non-exported partitions of modules. I think -ifcOnly maps to --precompile much better.
Fair enough; we can’t make an assumption today and then break that down the line.
That being said, as mentioned above, even with /clang:, clang-cl.exe’s behaviour is not consistent with clang.exe’s, and fails to compile, so that behaviour definitely needs to be looked at.
Reading through this, I see there is no consensus on how to expose the module flags via clang-cl as there are differences in behavior. Currently the only way is use /clang: (and -Xclang?), which are not nice flags to work with. Given that clang-cl has already several -f flags (See Clang Compiler User’s Manual — Clang 18.0.0git documentation), wouldn’t it be an idea to expose the clang.exe module flags with the same name in clang-cl.exe?