Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++20 module support #1026

Open
distee-ubi opened this issue Mar 4, 2024 · 3 comments
Open

C++20 module support #1026

distee-ubi opened this issue Mar 4, 2024 · 3 comments
Labels

Comments

@distee-ubi
Copy link

distee-ubi commented Mar 4, 2024

I experimented with fast-building C++20 modules and just wanted to share some observations.
So, this issue is kind of #94 necro.

The good: it can be done as is.
The bad: it is cumbersome and requires a nasty hack in order to express ifcobj dependencies.

As CMake now has official support for C++20 modules I gave it (yet another) try. I still do not like it. At all.

  • CMake's inconsistency wastes my time. It forces me to process large amounts of documentation while always wondering about the correctness of the material. It also forces me to experiment in order to understand the nature of the inconsistencies.
  • I am not fully in control of compiler flags when building C++ modules with CMake.
  • Specifying a build on different platforms the explicit way as done with FASTBuild is quicker than wrestling the platform abstraction provided by CMake into exactly doing what needs to be done. It is also more descriptive and transparent.
  • Multi-Ninja builds on Windows are still buggy. MSBuilds are slow.

I really would like to see C++ module support in FASTBuild. This is despite my doubts that it will be needed for the next 10 years to come.

The observations

  • Tried MSVC because it (allegedly) is still ahead of others when it comes to modules.
  • Adhered to MS module naming conventions to save on explicit compiler option specification.

Tell the compiler you want module support.

.CompilerOptions + ' /std:c++20'

When generating vcxproj files tell Intellisense that you want module support.

VCXProject('foo_proj')
{
   .AdditionalOptions = '/std:c++20'
}

Modules files can be build by just passing the proper options.

ObjectList('sl_ifc_$Cfg$')
{
   .CompilerOptions + ' /interface /ifcOutput $IfcOut$'
   .CompilerInputFiles = { '$SrcDir$/sl.ixx' }
}

Module build order is explicitly described via .PreBuildDependencies. This can become cumbersome quickly.

ObjectList('vl-ifc_$Cfg$')
{
   .CompilerOptions + ' /interface /ifcOutput $IfcOut$'
   .CompilerInputFiles = {
      '$SrcDir$/vl-v4f.ixx'
      '$SrcDir$/vl-v4d.ixx'
   }
   .PreBuildDependencies = { 'sl_$Cfg$' }
}
ObjectList('vl-color-ifc_$Cfg$')
{
   .CompilerOptions + ' /interface /ifcOutput $IfcOut$'
   .CompilerInputFiles = { '$SrcDir$/vl-color.ixx' }
   .PreBuildDependencies = {
      'sl_$Cfg$'
      'vl-ifc_$Cfg$'
   }
}
ObjectList( 'vl_ifc_$Cfg$')
{
   .CompilerOptions + ' /interface /ifcOutput $IfcOut$'
   .CompilerInputFiles = {'$SrcDir$/vl.ixx' }
   .PreBuildDependencies = { 'vl-color-ifc_$Cfg$' }
}

Object files may depend on ifc files. In general, an obj depends on all module ifc it imports. Consider for example an inline function defined in some ifc and injected into some standard obj. In consequence, we need to rebuild an obj whenever at least one of the ifc it depends on changes. I abused .CompilerForceUsing to model this dependency. Again, doing so becomes cumbersome quickly.

ObjectList('vl_obj_$Cfg$')
{
   .CompilerInputFiles = { '$SrcDir$/vl_lab.cpp' }

   // Hack to ensure rebuild when ifc changes.
   // For C++ '/FU xxx' is dropped, but input dependency is still used.
   .CompilerForceUsing = { '$IfcOut$/vl.ifc' }
   .PreBuildDependencies = { 'vl_ifc_$Cfg$' }
}
@ffulin
Copy link
Contributor

ffulin commented Mar 17, 2024

Thanks for providing the notes on your investigation. I think explicit support will be required to avoid things being cumbersome/error prone as you've noted.

As an aside, it seems like setting C++20 in AdditionalOptions shouldn't be needed for Intellisense as that should automatically extracted from the CompilerOptions.

@kridenberg
Copy link

I want to add that with partitions, things became even "better". Me and friend of mine have a medium-sized pet project with nearly 50+ projects and hundreds of modules. We also trie to integrate FastBuild at our current job, so I wanted to learn by integrating it into the pet project. Things became ugly for modules because:

  1. I need a separate path for partitions. So I decided to provide separate extensions for them, '.ixxp' and '.pxx' (two, because I am not sure which I like more). This is due to a separate /internal partition flag for the cl.exe.
  2. I need a clear dependency between modules. It was tedious to provide it manually, so I was forced by laziness to create an additional Python script pass that would generate a meta-info .bff file with all dependencies between modules. Also, this was easy because I already had one pass to extract some platform-dependent constant, but still, some work had to be done.
  3. Combining 1 and 2, I created a robust pipeline. But It was brutal. I could be more experienced with FastBuild (I am very excited and have been using it only for two weeks), but that is my current experience with modules.

In conclusion, life with modules is possible, but it is very difficult and demands a lot of dedication to integrate them into a "not a small" project. Hopefully, some good standardised solution for FastBuild will be found. Cheers.

@kelteseth
Copy link

Hi,
we now track fastbuild modules support progress at https://arewemodulesyet.org/tools/ . Feel free to create a PR if the status changes at https://github.com/kelteseth/arewemodulesyet/ 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants