Support `layering_check` with C++ path mapping

Users can opt into path mapping for C++ module map actions via `--modify_execution_info=CppModuleMap=+supports-path-mapping`. This is achieved by mapping paths in the module map files as well as converting the sequence variable for module map paths to a new structured `ArtifactSequenceVariable`.

Also makes it so that `ExecutionServer` gracefully handles failing commands instead of crashing.

Closes #22957.

PiperOrigin-RevId: 675073116
Change-Id: I13835c7fb01354a89ec5fd141cf892c6b733efe4
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContext.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContext.java
index 817a525..9d02b70 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContext.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationContext.java
@@ -574,8 +574,8 @@
     }
   }
 
-  /** @return modules maps from direct dependencies. */
-  public Iterable<Artifact> getDirectModuleMaps() {
+  /** Returns modules maps from direct dependencies. */
+  public ImmutableList<Artifact> getDirectModuleMaps() {
     return directModuleMaps;
   }
 
@@ -588,8 +588,8 @@
   }
 
   /**
-   * @return all declared headers of the current module if the current target is compiled as a
-   *     module.
+   * Returns all declared headers of the current module if the current target is compiled as a
+   * module.
    */
   ImmutableList<Artifact> getHeaderModuleSrcs(boolean separateModule) {
     if (separateModule) {
@@ -644,7 +644,7 @@
         headerTokens.build());
   }
 
-  /** @return the C++ module map of the owner. */
+  /** Returns the C++ module map of the owner. */
   public CppModuleMap getCppModuleMap() {
     return cppModuleMap;
   }
@@ -1147,7 +1147,7 @@
    * Gathers data about the PIC and no-PIC .pcm files belonging to this context and the associated
    * information about the headers, e.g. modular vs. textual headers and pre-grepped header files.
    *
-   * <p>This also implements a data structure very similar to NestedSet, but chosing slightly
+   * <p>This also implements a data structure very similar to NestedSet, but choosing slightly
    * different trade-offs to account for the specific data stored in here, specifically, we know
    * that there is going to be a single entry in every node of the DAG. Contrary to NestedSet, we
    * reuse memoization data from dependencies to conserve both runtime and memory. Experiments have
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcStarlarkInternal.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcStarlarkInternal.java
index 3b00657..5309498 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcStarlarkInternal.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcStarlarkInternal.java
@@ -31,6 +31,7 @@
 import com.google.devtools.build.lib.analysis.LicensesProvider.TargetLicense;
 import com.google.devtools.build.lib.analysis.LicensesProviderImpl;
 import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.actions.PathMappers;
 import com.google.devtools.build.lib.analysis.actions.SymlinkAction;
 import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
 import com.google.devtools.build.lib.analysis.starlark.Args;
@@ -419,7 +420,11 @@
             compiledModule,
             moduleMapHomeIsCwd,
             generateSubmodules,
-            withoutExternDependencies));
+            withoutExternDependencies,
+            PathMappers.getOutputPathsMode(ruleContext.getConfiguration()),
+            ruleContext
+                .getConfiguration()
+                .modifiedExecutionInfo(ImmutableMap.of(), CppModuleMapAction.MNEMONIC)));
   }
 
   @SerializationConstant @VisibleForSerialization
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainVariables.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainVariables.java
index 7150b1d..c725d4d 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainVariables.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainVariables.java
@@ -1185,6 +1185,54 @@
     }
   }
 
+  @Immutable
+  private static final class ArtifactSetSequence extends VariableValueAdapter {
+    private final NestedSet<Artifact> values;
+
+    private ArtifactSetSequence(NestedSet<Artifact> values) {
+      Preconditions.checkNotNull(values);
+      this.values = values;
+    }
+
+    @Override
+    public ImmutableList<VariableValue> getSequenceValue(
+        String variableName, PathMapper pathMapper) {
+      ImmutableList<Artifact> valuesList = values.toList();
+      ImmutableList.Builder<VariableValue> sequences =
+          ImmutableList.builderWithExpectedSize(valuesList.size());
+      for (Artifact value : valuesList) {
+        sequences.add(new StringValue(pathMapper.getMappedExecPathString(value)));
+      }
+      return sequences.build();
+    }
+
+    @Override
+    public String getVariableTypeName() {
+      return Sequence.SEQUENCE_VARIABLE_TYPE_NAME;
+    }
+
+    @Override
+    public boolean isTruthy() {
+      return !values.isEmpty();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (this == other) {
+        return true;
+      }
+      if (!(other instanceof ArtifactSetSequence otherArtifacts)) {
+        return false;
+      }
+      return values.shallowEquals(otherArtifacts.values);
+    }
+
+    @Override
+    public int hashCode() {
+      return values.shallowHashCode();
+    }
+  }
+
   /**
    * Single structure value. Be careful not to create sequences of single structures, as the memory
    * overhead is prohibitively big.
@@ -1497,6 +1545,19 @@
     }
 
     /**
+     * Add a sequence variable that expands {@code name} to {@link Artifact} {@code values}.
+     *
+     * <p>Accepts values as NestedSet. Nested set is stored directly, not cloned, not flattened.
+     */
+    @CanIgnoreReturnValue
+    public Builder addArtifactSequenceVariable(String name, NestedSet<Artifact> values) {
+      checkVariableNotPresentAlready(name);
+      Preconditions.checkNotNull(values, "Cannot set null as a value for variable '%s'", name);
+      variablesMap.put(name, new ArtifactSetSequence(values));
+      return this;
+    }
+
+    /**
      * Add a variable built using {@code VariableValueBuilder} api that expands {@code name} to the
      * value returned by the {@code builder}.
      */
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java
index 4f62040..54f8257 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java
@@ -162,7 +162,7 @@
       Artifact diagnosticsFile,
       ImmutableList<VariablesExtension> variablesExtensions,
       ImmutableMap<String, String> additionalBuildVariables,
-      Iterable<Artifact> directModuleMaps,
+      ImmutableList<Artifact> directModuleMaps,
       NestedSet<PathFragment> includeDirs,
       NestedSet<PathFragment> quoteIncludeDirs,
       NestedSet<PathFragment> systemIncludeDirs,
@@ -229,7 +229,7 @@
       Artifact diagnosticsFile,
       ImmutableList<VariablesExtension> variablesExtensions,
       ImmutableMap<String, String> additionalBuildVariables,
-      Iterable<Artifact> directModuleMaps,
+      ImmutableList<Artifact> directModuleMaps,
       NestedSet<String> includeDirs,
       NestedSet<String> quoteIncludeDirs,
       NestedSet<String> systemIncludeDirs,
@@ -297,7 +297,7 @@
       Artifact diagnosticsFile,
       ImmutableList<VariablesExtension> variablesExtensions,
       ImmutableMap<String, String> additionalBuildVariables,
-      Iterable<Artifact> directModuleMaps,
+      ImmutableList<Artifact> directModuleMaps,
       NestedSet<PathFragment> includeDirs,
       NestedSet<PathFragment> quoteIncludeDirs,
       NestedSet<PathFragment> systemIncludeDirs,
@@ -471,7 +471,7 @@
       boolean isUsingMemProf,
       List<VariablesExtension> variablesExtensions,
       Map<String, String> additionalBuildVariables,
-      Iterable<Artifact> directModuleMaps,
+      ImmutableList<Artifact> directModuleMaps,
       ImmutableList<PathFragment> includeDirs,
       ImmutableList<PathFragment> quoteIncludeDirs,
       ImmutableList<PathFragment> systemIncludeDirs,
@@ -507,7 +507,7 @@
       boolean isUsingMemProf,
       List<VariablesExtension> variablesExtensions,
       Map<String, String> additionalBuildVariables,
-      Iterable<Artifact> directModuleMaps,
+      ImmutableList<Artifact> directModuleMaps,
       NestedSet<PathFragment> includeDirs,
       NestedSet<PathFragment> quoteIncludeDirs,
       NestedSet<PathFragment> systemIncludeDirs,
@@ -526,9 +526,9 @@
       buildVariables.addStringVariable(MODULE_NAME.getVariableName(), cppModuleMap.getName());
       buildVariables.addArtifactVariable(
           MODULE_MAP_FILE.getVariableName(), cppModuleMap.getArtifact());
-      buildVariables.addStringSequenceVariable(
+      buildVariables.addArtifactSequenceVariable(
           DEPENDENT_MODULE_MAP_FILES.getVariableName(),
-          Iterables.transform(directModuleMaps, Artifact::getExecPathString));
+          NestedSetBuilder.wrap(Order.STABLE_ORDER, directModuleMaps));
     }
     if (featureConfiguration.isEnabled(CppRuleClasses.USE_HEADER_MODULES)) {
       // Module inputs will be set later when the action is executed.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModuleMapAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModuleMapAction.java
index bc37093..8ffbe6c 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModuleMapAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppModuleMapAction.java
@@ -16,15 +16,26 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.Interner;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
 import com.google.devtools.build.lib.actions.ActionExecutionContext;
 import com.google.devtools.build.lib.actions.ActionKeyContext;
 import com.google.devtools.build.lib.actions.ActionOwner;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.actions.ArtifactExpander;
+import com.google.devtools.build.lib.actions.CommandLineExpansionException;
+import com.google.devtools.build.lib.actions.ExecutionRequirements;
+import com.google.devtools.build.lib.actions.PathMapper;
 import com.google.devtools.build.lib.analysis.actions.AbstractFileWriteAction;
 import com.google.devtools.build.lib.analysis.actions.DeterministicWriter;
+import com.google.devtools.build.lib.analysis.actions.PathMappers;
+import com.google.devtools.build.lib.analysis.config.CoreOptions.OutputPathsMode;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.concurrent.BlazeInterners;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.util.Fingerprint;
 import com.google.devtools.build.lib.vfs.PathFragment;
@@ -35,6 +46,8 @@
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import javax.annotation.Nullable;
 
 /**
@@ -43,8 +56,11 @@
  */
 @Immutable
 public final class CppModuleMapAction extends AbstractFileWriteAction {
+  public static final String MNEMONIC = "CppModuleMap";
 
   private static final String GUID = "4f407081-1951-40c1-befc-d6b4daff5de3";
+  private static final Interner<ImmutableSortedMap<String, String>> executionInfoInterner =
+      BlazeInterners.newWeakInterner();
 
   // C++ module map of the current target
   private final CppModuleMap cppModuleMap;
@@ -65,6 +81,7 @@
   private final boolean compiledModule;
   private final boolean generateSubmodules;
   private final boolean externDependencies;
+  private final ImmutableSortedMap<String, String> executionInfo;
 
   public CppModuleMapAction(
       ActionOwner owner,
@@ -77,7 +94,9 @@
       boolean compiledModule,
       boolean moduleMapHomeIsCwd,
       boolean generateSubmodules,
-      boolean externDependencies) {
+      boolean externDependencies,
+      OutputPathsMode outputPathsMode,
+      ImmutableMap<String, String> executionInfo) {
     super(
         owner,
         NestedSetBuilder.<Artifact>stableOrder()
@@ -95,6 +114,22 @@
     this.compiledModule = compiledModule;
     this.generateSubmodules = generateSubmodules;
     this.externDependencies = externDependencies;
+    // Save memory by storing outputPathsMode implicitly via the presence of
+    // ExecutionRequirements.SUPPORTS_PATH_MAPPING in the key set. Path mapping is only effectively
+    // enabled if the key is present *and* the mode is set to STRIP, so if the latter is not the
+    // case, we can safely not store the key.
+    Map<String, String> storedExecutionInfo;
+    if (outputPathsMode == OutputPathsMode.STRIP) {
+      storedExecutionInfo = executionInfo;
+    } else {
+      storedExecutionInfo =
+          Maps.filterKeys(
+              executionInfo, k -> !k.equals(ExecutionRequirements.SUPPORTS_PATH_MAPPING));
+    }
+    this.executionInfo =
+        storedExecutionInfo.isEmpty()
+            ? ImmutableSortedMap.of()
+            : executionInfoInterner.intern(ImmutableSortedMap.copyOf(storedExecutionInfo));
   }
 
   @Override
@@ -110,9 +145,16 @@
   @Override
   public DeterministicWriter newDeterministicWriter(ActionExecutionContext ctx) {
     final ArtifactExpander artifactExpander = ctx.getArtifactExpander();
+    // TODO: It is possible that compile actions consuming the module map have path mapping disabled
+    //  due to inputs conflicting across configurations. Since these inputs aren't inputs of the
+    //  module map action, the generated map still contains mapped paths, which then results in
+    //  compilation failures. This should be very rare as #include doesn't allow to disambiguate
+    //  between headers from different configurations but with identical root-relative paths.
+    final PathMapper pathMapper =
+        PathMappers.create(this, getOutputPathsMode(), /* isStarlarkAction= */ false);
     return out -> {
       OutputStreamWriter content = new OutputStreamWriter(out, StandardCharsets.ISO_8859_1);
-      PathFragment fragment = cppModuleMap.getArtifact().getExecPath();
+      PathFragment fragment = pathMapper.map(cppModuleMap.getArtifact().getExecPath());
       int segmentsToExecPath = fragment.segmentCount() - 1;
       Optional<Artifact> umbrellaHeader = cppModuleMap.getUmbrellaHeader();
       String leadingPeriods = moduleMapHomeIsCwd ? "" : "../".repeat(segmentsToExecPath);
@@ -134,7 +176,8 @@
             leadingPeriods,
             /* canCompile= */ false,
             deduper,
-            /*isUmbrellaHeader*/ true);
+            /*isUmbrellaHeader*/ true,
+            pathMapper);
       } else {
         for (Artifact artifact : expandedHeaders(artifactExpander, publicHeaders)) {
           appendHeader(
@@ -144,7 +187,8 @@
               leadingPeriods,
               /* canCompile= */ true,
               deduper,
-              /*isUmbrellaHeader*/ false);
+              /*isUmbrellaHeader*/ false,
+              pathMapper);
         }
         for (Artifact artifact : expandedHeaders(artifactExpander, privateHeaders)) {
           appendHeader(
@@ -154,7 +198,8 @@
               leadingPeriods,
               /* canCompile= */ true,
               deduper,
-              /*isUmbrellaHeader*/ false);
+              /*isUmbrellaHeader*/ false,
+              pathMapper);
         }
         for (Artifact artifact : separateModuleHdrs) {
           appendHeader(
@@ -164,7 +209,8 @@
               leadingPeriods,
               /* canCompile= */ false,
               deduper,
-              /*isUmbrellaHeader*/ false);
+              /*isUmbrellaHeader*/ false,
+              pathMapper);
         }
         for (PathFragment additionalExportedHeader : additionalExportedHeaders) {
           appendHeader(
@@ -174,7 +220,8 @@
               leadingPeriods,
               /*canCompile*/ false,
               deduper,
-              /*isUmbrellaHeader*/ false);
+              /*isUmbrellaHeader*/ false,
+              pathMapper);
         }
       }
       for (CppModuleMap dep : dependencies) {
@@ -196,7 +243,8 @@
               leadingPeriods,
               /* canCompile= */ true,
               deduper,
-              /*isUmbrellaHeader*/ false);
+              /*isUmbrellaHeader*/ false,
+              pathMapper);
         }
         for (CppModuleMap dep : dependencies) {
           content.append("  use \"").append(dep.getName()).append("\"\n");
@@ -211,7 +259,7 @@
               .append(dep.getName())
               .append("\" \"")
               .append(leadingPeriods)
-              .append(dep.getArtifact().getExecPathString())
+              .append(pathMapper.getMappedExecPathString(dep.getArtifact()))
               .append("\"");
         }
       }
@@ -219,8 +267,8 @@
     };
   }
 
-  private static Iterable<Artifact> expandedHeaders(ArtifactExpander artifactExpander,
-      Iterable<Artifact> unexpandedHeaders) {
+  private static ImmutableList<Artifact> expandedHeaders(
+      ArtifactExpander artifactExpander, Iterable<Artifact> unexpandedHeaders) {
     List<Artifact> expandedHeaders = new ArrayList<>();
     for (Artifact unexpandedHeader : unexpandedHeaders) {
       if (unexpandedHeader.isTreeArtifact()) {
@@ -233,9 +281,17 @@
     return ImmutableList.copyOf(expandedHeaders);
   }
 
-  private void appendHeader(Appendable content, String visibilitySpecifier,
-      PathFragment path, String leadingPeriods, boolean canCompile, HashSet<PathFragment> deduper,
-      boolean isUmbrellaHeader) throws IOException {
+  private void appendHeader(
+      Appendable content,
+      String visibilitySpecifier,
+      PathFragment unmappedPath,
+      String leadingPeriods,
+      boolean canCompile,
+      Set<PathFragment> deduper,
+      boolean isUmbrellaHeader,
+      PathMapper pathMapper)
+      throws IOException {
+    PathFragment path = pathMapper.map(unmappedPath);
     if (deduper.contains(path)) {
       return;
     }
@@ -268,14 +324,15 @@
 
   @Override
   public String getMnemonic() {
-    return "CppModuleMap";
+    return MNEMONIC;
   }
 
   @Override
   protected void computeKey(
       ActionKeyContext actionKeyContext,
       @Nullable ArtifactExpander artifactExpander,
-      Fingerprint fp) {
+      Fingerprint fp)
+      throws CommandLineExpansionException, InterruptedException {
     fp.addString(GUID);
     fp.addInt(privateHeaders.size());
     for (Artifact artifact : privateHeaders) {
@@ -308,6 +365,25 @@
     fp.addBoolean(compiledModule);
     fp.addBoolean(generateSubmodules);
     fp.addBoolean(externDependencies);
+    PathMappers.addToFingerprint(
+        getMnemonic(),
+        getExecutionInfo(),
+        NestedSetBuilder.emptySet(Order.STABLE_ORDER),
+        actionKeyContext,
+        getOutputPathsMode(),
+        fp);
+  }
+
+  @Override
+  public ImmutableMap<String, String> getExecutionInfo() {
+    return executionInfo;
+  }
+
+  private OutputPathsMode getOutputPathsMode() {
+    // See comment in the constructor for how outputPathsMode is stored implicitly.
+    return executionInfo.containsKey(ExecutionRequirements.SUPPORTS_PATH_MAPPING)
+        ? OutputPathsMode.STRIP
+        : OutputPathsMode.OFF;
   }
 
   @VisibleForTesting
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD b/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD
index cdc2026..4e032ca 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/BUILD
@@ -496,6 +496,7 @@
     deps = [
         "//src/main/java/com/google/devtools/build/lib/actions",
         "//src/main/java/com/google/devtools/build/lib/actions:artifacts",
+        "//src/main/java/com/google/devtools/build/lib/analysis:config/core_options",
         "//src/main/java/com/google/devtools/build/lib/rules/cpp",
         "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
         "//src/test/java/com/google/devtools/build/lib/actions/util",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppModuleMapActionTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppModuleMapActionTest.java
index 4d53fbb..5762fde 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppModuleMapActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppModuleMapActionTest.java
@@ -16,11 +16,13 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.actions.ActionKeyContext;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.actions.ArtifactRoot;
 import com.google.devtools.build.lib.actions.ArtifactRoot.RootType;
 import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
+import com.google.devtools.build.lib.analysis.config.CoreOptions;
 import com.google.devtools.build.lib.testutil.Scratch;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import java.io.IOException;
@@ -60,15 +62,17 @@
     return new CppModuleMapAction(
         ActionsTestUtil.NULL_ACTION_OWNER,
         cppModuleMap,
-        /*privateHeaders=*/ ImmutableList.of(),
-        /*publicHeaders=*/ ImmutableList.of(),
+        /* privateHeaders= */ ImmutableList.of(),
+        /* publicHeaders= */ ImmutableList.of(),
         ImmutableList.copyOf(dependencies),
-        /*additionalExportedHeaders=*/ ImmutableList.of(),
-        /*separateModuleHeaders=*/ ImmutableList.of(),
-        /*compiledModule=*/ false,
-        /*moduleMapHomeIsCwd=*/ false,
-        /*generateSubmodules=*/ false,
-        /*externDependencies=*/ false);
+        /* additionalExportedHeaders= */ ImmutableList.of(),
+        /* separateModuleHeaders= */ ImmutableList.of(),
+        /* compiledModule= */ false,
+        /* moduleMapHomeIsCwd= */ false,
+        /* generateSubmodules= */ false,
+        /* externDependencies= */ false,
+        CoreOptions.OutputPathsMode.OFF,
+        /* executionInfo= */ ImmutableMap.of());
   }
 
   private Artifact createOutputArtifact(String rootRelativePath) {
diff --git a/src/test/shell/bazel/path_mapping_test.sh b/src/test/shell/bazel/path_mapping_test.sh
index d16a38c..239d6e9 100755
--- a/src/test/shell/bazel/path_mapping_test.sh
+++ b/src/test/shell/bazel/path_mapping_test.sh
@@ -623,9 +623,9 @@
   bazel run \
     --verbose_failures \
     --experimental_output_paths=strip \
-    --modify_execution_info=CppCompile=+supports-path-mapping \
+    --modify_execution_info=CppCompile=+supports-path-mapping,CppModuleMap=+supports-path-mapping \
     --remote_executor=grpc://localhost:${worker_port} \
-    --features=-module_maps \
+    --features=layering_check \
     "//$pkg:main" &>"$TEST_log" || fail "Expected success"
 
   expect_log 'Hello, lib1!'
@@ -636,9 +636,9 @@
   bazel run \
     --verbose_failures \
     --experimental_output_paths=strip \
-    --modify_execution_info=CppCompile=+supports-path-mapping \
+    --modify_execution_info=CppCompile=+supports-path-mapping,CppModuleMap=+supports-path-mapping \
     --remote_executor=grpc://localhost:${worker_port} \
-    --features=-module_maps \
+    --features=layering_check \
     "//$pkg:transitioned_main" &>"$TEST_log" || fail "Expected success"
 
   expect_log 'Hi there, lib1!'
diff --git a/src/tools/remote/src/main/java/com/google/devtools/build/remote/worker/ExecutionServer.java b/src/tools/remote/src/main/java/com/google/devtools/build/remote/worker/ExecutionServer.java
index 22c893c..cbbb55e 100644
--- a/src/tools/remote/src/main/java/com/google/devtools/build/remote/worker/ExecutionServer.java
+++ b/src/tools/remote/src/main/java/com/google/devtools/build/remote/worker/ExecutionServer.java
@@ -175,11 +175,7 @@
       return;
     }
     ((ServerCallStreamObserver<Operation>) responseObserver)
-        .setOnCancelHandler(
-            () -> {
-              future.cancel(true);
-              operationsCache.remove(opName);
-            });
+        .setOnCancelHandler(() -> operationsCache.remove(opName));
     waitExecution(opName, future, responseObserver);
   }
 
@@ -241,11 +237,7 @@
         executorService.submit(() -> execute(context, request, opName));
     operationsCache.put(opName, future);
     ((ServerCallStreamObserver<Operation>) responseObserver)
-        .setOnCancelHandler(
-            () -> {
-              future.cancel(true);
-              operationsCache.remove(opName);
-            });
+        .setOnCancelHandler(() -> operationsCache.remove(opName));
     // Send the first operation.
     responseObserver.onNext(Operation.newBuilder().setName(opName).build());
     // When the operation completes, send the result.