First step towards the migration of --desugar_java8_libs to starlark. This cl is NOOP. #FLAG_CHANGE

PiperOrigin-RevId: 945839632
Change-Id: I1332095d7eae7a7c66000b3cc7fddcd1998d1c04
diff --git a/flags/BUILD b/flags/BUILD
new file mode 100644
index 0000000..e6f363b
--- /dev/null
+++ b/flags/BUILD
@@ -0,0 +1,211 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "int_flag", "string_flag", "string_list_flag")
+load("//flags:flags_wrapper.bzl", "flags_wrapper_macro")
+
+package(
+    default_applicable_licenses = ["//:license"],
+    default_visibility = ["//:__subpackages__"],
+)
+
+licenses(["notice"])
+
+filegroup(
+    name = "all_files",
+    srcs = glob(["**"]),
+    visibility = [
+        "//:__subpackages__",
+    ],
+)
+
+filegroup(
+    name = "empty",
+    srcs = [],
+)
+
+bzl_library(
+    name = "bzl",
+    srcs = glob(["*.bzl"]),
+    visibility = ["//:__subpackages__"],
+    deps = [
+        "//rules:visibility_bzl",
+        "@bazel_features//:features",
+        "@bazel_skylib//rules:common_settings",
+    ],
+)
+
+bool_flag(
+    name = "desugar_for_android",
+    build_setting_default = True,
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+string_flag(
+    name = "runfiles_root_prefix",
+    build_setting_default = "google3/",
+    visibility = ["//visibility:public"],
+)
+
+string_list_flag(
+    name = "android_platforms",
+    build_setting_default = [],
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
+    name = "desugar_java8_libs",
+    build_setting_default = True,
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
+    name = "android_resource_shrinking",
+    build_setting_default = False,
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
+    name = "experimental_android_resource_path_shortening",
+    build_setting_default = False,
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+string_flag(
+    name = "android_manifest_merger_order",
+    build_setting_default = "alphabetical",
+    scope = "universal",
+    values = [
+        "alphabetical",
+        "alphabetical_by_configuration",
+        "dependency",
+    ],
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
+    name = "experimental_android_compress_java_resources",
+    build_setting_default = False,
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
+    name = "android_fixed_resource_neverlinking",
+    build_setting_default = True,
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
+    name = "internal_persistent_busybox_tools",
+    build_setting_default = False,
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
+    name = "internal_persistent_multiplex_busybox_tools",
+    build_setting_default = False,
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
+    name = "internal_persistent_android_dex_desugar",
+    build_setting_default = False,
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
+    name = "internal_persistent_multiplex_android_dex_desugar",
+    build_setting_default = False,
+    visibility = ["//visibility:public"],
+)
+
+label_flag(
+    name = "optimizing_dexer",
+    build_setting_default = ":empty",
+    visibility = ["//visibility:public"],
+)
+
+string_flag(
+    name = "android_compiler",
+    build_setting_default = "null",
+    visibility = ["//visibility:public"],
+)
+
+string_list_flag(
+    name = "dexopts_supported_in_dexmerger",
+    build_setting_default = [
+        "--minimal-main-dex",
+        "--set-max-idx-number",
+    ],
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+string_list_flag(
+    name = "dexopts_supported_in_dexsharder",
+    build_setting_default = ["--minimal-main-dex"],
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
+    name = "experimental_android_resource_shrinking",
+    build_setting_default = False,
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
+    name = "experimental_android_resource_cycle_shrinking",
+    build_setting_default = False,
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+string_flag(
+    name = "android_manifest_merger",
+    build_setting_default = "android",
+    scope = "universal",
+    values = [
+        "legacy",
+        "android",
+        "force_android",
+    ],
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
+    name = "experimental_android_library_exports_manifest_default",
+    build_setting_default = False,
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+label_flag(
+    name = "legacy_main_dex_list_generator",
+    build_setting_default = ":empty",
+    visibility = ["//visibility:public"],
+)
+
+bool_flag(
+    name = "experimental_get_android_java_resources_from_optimized_jar",
+    build_setting_default = False,
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+int_flag(
+    name = "incremental_dexing_after_proguard",
+    build_setting_default = 50,
+    scope = "universal",
+    visibility = ["//visibility:public"],
+)
+
+flags_wrapper_macro(
+    name = "flags_wrapper",
+)
diff --git a/flags/flags_wrapper.bzl b/flags/flags_wrapper.bzl
new file mode 100644
index 0000000..f82e922
--- /dev/null
+++ b/flags/flags_wrapper.bzl
@@ -0,0 +1,46 @@
+# Copyright 2026 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.
+"""Custom flags wrapper."""
+
+load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
+
+WrappedFlagsInfo = provider(
+    doc = "Provides all flags",
+    fields = dict(
+        flags = "Map of flag name to BuildSettingInfo",
+    ),
+)
+
+def _flags_wrapper_impl(ctx):
+    return [WrappedFlagsInfo(
+        flags = {
+            t.label.name: t[BuildSettingInfo]
+            for t in ctx.attr.targets
+            if BuildSettingInfo in t
+        },
+    )]
+
+flags_wrapper = rule(
+    implementation = _flags_wrapper_impl,
+    attrs = dict(
+        targets = attr.label_list(),
+    ),
+)
+
+def flags_wrapper_macro(name = "flags_wrapper"):
+    flags_wrapper(
+        name = name,
+        targets = native.existing_rules().keys(),
+        visibility = ["//visibility:public"],
+    )
diff --git a/rules/android_binary/attrs.bzl b/rules/android_binary/attrs.bzl
index b6cd67c..fffa5b7 100644
--- a/rules/android_binary/attrs.bzl
+++ b/rules/android_binary/attrs.bzl
@@ -274,6 +274,7 @@
         _attrs.DATA_CONTEXT,
         _attrs.ANDROID_TOOLCHAIN_ATTRS,
         _attrs.AUTOMATIC_EXEC_GROUPS_ENABLED,
+        _attrs.FLAGS_ATTRS,
     ),
     # TODO(b/167599192): don't override manifest attr to remove .xml file restriction.
     manifest = attr.label(
diff --git a/rules/android_binary/impl.bzl b/rules/android_binary/impl.bzl
index 5461f17..2b99ff1 100644
--- a/rules/android_binary/impl.bzl
+++ b/rules/android_binary/impl.bzl
@@ -46,7 +46,7 @@
     "utils",
 )
 load("//rules:visibility.bzl", "PROJECT_VISIBILITY")
-load("//rules/flags:flags.bzl", _flags = "flags")
+load("//rules/flags:flags.bzl", "read_possibly_native_flag", _flags = "flags")
 load("@rules_java//java/common:java_common.bzl", "java_common")
 load("@rules_java//java/common:java_info.bzl", "JavaInfo")
 load("@rules_java//java/common:java_plugin_info.bzl", "JavaPluginInfo")
@@ -292,14 +292,14 @@
     if proguard_output_map:
         # Proguard map from preprocessing will be merged with Proguard map for desugared
         # library.
-        if should_optimize_dex and ctx.fragments.android.desugar_java8_libs:
+        if should_optimize_dex and read_possibly_native_flag(ctx, "desugar_java8_libs"):
             postprocessing_output_map = _dex.get_dx_artifact(ctx, "_proguard_output_for_desugared_library.map")
             final_proguard_output_map = ctx.actions.declare_file(ctx.label.name + "_proguard.map")
         elif should_optimize_dex:
             # No desugared library, Proguard map from postprocessing is the final Proguard map.
             postprocessing_output_map = ctx.actions.declare_file(ctx.label.name + "_proguard.map")
             final_proguard_output_map = postprocessing_output_map
-        elif ctx.fragments.android.desugar_java8_libs:
+        elif read_possibly_native_flag(ctx, "desugar_java8_libs"):
             # No postprocessing, Proguard map from merging with the desugared library map is the
             # final Proguard map.
             postprocessing_output_map = proguard_output_map
@@ -357,7 +357,7 @@
             toolchain_type = ANDROID_TOOLCHAIN_TYPE,
         )
 
-    if ctx.fragments.android.desugar_java8_libs and classes_dex_zip.extension == "zip":
+    if read_possibly_native_flag(ctx, "desugar_java8_libs") and classes_dex_zip.extension == "zip":
         final_classes_dex_zip = _dex.get_dx_artifact(ctx, "final_classes_dex.zip")
 
         java8_legacy_dex, java8_legacy_dex_map = _dex.get_java8_legacy_dex_and_map(
diff --git a/rules/android_binary/r8.bzl b/rules/android_binary/r8.bzl
index 651dd23..4c63947 100644
--- a/rules/android_binary/r8.bzl
+++ b/rules/android_binary/r8.bzl
@@ -34,7 +34,7 @@
     "utils",
 )
 load("//rules:visibility.bzl", "PROJECT_VISIBILITY")
-load("//rules/flags:flags.bzl", _flags = "flags")
+load("//rules/flags:flags.bzl", "read_possibly_native_flag", _flags = "flags")
 
 visibility(PROJECT_VISIBILITY)
 
@@ -131,7 +131,7 @@
     args.add("--build-metadata-output", build_metadata_output)
 
     r8_inputs = [android_jar, deploy_jar] + proguard_specs
-    if ctx.fragments.android.desugar_java8_libs and desugared_lib_config:
+    if read_possibly_native_flag(ctx, "desugar_java8_libs") and desugared_lib_config:
         args.add("--desugared-lib", desugared_lib_config)
         r8_inputs.append(desugared_lib_config)
 
@@ -151,7 +151,7 @@
     # backport references, but does NOT include the j$.* implementation classes
     # in its output. Append the prebuilt desugared library DEX so the j$.*
     # classes are available at runtime.
-    if ctx.fragments.android.desugar_java8_libs and desugared_lib_config:
+    if read_possibly_native_flag(ctx, "desugar_java8_libs") and desugared_lib_config:
         final_classes_dex_zip = ctx.actions.declare_file(ctx.label.name + "_final_dexes.zip")
         java8_legacy_dex = utils.only(
             get_android_toolchain(ctx).java8_legacy_dex.files.to_list(),
diff --git a/rules/attrs.bzl b/rules/attrs.bzl
index fbe58fe..d80a0a0 100644
--- a/rules/attrs.bzl
+++ b/rules/attrs.bzl
@@ -13,6 +13,7 @@
 # limitations under the License.
 """Common attributes for Android rules."""
 
+load("//flags:flags_wrapper.bzl", "WrappedFlagsInfo")
 load("//providers:providers.bzl", "ApkInfo")
 load("//rules:android_split_transition.bzl", "android_transition")
 load("//rules:visibility.bzl", "PROJECT_VISIBILITY")
@@ -312,6 +313,10 @@
         _desugared_java8_legacy_apis = attr.label(
             default = Label("//tools/android:desugared_java8_legacy_apis"),
         ),
+        _wrapped_flags = attr.label(
+            default = Label("//flags:flags_wrapper"),
+            providers = [WrappedFlagsInfo],
+        ),
         _dexbuilder = attr.label(
             cfg = "exec",
             default = Label("//tools/android:dexbuilder"),
@@ -508,6 +513,13 @@
     _use_auto_exec_groups = attr.bool(default = True),
 )
 
+_FLAGS_ATTRS = dict(
+    _wrapped_flags = attr.label(
+        default = Label("//flags:flags_wrapper"),
+        providers = [WrappedFlagsInfo],
+    ),
+)
+
 attrs = struct(
     ANDROID_SDK = _ANDROID_SDK,
     compilation_attributes = _compilation_attributes,
@@ -518,4 +530,5 @@
     add = _add,
     replace = _replace,
     AUTOMATIC_EXEC_GROUPS_ENABLED = _AUTOMATIC_EXEC_GROUPS_ENABLED,
+    FLAGS_ATTRS = _FLAGS_ATTRS,
 )
diff --git a/rules/desugar.bzl b/rules/desugar.bzl
index 74348f8..d75133f 100644
--- a/rules/desugar.bzl
+++ b/rules/desugar.bzl
@@ -14,6 +14,7 @@
 """Bazel Desugar Commands."""
 
 load("//rules:visibility.bzl", "PROJECT_VISIBILITY")
+load("//rules/flags:flags.bzl", "read_possibly_native_flag")
 
 visibility(PROJECT_VISIBILITY)
 
@@ -56,7 +57,7 @@
     input_file_deps = [input]
     if library_desugaring:
         args.add("--emit_dependency_metadata_as_needed")
-        if ctx.fragments.android.desugar_java8_libs:
+        if read_possibly_native_flag(ctx, "desugar_java8_libs"):
             args.add("--desugar_supported_core_libs")
 
     # Unconditionally add --desugared_lib_config. This matches the behavior of tools/android/d8_desugar.sh.
diff --git a/rules/dex_desugar_aspect.bzl b/rules/dex_desugar_aspect.bzl
index 32a73f4..2d4850b 100644
--- a/rules/dex_desugar_aspect.bzl
+++ b/rules/dex_desugar_aspect.bzl
@@ -296,6 +296,7 @@
         },
         _attrs.ANDROID_SDK,
         _min_sdk_version.attrs,
+        _attrs.FLAGS_ATTRS,
     ),
     fragments = ["android"],
     toolchains = [
diff --git a/rules/flags/BUILD b/rules/flags/BUILD
index ed32594..9c305a4 100644
--- a/rules/flags/BUILD
+++ b/rules/flags/BUILD
@@ -1,7 +1,7 @@
 # Flags for Android rules and mobile-install
 
 load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
-load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "int_flag", "string_flag", "string_list_flag")
+load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
 load("//rules/flags:additional_flags.bzl", "additional_flags")
 load("//rules/flags:configurations.bzl", "configurations")
 load("//rules/flags:flag_defs.bzl", "define_flags")
@@ -28,183 +28,17 @@
     visibility = ["//:__subpackages__"],
     deps = [
         "//rules:visibility_bzl",
+        "@bazel_features//:features",
         "@bazel_skylib//rules:common_settings",
     ],
 )
 
-bool_flag(
-    name = "desugar_for_android",
-    build_setting_default = True,
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
 string_flag(
     name = "runfiles_root_prefix",
     build_setting_default = "rules_android/",
     visibility = ["//visibility:public"],
 )
 
-string_list_flag(
-    name = "android_platforms",
-    build_setting_default = [],
-    visibility = ["//visibility:public"],
-)
-
-bool_flag(
-    name = "desugar_java8_libs",
-    build_setting_default = False,
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
-bool_flag(
-    name = "android_resource_shrinking",
-    build_setting_default = False,
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
-bool_flag(
-    name = "experimental_android_resource_path_shortening",
-    build_setting_default = False,
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
-string_flag(
-    name = "android_manifest_merger_order",
-    build_setting_default = "alphabetical",
-    scope = "universal",
-    values = [
-        "alphabetical",
-        "alphabetical_by_configuration",
-        "dependency",
-    ],
-    visibility = ["//visibility:public"],
-)
-
-bool_flag(
-    name = "experimental_android_compress_java_resources",
-    build_setting_default = False,
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
-bool_flag(
-    name = "android_fixed_resource_neverlinking",
-    build_setting_default = True,
-    visibility = ["//visibility:public"],
-)
-
-bool_flag(
-    name = "internal_persistent_busybox_tools",
-    build_setting_default = False,
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
-bool_flag(
-    name = "internal_persistent_multiplex_busybox_tools",
-    build_setting_default = False,
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
-bool_flag(
-    name = "internal_persistent_android_dex_desugar",
-    build_setting_default = False,
-    visibility = ["//visibility:public"],
-)
-
-bool_flag(
-    name = "internal_persistent_multiplex_android_dex_desugar",
-    build_setting_default = False,
-    visibility = ["//visibility:public"],
-)
-
-label_flag(
-    name = "optimizing_dexer",
-    build_setting_default = ":empty",
-    visibility = ["//visibility:public"],
-)
-
-string_flag(
-    name = "android_compiler",
-    build_setting_default = "null",
-    visibility = ["//visibility:public"],
-)
-
-string_list_flag(
-    name = "dexopts_supported_in_dexmerger",
-    build_setting_default = [
-        "--minimal-main-dex",
-        "--set-max-idx-number",
-    ],
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
-string_list_flag(
-    name = "dexopts_supported_in_dexsharder",
-    build_setting_default = ["--minimal-main-dex"],
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
-bool_flag(
-    name = "experimental_android_resource_shrinking",
-    build_setting_default = False,
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
-bool_flag(
-    name = "experimental_android_resource_cycle_shrinking",
-    build_setting_default = False,
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
-string_flag(
-    name = "android_manifest_merger",
-    build_setting_default = "android",
-    scope = "universal",
-    values = [
-        "legacy",
-        "android",
-        "force_android",
-    ],
-    visibility = ["//visibility:public"],
-)
-
-bool_flag(
-    name = "experimental_android_library_exports_manifest_default",
-    build_setting_default = False,
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
-label_flag(
-    name = "legacy_main_dex_list_generator",
-    build_setting_default = ":empty",
-    visibility = ["//visibility:public"],
-)
-
-bool_flag(
-    name = "experimental_get_android_java_resources_from_optimized_jar",
-    build_setting_default = False,
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
-int_flag(
-    name = "incremental_dexing_after_proguard",
-    build_setting_default = 50,
-    scope = "universal",
-    visibility = ["//visibility:public"],
-)
-
 configurations()
 
 define_flags()
diff --git a/rules/flags/flags.bzl b/rules/flags/flags.bzl
index 6a8d4f7..7af21b9 100644
--- a/rules/flags/flags.bzl
+++ b/rules/flags/flags.bzl
@@ -13,6 +13,8 @@
 # limitations under the License.
 """Bazel Flags."""
 
+load("@bazel_features//private:util.bzl", "lt")  # buildifier: disable=bzl-visibility
+load("//flags:flags_wrapper.bzl", "WrappedFlagsInfo")
 load("//rules:utils.bzl", "utils")
 load("//rules:visibility.bzl", "PROJECT_VISIBILITY")
 
@@ -187,6 +189,51 @@
         return flags[FlagsInfo]
     return flags[0][FlagsInfo]
 
+_POSSIBLY_NATIVE_FLAGS = {
+    "desugar_java8_libs": (lambda ctx: ctx.fragments.android.desugar_java8_libs, "starlark"),
+}
+
+def read_possibly_native_flag(ctx, flag_name):
+    """
+    Canonical API for reading a Android build flag.
+
+    Flags might be defined in Starlark or native-Bazel. This function reads flags
+    from the correct source based on supporting Bazel version and --incompatible*
+    flags that disable native references.
+
+    Args:
+        ctx: Rule's configuration context.
+        flag_name: Name of the flag to read, without preceding "--".
+
+    Returns:
+        The flag's value.
+    """
+
+    # Bazel 9.1+ can disable these fragments with --incompatible_remove_ctx_android_fragment.
+    # Disabling them means bazel expects Android to read Starlark flags.
+    use_native_def = hasattr(ctx.fragments, "android")
+
+    # Developer override to force the Starlark definition for testing.
+    # This would allow us to migrate flags gradually from native to Starlark with flag aliases.
+    if _POSSIBLY_NATIVE_FLAGS[flag_name][1] == "starlark":
+        use_native_def = False
+
+    if lt("9.1.0"):
+        use_native_def = True
+
+    if use_native_def:
+        return _POSSIBLY_NATIVE_FLAGS[flag_name][0](ctx)
+    else:
+        # First check the new wrapped_flags attribute
+        if hasattr(ctx.attr, "_wrapped_flags") and ctx.attr._wrapped_flags:
+            wrapped_flags = ctx.attr._wrapped_flags[WrappedFlagsInfo].flags
+            if flag_name in wrapped_flags:
+                return wrapped_flags[flag_name].value
+        elif hasattr(ctx.fragments, "android"):
+            return _POSSIBLY_NATIVE_FLAGS[flag_name][0](ctx)
+
+    fail("Unable to read flag value for " + flag_name)
+
 flags = struct(
     DEFINE_bool = bool_flag,
     DEFINE_bool_group = bool_flag_group,