| # 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 py_package rule" |
| |
| load(":builders.bzl", "builders") |
| load(":py_info.bzl", "PyInfoBuilder") |
| |
| def _path_inside_wheel(input_file): |
| # input_file.short_path is sometimes relative ("../${repository_root}/foobar") |
| # which is not a valid path within a zip file. Fix that. |
| short_path = input_file.short_path |
| if short_path.startswith("..") and len(short_path) >= 3: |
| # Path separator. '/' on linux. |
| separator = short_path[2] |
| |
| # Consume '../' part. |
| short_path = short_path[3:] |
| |
| # Find position of next '/' and consume everything up to that character. |
| pos = short_path.find(separator) |
| short_path = short_path[pos + 1:] |
| return short_path |
| |
| def _py_package_impl(ctx): |
| inputs = builders.DepsetBuilder() |
| py_info = PyInfoBuilder() |
| for dep in ctx.attr.deps: |
| inputs.add(dep[DefaultInfo].data_runfiles.files) |
| inputs.add(dep[DefaultInfo].default_runfiles.files) |
| py_info.merge_target(dep) |
| py_info = py_info.build() |
| inputs.add(py_info.transitive_sources) |
| |
| # Remove conditional once Bazel 6 support dropped. |
| if hasattr(py_info, "transitive_pyc_files"): |
| inputs.add(py_info.transitive_pyc_files) |
| |
| inputs = inputs.build() |
| |
| # TODO: '/' is wrong on windows, but the path separator is not available in starlark. |
| # Fix this once ctx.configuration has directory separator information. |
| packages = [p.replace(".", "/") for p in ctx.attr.packages] |
| if not packages: |
| filtered_inputs = inputs |
| else: |
| filtered_files = [] |
| |
| # TODO: flattening depset to list gives poor performance, |
| for input_file in inputs.to_list(): |
| wheel_path = _path_inside_wheel(input_file) |
| for package in packages: |
| if wheel_path.startswith(package): |
| filtered_files.append(input_file) |
| filtered_inputs = depset(direct = filtered_files) |
| |
| return [DefaultInfo( |
| files = filtered_inputs, |
| )] |
| |
| py_package_lib = struct( |
| implementation = _py_package_impl, |
| attrs = { |
| "deps": attr.label_list( |
| doc = "", |
| ), |
| "packages": attr.string_list( |
| mandatory = False, |
| allow_empty = True, |
| doc = """\ |
| List of Python packages to include in the distribution. |
| Sub-packages are automatically included. |
| """, |
| ), |
| }, |
| path_inside_wheel = _path_inside_wheel, |
| ) |