xref: /aosp_15_r20/external/bazelbuild-rules_python/python/private/py_package.bzl (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
1# Copyright 2023 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"Implementation of py_package rule"
16
17def _path_inside_wheel(input_file):
18    # input_file.short_path is sometimes relative ("../${repository_root}/foobar")
19    # which is not a valid path within a zip file. Fix that.
20    short_path = input_file.short_path
21    if short_path.startswith("..") and len(short_path) >= 3:
22        # Path separator. '/' on linux.
23        separator = short_path[2]
24
25        # Consume '../' part.
26        short_path = short_path[3:]
27
28        # Find position of next '/' and consume everything up to that character.
29        pos = short_path.find(separator)
30        short_path = short_path[pos + 1:]
31    return short_path
32
33def _py_package_impl(ctx):
34    inputs = depset(
35        transitive = [dep[DefaultInfo].data_runfiles.files for dep in ctx.attr.deps] +
36                     [dep[DefaultInfo].default_runfiles.files for dep in ctx.attr.deps],
37    )
38
39    # TODO: '/' is wrong on windows, but the path separator is not available in starlark.
40    # Fix this once ctx.configuration has directory separator information.
41    packages = [p.replace(".", "/") for p in ctx.attr.packages]
42    if not packages:
43        filtered_inputs = inputs
44    else:
45        filtered_files = []
46
47        # TODO: flattening depset to list gives poor performance,
48        for input_file in inputs.to_list():
49            wheel_path = _path_inside_wheel(input_file)
50            for package in packages:
51                if wheel_path.startswith(package):
52                    filtered_files.append(input_file)
53        filtered_inputs = depset(direct = filtered_files)
54
55    return [DefaultInfo(
56        files = filtered_inputs,
57    )]
58
59py_package_lib = struct(
60    implementation = _py_package_impl,
61    attrs = {
62        "deps": attr.label_list(
63            doc = "",
64        ),
65        "packages": attr.string_list(
66            mandatory = False,
67            allow_empty = True,
68            doc = """\
69List of Python packages to include in the distribution.
70Sub-packages are automatically included.
71""",
72        ),
73    },
74    path_inside_wheel = _path_inside_wheel,
75)
76