| # Copyright 2023 Jeremy Volkman. All rights reserved. |
| # Copyright 2023 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. |
| |
| """Implementation of the pycross_wheel_library rule.""" |
| |
| load("@bazel_skylib//lib:paths.bzl", "paths") |
| load("@rules_python//python:defs.bzl", "PyInfo") |
| load(":providers.bzl", "PycrossWheelInfo") |
| |
| def _pycross_wheel_library_impl(ctx): |
| out = ctx.actions.declare_directory(ctx.attr.name) |
| |
| wheel_target = ctx.attr.wheel |
| if PycrossWheelInfo in wheel_target: |
| wheel_file = wheel_target[PycrossWheelInfo].wheel_file |
| name_file = wheel_target[PycrossWheelInfo].name_file |
| else: |
| wheel_file = ctx.file.wheel |
| name_file = None |
| |
| args = ctx.actions.args().use_param_file("--flagfile=%s") |
| args.add("--wheel", wheel_file) |
| args.add("--directory", out.path) |
| |
| inputs = [wheel_file] |
| if name_file: |
| inputs.append(name_file) |
| args.add("--wheel-name-file", name_file) |
| |
| if ctx.attr.enable_implicit_namespace_pkgs: |
| args.add("--enable-implicit-namespace-pkgs") |
| |
| ctx.actions.run( |
| inputs = inputs, |
| outputs = [out], |
| executable = ctx.executable._tool, |
| arguments = [args], |
| # Set environment variables to make generated .pyc files reproducible. |
| env = { |
| "PYTHONHASHSEED": "0", |
| "SOURCE_DATE_EPOCH": "315532800", |
| }, |
| mnemonic = "WheelInstall", |
| progress_message = "Installing %s" % ctx.file.wheel.basename, |
| ) |
| |
| has_py2_only_sources = ctx.attr.python_version == "PY2" |
| has_py3_only_sources = ctx.attr.python_version == "PY3" |
| if not has_py2_only_sources: |
| for d in ctx.attr.deps: |
| if d[PyInfo].has_py2_only_sources: |
| has_py2_only_sources = True |
| break |
| if not has_py3_only_sources: |
| for d in ctx.attr.deps: |
| if d[PyInfo].has_py3_only_sources: |
| has_py3_only_sources = True |
| break |
| |
| # TODO: Is there a more correct way to get this runfiles-relative import path? |
| imp = paths.join( |
| ctx.label.workspace_name or ctx.workspace_name, # Default to the local workspace. |
| ctx.label.package, |
| ctx.label.name, |
| "site-packages", # we put lib files in this subdirectory. |
| ) |
| |
| imports = depset( |
| direct = [imp], |
| transitive = [d[PyInfo].imports for d in ctx.attr.deps], |
| ) |
| transitive_sources = depset( |
| direct = [out], |
| transitive = [dep[PyInfo].transitive_sources for dep in ctx.attr.deps if PyInfo in dep], |
| ) |
| runfiles = ctx.runfiles(files = [out]) |
| for d in ctx.attr.deps: |
| runfiles = runfiles.merge(d[DefaultInfo].default_runfiles) |
| |
| return [ |
| DefaultInfo( |
| files = depset(direct = [out]), |
| runfiles = runfiles, |
| ), |
| PyInfo( |
| has_py2_only_sources = has_py2_only_sources, |
| has_py3_only_sources = has_py3_only_sources, |
| imports = imports, |
| transitive_sources = transitive_sources, |
| uses_shared_libraries = True, # Docs say this is unused |
| ), |
| ] |
| |
| pycross_wheel_library = rule( |
| implementation = _pycross_wheel_library_impl, |
| attrs = { |
| "deps": attr.label_list( |
| doc = "A list of this wheel's Python library dependencies.", |
| providers = [DefaultInfo, PyInfo], |
| ), |
| "enable_implicit_namespace_pkgs": attr.bool( |
| default = True, |
| doc = """ |
| If true, disables conversion of native namespace packages into pkg-util style namespace packages. When set all py_binary |
| and py_test targets must specify either `legacy_create_init=False` or the global Bazel option |
| `--incompatible_default_to_explicit_init_py` to prevent `__init__.py` being automatically generated in every directory. |
| This option is required to support some packages which cannot handle the conversion to pkg-util style. |
| """, |
| ), |
| "python_version": attr.string( |
| doc = "The python version required for this wheel ('PY2' or 'PY3')", |
| values = ["PY2", "PY3", ""], |
| ), |
| "wheel": attr.label( |
| doc = "The wheel file.", |
| allow_single_file = [".whl"], |
| mandatory = True, |
| ), |
| "_tool": attr.label( |
| default = Label("//pycross/private/tools:wheel_installer"), |
| cfg = "exec", |
| executable = True, |
| ), |
| }, |
| ) |