feat: add attrs/fields for the runtime's ABI flags (#2390)

This adds attributes and fields for storing the runtimes ABI flags
value, i.e. `sys.abiflags`.

For freethreaded interpreters, the abi flags contain `t`, which is used
creating e.g.
virtualenvs.

The attribute can be directly specified, or if it's not, will be
computed based on the
`--py_freethreaded` flag.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e896c54..88d17de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -51,6 +51,8 @@
 * (toolchain) Support for freethreaded Python toolchains is now available. Use
   the config flag `//python/config_settings:py_freethreaded` to toggle the
   selection of the free-threaded toolchains.
+* (toolchain) {obj}`py_runtime.abi_flags` attribute and
+  {obj}`PyRuntimeInfo.abi_flags` field added.
 
 {#v0-0-0-removed}
 ### Removed
diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel
index 1e972c5..6bb6a9f 100644
--- a/python/private/BUILD.bazel
+++ b/python/private/BUILD.bazel
@@ -475,6 +475,7 @@
     srcs = ["py_runtime_rule.bzl"],
     deps = [
         ":attributes_bzl",
+        ":flags_bzl",
         ":py_internal_bzl",
         ":py_runtime_info_bzl",
         ":reexports_bzl",
diff --git a/python/private/py_runtime_info.bzl b/python/private/py_runtime_info.bzl
index 359a9e7..ff95c4a 100644
--- a/python/private/py_runtime_info.bzl
+++ b/python/private/py_runtime_info.bzl
@@ -67,7 +67,8 @@
         bootstrap_template = None,
         interpreter_version_info = None,
         stage2_bootstrap_template = None,
-        zip_main_template = None):
+        zip_main_template = None,
+        abi_flags = ""):
     if (interpreter_path and interpreter) or (not interpreter_path and not interpreter):
         fail("exactly one of interpreter or interpreter_path must be specified")
 
@@ -105,6 +106,7 @@
         stub_shebang = DEFAULT_STUB_SHEBANG
 
     return {
+        "abi_flags": abi_flags,
         "bootstrap_template": bootstrap_template,
         "coverage_files": coverage_files,
         "coverage_tool": coverage_tool,
@@ -133,6 +135,11 @@
 """,
     init = _PyRuntimeInfo_init,
     fields = {
+        "abi_flags": """
+:type: str
+
+The runtime's ABI flags, i.e. `sys.abiflags`.
+""",
         "bootstrap_template": """
 :type: File
 
diff --git a/python/private/py_runtime_rule.bzl b/python/private/py_runtime_rule.bzl
index ba9b36d..746cd19 100644
--- a/python/private/py_runtime_rule.bzl
+++ b/python/private/py_runtime_rule.bzl
@@ -17,6 +17,7 @@
 load("@bazel_skylib//lib:paths.bzl", "paths")
 load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
 load(":attributes.bzl", "NATIVE_RULES_ALLOWLIST_ATTRS")
+load(":flags.bzl", "FreeThreadedFlag")
 load(":py_internal.bzl", "py_internal")
 load(":py_runtime_info.bzl", "DEFAULT_BOOTSTRAP_TEMPLATE", "DEFAULT_STUB_SHEBANG", "PyRuntimeInfo")
 load(":reexports.bzl", "BuiltinPyRuntimeInfo")
@@ -101,6 +102,13 @@
             interpreter_version_info["minor"],
         )
 
+    abi_flags = ctx.attr.abi_flags
+    if abi_flags == "<AUTO>":
+        abi_flags = ""
+        if ctx.attr._py_freethreaded_flag[BuildSettingInfo].value == FreeThreadedFlag.YES:
+            abi_flags += "t"
+
+    # Args common to both BuiltinPyRuntimeInfo and PyRuntimeInfo
     py_runtime_info_kwargs = dict(
         interpreter_path = interpreter_path or None,
         interpreter = interpreter,
@@ -120,6 +128,7 @@
         pyc_tag = pyc_tag,
         stage2_bootstrap_template = ctx.file.stage2_bootstrap_template,
         zip_main_template = ctx.file.zip_main_template,
+        abi_flags = abi_flags,
     ))
 
     if not IS_BAZEL_7_OR_HIGHER:
@@ -179,6 +188,14 @@
 """,
     fragments = ["py"],
     attrs = dicts.add(NATIVE_RULES_ALLOWLIST_ATTRS, {
+        "abi_flags": attr.string(
+            default = "<AUTO>",
+            doc = """
+The runtime's ABI flags, i.e. `sys.abiflags`.
+
+If not set, then it will be set based on flags.
+""",
+        ),
         "bootstrap_template": attr.label(
             allow_single_file = True,
             default = DEFAULT_BOOTSTRAP_TEMPLATE,
@@ -335,6 +352,9 @@
 :::
 """,
         ),
+        "_py_freethreaded_flag": attr.label(
+            default = "//python/config_settings:py_freethreaded",
+        ),
         "_python_version_flag": attr.label(
             default = "//python/config_settings:python_version",
         ),