diff options
author | Yifan Hong <elsk@google.com> | 2024-01-17 18:02:18 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2024-01-17 18:02:18 +0000 |
commit | 6954130f57eb9a56a82d4146aeafb16ef30f907a (patch) | |
tree | acdcc148cabfb6f8242a6c83e37625eeaea4e628 /python/private/common/py_executable.bzl | |
parent | 1508c18d9e27325a8d9c574a90d205e0a1d8a78f (diff) | |
download | bazelbuild-rules_python-6954130f57eb9a56a82d4146aeafb16ef30f907a.tar.gz |
Revert "Upgrade bazelbuild-rules_python to 0.28.0"
This reverts commit 1508c18d9e27325a8d9c574a90d205e0a1d8a78f.
Reason for revert: breaks build, not caught by treehugger
Change-Id: Id40b5eefc5f4f0f6e4a0122fbfa53b0c3c265985
Diffstat (limited to 'python/private/common/py_executable.bzl')
-rw-r--r-- | python/private/common/py_executable.bzl | 860 |
1 files changed, 0 insertions, 860 deletions
diff --git a/python/private/common/py_executable.bzl b/python/private/common/py_executable.bzl deleted file mode 100644 index a24bad6..0000000 --- a/python/private/common/py_executable.bzl +++ /dev/null @@ -1,860 +0,0 @@ -# Copyright 2022 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. -"""Common functionality between test/binary executables.""" - -load("@bazel_skylib//lib:dicts.bzl", "dicts") -load( - ":attributes.bzl", - "AGNOSTIC_EXECUTABLE_ATTRS", - "COMMON_ATTRS", - "PY_SRCS_ATTRS", - "SRCS_VERSION_ALL_VALUES", - "create_srcs_attr", - "create_srcs_version_attr", -) -load(":cc_helper.bzl", "cc_helper") -load( - ":common.bzl", - "TOOLCHAIN_TYPE", - "check_native_allowed", - "collect_imports", - "collect_runfiles", - "create_instrumented_files_info", - "create_output_group_info", - "create_py_info", - "csv", - "filter_to_py_srcs", - "target_platform_has_any_constraint", - "union_attrs", -) -load( - ":providers.bzl", - "PyCcLinkParamsProvider", - "PyRuntimeInfo", -) -load(":py_internal.bzl", "py_internal") -load( - ":semantics.bzl", - "ALLOWED_MAIN_EXTENSIONS", - "BUILD_DATA_SYMLINK_PATH", - "IS_BAZEL", - "PY_RUNTIME_ATTR_NAME", -) - -# TODO: Load cc_common from rules_cc -_cc_common = cc_common - -_py_builtins = py_internal - -# Bazel 5.4 doesn't have config_common.toolchain_type -_CC_TOOLCHAINS = [config_common.toolchain_type( - "@bazel_tools//tools/cpp:toolchain_type", - mandatory = False, -)] if hasattr(config_common, "toolchain_type") else [] - -# Non-Google-specific attributes for executables -# These attributes are for rules that accept Python sources. -EXECUTABLE_ATTRS = union_attrs( - COMMON_ATTRS, - AGNOSTIC_EXECUTABLE_ATTRS, - PY_SRCS_ATTRS, - { - # TODO(b/203567235): In the Java impl, any file is allowed. While marked - # label, it is more treated as a string, and doesn't have to refer to - # anything that exists because it gets treated as suffix-search string - # over `srcs`. - "main": attr.label( - allow_single_file = True, - doc = """\ -Optional; the name of the source file that is the main entry point of the -application. This file must also be listed in `srcs`. If left unspecified, -`name`, with `.py` appended, is used instead. If `name` does not match any -filename in `srcs`, `main` must be specified. -""", - ), - # TODO(b/203567235): In Google, this attribute is deprecated, and can - # only effectively be PY3. Externally, with Bazel, this attribute has - # a separate story. - "python_version": attr.string( - # TODO(b/203567235): In the Java impl, the default comes from - # --python_version. Not clear what the Starlark equivalent is. - default = "PY3", - # NOTE: Some tests care about the order of these values. - values = ["PY2", "PY3"], - ), - "_windows_constraints": attr.label_list( - default = [ - "@platforms//os:windows", - ], - ), - }, - create_srcs_version_attr(values = SRCS_VERSION_ALL_VALUES), - create_srcs_attr(mandatory = True), - allow_none = True, -) - -def py_executable_base_impl(ctx, *, semantics, is_test, inherited_environment = []): - """Base rule implementation for a Python executable. - - Google and Bazel call this common base and apply customizations using the - semantics object. - - Args: - ctx: The rule ctx - semantics: BinarySemantics struct; see create_binary_semantics_struct() - is_test: bool, True if the rule is a test rule (has `test=True`), - False if not (has `executable=True`) - inherited_environment: List of str; additional environment variable - names that should be inherited from the runtime environment when the - executable is run. - Returns: - DefaultInfo provider for the executable - """ - _validate_executable(ctx) - - main_py = determine_main(ctx) - direct_sources = filter_to_py_srcs(ctx.files.srcs) - output_sources = semantics.maybe_precompile(ctx, direct_sources) - imports = collect_imports(ctx, semantics) - executable, files_to_build = _compute_outputs(ctx, output_sources) - - runtime_details = _get_runtime_details(ctx, semantics) - if ctx.configuration.coverage_enabled: - extra_deps = semantics.get_coverage_deps(ctx, runtime_details) - else: - extra_deps = [] - - # The debugger dependency should be prevented by select() config elsewhere, - # but just to be safe, also guard against adding it to the output here. - if not _is_tool_config(ctx): - extra_deps.extend(semantics.get_debugger_deps(ctx, runtime_details)) - - cc_details = semantics.get_cc_details_for_binary(ctx, extra_deps = extra_deps) - native_deps_details = _get_native_deps_details( - ctx, - semantics = semantics, - cc_details = cc_details, - is_test = is_test, - ) - runfiles_details = _get_base_runfiles_for_binary( - ctx, - executable = executable, - extra_deps = extra_deps, - files_to_build = files_to_build, - extra_common_runfiles = [ - runtime_details.runfiles, - cc_details.extra_runfiles, - native_deps_details.runfiles, - semantics.get_extra_common_runfiles_for_binary(ctx), - ], - semantics = semantics, - ) - exec_result = semantics.create_executable( - ctx, - executable = executable, - main_py = main_py, - imports = imports, - is_test = is_test, - runtime_details = runtime_details, - cc_details = cc_details, - native_deps_details = native_deps_details, - runfiles_details = runfiles_details, - ) - files_to_build = depset(transitive = [ - exec_result.extra_files_to_build, - files_to_build, - ]) - extra_exec_runfiles = ctx.runfiles(transitive_files = files_to_build) - runfiles_details = struct( - default_runfiles = runfiles_details.default_runfiles.merge(extra_exec_runfiles), - data_runfiles = runfiles_details.data_runfiles.merge(extra_exec_runfiles), - ) - - legacy_providers, modern_providers = _create_providers( - ctx = ctx, - executable = executable, - runfiles_details = runfiles_details, - main_py = main_py, - imports = imports, - direct_sources = direct_sources, - files_to_build = files_to_build, - runtime_details = runtime_details, - cc_info = cc_details.cc_info_for_propagating, - inherited_environment = inherited_environment, - semantics = semantics, - output_groups = exec_result.output_groups, - ) - return struct( - legacy_providers = legacy_providers, - providers = modern_providers, - ) - -def _validate_executable(ctx): - if ctx.attr.python_version != "PY3": - fail("It is not allowed to use Python 2") - check_native_allowed(ctx) - -def _compute_outputs(ctx, output_sources): - if target_platform_has_any_constraint(ctx, ctx.attr._windows_constraints): - executable = ctx.actions.declare_file(ctx.label.name + ".exe") - else: - executable = ctx.actions.declare_file(ctx.label.name) - - # TODO(b/208657718): Remove output_sources from the default outputs - # once the depot is cleaned up. - return executable, depset([executable] + output_sources) - -def _get_runtime_details(ctx, semantics): - """Gets various information about the Python runtime to use. - - While most information comes from the toolchain, various legacy and - compatibility behaviors require computing some other information. - - Args: - ctx: Rule ctx - semantics: A `BinarySemantics` struct; see `create_binary_semantics_struct` - - Returns: - A struct; see inline-field comments of the return value for details. - """ - - # Bazel has --python_path. This flag has a computed default of "python" when - # its actual default is null (see - # BazelPythonConfiguration.java#getPythonPath). This flag is only used if - # toolchains are not enabled and `--python_top` isn't set. Note that Google - # used to have a variant of this named --python_binary, but it has since - # been removed. - # - # TOOD(bazelbuild/bazel#7901): Remove this once --python_path flag is removed. - - if IS_BAZEL: - flag_interpreter_path = ctx.fragments.bazel_py.python_path - toolchain_runtime, effective_runtime = _maybe_get_runtime_from_ctx(ctx) - if not effective_runtime: - # Clear these just in case - toolchain_runtime = None - effective_runtime = None - - else: # Google code path - flag_interpreter_path = None - toolchain_runtime, effective_runtime = _maybe_get_runtime_from_ctx(ctx) - if not effective_runtime: - fail("Unable to find Python runtime") - - if effective_runtime: - direct = [] # List of files - transitive = [] # List of depsets - if effective_runtime.interpreter: - direct.append(effective_runtime.interpreter) - transitive.append(effective_runtime.files) - - if ctx.configuration.coverage_enabled: - if effective_runtime.coverage_tool: - direct.append(effective_runtime.coverage_tool) - if effective_runtime.coverage_files: - transitive.append(effective_runtime.coverage_files) - runtime_files = depset(direct = direct, transitive = transitive) - else: - runtime_files = depset() - - executable_interpreter_path = semantics.get_interpreter_path( - ctx, - runtime = effective_runtime, - flag_interpreter_path = flag_interpreter_path, - ) - - return struct( - # Optional PyRuntimeInfo: The runtime found from toolchain resolution. - # This may be None because, within Google, toolchain resolution isn't - # yet enabled. - toolchain_runtime = toolchain_runtime, - # Optional PyRuntimeInfo: The runtime that should be used. When - # toolchain resolution is enabled, this is the same as - # `toolchain_resolution`. Otherwise, this probably came from the - # `_python_top` attribute that the Google implementation still uses. - # This is separate from `toolchain_runtime` because toolchain_runtime - # is propagated as a provider, while non-toolchain runtimes are not. - effective_runtime = effective_runtime, - # str; Path to the Python interpreter to use for running the executable - # itself (not the bootstrap script). Either an absolute path (which - # means it is platform-specific), or a runfiles-relative path (which - # means the interpreter should be within `runtime_files`) - executable_interpreter_path = executable_interpreter_path, - # runfiles: Additional runfiles specific to the runtime that should - # be included. For in-build runtimes, this shold include the interpreter - # and any supporting files. - runfiles = ctx.runfiles(transitive_files = runtime_files), - ) - -def _maybe_get_runtime_from_ctx(ctx): - """Finds the PyRuntimeInfo from the toolchain or attribute, if available. - - Returns: - 2-tuple of toolchain_runtime, effective_runtime - """ - if ctx.fragments.py.use_toolchains: - toolchain = ctx.toolchains[TOOLCHAIN_TYPE] - - if not hasattr(toolchain, "py3_runtime"): - fail("Python toolchain field 'py3_runtime' is missing") - if not toolchain.py3_runtime: - fail("Python toolchain missing py3_runtime") - py3_runtime = toolchain.py3_runtime - - # Hack around the fact that the autodetecting Python toolchain, which is - # automatically registered, does not yet support Windows. In this case, - # we want to return null so that _get_interpreter_path falls back on - # --python_path. See tools/python/toolchain.bzl. - # TODO(#7844): Remove this hack when the autodetecting toolchain has a - # Windows implementation. - if py3_runtime.interpreter_path == "/_magic_pyruntime_sentinel_do_not_use": - return None, None - - if py3_runtime.python_version != "PY3": - fail("Python toolchain py3_runtime must be python_version=PY3, got {}".format( - py3_runtime.python_version, - )) - toolchain_runtime = toolchain.py3_runtime - effective_runtime = toolchain_runtime - else: - toolchain_runtime = None - attr_target = getattr(ctx.attr, PY_RUNTIME_ATTR_NAME) - - # In Bazel, --python_top is null by default. - if attr_target and PyRuntimeInfo in attr_target: - effective_runtime = attr_target[PyRuntimeInfo] - else: - return None, None - - return toolchain_runtime, effective_runtime - -def _get_base_runfiles_for_binary( - ctx, - *, - executable, - extra_deps, - files_to_build, - extra_common_runfiles, - semantics): - """Returns the set of runfiles necessary prior to executable creation. - - NOTE: The term "common runfiles" refers to the runfiles that both the - default and data runfiles have in common. - - Args: - ctx: The rule ctx. - executable: The main executable output. - extra_deps: List of Targets; additional targets whose runfiles - will be added to the common runfiles. - files_to_build: depset of File of the default outputs to add into runfiles. - extra_common_runfiles: List of runfiles; additional runfiles that - will be added to the common runfiles. - semantics: A `BinarySemantics` struct; see `create_binary_semantics_struct`. - - Returns: - struct with attributes: - * default_runfiles: The default runfiles - * data_runfiles: The data runfiles - """ - common_runfiles = collect_runfiles(ctx, depset( - direct = [executable], - transitive = [files_to_build], - )) - if extra_deps: - common_runfiles = common_runfiles.merge_all([ - t[DefaultInfo].default_runfiles - for t in extra_deps - ]) - common_runfiles = common_runfiles.merge_all(extra_common_runfiles) - - if semantics.should_create_init_files(ctx): - common_runfiles = _py_builtins.merge_runfiles_with_generated_inits_empty_files_supplier( - ctx = ctx, - runfiles = common_runfiles, - ) - - # Don't include build_data.txt in data runfiles. This allows binaries to - # contain other binaries while still using the same fixed location symlink - # for the build_data.txt file. Really, the fixed location symlink should be - # removed and another way found to locate the underlying build data file. - data_runfiles = common_runfiles - - if is_stamping_enabled(ctx, semantics) and semantics.should_include_build_data(ctx): - default_runfiles = common_runfiles.merge(_create_runfiles_with_build_data( - ctx, - semantics.get_central_uncachable_version_file(ctx), - semantics.get_extra_write_build_data_env(ctx), - )) - else: - default_runfiles = common_runfiles - - return struct( - default_runfiles = default_runfiles, - data_runfiles = data_runfiles, - ) - -def _create_runfiles_with_build_data( - ctx, - central_uncachable_version_file, - extra_write_build_data_env): - return ctx.runfiles( - symlinks = { - BUILD_DATA_SYMLINK_PATH: _write_build_data( - ctx, - central_uncachable_version_file, - extra_write_build_data_env, - ), - }, - ) - -def _write_build_data(ctx, central_uncachable_version_file, extra_write_build_data_env): - # TODO: Remove this logic when a central file is always available - if not central_uncachable_version_file: - version_file = ctx.actions.declare_file(ctx.label.name + "-uncachable_version_file.txt") - _py_builtins.copy_without_caching( - ctx = ctx, - read_from = ctx.version_file, - write_to = version_file, - ) - else: - version_file = central_uncachable_version_file - - direct_inputs = [ctx.info_file, version_file] - - # A "constant metadata" file is basically a special file that doesn't - # support change detection logic and reports that it is unchanged. i.e., it - # behaves like ctx.version_file and is ignored when computing "what inputs - # changed" (see https://bazel.build/docs/user-manual#workspace-status). - # - # We do this so that consumers of the final build data file don't have - # to transitively rebuild everything -- the `uncachable_version_file` file - # isn't cachable, which causes the build data action to always re-run. - # - # While this technically means a binary could have stale build info, - # it ends up not mattering in practice because the volatile information - # doesn't meaningfully effect other outputs. - # - # This is also done for performance and Make It work reasons: - # * Passing the transitive dependencies into the action requires passing - # the runfiles, but actions don't directly accept runfiles. While - # flattening the depsets can be deferred, accessing the - # `runfiles.empty_filenames` attribute will will invoke the empty - # file supplier a second time, which is too much of a memory and CPU - # performance hit. - # * Some targets specify a directory in `data`, which is unsound, but - # mostly works. Google's RBE, unfortunately, rejects it. - # * A binary's transitive closure may be so large that it exceeds - # Google RBE limits for action inputs. - build_data = _py_builtins.declare_constant_metadata_file( - ctx = ctx, - name = ctx.label.name + ".build_data.txt", - root = ctx.bin_dir, - ) - - ctx.actions.run( - executable = ctx.executable._build_data_gen, - env = dicts.add({ - # NOTE: ctx.info_file is undocumented; see - # https://github.com/bazelbuild/bazel/issues/9363 - "INFO_FILE": ctx.info_file.path, - "OUTPUT": build_data.path, - "PLATFORM": cc_helper.find_cpp_toolchain(ctx).toolchain_id, - "TARGET": str(ctx.label), - "VERSION_FILE": version_file.path, - }, extra_write_build_data_env), - inputs = depset( - direct = direct_inputs, - ), - outputs = [build_data], - mnemonic = "PyWriteBuildData", - progress_message = "Generating %{label} build_data.txt", - ) - return build_data - -def _get_native_deps_details(ctx, *, semantics, cc_details, is_test): - if not semantics.should_build_native_deps_dso(ctx): - return struct(dso = None, runfiles = ctx.runfiles()) - - cc_info = cc_details.cc_info_for_self_link - - if not cc_info.linking_context.linker_inputs: - return struct(dso = None, runfiles = ctx.runfiles()) - - dso = ctx.actions.declare_file(semantics.get_native_deps_dso_name(ctx)) - share_native_deps = py_internal.share_native_deps(ctx) - cc_feature_config = cc_configure_features( - ctx, - cc_toolchain = cc_details.cc_toolchain, - # See b/171276569#comment18: this feature string is just to allow - # Google's RBE to know the link action is for the Python case so it can - # take special actions (though as of Jun 2022, no special action is - # taken). - extra_features = ["native_deps_link"], - ) - if share_native_deps: - linked_lib = _create_shared_native_deps_dso( - ctx, - cc_info = cc_info, - is_test = is_test, - requested_features = cc_feature_config.requested_features, - feature_configuration = cc_feature_config.feature_configuration, - ) - ctx.actions.symlink( - output = dso, - target_file = linked_lib, - progress_message = "Symlinking shared native deps for %{label}", - ) - else: - linked_lib = dso - _cc_common.link( - name = ctx.label.name, - actions = ctx.actions, - linking_contexts = [cc_info.linking_context], - output_type = "dynamic_library", - never_link = True, - native_deps = True, - feature_configuration = cc_feature_config.feature_configuration, - cc_toolchain = cc_details.cc_toolchain, - test_only_target = is_test, - stamp = 1 if is_stamping_enabled(ctx, semantics) else 0, - main_output = linked_lib, - use_shareable_artifact_factory = True, - # NOTE: Only flags not captured by cc_info.linking_context need to - # be manually passed - user_link_flags = semantics.get_native_deps_user_link_flags(ctx), - ) - return struct( - dso = dso, - runfiles = ctx.runfiles(files = [dso]), - ) - -def _create_shared_native_deps_dso( - ctx, - *, - cc_info, - is_test, - feature_configuration, - requested_features): - linkstamps = cc_info.linking_context.linkstamps() - - partially_disabled_thin_lto = ( - _cc_common.is_enabled( - feature_name = "thin_lto_linkstatic_tests_use_shared_nonlto_backends", - feature_configuration = feature_configuration, - ) and not _cc_common.is_enabled( - feature_name = "thin_lto_all_linkstatic_use_shared_nonlto_backends", - feature_configuration = feature_configuration, - ) - ) - dso_hash = _get_shared_native_deps_hash( - linker_inputs = cc_helper.get_static_mode_params_for_dynamic_library_libraries( - depset([ - lib - for linker_input in cc_info.linking_context.linker_inputs.to_list() - for lib in linker_input.libraries - ]), - ), - link_opts = [ - flag - for input in cc_info.linking_context.linker_inputs.to_list() - for flag in input.user_link_flags - ], - linkstamps = [linkstamp.file() for linkstamp in linkstamps.to_list()], - build_info_artifacts = _cc_common.get_build_info(ctx) if linkstamps else [], - features = requested_features, - is_test_target_partially_disabled_thin_lto = is_test and partially_disabled_thin_lto, - ) - return py_internal.declare_shareable_artifact(ctx, "_nativedeps/%x.so" % dso_hash) - -# This is a minimal version of NativeDepsHelper.getSharedNativeDepsPath, see -# com.google.devtools.build.lib.rules.nativedeps.NativeDepsHelper#getSharedNativeDepsPath -# The basic idea is to take all the inputs that affect linking and encode (via -# hashing) them into the filename. -# TODO(b/234232820): The settings that affect linking must be kept in sync with the actual -# C++ link action. For more information, see the large descriptive comment on -# NativeDepsHelper#getSharedNativeDepsPath. -def _get_shared_native_deps_hash( - *, - linker_inputs, - link_opts, - linkstamps, - build_info_artifacts, - features, - is_test_target_partially_disabled_thin_lto): - # NOTE: We use short_path because the build configuration root in which - # files are always created already captures the configuration-specific - # parts, so no need to include them manually. - parts = [] - for artifact in linker_inputs: - parts.append(artifact.short_path) - parts.append(str(len(link_opts))) - parts.extend(link_opts) - for artifact in linkstamps: - parts.append(artifact.short_path) - for artifact in build_info_artifacts: - parts.append(artifact.short_path) - parts.extend(sorted(features)) - - # Sharing of native dependencies may cause an {@link - # ActionConflictException} when ThinLTO is disabled for test and test-only - # targets that are statically linked, but enabled for other statically - # linked targets. This happens in case the artifacts for the shared native - # dependency are output by {@link Action}s owned by the non-test and test - # targets both. To fix this, we allow creation of multiple artifacts for the - # shared native library - one shared among the test and test-only targets - # where ThinLTO is disabled, and the other shared among other targets where - # ThinLTO is enabled. See b/138118275 - parts.append("1" if is_test_target_partially_disabled_thin_lto else "0") - - return hash("".join(parts)) - -def determine_main(ctx): - """Determine the main entry point .py source file. - - Args: - ctx: The rule ctx. - - Returns: - Artifact; the main file. If one can't be found, an error is raised. - """ - if ctx.attr.main: - proposed_main = ctx.attr.main.label.name - if not proposed_main.endswith(tuple(ALLOWED_MAIN_EXTENSIONS)): - fail("main must end in '.py'") - else: - if ctx.label.name.endswith(".py"): - fail("name must not end in '.py'") - proposed_main = ctx.label.name + ".py" - - main_files = [src for src in ctx.files.srcs if _path_endswith(src.short_path, proposed_main)] - if not main_files: - if ctx.attr.main: - fail("could not find '{}' as specified by 'main' attribute".format(proposed_main)) - else: - fail(("corresponding default '{}' does not appear in srcs. Add " + - "it or override default file name with a 'main' attribute").format( - proposed_main, - )) - - elif len(main_files) > 1: - if ctx.attr.main: - fail(("file name '{}' specified by 'main' attributes matches multiple files. " + - "Matches: {}").format( - proposed_main, - csv([f.short_path for f in main_files]), - )) - else: - fail(("default main file '{}' matches multiple files in srcs. Perhaps specify " + - "an explicit file with 'main' attribute? Matches were: {}").format( - proposed_main, - csv([f.short_path for f in main_files]), - )) - return main_files[0] - -def _path_endswith(path, endswith): - # Use slash to anchor each path to prevent e.g. - # "ab/c.py".endswith("b/c.py") from incorrectly matching. - return ("/" + path).endswith("/" + endswith) - -def is_stamping_enabled(ctx, semantics): - """Tells if stamping is enabled or not. - - Args: - ctx: The rule ctx - semantics: a semantics struct (see create_semantics_struct). - Returns: - bool; True if stamping is enabled, False if not. - """ - if _is_tool_config(ctx): - return False - - stamp = ctx.attr.stamp - if stamp == 1: - return True - elif stamp == 0: - return False - elif stamp == -1: - return semantics.get_stamp_flag(ctx) - else: - fail("Unsupported `stamp` value: {}".format(stamp)) - -def _is_tool_config(ctx): - # NOTE: The is_tool_configuration() function is only usable by builtins. - # See https://github.com/bazelbuild/bazel/issues/14444 for the FR for - # a more public API. Until that's available, py_internal to the rescue. - return py_internal.is_tool_configuration(ctx) - -def _create_providers( - *, - ctx, - executable, - main_py, - direct_sources, - files_to_build, - runfiles_details, - imports, - cc_info, - inherited_environment, - runtime_details, - output_groups, - semantics): - """Creates the providers an executable should return. - - Args: - ctx: The rule ctx. - executable: File; the target's executable file. - main_py: File; the main .py entry point. - direct_sources: list of Files; the direct, raw `.py` sources for the target. - This should only be Python source files. It should not include pyc - files. - files_to_build: depset of Files; the files for DefaultInfo.files - runfiles_details: runfiles that will become the default and data runfiles. - imports: depset of strings; the import paths to propagate - cc_info: optional CcInfo; Linking information to propagate as - PyCcLinkParamsProvider. Note that only the linking information - is propagated, not the whole CcInfo. - inherited_environment: list of strings; Environment variable names - that should be inherited from the environment the executuble - is run within. - runtime_details: struct of runtime information; see _get_runtime_details() - output_groups: dict[str, depset[File]]; used to create OutputGroupInfo - semantics: BinarySemantics struct; see create_binary_semantics() - - Returns: - A two-tuple of: - 1. A dict of legacy providers. - 2. A list of modern providers. - """ - providers = [ - DefaultInfo( - executable = executable, - files = files_to_build, - default_runfiles = _py_builtins.make_runfiles_respect_legacy_external_runfiles( - ctx, - runfiles_details.default_runfiles, - ), - data_runfiles = _py_builtins.make_runfiles_respect_legacy_external_runfiles( - ctx, - runfiles_details.data_runfiles, - ), - ), - create_instrumented_files_info(ctx), - _create_run_environment_info(ctx, inherited_environment), - ] - - # TODO(b/265840007): Make this non-conditional once Google enables - # --incompatible_use_python_toolchains. - if runtime_details.toolchain_runtime: - providers.append(runtime_details.toolchain_runtime) - - # TODO(b/163083591): Remove the PyCcLinkParamsProvider once binaries-in-deps - # are cleaned up. - if cc_info: - providers.append( - PyCcLinkParamsProvider(cc_info = cc_info), - ) - - py_info, deps_transitive_sources, builtin_py_info = create_py_info( - ctx, - direct_sources = depset(direct_sources), - imports = imports, - ) - - # TODO(b/253059598): Remove support for extra actions; https://github.com/bazelbuild/bazel/issues/16455 - listeners_enabled = _py_builtins.are_action_listeners_enabled(ctx) - if listeners_enabled: - _py_builtins.add_py_extra_pseudo_action( - ctx = ctx, - dependency_transitive_python_sources = deps_transitive_sources, - ) - - providers.append(py_info) - providers.append(builtin_py_info) - providers.append(create_output_group_info(py_info.transitive_sources, output_groups)) - - extra_legacy_providers, extra_providers = semantics.get_extra_providers( - ctx, - main_py = main_py, - runtime_details = runtime_details, - ) - providers.extend(extra_providers) - return extra_legacy_providers, providers - -def _create_run_environment_info(ctx, inherited_environment): - expanded_env = {} - for key, value in ctx.attr.env.items(): - expanded_env[key] = _py_builtins.expand_location_and_make_variables( - ctx = ctx, - attribute_name = "env[{}]".format(key), - expression = value, - targets = ctx.attr.data, - ) - return RunEnvironmentInfo( - environment = expanded_env, - inherited_environment = inherited_environment, - ) - -def create_base_executable_rule(*, attrs, fragments = [], **kwargs): - """Create a function for defining for Python binary/test targets. - - Args: - attrs: Rule attributes - fragments: List of str; extra config fragments that are required. - **kwargs: Additional args to pass onto `rule()` - - Returns: - A rule function - """ - if "py" not in fragments: - # The list might be frozen, so use concatentation - fragments = fragments + ["py"] - return rule( - # TODO: add ability to remove attrs, i.e. for imports attr - attrs = dicts.add(EXECUTABLE_ATTRS, attrs), - toolchains = [TOOLCHAIN_TYPE] + _CC_TOOLCHAINS, - fragments = fragments, - **kwargs - ) - -def cc_configure_features(ctx, *, cc_toolchain, extra_features): - """Configure C++ features for Python purposes. - - Args: - ctx: Rule ctx - cc_toolchain: The CcToolchain the target is using. - extra_features: list of strings; additional features to request be - enabled. - - Returns: - struct of the feature configuration and all requested features. - """ - requested_features = ["static_linking_mode"] - requested_features.extend(extra_features) - requested_features.extend(ctx.features) - if "legacy_whole_archive" not in ctx.disabled_features: - requested_features.append("legacy_whole_archive") - feature_configuration = _cc_common.configure_features( - ctx = ctx, - cc_toolchain = cc_toolchain, - requested_features = requested_features, - unsupported_features = ctx.disabled_features, - ) - return struct( - feature_configuration = feature_configuration, - requested_features = requested_features, - ) - -only_exposed_for_google_internal_reason = struct( - create_runfiles_with_build_data = _create_runfiles_with_build_data, -) |