C++ 20 Modules Update

EDIT: the link from the import CMake Post doesn’t work because there’s an extra . in the link Bill posted there. Just remove it and you should get to the repo.

But yeah, I tried to patch it again and got the errors you were probably referring to [some _Float32, _Float64, etc. stuff]

Anyways.
Thanks for the reply! I’ll keep using MSVC for now, until there’s a new g++ release with the support.

I fixed the extra . issue.

1 Like

Can someone help me, I’ve tried compile the post code of Bill with modules. But I can’t make work the dependencies scan. This is what is throw me

Cmake Log

-- The CXX compiler identification is Clang 17.0.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
CMake Warning (dev) at CMakeLists.txt:13 (target_sources):
  CMake's C++ module support is experimental.  It is meant only for
  experimentation and feedback to CMake developers.
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Configuring done (2.8s)
CMake Warning (dev):
  C++20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
  experimental.  It is meant only for compiler developers to try.
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Generating done (0.0s)
-- Build files have been written to: /mnt/c/Repositories/CppModules/Cmake

When I run ninja, I get this

[2/8] Scanning /mnt/c/Repositories/CppModules/Cmake/foo.cxx for CXX dependencies
FAILED: CMakeFiles/foo.dir/foo.cxx.o.ddi CMakeFiles/foo.dir/foo.cxx.o.modmap
"/usr/bin/clang-scan-deps-17" -format=p1689 -- /usr/bin/c++   -std=c++20 -x c++ /mnt/c/Repositories/CppModules/Cmake/foo.cxx -c -o CMakeFiles/foo.dir/foo.cxx.o -MT CMakeFiles/foo.dir/foo.cxx.o.ddi -MD -MF CMakeFiles/foo.dir/foo.cxx.o.ddi.d > CMakeFiles/foo.dir/foo.cxx.o.ddi
Error while scanning dependencies for /mnt/c/Repositories/CppModules/Cmake/foo.cxx:
In file included from /mnt/c/Repositories/CppModules/Cmake/foo.cxx:3:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/iostream:39:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/ostream:38:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/ios:38:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/iosfwd:40:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/postypes.h:40:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/cwchar:44:
/usr/include/wchar.h:35:10: fatal error: 'stddef.h' file not found
ninja: build stopped: subcommand failed.

Checking in web, looks like I’m missing a header, but why this happen only with this code and not with other projects of my pc?

I suspect you are trying to import a header. That is not supported. Though you’re also using clang-scan-deps to scan things from libstdc++ things which may not be supported either.

Actually I’ve found in clang-scan deps repo that by default the llvm create wrong sym links. I did the change with issue suggestion an is working now.

Is there a way for change the extensions of precompiled headers of cmake to be ignored by clang scan deps?. Clang scan deps thinks that is a module the pch file generated by precompiled headers of cmake option.

Precompiled headers and C++ modules are of 100% unknown status right now (e.g., should -include flags be used when looking at imported headers or modules?). There’s no hard gate, but I would not recommend it. I would also only recommend using clang-scan-deps with a (matching) Clang compiler as the GCC will not make modules that clang-scan-deps knows how to deal with.

Why can I only click like once? +9000. Thank you, really.

@bill.hoffman Hello Bill, you might remember me from last year’s CppCon. Short after my keynote we’ve been talking about Cmake’s modules support. Thank you for providing the exiting news about KitWares efforts on this topic!

In preparation of my upcoming talks I dared to add CMake support to the project that I’ve been presenting back then, using VS2022 and MSBuild - the only compiler and build system capable of building the code at that time.

Learning all this CMake stuff with a lot of false starts I can now build it with CMake, too. And with a ton of fixes and mostly horrible workarounds for bugs in Clang 16 and libstdc++, it builds the project using Clang 16 as well. This includes a modularized C++23 standard library.

There is one thing that bothers me, though: the lack of support for header units. The code from 2022 has no #includes. But I have to pay a price now for building with CMake: demote the two header units back to textual inclusion which requires the GMF wart added to all the affected modules. What is it that stops CMake from supporting header units similar to the support implemented in MSBuild? The module scanners are not the issue afaics.

TBH, I found a way to compile at least one header unit after a week-long fight with CMake because it is the key to import std; with Clang 16. The CMake syntax to get there is ugly and not scalable at all. I wouldn’t do it another time. And certainly not recommend to others.

Thanks
Dani

I was looking into building shared libraries with modules on MSVC and ran into an issue.

Starting from the example from the blog post, I change the library foo to be a shared library(set(BUILD_SHARED_LIBS ON)). With this the MSVC build will fail with error C2230: Translation units from the executable will be unable to find the module ‘foo’.

It turns out that MSVC by default will not make modules from DLL targets public. There is however an option to control which modules become public. The easiest way to make the example work is to just set Project Properties->VC++ Directories->All Modules are Public to Yes on the ‘foo’ project. This will add the following entry to the foo.vcxproj file:


<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <AllProjectBMIsArePublic>true</AllProjectBMIsArePublic>
</PropertyGroup>

With this entry in place, the example compiles fine. However, as far as I can see, there is no way to enable this option from within CMake, so regenerating the file with CMake will break the build.

From the limited amount of investigation that I did, it seems to me that if CMake emitted this option by default for shared library projects that contain C++ module source files, the behavior would be consistent with that of static libraries. Has this been considered?

A usable model of what it means for header units to be “visible” between targets. There needs to, basically, be a way for CMake to collect the list of all such importable headers and communicate that to the scanner/compiler at the right time. CMake also needs to learn how to generate BMIs (potentially per-usage) for each of these usages (as they might not all agree on a suitable set of flags).

The “use named modules from outside this project” work goes a long way towards laying the groundwork needed for header units. Note that current support just assumes that the one BMI made during compilation is suitable for all importers (which is not strictly true in the face of mixed language standards at least, but also for arbitrary flag sets/orderings depending on the toolchain in use).

I also don’t forsee header units actually making anything faster in practice because of all the extra scanning/BMI generation that needs to be done, but I do think we need numbers for that (even if the work to make it even testable ends up being useless because it may be so much worse).

We’ll need per-source control of that as PRIVATE sources should not be accessible. Is there a per-source mechanism?

What makes a header importable these days?

  • the header is blessed by the standard
  • the programmer says so and the compiler doesn’t choke on this request

At least this is what both Clang and MSVC implement.
What makes a header unit usable in some context? The programmer communicates its intent to do so by

  • some syntax within a project (both in the MSBuild and the CMake sense), e.g.
    • ‘compile as header unit’ in MSBuild (works beautifully)
    • FILESET xyz CXX_MODULES_HEADER_UNITS in CMake (not functional in CMake)
  • referring to another project (usually a static/object library) that creates the build artifacts of header units. This is not possible today in CMake because it refuses to compile anything with libraries that consist only of headers. This was the hardest part to overcome with my header unit in the modularized standard library.

Both options communicate the scope where the validity of a header unit must be maintained.

Scanners can handle this without further information. At least this is what I’ve learned from VisualStudio in our company’s codebase and my experiments with CMake, MSVC, and Clang.

I think, giving some meaning to the FILESET above would go a long way, both within a project and as a flag to a library to actually compile the headers listed there.

I agree and that’s the plan. In fact, I have an MR to disclaim support for the relevant fileset type at the moment because we don’t actually do anything with them (yet). It’ll get added back when it’s ready.

1 Like

The only option I could find was Project Properties->VC++ Directories->Public C++ Module Directories, which works on the directory level. I don’t know if there is an option that allows to control this on a source-file level, but I could not find one.

However, for the static library case CMake today already does not distinguish PRIVATE and PUBLIC module sources in libraries as far as I can see. If I change the project sources of foo from PUBLIC to PRIVATE, I can still import the respective modules in the executable with MSVC. So to me it seems that AllProjectBMIsArePublic is the correct behavior to stay consistent with the static library case on MSVC.

I don’t feel this is necessarily problematic. For DLLs we already have the situation that symbol visibility depends on __declspec/__attribute__((__visibility__)) annotations in the code, not the scope specifier in target_sources.

I think once we have collation rules possible in VS where CMake can enforce the “you cannot use PRIVATE modules here” rules we expect, we can then just tell VS “sure, everything is public” and let the collation checking fail the build where it needs (probably with far better error messages as it has more context too).

Currently, there is no enforcement of CMake visibility rules in VS at all. It will be necessary before VS C++ module support is allowed past the experimental gate (so that VS developers are not surprised when NInja generators refuse to build their code that worked in VS).

Agreed, that sounds like a sensible way forward in the long term.

Unfortunately the status quo is worse: MSVC will behave surprising in that way for static libraries, but for shared libraries it will fail to build a perfectly valid configuration that also works fine with Ninja. This is in my opinion the worst of both worlds: We have inconsistencies, not only between different generators, but also within a single generator (VC static vs. VC dll) and instead of breaking on one generator with an invalid configuration (which is an annoyance), we break on another with a valid configuration (which is a real deal breaker).

I am sufficiently annoyed by the issue that I would be volunteering to work on a PR for adding AllProjectBMIsArePublic to generated vcxproj files, if people agreed that this is a sensible change in the short term.

Right. I suppose adding it now is fine too; the collation feature will properly say “no” to any invalid usage when it finally does land. We should note the change in the docs though.

I’ve only been following this discussion loosely, so my apologies if this was already covered. Would such a change mean that a project would not be able to define any modules that are kept private to the project? In other words, would the project be forced to make all modules available for consumption outside the project, even if they didn’t want them to be visible outside the project? Or is this proposed change an opt-in thing?

Enforcement would be done by a CMake component that would plumb out the P1689 output and error on visibility (just like is done in Ninja today). We should be able to give better error messages too. I suppose the “owning target” could be named there… Theoretically yes, but the target name mapping would need plumbed through from somewhere; I don’t think anything has it at the moment.