Support Apple embedded bitcode flags in cc_library

Fixes https://github.com/bazelbuild/bazel/issues/12063.

PiperOrigin-RevId: 368007191
diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java
index 771aefb..e7b8a75 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/apple/AppleConfiguration.java
@@ -14,12 +14,14 @@
 
 package com.google.devtools.build.lib.rules.apple;
 
+import com.google.auto.value.AutoValue;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.analysis.config.BuildOptions;
 import com.google.devtools.build.lib.analysis.config.CoreOptions;
 import com.google.devtools.build.lib.analysis.config.Fragment;
@@ -64,45 +66,22 @@
   /** Default cpu for iOS builds. */
   @VisibleForTesting static final String DEFAULT_IOS_CPU = "x86_64";
 
-  private final String iosCpu;
-  private final String appleSplitCpu;
   private final PlatformType applePlatformType;
   private final ConfigurationDistinguisher configurationDistinguisher;
-  private final ImmutableList<String> iosMultiCpus;
-  private final ImmutableList<String> watchosCpus;
-  private final ImmutableList<String> tvosCpus;
-  private final ImmutableList<String> macosCpus;
-  private final ImmutableList<String> catalystCpus;
   private final EnumMap<ApplePlatform.PlatformType, AppleBitcodeMode> platformBitcodeModes;
   private final Label xcodeConfigLabel;
   private final AppleCommandLineOptions options;
+  private final AppleCpus appleCpus;
   private final boolean mandatoryMinimumVersion;
   private final boolean objcProviderFromLinked;
 
   public AppleConfiguration(BuildOptions buildOptions) {
     AppleCommandLineOptions options = buildOptions.get(AppleCommandLineOptions.class);
-    String iosCpu = iosCpuFromCpu(buildOptions.get(CoreOptions.class).cpu);
     this.options = options;
-    this.iosCpu = iosCpu;
-    this.appleSplitCpu = Preconditions.checkNotNull(options.appleSplitCpu, "appleSplitCpu");
+    this.appleCpus = AppleCpus.create(options, buildOptions.get(CoreOptions.class));
     this.applePlatformType =
         Preconditions.checkNotNull(options.applePlatformType, "applePlatformType");
     this.configurationDistinguisher = options.configurationDistinguisher;
-    this.iosMultiCpus = ImmutableList.copyOf(
-        Preconditions.checkNotNull(options.iosMultiCpus, "iosMultiCpus"));
-    this.watchosCpus = (options.watchosCpus == null || options.watchosCpus.isEmpty())
-        ? ImmutableList.of(AppleCommandLineOptions.DEFAULT_WATCHOS_CPU)
-        : ImmutableList.copyOf(options.watchosCpus);
-    this.tvosCpus = (options.tvosCpus == null || options.tvosCpus.isEmpty())
-        ? ImmutableList.of(AppleCommandLineOptions.DEFAULT_TVOS_CPU)
-        : ImmutableList.copyOf(options.tvosCpus);
-    this.macosCpus = (options.macosCpus == null || options.macosCpus.isEmpty())
-        ? ImmutableList.of(AppleCommandLineOptions.DEFAULT_MACOS_CPU)
-        : ImmutableList.copyOf(options.macosCpus);
-    this.catalystCpus =
-        (options.catalystCpus == null || options.catalystCpus.isEmpty())
-            ? ImmutableList.of(AppleCommandLineOptions.DEFAULT_CATALYST_CPU)
-            : ImmutableList.copyOf(options.catalystCpus);
     this.platformBitcodeModes = collectBitcodeModes(options.appleBitcodeMode);
     this.xcodeConfigLabel =
         Preconditions.checkNotNull(options.xcodeVersionConfig, "xcodeConfigLabel");
@@ -110,6 +89,50 @@
     this.objcProviderFromLinked = options.objcProviderFromLinked;
   }
 
+  /** A class that contains information pertaining to Apple CPUs. */
+  @AutoValue
+  public abstract static class AppleCpus {
+    public static AppleCpus create(AppleCommandLineOptions options, CoreOptions coreOptions) {
+      String iosCpu = iosCpuFromCpu(coreOptions.cpu);
+      String appleSplitCpu = Preconditions.checkNotNull(options.appleSplitCpu, "appleSplitCpu");
+      ImmutableList<String> iosMultiCpus =
+          ImmutableList.copyOf(Preconditions.checkNotNull(options.iosMultiCpus, "iosMultiCpus"));
+      ImmutableList<String> watchosCpus =
+          (options.watchosCpus == null || options.watchosCpus.isEmpty())
+              ? ImmutableList.of(AppleCommandLineOptions.DEFAULT_WATCHOS_CPU)
+              : ImmutableList.copyOf(options.watchosCpus);
+      ImmutableList<String> tvosCpus =
+          (options.tvosCpus == null || options.tvosCpus.isEmpty())
+              ? ImmutableList.of(AppleCommandLineOptions.DEFAULT_TVOS_CPU)
+              : ImmutableList.copyOf(options.tvosCpus);
+      ImmutableList<String> macosCpus =
+          (options.macosCpus == null || options.macosCpus.isEmpty())
+              ? ImmutableList.of(AppleCommandLineOptions.DEFAULT_MACOS_CPU)
+              : ImmutableList.copyOf(options.macosCpus);
+      ImmutableList<String> catalystCpus =
+          (options.catalystCpus == null || options.catalystCpus.isEmpty())
+              ? ImmutableList.of(AppleCommandLineOptions.DEFAULT_CATALYST_CPU)
+              : ImmutableList.copyOf(options.catalystCpus);
+
+      return new AutoValue_AppleConfiguration_AppleCpus(
+          iosCpu, appleSplitCpu, iosMultiCpus, watchosCpus, tvosCpus, macosCpus, catalystCpus);
+    }
+
+    abstract String iosCpu();
+
+    abstract String appleSplitCpu();
+
+    abstract ImmutableList<String> iosMultiCpus();
+
+    abstract ImmutableList<String> watchosCpus();
+
+    abstract ImmutableList<String> tvosCpus();
+
+    abstract ImmutableList<String> macosCpus();
+
+    abstract ImmutableList<String> catalystCpus();
+  }
+
   /** Determines cpu value from apple-specific toolchain identifier. */
   public static String iosCpuFromCpu(String cpu) {
     if (cpu.startsWith(IOS_CPU_PREFIX)) {
@@ -162,7 +185,7 @@
    */
   @Override
   public String getIosCpu() {
-    return iosCpu;
+    return appleCpus.iosCpu();
   }
 
   /**
@@ -184,24 +207,24 @@
    */
   @Override
   public String getSingleArchitecture() {
-    if (!Strings.isNullOrEmpty(appleSplitCpu)) {
-      return appleSplitCpu;
+    return getSingleArchitecture(applePlatformType, appleCpus);
+  }
+
+  private static String getSingleArchitecture(PlatformType applePlatformType, AppleCpus appleCpus) {
+    if (!Strings.isNullOrEmpty(appleCpus.appleSplitCpu())) {
+      return appleCpus.appleSplitCpu();
     }
     switch (applePlatformType) {
       case IOS:
-        if (!getIosMultiCpus().isEmpty()) {
-          return getIosMultiCpus().get(0);
-        } else {
-          return getIosCpu();
-        }
+        return Iterables.getFirst(appleCpus.iosMultiCpus(), appleCpus.iosCpu());
       case WATCHOS:
-        return watchosCpus.get(0);
+        return appleCpus.watchosCpus().get(0);
       case TVOS:
-        return tvosCpus.get(0);
+        return appleCpus.tvosCpus().get(0);
       case MACOS:
-        return macosCpus.get(0);
+        return appleCpus.macosCpus().get(0);
       case CATALYST:
-        return catalystCpus.get(0);
+        return appleCpus.catalystCpus().get(0);
       default:
         throw new IllegalArgumentException("Unhandled platform type " + applePlatformType);
     }
@@ -226,13 +249,13 @@
    *     configuration transition) yet does not match {@code platformType}
    */
   public List<String> getMultiArchitectures(PlatformType platformType) {
-    if (!Strings.isNullOrEmpty(appleSplitCpu)) {
+    if (!Strings.isNullOrEmpty(appleCpus.appleSplitCpu())) {
       if (applePlatformType != platformType) {
         throw new IllegalArgumentException(
             String.format("Expected post-split-transition platform type %s to match input %s ",
                 applePlatformType, platformType));
       }
-      return ImmutableList.of(appleSplitCpu);
+      return ImmutableList.of(appleCpus.appleSplitCpu());
     }
     switch (platformType) {
       case IOS:
@@ -242,13 +265,13 @@
           return getIosMultiCpus();
         }
       case WATCHOS:
-        return watchosCpus;
+        return appleCpus.watchosCpus();
       case TVOS:
-        return tvosCpus;
+        return appleCpus.tvosCpus();
       case MACOS:
-        return macosCpus;
+        return appleCpus.macosCpus();
       case CATALYST:
-        return catalystCpus;
+        return appleCpus.catalystCpus();
       default:
         throw new IllegalArgumentException("Unhandled platform type " + platformType);
     }
@@ -265,11 +288,6 @@
     return ApplePlatform.forTarget(applePlatformType, getSingleArchitecture());
   }
 
-  private boolean hasValidSingleArchPlatform() {
-    return ApplePlatform.isApplePlatform(
-        ApplePlatform.cpuStringForTarget(applePlatformType, getSingleArchitecture()));
-  }
-
   /**
    * Gets the current configuration {@link ApplePlatform} for the given {@link PlatformType}.
    * ApplePlatform is determined via a combination between the given platform type and the
@@ -323,7 +341,7 @@
   // TODO(b/28754442): Deprecate for more general Starlark-exposed platform retrieval.
   @Override
   public ApplePlatform getIosCpuPlatform() {
-    return ApplePlatform.forTarget(PlatformType.IOS, iosCpu);
+    return ApplePlatform.forTarget(PlatformType.IOS, getIosCpu());
   }
 
   /**
@@ -347,7 +365,7 @@
    * which is the specific CPU <b>this target</b> is being built for.
    */
   public ImmutableList<String> getIosMultiCpus() {
-    return iosMultiCpus;
+    return appleCpus.iosMultiCpus();
   }
 
   /**
@@ -361,8 +379,18 @@
    */
   @Override
   public AppleBitcodeMode getBitcodeMode() {
-    if (hasValidSingleArchPlatform()) {
-      ApplePlatform platform = getSingleArchPlatform();
+    return getAppleBitcodeMode(applePlatformType, appleCpus, platformBitcodeModes);
+  }
+
+  /** Returns the bitcode mode to use for compilation steps. */
+  public static AppleBitcodeMode getAppleBitcodeMode(
+      PlatformType applePlatformType,
+      AppleCpus appleCpus,
+      EnumMap<ApplePlatform.PlatformType, AppleBitcodeMode> platformBitcodeModes) {
+    String architecture = getSingleArchitecture(applePlatformType, appleCpus);
+    String cpuString = ApplePlatform.cpuStringForTarget(applePlatformType, architecture);
+    if (ApplePlatform.isApplePlatform(cpuString)) {
+      ApplePlatform platform = ApplePlatform.forTarget(applePlatformType, architecture);
       if (platform.isDevice()) {
         return platformBitcodeModes.get(applePlatformType);
       }
@@ -386,9 +414,9 @@
   @Override
   public String getOutputDirectoryName() {
     List<String> components = new ArrayList<>();
-    if (!appleSplitCpu.isEmpty()) {
+    if (!appleCpus.appleSplitCpu().isEmpty()) {
       components.add(applePlatformType.toString().toLowerCase());
-      components.add(appleSplitCpu);
+      components.add(appleCpus.appleSplitCpu());
 
       if (options.getMinimumOsVersion() != null) {
         components.add("min" + options.getMinimumOsVersion());
@@ -438,7 +466,7 @@
    * Compute the platform-type-to-bitcode-mode mapping from the pairs that were passed on the
    * command line.
    */
-  private static EnumMap<ApplePlatform.PlatformType, AppleBitcodeMode> collectBitcodeModes(
+  public static EnumMap<ApplePlatform.PlatformType, AppleBitcodeMode> collectBitcodeModes(
       List<Map.Entry<ApplePlatform.PlatformType, AppleBitcodeMode>> platformModeMappings) {
     EnumMap<ApplePlatform.PlatformType, AppleBitcodeMode> modes =
         new EnumMap<>(ApplePlatform.PlatformType.class);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/apple/BUILD b/src/main/java/com/google/devtools/build/lib/rules/apple/BUILD
index a8bcaca..3a1badc 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/apple/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/rules/apple/BUILD
@@ -25,7 +25,6 @@
         "//src/main/java/com/google/devtools/build/lib/analysis:config/core_options",
         "//src/main/java/com/google/devtools/build/lib/analysis:config/fragment",
         "//src/main/java/com/google/devtools/build/lib/analysis:config/fragment_options",
-        "//src/main/java/com/google/devtools/build/lib/analysis:config/fragment_provider",
         "//src/main/java/com/google/devtools/build/lib/analysis:configured_target",
         "//src/main/java/com/google/devtools/build/lib/analysis:rule_definition_environment",
         "//src/main/java/com/google/devtools/build/lib/analysis:transitive_info_provider",
@@ -44,6 +43,7 @@
         "//src/main/java/net/starlark/java/eval",
         "//src/main/java/net/starlark/java/syntax",
         "//src/main/protobuf:xcode_java_proto",
+        "//third_party:auto_value",
         "//third_party:flogger",
         "//third_party:guava",
         "//third_party:jsr305",
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
index 36d5390..fc5c841 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCommon.java
@@ -34,6 +34,7 @@
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.analysis.actions.FileWriteAction;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.analysis.config.CompilationMode;
 import com.google.devtools.build.lib.analysis.starlark.StarlarkRuleContext;
 import com.google.devtools.build.lib.analysis.stringtemplate.ExpansionException;
@@ -801,6 +802,22 @@
       ImmutableSet<String> unsupportedFeatures,
       CcToolchainProvider toolchain,
       CppSemantics cppSemantics) {
+    return configureFeaturesOrReportRuleError(
+        ruleContext,
+        ruleContext.getConfiguration(),
+        requestedFeatures,
+        unsupportedFeatures,
+        toolchain,
+        cppSemantics);
+  }
+
+  public static FeatureConfiguration configureFeaturesOrReportRuleError(
+      RuleContext ruleContext,
+      BuildConfiguration buildConfiguration,
+      ImmutableSet<String> requestedFeatures,
+      ImmutableSet<String> unsupportedFeatures,
+      CcToolchainProvider toolchain,
+      CppSemantics cppSemantics) {
     cppSemantics.validateLayeringCheckFeatures(
         ruleContext, /* aspectDescriptor= */ null, toolchain, ImmutableSet.of());
     try {
@@ -808,7 +825,7 @@
           requestedFeatures,
           unsupportedFeatures,
           toolchain,
-          ruleContext.getFragment(CppConfiguration.class));
+          buildConfiguration.getFragment(CppConfiguration.class));
     } catch (EvalException e) {
       ruleContext.ruleError(e.getMessage());
       return FeatureConfiguration.EMPTY;
@@ -871,7 +888,8 @@
             .addAll(ImmutableSet.of(cppConfiguration.getCompilationMode().toString()))
             .addAll(DEFAULT_ACTION_CONFIGS)
             .addAll(requestedFeatures)
-            .addAll(toolchain.getFeatures().getDefaultFeaturesAndActionConfigs());
+            .addAll(toolchain.getFeatures().getDefaultFeaturesAndActionConfigs())
+            .addAll(cppConfiguration.getAppleBitcodeMode().getFeatureNames());
 
     if (!cppConfiguration.dontEnableHostNonhost()) {
       if (toolchain.isToolConfiguration()) {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java
index ecdd821..30c1972 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.lib.rules.cpp;
 
+import com.google.common.base.Preconditions;
 import com.google.common.base.Verify;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -32,9 +33,15 @@
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.lib.packages.BazelModuleContext;
+import com.google.devtools.build.lib.rules.apple.AppleCommandLineOptions;
+import com.google.devtools.build.lib.rules.apple.AppleCommandLineOptions.AppleBitcodeMode;
+import com.google.devtools.build.lib.rules.apple.AppleConfiguration;
+import com.google.devtools.build.lib.rules.apple.AppleConfiguration.AppleCpus;
+import com.google.devtools.build.lib.rules.apple.ApplePlatform;
 import com.google.devtools.build.lib.starlarkbuildapi.cpp.CppConfigurationApi;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import java.util.EnumMap;
 import javax.annotation.Nullable;
 import net.starlark.java.annot.StarlarkMethod;
 import net.starlark.java.eval.EvalException;
@@ -47,7 +54,7 @@
  * architecture, target architecture, compiler version, and a standard library version.
  */
 @Immutable
-@RequiresOptions(options = {CppOptions.class})
+@RequiresOptions(options = {AppleCommandLineOptions.class, CppOptions.class})
 public final class CppConfiguration extends Fragment
     implements CppConfigurationApi<InvalidConfigurationException> {
   /**
@@ -181,6 +188,7 @@
   private final boolean isToolConfigurationDoNotUseWillBeRemovedFor129045294;
 
   private final boolean appleGenerateDsym;
+  private final AppleBitcodeMode appleBitcodeMode;
 
   public CppConfiguration(BuildOptions options) throws InvalidConfigurationException {
     CppOptions cppOptions = options.get(CppOptions.class);
@@ -288,6 +296,20 @@
     this.appleGenerateDsym =
         (cppOptions.appleGenerateDsym
             || (cppOptions.appleEnableAutoDsymDbg && compilationMode == CompilationMode.DBG));
+    this.appleBitcodeMode =
+        computeAppleBitcodeMode(options.get(AppleCommandLineOptions.class), commonOptions);
+  }
+
+  private static AppleBitcodeMode computeAppleBitcodeMode(
+      AppleCommandLineOptions options, CoreOptions commonOptions) {
+    ApplePlatform.PlatformType applePlatformType =
+        Preconditions.checkNotNull(options.applePlatformType, "applePlatformType");
+    AppleCpus appleCpus = AppleCpus.create(options, commonOptions);
+    EnumMap<ApplePlatform.PlatformType, AppleBitcodeMode> platformBitcodeModes =
+        AppleConfiguration.collectBitcodeModes(options.appleBitcodeMode);
+
+    return AppleConfiguration.getAppleBitcodeMode(
+        applePlatformType, appleCpus, platformBitcodeModes);
   }
 
   /** Returns the label of the <code>cc_compiler</code> rule for the C++ configuration. */
@@ -823,4 +845,15 @@
     checkInExpandedApiAllowlist(thread, "fission_active_for_current_compilation_mode");
     return fissionIsActiveForCurrentCompilationMode();
   }
+
+  /**
+   * Returns the bitcode mode to use for compilation.
+   *
+   * <p>Users can control bitcode mode using the {@code apple_bitcode} build flag, but bitcode will
+   * be disabled for all simulator architectures regardless of this flag.
+   */
+  @Override
+  public AppleBitcodeMode getAppleBitcodeMode() {
+    return appleBitcodeMode;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleBinary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleBinary.java
index 4ad4233..49e1604 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/AppleBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/AppleBinary.java
@@ -47,6 +47,7 @@
 import com.google.devtools.build.lib.rules.apple.ApplePlatform.PlatformType;
 import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper;
 import com.google.devtools.build.lib.rules.cpp.CcToolchainProvider;
+import com.google.devtools.build.lib.rules.cpp.CppConfiguration;
 import com.google.devtools.build.lib.rules.objc.AppleDebugOutputsInfo.OutputType;
 import com.google.devtools.build.lib.rules.objc.CompilationSupport.ExtraLinkArgs;
 import com.google.devtools.build.lib.rules.objc.MultiArchBinarySupport.DependencySpecificConfiguration;
@@ -263,6 +264,8 @@
         dependencySpecificConfigurations) {
       AppleConfiguration childAppleConfig =
           dependencySpecificConfiguration.config().getFragment(AppleConfiguration.class);
+      CppConfiguration childCppConfig =
+          dependencySpecificConfiguration.config().getFragment(CppConfiguration.class);
       ObjcConfiguration childObjcConfig =
           dependencySpecificConfiguration.config().getFragment(ObjcConfiguration.class);
       IntermediateArtifacts intermediateArtifacts =
@@ -273,7 +276,7 @@
               dependencySpecificConfiguration.config());
       String arch = childAppleConfig.getSingleArchitecture();
 
-      if (childAppleConfig.getBitcodeMode() == AppleBitcodeMode.EMBEDDED) {
+      if (childCppConfig.getAppleBitcodeMode() == AppleBitcodeMode.EMBEDDED) {
         Artifact bitcodeSymbol = intermediateArtifacts.bitcodeSymbolMap();
         builder.addOutput(arch, OutputType.BITCODE_SYMBOLS, bitcodeSymbol);
       }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java
index 8831de7..d669850 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/CompilationSupport.java
@@ -540,11 +540,6 @@
         ImmutableSet.<String>builder()
             .addAll(ccToolchain.getFeatures().getDefaultFeaturesAndActionConfigs())
             .addAll(ACTIVATED_ACTIONS)
-            .addAll(
-                ruleContext
-                    .getFragment(AppleConfiguration.class)
-                    .getBitcodeMode()
-                    .getFeatureNames())
             .add(CppRuleClasses.LANG_OBJC)
             .add(CppRuleClasses.DEPENDENCY_FILE)
             .add(CppRuleClasses.INCLUDE_PATHS)
@@ -568,11 +563,6 @@
     if (configuration.getFragment(ObjcConfiguration.class).generateLinkmap()) {
       activatedCrosstoolSelectables.add(GENERATE_LINKMAP_FEATURE_NAME);
     }
-    AppleBitcodeMode bitcodeMode =
-        configuration.getFragment(AppleConfiguration.class).getBitcodeMode();
-    if (bitcodeMode != AppleBitcodeMode.NONE) {
-      activatedCrosstoolSelectables.addAll(bitcodeMode.getFeatureNames());
-    }
     // Add a feature identifying the Xcode version so CROSSTOOL authors can enable flags for
     // particular versions of Xcode. To ensure consistency across platforms, use exactly two
     // components in the version number.
@@ -608,6 +598,7 @@
 
     return CcCommon.configureFeaturesOrReportRuleError(
         ruleContext,
+        buildConfiguration,
         activatedCrosstoolSelectables.build(),
         disabledFeatures.build(),
         ccToolchain,
@@ -1210,6 +1201,7 @@
       // Formed from existing label, just replacing name with artifact name.
     }
 
+    CppConfiguration cppConfiguration = buildConfiguration.getFragment(CppConfiguration.class);
     CcLinkingHelper executableLinkingHelper =
         new CcLinkingHelper(
                 ruleContext,
@@ -1221,7 +1213,7 @@
                 toolchain,
                 toolchain.getFdoContext(),
                 buildConfiguration,
-                buildConfiguration.getFragment(CppConfiguration.class),
+                cppConfiguration,
                 ruleContext.getSymbolGenerator(),
                 TargetUtils.getExecutionInfo(
                     ruleContext.getRule(), ruleContext.isAllowTagsPropagation()))
@@ -1261,7 +1253,7 @@
       linkerOutputs.add(linkmap);
     }
 
-    if (appleConfiguration.getBitcodeMode() == AppleBitcodeMode.EMBEDDED) {
+    if (cppConfiguration.getAppleBitcodeMode() == AppleBitcodeMode.EMBEDDED) {
       Artifact bitcodeSymbolMap = intermediateArtifacts.bitcodeSymbolMap();
       extensionBuilder
           .setBitcodeSymbolMap(bitcodeSymbolMap)
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/BUILD b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/BUILD
index 3670256..cb80321 100644
--- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/BUILD
@@ -25,6 +25,7 @@
         "//src/main/java/com/google/devtools/build/lib/collect/nestedset",
         "//src/main/java/com/google/devtools/build/lib/packages/semantics",
         "//src/main/java/com/google/devtools/build/lib/starlarkbuildapi",
+        "//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/apple",
         "//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/core",
         "//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/go",
         "//src/main/java/com/google/devtools/build/lib/starlarkbuildapi/platform",
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CppConfigurationApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CppConfigurationApi.java
index 9273186..5614801 100644
--- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CppConfigurationApi.java
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CppConfigurationApi.java
@@ -17,6 +17,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.devtools.build.docgen.annot.DocCategory;
 import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.starlarkbuildapi.apple.AppleBitcodeModeApi;
 import net.starlark.java.annot.StarlarkBuiltin;
 import net.starlark.java.annot.StarlarkMethod;
 import net.starlark.java.eval.EvalException;
@@ -111,4 +112,13 @@
       useStarlarkThread = true)
   boolean fissionActiveForCurrentCompilationModeStarlark(StarlarkThread thread)
       throws EvalException;
+
+  @StarlarkMethod(
+      name = "apple_bitcode_mode",
+      doc =
+          "Returns the Bitcode mode to use for compilation steps.<p>This field is only valid for"
+              + " Apple, and only for device builds; for simulator builds, it always returns "
+              + "<code>'none'</code>.",
+      structField = true)
+  AppleBitcodeModeApi getAppleBitcodeMode();
 }
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/mock/osx_cc_toolchain_config.bzl b/src/test/java/com/google/devtools/build/lib/packages/util/mock/osx_cc_toolchain_config.bzl
index 64771fd..6cec27f 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/mock/osx_cc_toolchain_config.bzl
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/mock/osx_cc_toolchain_config.bzl
@@ -5942,6 +5942,8 @@
             flag_sets = [
                 flag_set(
                     actions = [
+                        ACTION_NAMES.c_compile,
+                        ACTION_NAMES.cpp_compile,
                         ACTION_NAMES.objc_compile,
                         ACTION_NAMES.objcpp_compile,
                         "objc-executable",
@@ -7369,6 +7371,8 @@
             flag_sets = [
                 flag_set(
                     actions = [
+                        ACTION_NAMES.c_compile,
+                        ACTION_NAMES.cpp_compile,
                         ACTION_NAMES.objc_compile,
                         ACTION_NAMES.objcpp_compile,
                         "objc-executable",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/CcLibraryTest.java b/src/test/java/com/google/devtools/build/lib/rules/objc/CcLibraryTest.java
new file mode 100644
index 0000000..cdaafce
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/objc/CcLibraryTest.java
@@ -0,0 +1,192 @@
+// Copyright 2021 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.rules.objc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.devtools.build.lib.actions.CommandAction;
+import com.google.devtools.build.lib.analysis.util.ScratchAttributeWriter;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for cc_library with Apple-specific logic. */
+@RunWith(JUnit4.class)
+public class CcLibraryTest extends ObjcRuleTestCase {
+  @Override
+  protected ScratchAttributeWriter createLibraryTargetWriter(String labelString) {
+    return ScratchAttributeWriter.fromLabelString(this, "cc_library", labelString);
+  }
+
+  @Test
+  public void testCompilationActionsWithEmbeddedBitcode() throws Exception {
+    useConfiguration("--apple_platform_type=ios", "--cpu=ios_arm64", "--apple_bitcode=embedded");
+    createLibraryTargetWriter("//cc:lib")
+        .setAndCreateFiles("srcs", "a.cc", "b.cc", "private.h")
+        .setAndCreateFiles("hdrs", "c.h")
+        .write();
+
+    CommandAction compileActionA = compileAction("//cc:lib", "a.o");
+
+    assertThat(compileActionA.getArguments()).contains("-fembed-bitcode");
+  }
+
+  @Test
+  public void testCompilationActionsWithEmbeddedBitcodeMarkers() throws Exception {
+    useConfiguration(
+        "--apple_platform_type=ios", "--cpu=ios_arm64", "--apple_bitcode=embedded_markers");
+
+    createLibraryTargetWriter("//cc:lib")
+        .setAndCreateFiles("srcs", "a.cc", "b.cc", "private.h")
+        .setAndCreateFiles("hdrs", "c.h")
+        .write();
+
+    CommandAction compileActionA = compileAction("//cc:lib", "a.o");
+
+    assertThat(compileActionA.getArguments()).contains("-fembed-bitcode-marker");
+  }
+
+  @Test
+  public void testCompilationActionsWithNoBitcode() throws Exception {
+    useConfiguration("--apple_platform_type=ios", "--cpu=ios_arm64", "--apple_bitcode=none");
+
+    createLibraryTargetWriter("//cc:lib")
+        .setAndCreateFiles("srcs", "a.cc", "b.cc", "private.h")
+        .setAndCreateFiles("hdrs", "c.h")
+        .write();
+
+    CommandAction compileActionA = compileAction("//cc:lib", "a.o");
+
+    assertThat(compileActionA.getArguments()).doesNotContain("-fembed-bitcode");
+    assertThat(compileActionA.getArguments()).doesNotContain("-fembed-bitcode-marker");
+  }
+
+  /** Tests that bitcode is disabled for simulator builds even if enabled by flag. */
+  @Test
+  public void testCompilationActionsWithBitcode_simulator() throws Exception {
+    useConfiguration("--apple_platform_type=ios", "--cpu=ios_x86_64", "--apple_bitcode=embedded");
+
+    createLibraryTargetWriter("//cc:lib")
+        .setAndCreateFiles("srcs", "a.cc", "b.cc", "private.h")
+        .setAndCreateFiles("hdrs", "c.h")
+        .write();
+
+    CommandAction compileActionA = compileAction("//cc:lib", "a.o");
+
+    assertThat(compileActionA.getArguments()).doesNotContain("-fembed-bitcode");
+    assertThat(compileActionA.getArguments()).doesNotContain("-fembed-bitcode-marker");
+  }
+
+  @Test
+  public void testCompilationActionsWithEmbeddedBitcodeForMultiplePlatformsWithMatch()
+      throws Exception {
+    useConfiguration(
+        "--apple_platform_type=ios",
+        "--cpu=ios_arm64",
+        "--apple_bitcode=ios=embedded",
+        "--apple_bitcode=watchos=embedded");
+    createLibraryTargetWriter("//cc:lib")
+        .setAndCreateFiles("srcs", "a.cc", "b.cc", "private.h")
+        .setAndCreateFiles("hdrs", "c.h")
+        .write();
+
+    CommandAction compileActionA = compileAction("//cc:lib", "a.o");
+
+    assertThat(compileActionA.getArguments()).contains("-fembed-bitcode");
+  }
+
+  @Test
+  public void testCompilationActionsWithEmbeddedBitcodeForMultiplePlatformsWithoutMatch()
+      throws Exception {
+    useConfiguration(
+        "--apple_platform_type=ios",
+        "--cpu=ios_arm64",
+        "--apple_bitcode=tvos=embedded",
+        "--apple_bitcode=watchos=embedded");
+    createLibraryTargetWriter("//cc:lib")
+        .setAndCreateFiles("srcs", "a.cc", "b.cc", "private.h")
+        .setAndCreateFiles("hdrs", "c.h")
+        .write();
+
+    CommandAction compileActionA = compileAction("//cc:lib", "a.o");
+
+    assertThat(compileActionA.getArguments()).doesNotContain("-fembed-bitcode");
+    assertThat(compileActionA.getArguments()).doesNotContain("-fembed-bitcode-marker");
+  }
+
+  @Test
+  public void testLaterBitcodeOptionsOverrideEarlierOptionsForSamePlatform() throws Exception {
+    useConfiguration(
+        "--apple_platform_type=ios",
+        "--cpu=ios_arm64",
+        "--apple_bitcode=ios=embedded",
+        "--apple_bitcode=ios=embedded_markers");
+    createLibraryTargetWriter("//cc:lib")
+        .setAndCreateFiles("srcs", "a.cc", "b.cc", "private.h")
+        .setAndCreateFiles("hdrs", "c.h")
+        .write();
+
+    CommandAction compileActionA = compileAction("//cc:lib", "a.o");
+
+    assertThat(compileActionA.getArguments()).doesNotContain("-fembed-bitcode");
+    assertThat(compileActionA.getArguments()).contains("-fembed-bitcode-marker");
+  }
+
+  @Test
+  public void testLaterBitcodeOptionWithoutPlatformOverridesEarlierOptionWithPlatform()
+      throws Exception {
+    useConfiguration(
+        "--apple_platform_type=ios",
+        "--cpu=ios_arm64",
+        "--apple_bitcode=ios=embedded",
+        "--apple_bitcode=embedded_markers");
+    createLibraryTargetWriter("//cc:lib")
+        .setAndCreateFiles("srcs", "a.cc", "b.cc", "private.h")
+        .setAndCreateFiles("hdrs", "c.h")
+        .write();
+
+    CommandAction compileActionA = compileAction("//cc:lib", "a.o");
+
+    assertThat(compileActionA.getArguments()).doesNotContain("-fembed-bitcode");
+    assertThat(compileActionA.getArguments()).contains("-fembed-bitcode-marker");
+  }
+
+  @Test
+  public void testLaterPlatformBitcodeOptionWithPlatformOverridesEarlierOptionWithoutPlatform()
+      throws Exception {
+    useConfiguration(
+        "--apple_platform_type=ios",
+        "--cpu=ios_arm64",
+        "--apple_bitcode=embedded",
+        "--apple_bitcode=ios=embedded_markers");
+    createLibraryTargetWriter("//cc:lib")
+        .setAndCreateFiles("srcs", "a.cc", "b.cc", "private.h")
+        .setAndCreateFiles("hdrs", "c.h")
+        .write();
+
+    CommandAction compileActionA = compileAction("//cc:lib", "a.o");
+
+    assertThat(compileActionA.getArguments()).doesNotContain("-fembed-bitcode");
+    assertThat(compileActionA.getArguments()).contains("-fembed-bitcode-marker");
+  }
+
+  @Test
+  public void testGenerateDsymFlagPropagatesToCcLibraryFeature() throws Exception {
+    useConfiguration("--apple_generate_dsym");
+    createLibraryTargetWriter("//cc/lib").setList("srcs", "a.cc").write();
+    CommandAction compileAction = compileAction("//cc/lib", "a.o");
+    assertThat(compileAction.getArguments()).contains("-DDUMMY_GENERATE_DSYM_FILE");
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcLibraryTest.java b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcLibraryTest.java
index e10a048..b839ca1 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcLibraryTest.java
@@ -2087,16 +2087,6 @@
   }
 
   @Test
-  public void testGenerateDsymFlagPropagatesToCcLibraryFeature() throws Exception {
-    useConfiguration("--apple_generate_dsym");
-    ScratchAttributeWriter.fromLabelString(this, "cc_library", "//cc/lib")
-        .setList("srcs", "a.cc")
-        .write();
-    CommandAction compileAction = compileAction("//cc/lib", "a.o");
-    assertThat(compileAction.getArguments()).contains("-DDUMMY_GENERATE_DSYM_FILE");
-  }
-
-  @Test
   public void testArtifactsToAlwaysBuild() throws Exception {
     ConfiguredTarget x =
         scratchConfiguredTarget(