Skip to content

Conversation

@fmeum
Copy link
Collaborator

@fmeum fmeum commented Dec 10, 2025

When multiple module_interfaces are specified on a single cc_library, the individual compilation actions form a DAG based on imports between these modules. Consider the following situation:

  • a.cppm imports b.cppm, both of which are in the module_interfaces of a single cc_library.
  • Building the target populates the action cache with an entry for a.pcm that stores b.pcm as a discovered input.
  • Now edit a.cppm and b.cppm so that b.cppm imports a.cppm and a.cppm no longer imports b.cppm.
  • Build again (optionally after a shutdown).

Before this commit, this resulted in an action cycle since during action cache checking, Bazel would reuse or look up the inputs discovered in the previous build, thus introducing an edge from a.pcm to b.pcm. Together with the newly discovered edge from b.pcm to a.pcm, this resulted in a cycle.

This is fixed by not requesting the previously discovered inputs (either retained in memory or in the action cache) if the mandatory inputs changed. In the case of C++20 modules, this is sufficient since the modmap file, which lists all transitive .pcm files required for compilation, is a mandatory input.

As part of this change, MetadataDigestUtils.fromMetadata had to be modified to always return a byte array of proper digest length, even if called with an empty map, to match the assumptions of the action cache.

This change is pretty much Fabian's PR #27492 with a tiny fix added on top (not returning from computeMandatoryInputsDigest() early on valuesMissing() if inErrorBubbling() is true)

Closes #27492.

PiperOrigin-RevId: 842733471
Change-Id: I48fa2c0bceb888dcb58db29d50c30719b2122c5d
(cherry picked from commit cb9bd86)

Closes #27544

When multiple `module_interfaces` are specified on a single `cc_library`, the individual compilation actions form a DAG based on `import`s between these modules. Consider the following situation:

* `a.cppm` imports `b.cppm`, both of which are in the `module_interfaces` of a single `cc_library`.
* Building the target populates the action cache with an entry for `a.pcm` that stores `b.pcm` as a discovered input.
* Now edit `a.cppm` and `b.cppm` so that `b.cppm` imports `a.cppm` and `a.cppm` no longer imports `b.cppm`.
* Build again (optionally after a shutdown).

Before this commit, this resulted in an action cycle since during action cache checking, Bazel would reuse or look up the inputs discovered in the previous build, thus introducing an edge from `a.pcm` to `b.pcm`. Together with the newly discovered edge from `b.pcm` to `a.pcm`, this resulted in a cycle.

This is fixed by not requesting the previously discovered inputs (either retained in memory or in the action cache) if the mandatory inputs changed. In the case of C++20 modules, this is sufficient since the modmap file, which lists all transitive `.pcm` files required for compilation, is a mandatory input.

As part of this change, `MetadataDigestUtils.fromMetadata` had to be modified to always return a byte array of proper digest length, even if called with an empty map, to match the assumptions of the action cache.

This change is pretty much Fabian's PR bazelbuild#27492 with a tiny fix added on top (not returning from computeMandatoryInputsDigest() early on valuesMissing() if inErrorBubbling() is true)

Closes bazelbuild#27492.

PiperOrigin-RevId: 842733471
Change-Id: I48fa2c0bceb888dcb58db29d50c30719b2122c5d
(cherry picked from commit cb9bd86)
@fmeum fmeum requested a review from a team as a code owner December 10, 2025 16:24
@fmeum fmeum requested a review from lberki December 10, 2025 16:25
@github-actions github-actions bot added team-Performance Issues for Performance teams team-Configurability platforms, toolchains, cquery, select(), config transitions awaiting-review PR is awaiting review from an assigned reviewer labels Dec 10, 2025
@gemini-code-assist
Copy link

Summary of Changes

Hello @fmeum, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical issue in Bazel's action caching system that could lead to action cycles when working with C++20 modules, particularly when the import relationships between module_interfaces within a cc_library change. The core of the solution involves enhancing the action cache to track and validate a 'mandatory inputs digest'. This ensures that if the essential inputs of an action, such as the C++20 module map, change, the action is re-executed rather than relying on potentially stale discovered inputs, thereby preventing erroneous build cycles.

Highlights

  • Action Cycle Prevention: Introduced a mechanism to prevent action cycles in Bazel's action cache when C++20 module import dependencies change. This is achieved by not reusing previously discovered inputs if the mandatory inputs for an action have been altered.
  • Mandatory Inputs Digest: Added a new concept of 'mandatory inputs digest' to the action cache. This digest is computed for an action's essential inputs and is used to determine if a re-execution is necessary, even if previously discovered inputs are available.
  • Action Cache Updates: Modified the ActionCacheChecker and ActionCache to store and check this new mandatory inputs digest, ensuring that the cache correctly invalidates entries when module dependencies shift.
  • Metadata Handling: Updated MetadataDigestUtils.fromMetadata to consistently return a byte array of the correct digest length, even for empty metadata maps, to align with action cache assumptions.
  • C++20 Module Integration Tests: Added new integration tests specifically designed to validate the fix for action cycles when C++20 module import graphs change (e.g., a imports b changing to b imports a).
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@lberki lberki added awaiting-PR-merge PR has been approved by a reviewer and is ready to be merge internally and removed awaiting-review PR is awaiting review from an assigned reviewer labels Dec 10, 2025
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a robust mechanism to handle dependency changes within C++20 modules by tracking a digest of mandatory inputs. This effectively prevents action cycles when module import graphs are modified. The changes are well-implemented across the action caching and execution logic. I've identified a minor typo in a new test case and an opportunity to improve code clarity with a comment. Overall, this is a solid improvement for incremental builds involving C++ modules.

Comment on lines +988 to +990
if (mandatoryInputsDigest.length != digest.length) {
throw new IOException("Corrupted mandatory inputs digest");
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This check is correct because both mandatoryInputsDigest and the action digest are generated using the same hash function, resulting in digests of the same length. However, this is not immediately obvious from the code. Adding a comment to explain this assumption would improve clarity for future maintainers.

        if (mandatoryInputsDigest.length != digest.length) {
          // Both digests are generated using the same hash function, so they must have the same
          // length. A mismatch indicates corruption.
          throw new IOException("Corrupted mandatory inputs digest");
        }

fmeum added a commit to fmeum/rules_cc that referenced this pull request Dec 10, 2025
This is now supported in Bazel (HEAD and 9.0.0) since bazelbuild/bazel#27927.
@iancha1992 iancha1992 enabled auto-merge December 10, 2025 16:36
@iancha1992 iancha1992 removed the awaiting-PR-merge PR has been approved by a reviewer and is ready to be merge internally label Dec 10, 2025
auto-merge was automatically disabled December 10, 2025 16:39

Head branch was pushed to by a user without write access

@iancha1992
Copy link
Member

@fmeum Could you please resolve the conflicts?

@fmeum
Copy link
Collaborator Author

fmeum commented Dec 10, 2025

@iancha1992 Should be fixed

@iancha1992 iancha1992 added this pull request to the merge queue Dec 10, 2025
Merged via the queue into bazelbuild:release-9.0.0 with commit 251bfef Dec 10, 2025
45 checks passed
copybara-service bot pushed a commit to bazelbuild/rules_cc that referenced this pull request Dec 15, 2025
This is now supported in Bazel (HEAD and 9.0.0) since bazelbuild/bazel#27927.

Closes #545

COPYBARA_INTEGRATE_REVIEW=#545 from fmeum:patch-1 b4ec47b
PiperOrigin-RevId: 844562596
Change-Id: If41dc44ddaaba7d3cf940b5fa126b29bd2e461b4
copybara-service bot pushed a commit that referenced this pull request Dec 15, 2025
This is now supported in Bazel (HEAD and 9.0.0) since #27927.

Closes #545

PiperOrigin-RevId: 844562596
Change-Id: I211632321dd4c2f9b62a86f0f82dbbfc3e5bdaf9
fmeum added a commit to fmeum/bazel that referenced this pull request Jan 8, 2026
github-merge-queue bot pushed a commit that referenced this pull request Jan 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

team-Configurability platforms, toolchains, cquery, select(), config transitions team-Performance Issues for Performance teams

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants