// Copyright 2019 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.packages.semantics;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Interner;
import com.google.devtools.build.lib.concurrent.BlazeInterners;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.common.options.BoolOrEnumConverter;
import com.google.devtools.common.options.Converters.CommaSeparatedNonEmptyOptionListConverter;
import com.google.devtools.common.options.Converters.CommaSeparatedOptionListConverter;
import com.google.devtools.common.options.Converters.CommaSeparatedOptionSetConverter;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionDocumentationCategory;
import com.google.devtools.common.options.OptionEffectTag;
import com.google.devtools.common.options.OptionMetadataTag;
import com.google.devtools.common.options.Options;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsClass;
import com.google.protobuf.ByteString;
import java.util.List;
import java.util.TreeMap;
import java.util.TreeSet;
import net.starlark.java.eval.StarlarkSemantics;

/**
 * Options that affect the semantics of Bazel's build language.
 *
 * <p>These are injected into Skyframe (as an instance of {@link StarlarkSemantics}) when a new
 * build invocation occurs. Changing these options between builds will therefore trigger a
 * reevaluation of everything that depends on the Starlark interpreter &mdash; in particular,
 * evaluation of all BUILD and .bzl files.
 *
 * <p><em>To add a new option, update the following:</em>
 *
 * <ul>
 *   <li>Add a new {@code @Option}-annotated field to this class. The field name and default value
 *       should be the same as in {@link StarlarkSemantics}, and the option name in the annotation
 *       should be that name written in snake_case. Add a line to set the new field in {@link
 *       #toStarlarkSemantics}. New options should always default to {@code false} and have no
 *       observable effect when disabled.
 *   <li>Define a new {@code StarlarkSemantics.Key} or {@code StarlarkSemantics} boolean flag
 *       identifier.
 *   <li>Add a line to set the new field in both {@link ConsistencyTest#buildRandomOptions} and
 *       {@link ConsistencyTest#buildRandomSemantics}.
 *   <li>Update manual documentation in site/docs/skylark/backward-compatibility.md. Also remember
 *       to update this when flipping a flag's default value.
 *   <li>Boolean semantic flags can toggle StarlarkMethod-annotated Java methods (or their
 *       parameters) on or off, making them selectively invisible to Starlark. To do this, add a new
 *       entry to {@link BuildLanguageOptions}, then specify the identifier in {@link
 *       net.starlark.java.annot.StarlarkMethod#enableOnlyWithFlag} or {@link
 *       net.starlark.java.annot.StarlarkMethod#disableWithFlag}.
 * </ul>
 */
@OptionsClass
public abstract class BuildLanguageOptions extends OptionsBase {
  @Option(
      name = "incompatible_stop_exporting_language_modules",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "If enabled, certain language-specific modules (such as `cc_common`) are unavailable in"
              + " user .bzl files and may only be called from their respective rules repositories.")
  public abstract boolean getIncompatibleStopExportingLanguageModules();

  // TODO(#11437): Delete the special empty string value so that it's on unconditionally.
  @Option(
      name = "experimental_builtins_bzl_path",
      defaultValue = "%bundled%",
      documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
      effectTags = {OptionEffectTag.LOSES_INCREMENTAL_STATE, OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help =
          "This flag tells Bazel how to find the \"@_builtins\" .bzl files that govern how "
              + "predeclared symbols for BUILD and .bzl files are defined. This flag is only "
              + "intended for Bazel developers, to help when writing @_builtins .bzl code. "
              + "Ordinarily this value is set to \"%bundled%\", which means to use the "
              + "builtins_bzl/ directory packaged in the Bazel binary. However, it can be set to "
              + "the path (relative to the root of the current workspace) of an alternate "
              + "builtins_bzl/ directory, such as one in a Bazel source tree workspace. A literal "
              + "value of \"%workspace%\" is equivalent to the relative package path of "
              + "builtins_bzl/ within a Bazel source tree; this should only be used when running "
              + "Bazel within its own source tree. Finally, a value of the empty string disables "
              + "the builtins injection mechanism entirely.")
  public abstract String getExperimentalBuiltinsBzlPath();

  @Option(
      name = "experimental_builtins_dummy",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help = "Enables an internal dummy symbol used to test builtins injection.")
  public abstract boolean getExperimentalBuiltinsDummy();

  @Option(
      name = "experimental_builtins_injection_override",
      converter = CommaSeparatedNonEmptyOptionListConverter.class,
      defaultValue = "null",
      allowMultiple = true,
      documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help =
          "A comma-separated list of symbol names prefixed by a + or - character, indicating which"
              + " symbols from `@_builtins//:exports.bzl` to inject, overriding their default"
              + " injection status. Precisely, this works as follows. Each dict key of"
              + " `exported_toplevels` or `exported_rules` has the form `foo`, `+foo`, or `-foo`."
              + " The first two forms mean it gets injected by default, while the last form means"
              + " it does not get injected by default. In the first case (unprefixed), the default"
              + " is absolute and cannot be overridden. Otherwise, we then consult this options"
              + " list, and if we see foo occur here, we take the prefix of its last occurrence and"
              + " use that to decide whether or not to inject. It is a no-op to specify an unknown"
              + " symbol, or to attempt to not inject a symbol that occurs unprefixed in a dict"
              + " key.")
  public abstract List<String> getExperimentalBuiltinsInjectionOverride();

  @Option(
      name = "experimental_bzl_visibility",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help =
          "If enabled, adds a `visibility()` function that .bzl files may call during top-level"
              + " evaluation to set their visibility for the purpose of load() statements.")
  public abstract boolean getExperimentalBzlVisibility();

  @Option(
      name = "experimental_single_package_toolchain_binding",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "If enabled, the register_toolchain function may not include target patterns which may "
              + "refer to more than one package.")
  public abstract boolean getExperimentalSinglePackageToolchainBinding();

  @Option(
      name = "allow_experimental_loads",
      documentationCategory = OptionDocumentationCategory.INPUT_STRICTNESS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      defaultValue = "false",
      help =
          "If enabled, issue only a warning instead of an error for loads of experimental .bzls.")
  public abstract boolean getAllowExperimentalLoads();

  @Option(
      name = "check_bzl_visibility",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.INPUT_STRICTNESS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      help = "If disabled, .bzl load visibility errors are demoted to warnings.")
  public abstract boolean getCheckBzlVisibility();

  @Option(
      name = "experimental_cc_skylark_api_enabled_packages",
      converter = CommaSeparatedOptionListConverter.class,
      defaultValue = "",
      documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help =
          "Passes list of packages that can use the C++ Starlark API. Don't enable this flag yet, "
              + "we will be making breaking changes.")
  public abstract List<String> getExperimentalCcStarlarkApiEnabledPackages();

  @Option(
      name = "experimental_enable_android_migration_apis",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = OptionEffectTag.BUILD_FILE_SEMANTICS,
      help = "If set to true, enables the APIs required to support the Android Starlark migration.")
  public abstract boolean getExperimentalEnableAndroidMigrationApis();

  @Option(
      name = "experimental_enable_first_class_macros",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = OptionEffectTag.BUILD_FILE_SEMANTICS,
      help = "If set to true, enables the `macro()` construct for defining symbolic macros.")
  public abstract boolean getExperimentalEnableFirstClassMacros();

  @Option(
      name = "experimental_enable_scl_dialect",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = OptionEffectTag.BUILD_FILE_SEMANTICS,
      // TODO(brandjon): point to more extensive user documentation somewhere
      help = "If set to true, .scl files may be used in load() statements.")
  public abstract boolean getExperimentalEnableSclDialect();

  public abstract void setExperimentalEnableSclDialect(boolean value);

  @Option(
      name = "experimental_isolated_extension_usages",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = OptionEffectTag.LOADING_AND_ANALYSIS,
      help =
          "If true, enables the `isolate` parameter in the"
              + " [`use_extension`](https://bazel.build/rules/lib/globals/module#use_extension)"
              + " function.")
  public abstract boolean getExperimentalIsolatedExtensionUsages();

  @Option(
      name = "incompatible_no_implicit_watch_label",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      metadataTags = OptionMetadataTag.INCOMPATIBLE_CHANGE,
      effectTags = OptionEffectTag.LOADING_AND_ANALYSIS,
      help =
          "If true, then methods on `repository_ctx` that are passed a Label will no"
              + " longer automatically watch the file under that label for changes even if"
              + " `watch = \"no\"`, and `repository_ctx.path` no longer"
              + " causes the returned path to be watched. Use `repository_ctx.watch` instead.")
  public abstract boolean getIncompatibleNoImplicitWatchLabel();

  @Option(
      name = "incompatible_stop_exporting_build_file_path",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "If set to true, deprecated ctx.build_file_path will not be available. ctx.label.package"
              + " + '/BUILD' can be used instead.")
  public abstract boolean getIncompatibleStopExportingBuildFilePath();

  @Option(
      name = "experimental_google_legacy_api",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help =
          "If set to true, exposes a number of experimental pieces of Starlark build API "
              + "pertaining to Google legacy code.")
  public abstract boolean getExperimentalGoogleLegacyApi();

  public abstract void setExperimentalGoogleLegacyApi(boolean value);

  @Option(
      name = "experimental_platforms_api",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help =
          "If set to true, enables a number of platform-related Starlark APIs useful for "
              + "debugging.")
  public abstract boolean getExperimentalPlatformsApi();

  @Option(
      name = "experimental_cc_shared_library",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS, OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {
        OptionMetadataTag.EXPERIMENTAL,
      },
      help =
          "If set to true, rule attributes and Starlark API methods needed for the rule "
              + "cc_shared_library will be available")
  public abstract boolean getExperimentalCcSharedLibrary();

  @Option(
      name = "experimental_repo_remote_exec",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS, OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {
        OptionMetadataTag.EXPERIMENTAL,
      },
      help = "If set to true, repository_rule gains some remote execution capabilities.")
  public abstract boolean getExperimentalRepoRemoteExec();

  @Option(
      name = "experimental_disable_external_package",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS, OptionEffectTag.LOSES_INCREMENTAL_STATE},
      metadataTags = {
        OptionMetadataTag.EXPERIMENTAL,
      },
      help =
          "If set to true, the auto-generated //external package will not be available anymore. "
              + "Bazel will still be unable to parse the file 'external/BUILD', but globs reaching "
              + "into external/ from the unnamed package will work.")
  public abstract boolean getExperimentalDisableExternalPackage();

  @Option(
      name = "experimental_sibling_repository_layout",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {
        OptionEffectTag.ACTION_COMMAND_LINES,
        OptionEffectTag.BAZEL_INTERNAL_CONFIGURATION,
        OptionEffectTag.LOADING_AND_ANALYSIS,
        OptionEffectTag.LOSES_INCREMENTAL_STATE
      },
      metadataTags = {
        OptionMetadataTag.EXPERIMENTAL,
      },
      help =
          "If set to true, non-main repositories are planted as symlinks to the main repository in"
              + " the execution root. That is, all repositories are direct children of the"
              + " $output_base/execution_root directory. This has the side effect of freeing up"
              + " $output_base/execution_root/__main__/external for the real top-level 'external' "
              + "directory.")
  public abstract boolean getExperimentalSiblingRepositoryLayout();

  @Option(
      name = "incompatible_allow_tags_propagation",
      oldName = "experimental_allow_tags_propagation",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {
        OptionMetadataTag.EXPERIMENTAL,
      },
      help =
          "If set to true, tags will be propagated from a target to the actions' execution"
              + " requirements; otherwise tags are not propagated. See"
              + " https://github.com/bazelbuild/bazel/issues/8830 for details.")
  public abstract boolean getExperimentalAllowTagsPropagation();

  @Option(
      name = "incompatible_always_check_depset_elements",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "Check the validity of elements added to depsets, in all constructors. Elements must be"
              + " immutable, but historically the depset(direct=...) constructor forgot to check."
              + " Use tuples instead of lists in depset elements."
              + " See https://github.com/bazelbuild/bazel/issues/10313 for details.")
  public abstract boolean getIncompatibleAlwaysCheckDepsetElements();

  @Option(
      name = "incompatible_disable_target_default_provider_fields",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "If set to true, disable the ability to utilize the default provider via field "
              + "syntax. Use provider-key syntax instead. For example, instead of using "
              + "`ctx.attr.dep.files` to access `files`, utilize `ctx.attr.dep[DefaultInfo].files "
              + "See "
              + "https://github.com/bazelbuild/bazel/issues/9014 for details.")
  public abstract boolean getIncompatibleDisableTargetDefaultProviderFields();

  // DO NOT inline this constant: it's used by copybara to alter the default value. It's different
  // in Google and in OSS Bazel.
  private static final String DISALLOW_EMPTY_GLOB_DEFAULT = "true";

  @Option(
      name = "incompatible_disallow_empty_glob",
      defaultValue = DISALLOW_EMPTY_GLOB_DEFAULT,
      category = "incompatible changes",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help = "If set to true, the default value of the `allow_empty` argument of glob() is False.")
  public abstract boolean getIncompatibleDisallowEmptyGlob();

  @Option(
      name = "incompatible_package_group_has_public_syntax",
      defaultValue = FlagConstants.DEFAULT_INCOMPATIBLE_PACKAGE_GROUP_HAS_PUBLIC_SYNTAX,
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "In package_group's `packages` attribute, allows writing \"public\" or \"private\" to"
              + " refer to all packages or no packages respectively.")
  public abstract boolean getIncompatiblePackageGroupHasPublicSyntax();

  @Option(
      name = "incompatible_fix_package_group_reporoot_syntax",
      defaultValue = FlagConstants.DEFAULT_INCOMPATIBLE_FIX_PACKAGE_GROUP_REPOROOT_SYNTAX,
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "In package_group's `packages` attribute, changes the meaning of the value \"//...\" to"
              + " refer to all packages in the current repository instead of all packages in any"
              + " repository. You can use the special value \"public\" in place of \"//...\" to"
              + " obtain the old behavior. This flag requires"
              + " that --incompatible_package_group_has_public_syntax also be enabled.")
  public abstract boolean getIncompatibleFixPackageGroupReporootSyntax();

  @Option(
      name = "incompatible_no_attr_license",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help = "If set to true, disables the function `attr.license`.")
  public abstract boolean getIncompatibleNoAttrLicense();

  @Option(
      name = "incompatible_no_implicit_file_export",
      defaultValue = FlagConstants.DEFAULT_INCOMPATIBLE_NO_IMPLICIT_FILE_EXPORT,
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "If set, (used) source files are package private unless exported explicitly. See "
              + "https://github.com/bazelbuild/proposals/blob/master/designs/"
              + "2019-10-24-file-visibility.md")
  public abstract boolean getIncompatibleNoImplicitFileExport();

  @Option(
      name = "incompatible_no_rule_outputs_param",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help = "If set to true, disables the `outputs` parameter of the `rule()` Starlark function.")
  public abstract boolean getIncompatibleNoRuleOutputsParam();

  @Option(
      name = "incompatible_run_shell_command_string",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help = "If set to true, the command parameter of actions.run_shell will only accept string")
  public abstract boolean getIncompatibleRunShellCommandString();

  @Option(
      name = "incompatible_require_mnemonic_for_run_actions",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "If set to true, ctx.actions.run and ctx.actions.run_shell will require an explicit"
              + " mnemonic")
  public abstract boolean getIncompatibleRequireMnemonicForRunActions();

  /** Used in an integration test to confirm that flags are visible to the interpreter. */
  @Option(
      name = "internal_starlark_flag_test_canary",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
      effectTags = {OptionEffectTag.UNKNOWN})
  public abstract boolean getInternalStarlarkFlagTestCanary();

  @Option(
      name = "incompatible_do_not_split_linking_cmdline",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.BUILD_TIME_OPTIMIZATION,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "When true, Bazel no longer modifies command line flags used for linking, and also "
              + "doesn't selectively decide which flags go to the param file and which don't.  "
              + "See https://github.com/bazelbuild/bazel/issues/7670 for details.")
  public abstract boolean getIncompatibleDoNotSplitLinkingCmdline();

  @Option(
      name = "incompatible_unambiguous_label_stringification",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "When true, Bazel will stringify the label @//foo:bar to @//foo:bar, instead of"
              + " //foo:bar. This only affects the behavior of str(), the % operator, and so on;"
              + " the behavior of repr() is unchanged. See"
              + " https://github.com/bazelbuild/bazel/issues/15916 for more information.")
  public abstract boolean getIncompatibleUnambiguousLabelStringification();

  @Option(
      name = "incompatible_java_info_merge_runtime_module_flags",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
      effectTags = {OptionEffectTag.UNKNOWN},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "If set to true, the JavaInfo constructor will merge add_exports and "
              + "add_opens of runtime_deps in addition to deps and exports.")
  public abstract boolean getIncompatibleJavaInfoMergeRuntimeModuleFlags();

  @Option(
      name = "max_computation_steps",
      defaultValue = "0",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      help =
          "The maximum number of Starlark computation steps that may be executed by a BUILD file"
              + " (zero means no limit).")
  public abstract long getMaxComputationSteps();

  @Option(
      name = "nested_set_depth_limit",
      defaultValue = "3500",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      help =
          "The maximum depth of the graph internal to a depset (also known as NestedSet), above"
              + " which the depset() constructor will fail.")
  public abstract int getNestedSetDepthLimit();

  @Option(
      name = "incompatible_disable_starlark_host_transitions",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      help =
          "If set to true, rule attributes cannot set 'cfg = \"host\"'. Rules should set "
              + "'cfg = \"exec\"' instead.")
  public abstract boolean getIncompatibleDisableStarlarkHostTransitions();

  @Option(
      name = "incompatible_disable_objc_library_transition",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "Disable objc_library's custom transition and inherit "
              + "from the top level target instead (No-op in Bazel)")
  public abstract boolean getIncompatibleDisableObjcLibraryTransition();

  @Option(
      name = "incompatible_disable_transitions_on",
      converter = CommaSeparatedOptionSetConverter.class,
      defaultValue = "",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE, OptionMetadataTag.NON_CONFIGURABLE},
      help =
          "A comma-separated list of flags that cannot be used in transitions inputs or outputs.")
  public abstract ImmutableList<String> getIncompatibleDisableTransitionsOn();

  @Option(
      name = "add_go_exec_groups_to_binary_rules",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
      effectTags = OptionEffectTag.LOADING_AND_ANALYSIS,
      help =
          "When enabled, 'go_build' and 'go_link' execution groups are added to binary rules like"
              + " 'cc_binary'.")
  public abstract boolean getAddGoExecGroupsToBinaryRules();

  // remove after Bazel LTS in Nov 2023
  @Option(
      name = "incompatible_fail_on_unknown_attributes",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help = "If enabled, targets that have unknown attributes set to None fail.")
  public abstract boolean getIncompatibleFailOnUnknownAttributes();

  // Flip when dependencies to rules_* repos are upgraded and protobuf registers toolchains
  @Option(
      name = "incompatible_enable_proto_toolchain_resolution",
      defaultValue = FlagConstants.DEFAULT_INCOMPATIBLE_ENABLE_PROTO_TOOLCHAIN_RESOLUTION,
      documentationCategory = OptionDocumentationCategory.TOOLCHAIN,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help = "If true, proto lang rules define toolchains from protobuf repository.")
  public abstract boolean getIncompatibleEnableProtoToolchainResolution();

  // Flip when java_single_jar is feature complete
  @Option(
      name = "incompatible_disable_non_executable_java_binary",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help = "If true, java_binary is always executable. create_executable attribute is removed.")
  public abstract boolean getIncompatibleDisableNonExecutableJavaBinary();

  @Option(
      name = "experimental_rule_extension_api",
      defaultValue = FlagConstants.DEFAULT_EXPERIMENTAL_RULE_EXTENSION_API,
      documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help = "Enable experimental rule extension API and subrule APIs")
  public abstract boolean getExperimentalRuleExtensionApi();

  @Option(
      name = "experimental_dormant_deps",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help =
          " If set to true, attr.label(materializer=), attr(for_dependency_resolution=),"
              + " attr.dormant_label(), attr.dormant_label_list() and"
              + " rule(for_dependency_resolution=) are allowed.")
  public abstract boolean getExperimentalDormantDeps();

  public abstract void setExperimentalDormantDeps(boolean value);

  @Option(
      name = "experimental_starlark_type_syntax",
      defaultValue = FlagConstants.DEFAULT_EXPERIMENTAL_STARLARK_TYPE_SYNTAX,
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help =
          """
          Enables type annotations and related syntax in .bzl files. Locations of files where \
          these are allowed is further restricted by `--experimental_starlark_types_allowed_paths`.
          Type syntax is never permitted in .scl files regardless of this flag.
          """)
  public abstract boolean getExperimentalStarlarkTypeSyntax();

  @Option(
      name = "experimental_starlark_static_type_checking",
      defaultValue = FlagConstants.DEFAULT_EXPERIMENTAL_STARLARK_TYPE_CHECKING,
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help =
          "Enables static type checking in files and functions that contain type annotations or "
              + "related syntax.")
  public abstract boolean getExperimentalStarlarkStaticTypeChecking();

  @Option(
      name = "experimental_starlark_dynamic_type_checking",
      defaultValue = FlagConstants.DEFAULT_EXPERIMENTAL_STARLARK_TYPE_CHECKING,
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help =
          "Enables dynamic type checking of arguments and return values for functions that contain "
              + "type annotations or related syntax.")
  public abstract boolean getExperimentalStarlarkDynamicTypeChecking();

  @Option(
      name = "experimental_starlark_type_checking",
      defaultValue = "null",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      expansion = {
        "--experimental_starlark_static_type_checking",
        "--experimental_starlark_dynamic_type_checking",
      },
      help =
          "Enables both static and dynamic type checking in files and functions that contain type"
              + " annotations or related syntax. This is an expansion flag for"
              + " --experimental_starlark_static_type_checking and "
              + "--experimental_starlark_dynamic_type_checking."
              + " (When both flags are disabled, Bazel is more forgiving of invalid types in type"
              + " annotations.)")
  public abstract Void getExperimentalStarlarkTypeChecking();

  // TODO: b/350661266 - Delete this flag.
  @Option(
      name = "experimental_starlark_types",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help =
          "No-op. Previously used as --experimental_starlark_type_syntax +"
              + " --experimental_starlark_type_checking")
  public abstract boolean getExperimentalStarlarkTypes();

  @Option(
      name = "experimental_starlark_types_allowed_paths",
      converter = CommaSeparatedOptionListConverter.class,
      defaultValue = FlagConstants.DEFAULT_EXPERIMENTAL_STARLARK_TYPES_ALLOWED_PATHS,
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help = "List of canonical Label prefixes under which Starlark type annotations are allowed.")
  public abstract List<String> getExperimentalStarlarkTypesAllowedPaths();

  @Option(
      name = "incompatible_enable_deprecated_label_apis",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      help =
          "If enabled, certain deprecated APIs (native.repository_name, Label.workspace_name,"
              + " Label.relative) can be used.")
  public abstract boolean getEnableDeprecatedLabelApis();

  @Option(
      name = "incompatible_disallow_ctx_resolve_tools",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "If set to true, calling the deprecated ctx.resolve_tools API always fails. Uses of this"
              + " API should be replaced by an executable or tools argument to ctx.actions.run or"
              + " ctx.actions.run_shell.")
  public abstract boolean getIncompatibleDisallowCtxResolveTools();

  @Option(
      name = "incompatible_simplify_unconditional_selects_in_rule_attrs",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "If true, simplify configurable rule attributes which contain only unconditional selects;"
              + " for example, if [\"a\"] + select(\"//conditions:default\", [\"b\"]) is assigned"
              + " to a rule attribute, it is stored as [\"a\", \"b\"]. This option does not affect"
              + " attributes of symbolic macros or attribute default values.")
  public abstract boolean getIncompatibleSimplifyUnconditionalSelectsInRuleAttrs();

  @Option(
      name = "experimental_enable_starlark_set",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help = "If true, enable the set data type and set() constructor in Starlark.")
  public abstract boolean getExperimentalEnableStarlarkSet();

  @Option(
      name = "incompatible_locations_prefers_executable",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "Whether a target that provides an executable expands to the executable rather than the"
              + " files in `DefaultInfo.files` under $(locations ...) expansion if the"
              + " number of files is not 1.")
  public abstract boolean getIncompatibleLocationsPrefersExecutable();

  @Option(
      name = "internal_starlark_utf_8_byte_strings",
      defaultValue = "true",
      documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
      metadataTags = {OptionMetadataTag.HIDDEN},
      help =
          "Internal use only. Forces the Starlark implementation to operate on strings as raw"
              + " UTF-8 byte arrays, matching Bazel's internal string encoding.")
  public abstract boolean getInternalStarlarkUtf8ByteStrings();

  /** An enum for specifying different modes for UTF-8 checking of Starlark files. */
  public enum Utf8EnforcementMode {
    OFF,
    WARNING,
    ERROR;

    /** Converts to {@link Utf8EnforcementMode}. */
    public static class Converter extends BoolOrEnumConverter<Utf8EnforcementMode> {
      public Converter() {
        super(
            Utf8EnforcementMode.class,
            "UTF-8 enforcement mode",
            Utf8EnforcementMode.ERROR,
            Utf8EnforcementMode.OFF);
      }
    }
  }

  @Option(
      name = "incompatible_enforce_starlark_utf8",
      defaultValue = "warning",
      converter = Utf8EnforcementMode.Converter.class,
      documentationCategory = OptionDocumentationCategory.INPUT_STRICTNESS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "If enabled (or set to 'error'), fail if Starlark files are not UTF-8 encoded. If set to"
              + " 'warning', emit a warning instead. If set to 'off', Bazel assumes that Starlark"
              + " files are UTF-8 encoded but does not verify this assumption. Note that Starlark"
              + " files which are not UTF-8 encoded can cause Bazel to behave inconsistently.")
  public abstract Utf8EnforcementMode getIncompatibleEnforceStarlarkUtf8();

  @Option(
      name = "experimental_repository_ctx_execute_wasm",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help = "If true enables the repository_ctx `load_wasm` and `execute_wasm` methods.")
  public abstract boolean getRepositoryCtxExecuteWasm();

  @Option(
      name = "experimental_repository_ctx_wasm_compilation",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.EXPERIMENTAL},
      help = "If true enables compilation of WebAssembly modules.")
  public abstract boolean getRepositoryCtxWasmCompilation();

  @Option(
      name = "incompatible_resolve_select_keys_eagerly",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
      help =
          "If enabled, string keys in dicts passed to select() in .bzl files are immediately"
              + " resolved to Labels relative to the file instead of being interpreted relative to"
              + " the BUILD file they are ultimately loaded from.")
  public abstract boolean getIncompatibleResolveSelectKeysEagerly();

  @Option(
      name = "force_starlark_stack_trace",
      defaultValue = "false",
      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
      help =
          "If --force_starlark_stack_trace=true, Starlark stace traces will always be printed from"
              + " calls to fail(), including those normally supressed with fail(..., stack_trace ="
              + " False)")
  public abstract boolean getForceStarlarkStackTrace();

  /**
   * An interner to reduce the number of StarlarkSemantics instances. A single Blaze instance should
   * never accumulate a large number of these and being able to shortcut on object identity makes a
   * comparison later much faster. In particular, the semantics become part of the
   * MethodDescriptorKey in CallExpression and are thus compared for every function call.
   */
  private static final Interner<StarlarkSemantics> INTERNER = BlazeInterners.newWeakInterner();

  private interface FlagConsumer {
    <T> FlagConsumer set(StarlarkSemantics.Key<T> key, T value);

    FlagConsumer setBool(String key, boolean enabled);
  }

  private void setFlags(FlagConsumer consumer) {
    var unused =
        consumer
            .setBool(
                INCOMPATIBLE_STOP_EXPORTING_LANGUAGE_MODULES,
                getIncompatibleStopExportingLanguageModules())
            .setBool(INCOMPATIBLE_ALLOW_TAGS_PROPAGATION, getExperimentalAllowTagsPropagation())
            .set(EXPERIMENTAL_BUILTINS_BZL_PATH, getExperimentalBuiltinsBzlPath())
            .setBool(EXPERIMENTAL_BUILTINS_DUMMY, getExperimentalBuiltinsDummy())
            .set(
                EXPERIMENTAL_BUILTINS_INJECTION_OVERRIDE,
                getExperimentalBuiltinsInjectionOverride())
            .setBool(EXPERIMENTAL_BZL_VISIBILITY, getExperimentalBzlVisibility())
            .setBool(ALLOW_EXPERIMENTAL_LOADS, getAllowExperimentalLoads())
            .setBool(CHECK_BZL_VISIBILITY, getCheckBzlVisibility())
            .setBool(
                EXPERIMENTAL_ENABLE_ANDROID_MIGRATION_APIS,
                getExperimentalEnableAndroidMigrationApis())
            .setBool(
                EXPERIMENTAL_SINGLE_PACKAGE_TOOLCHAIN_BINDING,
                getExperimentalSinglePackageToolchainBinding())
            .setBool(
                EXPERIMENTAL_ENABLE_FIRST_CLASS_MACROS, getExperimentalEnableFirstClassMacros())
            .setBool(EXPERIMENTAL_ENABLE_SCL_DIALECT, getExperimentalEnableSclDialect())
            .setBool(
                EXPERIMENTAL_ISOLATED_EXTENSION_USAGES, getExperimentalIsolatedExtensionUsages())
            .setBool(INCOMPATIBLE_NO_IMPLICIT_WATCH_LABEL, getIncompatibleNoImplicitWatchLabel())
            .setBool(EXPERIMENTAL_GOOGLE_LEGACY_API, getExperimentalGoogleLegacyApi())
            .setBool(EXPERIMENTAL_PLATFORMS_API, getExperimentalPlatformsApi())
            .setBool(EXPERIMENTAL_CC_SHARED_LIBRARY, getExperimentalCcSharedLibrary())
            .setBool(EXPERIMENTAL_REPO_REMOTE_EXEC, getExperimentalRepoRemoteExec())
            .setBool(EXPERIMENTAL_DISABLE_EXTERNAL_PACKAGE, getExperimentalDisableExternalPackage())
            .setBool(
                EXPERIMENTAL_SIBLING_REPOSITORY_LAYOUT, getExperimentalSiblingRepositoryLayout())
            .setBool(
                INCOMPATIBLE_ALWAYS_CHECK_DEPSET_ELEMENTS,
                getIncompatibleAlwaysCheckDepsetElements())
            .setBool(INCOMPATIBLE_DISALLOW_EMPTY_GLOB, getIncompatibleDisallowEmptyGlob())
            .setBool(
                INCOMPATIBLE_PACKAGE_GROUP_HAS_PUBLIC_SYNTAX,
                getIncompatiblePackageGroupHasPublicSyntax())
            .setBool(
                INCOMPATIBLE_FIX_PACKAGE_GROUP_REPOROOT_SYNTAX,
                getIncompatibleFixPackageGroupReporootSyntax())
            .setBool(
                INCOMPATIBLE_JAVA_INFO_MERGE_RUNTIME_MODULE_FLAGS,
                getIncompatibleJavaInfoMergeRuntimeModuleFlags())
            .setBool(INCOMPATIBLE_NO_ATTR_LICENSE, getIncompatibleNoAttrLicense())
            .setBool(INCOMPATIBLE_NO_IMPLICIT_FILE_EXPORT, getIncompatibleNoImplicitFileExport())
            .setBool(INCOMPATIBLE_NO_RULE_OUTPUTS_PARAM, getIncompatibleNoRuleOutputsParam())
            .setBool(INCOMPATIBLE_RUN_SHELL_COMMAND_STRING, getIncompatibleRunShellCommandString())
            .setBool(
                INCOMPATIBLE_REQUIRE_MNEMONIC_FOR_RUN_ACTIONS,
                getIncompatibleRequireMnemonicForRunActions())
            .setBool(StarlarkSemantics.PRINT_TEST_MARKER, getInternalStarlarkFlagTestCanary())
            .setBool(
                INCOMPATIBLE_DO_NOT_SPLIT_LINKING_CMDLINE,
                getIncompatibleDoNotSplitLinkingCmdline())
            .set(INCOMPATIBLE_ENFORCE_STARLARK_UTF8, getIncompatibleEnforceStarlarkUtf8())
            .setBool(
                INCOMPATIBLE_UNAMBIGUOUS_LABEL_STRINGIFICATION,
                getIncompatibleUnambiguousLabelStringification())
            .set(MAX_COMPUTATION_STEPS, getMaxComputationSteps())
            .set(NESTED_SET_DEPTH_LIMIT, getNestedSetDepthLimit())
            .setBool(
                INCOMPATIBLE_DISABLE_STARLARK_HOST_TRANSITIONS,
                getIncompatibleDisableStarlarkHostTransitions())
            .setBool(
                INCOMPATIBLE_DISABLE_OBJC_LIBRARY_TRANSITION,
                getIncompatibleDisableObjcLibraryTransition())
            .set(INCOMPATIBLE_DISABLE_TRANSITIONS_OPTIONS, getIncompatibleDisableTransitionsOn())
            .setBool(ADD_GO_EXEC_GROUPS_TO_BINARY_RULES, getAddGoExecGroupsToBinaryRules())
            .setBool(
                INCOMPATIBLE_FAIL_ON_UNKNOWN_ATTRIBUTES, getIncompatibleFailOnUnknownAttributes())
            .setBool(
                INCOMPATIBLE_ENABLE_PROTO_TOOLCHAIN_RESOLUTION,
                getIncompatibleEnableProtoToolchainResolution())
            .setBool(
                INCOMPATIBLE_DISABLE_NON_EXECUTABLE_JAVA_BINARY,
                getIncompatibleDisableNonExecutableJavaBinary())
            .setBool(
                INCOMPATIBLE_DISABLE_TARGET_DEFAULT_PROVIDER_FIELDS,
                getIncompatibleDisableTargetDefaultProviderFields())
            .setBool(EXPERIMENTAL_RULE_EXTENSION_API, getExperimentalRuleExtensionApi())
            .setBool(EXPERIMENTAL_DORMANT_DEPS, getExperimentalDormantDeps())
            .setBool(EXPERIMENTAL_STARLARK_TYPE_SYNTAX, getExperimentalStarlarkTypeSyntax())
            .setBool(
                StarlarkSemantics.EXPERIMENTAL_STARLARK_STATIC_TYPE_CHECKING,
                getExperimentalStarlarkStaticTypeChecking())
            .setBool(
                StarlarkSemantics.EXPERIMENTAL_STARLARK_DYNAMIC_TYPE_CHECKING,
                getExperimentalStarlarkDynamicTypeChecking())
            .set(
                EXPERIMENTAL_STARLARK_TYPES_ALLOWED_PATHS,
                getExperimentalStarlarkTypesAllowedPaths())
            .setBool(INCOMPATIBLE_ENABLE_DEPRECATED_LABEL_APIS, getEnableDeprecatedLabelApis())
            .setBool(
                INCOMPATIBLE_STOP_EXPORTING_BUILD_FILE_PATH,
                getIncompatibleStopExportingBuildFilePath())
            .setBool(
                INCOMPATIBLE_DISALLOW_CTX_RESOLVE_TOOLS, getIncompatibleDisallowCtxResolveTools())
            .setBool(
                INCOMPATIBLE_SIMPLIFY_UNCONDITIONAL_SELECTS_IN_RULE_ATTRS,
                getIncompatibleSimplifyUnconditionalSelectsInRuleAttrs())
            .setBool(
                INCOMPATIBLE_LOCATIONS_PREFERS_EXECUTABLE,
                getIncompatibleLocationsPrefersExecutable())
            .setBool(
                INCOMPATIBLE_RESOLVE_SELECT_KEYS_EAGERLY, getIncompatibleResolveSelectKeysEagerly())
            .setBool(
                StarlarkSemantics.EXPERIMENTAL_ENABLE_STARLARK_SET,
                getExperimentalEnableStarlarkSet())
            .setBool(
                StarlarkSemantics.INTERNAL_BAZEL_ONLY_UTF_8_BYTE_STRINGS,
                getInternalStarlarkUtf8ByteStrings())
            .setBool(EXPERIMENTAL_REPOSITORY_CTX_EXECUTE_WASM, getRepositoryCtxExecuteWasm())
            .setBool(
                EXPERIMENTAL_REPOSITORY_CTX_WASM_COMPILATION, getRepositoryCtxWasmCompilation())
            .setBool(StarlarkSemantics.FORCE_STARLARK_STACK_TRACE, getForceStarlarkStackTrace());
  }

  /** Constructs a {@link StarlarkSemantics} object corresponding to this set of option values. */
  public StarlarkSemantics toStarlarkSemantics() {
    // This function connects command-line flags to their corresponding StarlarkSemantics keys.
    var builder = StarlarkSemantics.builder();
    setFlags(
        new FlagConsumer() {
          @Override
          public <T> FlagConsumer set(StarlarkSemantics.Key<T> key, T value) {
            builder.set(key, value);
            return this;
          }

          @Override
          public FlagConsumer setBool(String key, boolean enabled) {
            builder.setBool(key, enabled);
            return this;
          }
        });
    return INTERNER.intern(builder.build());
  }

  /**
   * Returns a fingerprint of the given {@link StarlarkSemantics} object that can be compared across
   * Bazel versions.
   */
  public static ByteString stableFingerprint(StarlarkSemantics semantics) {
    return FINGERPRINT_CACHE.getUnchecked(semantics);
  }

  // See the comment on INTERNER above, this cache should be very small.
  private static final LoadingCache<StarlarkSemantics, ByteString> FINGERPRINT_CACHE =
      CacheBuilder.newBuilder()
          .weakKeys()
          .build(
              new CacheLoader<>() {
                @Override
                public ByteString load(StarlarkSemantics key) {
                  return computeFingerprint(key);
                }
              });

  private static ByteString computeFingerprint(StarlarkSemantics semantics) {
    var enabledBoolFlags = new TreeSet<String>();
    var otherFlags = new TreeMap<String, String>();
    // We only care about the keys of the map, so the BuildLanguageOptions instance doesn't matter.
    Options.getDefaults(BuildLanguageOptions.class)
        .setFlags(
            new FlagConsumer() {
              @Override
              public <T> FlagConsumer set(StarlarkSemantics.Key<T> key, T ignored) {
                // This assumes that all non-boolean values have a stable and unique string
                // representation, which seems likely to remain true over time.
                otherFlags.put(key.name, semantics.get(key).toString());
                return this;
              }

              @Override
              public FlagConsumer setBool(String key, boolean ignored) {
                // Only fingerprint enabled options so that the fingerprint is stable across Bazel
                // versions that only add new options (e.g., minor and patch versions). This relies
                // on the assumption that disabled new options have no observable effect.
                if (semantics.getBool(key)) {
                  // Trim the leading '+' or '-' from the flag names - the default value doesn't
                  // matter for the current value, which is what we need to fingerprint.
                  enabledBoolFlags.add(key.substring(1));
                }
                return this;
              }
            });
    return ByteString.copyFrom(
        new Fingerprint().addStrings(enabledBoolFlags).addStringMap(otherFlags).digestAndReset());
  }

  // StarlarkSemantics keys used by Bazel
  //
  // Sadly it is impossible to move most of these declarations closer to the
  // code they affect: the toStarlarkSemantics function above must depend
  // on every key that is bound to a command-line flag, which means those keys must
  // live in this package, not in the application logic above.
  // (In principle, a key not associated with a command-line flag may be declared anywhere.)

  // booleans: the +/- prefix indicates the default value (true/false).
  public static final String INCOMPATIBLE_STOP_EXPORTING_LANGUAGE_MODULES =
      "-incompatible_stop_exporting_language_modules";
  public static final String INCOMPATIBLE_ALLOW_TAGS_PROPAGATION =
      "+incompatible_allow_tags_propagation";
  public static final String EXPERIMENTAL_BUILTINS_DUMMY = "-experimental_builtins_dummy";
  public static final String EXPERIMENTAL_BZL_VISIBILITY = "+experimental_bzl_visibility";
  public static final String ALLOW_EXPERIMENTAL_LOADS = "-allow_experimental_loads";
  public static final String CHECK_BZL_VISIBILITY = "+check_bzl_visibility";
  public static final String EXPERIMENTAL_CC_SHARED_LIBRARY = "-experimental_cc_shared_library";
  public static final String EXPERIMENTAL_DISABLE_EXTERNAL_PACKAGE =
      "-experimental_disable_external_package";
  public static final String EXPERIMENTAL_ENABLE_ANDROID_MIGRATION_APIS =
      "-experimental_enable_android_migration_apis";
  public static final String EXPERIMENTAL_SINGLE_PACKAGE_TOOLCHAIN_BINDING =
      "-experimental_single_package_toolchain_binding";
  public static final String EXPERIMENTAL_ENABLE_FIRST_CLASS_MACROS =
      "+experimental_enable_first_class_macros";
  public static final String EXPERIMENTAL_ENABLE_SCL_DIALECT = "+experimental_enable_scl_dialect";
  public static final String EXPERIMENTAL_ISOLATED_EXTENSION_USAGES =
      "-experimental_isolated_extension_usages";
  public static final String INCOMPATIBLE_NO_IMPLICIT_WATCH_LABEL =
      "+incompatible_no_implicit_watch_label";
  public static final String EXPERIMENTAL_GOOGLE_LEGACY_API = "-experimental_google_legacy_api";
  public static final String EXPERIMENTAL_PLATFORMS_API = "-experimental_platforms_api";
  public static final String EXPERIMENTAL_REPO_REMOTE_EXEC = "-experimental_repo_remote_exec";
  public static final String EXPERIMENTAL_SIBLING_REPOSITORY_LAYOUT =
      "-experimental_sibling_repository_layout";
  public static final String INCOMPATIBLE_ALWAYS_CHECK_DEPSET_ELEMENTS =
      "+incompatible_always_check_depset_elements";

  // Note that INCOMPATIBLE_DISALLOW_EMPTY_GLOB differs in Google and in OSS Bazel.
  public static final String INCOMPATIBLE_DISALLOW_EMPTY_GLOB = "+incompatible_disallow_empty_glob";
  public static final String INCOMPATIBLE_PACKAGE_GROUP_HAS_PUBLIC_SYNTAX =
      FlagConstants.INCOMPATIBLE_PACKAGE_GROUP_HAS_PUBLIC_SYNTAX;
  public static final String INCOMPATIBLE_FIX_PACKAGE_GROUP_REPOROOT_SYNTAX =
      FlagConstants.INCOMPATIBLE_FIX_PACKAGE_GROUP_REPOROOT_SYNTAX;
  public static final String INCOMPATIBLE_DO_NOT_SPLIT_LINKING_CMDLINE =
      "+incompatible_do_not_split_linking_cmdline";
  public static final String INCOMPATIBLE_JAVA_INFO_MERGE_RUNTIME_MODULE_FLAGS =
      "-incompatible_java_info_merge_runtime_module_flags";
  public static final String INCOMPATIBLE_NO_ATTR_LICENSE = "+incompatible_no_attr_license";
  public static final String INCOMPATIBLE_NO_IMPLICIT_FILE_EXPORT =
      FlagConstants.DEFAULT_INCOMPATIBLE_NO_IMPLICIT_FILE_EXPORT_NAME;
  public static final String INCOMPATIBLE_NO_RULE_OUTPUTS_PARAM =
      "-incompatible_no_rule_outputs_param";
  public static final String INCOMPATIBLE_RUN_SHELL_COMMAND_STRING =
      "+incompatible_run_shell_command_string";
  public static final String INCOMPATIBLE_REQUIRE_MNEMONIC_FOR_RUN_ACTIONS =
      "-incompatible_require_mnemonic_for_run_actions";

  public static final String INCOMPATIBLE_UNAMBIGUOUS_LABEL_STRINGIFICATION =
      "+incompatible_unambiguous_label_stringification";
  public static final String INCOMPATIBLE_DISABLE_STARLARK_HOST_TRANSITIONS =
      "-incompatible_disable_starlark_host_transitions";
  public static final String INCOMPATIBLE_DISABLE_OBJC_LIBRARY_TRANSITION =
      "+incompatible_disable_objc_library_transition";
  public static final String ADD_GO_EXEC_GROUPS_TO_BINARY_RULES =
      "-add_go_exec_groups_to_binary_rules";
  public static final String INCOMPATIBLE_FAIL_ON_UNKNOWN_ATTRIBUTES =
      "+incompatible_fail_on_unknown_attributes";
  public static final String INCOMPATIBLE_ENABLE_PROTO_TOOLCHAIN_RESOLUTION =
      FlagConstants.DEFAULT_INCOMPATIBLE_ENABLE_PROTO_TOOLCHAIN_RESOLUTION_NAME;
  public static final String INCOMPATIBLE_DISABLE_NON_EXECUTABLE_JAVA_BINARY =
      "-incompatible_disable_non_executable_java_binary";
  public static final String INCOMPATIBLE_DISABLE_TARGET_DEFAULT_PROVIDER_FIELDS =
      "-incompatible_disable_target_default_provider_fields";
  public static final String EXPERIMENTAL_RULE_EXTENSION_API =
      FlagConstants.DEFAULT_EXPERIMENTAL_RULE_EXTENSION_API_NAME;
  public static final String EXPERIMENTAL_DORMANT_DEPS = "-experimental_dormant_deps";

  public static final String EXPERIMENTAL_STARLARK_TYPE_SYNTAX =
      FlagConstants.EXPERIMENTAL_STARLARK_TYPE_SYNTAX_FLAG_NAME;
  public static final String INCOMPATIBLE_ENABLE_DEPRECATED_LABEL_APIS =
      "+incompatible_enable_deprecated_label_apis";
  public static final String INCOMPATIBLE_STOP_EXPORTING_BUILD_FILE_PATH =
      "-incompatible_stop_exporting_build_file_path";
  public static final String INCOMPATIBLE_DISALLOW_CTX_RESOLVE_TOOLS =
      "+incompatible_disallow_ctx_resolve_tools";
  public static final String INCOMPATIBLE_SIMPLIFY_UNCONDITIONAL_SELECTS_IN_RULE_ATTRS =
      "+incompatible_simplify_unconditional_selects_in_rule_attrs";
  public static final String INCOMPATIBLE_LOCATIONS_PREFERS_EXECUTABLE =
      "+incompatible_locations_prefers_executable";
  public static final String EXPERIMENTAL_REPOSITORY_CTX_EXECUTE_WASM =
      "-experimental_repository_ctx_execute_wasm";
  public static final String EXPERIMENTAL_REPOSITORY_CTX_WASM_COMPILATION =
      "-experimental_repository_ctx_wasm_compilation";
  public static final String INCOMPATIBLE_RESOLVE_SELECT_KEYS_EAGERLY =
      "-incompatible_resolve_select_keys_eagerly";

  // non-booleans
  public static final StarlarkSemantics.Key<List<String>> INCOMPATIBLE_DISABLE_TRANSITIONS_OPTIONS =
      new StarlarkSemantics.Key<>("incompatible_disable_transitions_on", ImmutableList.of());
  public static final StarlarkSemantics.Key<String> EXPERIMENTAL_BUILTINS_BZL_PATH =
      new StarlarkSemantics.Key<>("experimental_builtins_bzl_path", "%bundled%");
  public static final StarlarkSemantics.Key<List<String>> EXPERIMENTAL_BUILTINS_INJECTION_OVERRIDE =
      new StarlarkSemantics.Key<>("experimental_builtins_injection_override", ImmutableList.of());
  public static final StarlarkSemantics.Key<Utf8EnforcementMode>
      INCOMPATIBLE_ENFORCE_STARLARK_UTF8 =
          new StarlarkSemantics.Key<>(
              "incompatible_enforce_starlark_utf8", Utf8EnforcementMode.WARNING);
  public static final StarlarkSemantics.Key<List<String>>
      EXPERIMENTAL_STARLARK_TYPES_ALLOWED_PATHS =
          new StarlarkSemantics.Key<>(
              "experimental_starlark_types",
              FlagConstants.DEFAULT_EXPERIMENTAL_STARLARK_TYPES_ALLOWED_PATHS.isEmpty()
                  ? ImmutableList.of()
                  : ImmutableList.copyOf(
                      FlagConstants.DEFAULT_EXPERIMENTAL_STARLARK_TYPES_ALLOWED_PATHS.split(",")));

  public static final StarlarkSemantics.Key<Long> MAX_COMPUTATION_STEPS =
      new StarlarkSemantics.Key<>("max_computation_steps", 0L);
  public static final StarlarkSemantics.Key<Integer> NESTED_SET_DEPTH_LIMIT =
      new StarlarkSemantics.Key<>("nested_set_depth_limit", 3500);
}
