• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2024 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"""Tests for py_info."""
15
16load("@rules_python_internal//:rules_python_config.bzl", "config")
17load("@rules_testing//lib:analysis_test.bzl", "analysis_test")
18load("@rules_testing//lib:test_suite.bzl", "test_suite")
19load("@rules_testing//lib:util.bzl", rt_util = "util")
20load("//python:py_info.bzl", "PyInfo")
21load("//python/private:py_info.bzl", "PyInfoBuilder")  # buildifier: disable=bzl-visibility
22load("//python/private:reexports.bzl", "BuiltinPyInfo")  # buildifier: disable=bzl-visibility
23load("//tests/support:py_info_subject.bzl", "py_info_subject")
24
25def _provide_py_info_impl(ctx):
26    kwargs = {
27        "direct_pyc_files": depset(ctx.files.direct_pyc_files),
28        "imports": depset(ctx.attr.imports),
29        "transitive_pyc_files": depset(ctx.files.transitive_pyc_files),
30        "transitive_sources": depset(ctx.files.transitive_sources),
31    }
32    if ctx.attr.has_py2_only_sources != -1:
33        kwargs["has_py2_only_sources"] = bool(ctx.attr.has_py2_only_sources)
34    if ctx.attr.has_py3_only_sources != -1:
35        kwargs["has_py2_only_sources"] = bool(ctx.attr.has_py2_only_sources)
36
37    providers = []
38    if config.enable_pystar:
39        providers.append(PyInfo(**kwargs))
40
41    # Handle Bazel 6 or if Bazel autoloading is enabled
42    if not config.enable_pystar or (BuiltinPyInfo and PyInfo != BuiltinPyInfo):
43        providers.append(BuiltinPyInfo(**{
44            k: kwargs[k]
45            for k in (
46                "transitive_sources",
47                "has_py2_only_sources",
48                "has_py3_only_sources",
49                "uses_shared_libraries",
50                "imports",
51            )
52            if k in kwargs
53        }))
54    return providers
55
56provide_py_info = rule(
57    implementation = _provide_py_info_impl,
58    attrs = {
59        "direct_pyc_files": attr.label_list(allow_files = True),
60        "has_py2_only_sources": attr.int(default = -1),
61        "has_py3_only_sources": attr.int(default = -1),
62        "imports": attr.string_list(),
63        "transitive_pyc_files": attr.label_list(allow_files = True),
64        "transitive_sources": attr.label_list(allow_files = True),
65    },
66)
67
68_tests = []
69
70def _test_py_info_create(name):
71    rt_util.helper_target(
72        native.filegroup,
73        name = name + "_files",
74        srcs = ["trans.py", "direct.pyc", "trans.pyc"],
75    )
76    analysis_test(
77        name = name,
78        target = name + "_files",
79        impl = _test_py_info_create_impl,
80    )
81
82def _test_py_info_create_impl(env, target):
83    trans_py, direct_pyc, trans_pyc = target[DefaultInfo].files.to_list()
84    actual = PyInfo(
85        has_py2_only_sources = True,
86        has_py3_only_sources = True,
87        imports = depset(["import-path"]),
88        transitive_sources = depset([trans_py]),
89        uses_shared_libraries = True,
90        **(dict(
91            direct_pyc_files = depset([direct_pyc]),
92            transitive_pyc_files = depset([trans_pyc]),
93        ) if config.enable_pystar else {})
94    )
95
96    subject = py_info_subject(actual, meta = env.expect.meta)
97    subject.uses_shared_libraries().equals(True)
98    subject.has_py2_only_sources().equals(True)
99    subject.has_py3_only_sources().equals(True)
100    subject.transitive_sources().contains_exactly(["tests/base_rules/py_info/trans.py"])
101    subject.imports().contains_exactly(["import-path"])
102    if config.enable_pystar:
103        subject.direct_pyc_files().contains_exactly(["tests/base_rules/py_info/direct.pyc"])
104        subject.transitive_pyc_files().contains_exactly(["tests/base_rules/py_info/trans.pyc"])
105
106_tests.append(_test_py_info_create)
107
108def _test_py_info_builder(name):
109    rt_util.helper_target(
110        native.filegroup,
111        name = name + "_misc",
112        srcs = ["trans.py", "direct.pyc", "trans.pyc"],
113    )
114
115    py_info_targets = {}
116    for n in range(1, 7):
117        py_info_name = "{}_py{}".format(name, n)
118        py_info_targets["py{}".format(n)] = py_info_name
119        rt_util.helper_target(
120            provide_py_info,
121            name = py_info_name,
122            transitive_sources = ["py{}-trans.py".format(n)],
123            direct_pyc_files = ["py{}-direct.pyc".format(n)],
124            imports = ["py{}import".format(n)],
125            transitive_pyc_files = ["py{}-trans.pyc".format(n)],
126        )
127    analysis_test(
128        name = name,
129        impl = _test_py_info_builder_impl,
130        targets = {
131            "misc": name + "_misc",
132        } | py_info_targets,
133    )
134
135def _test_py_info_builder_impl(env, targets):
136    trans, direct_pyc, trans_pyc = targets.misc[DefaultInfo].files.to_list()
137    builder = PyInfoBuilder()
138    builder.direct_pyc_files.add(direct_pyc)
139    builder.merge_has_py2_only_sources(True)
140    builder.merge_has_py3_only_sources(True)
141    builder.imports.add("import-path")
142    builder.transitive_pyc_files.add(trans_pyc)
143    builder.transitive_sources.add(trans)
144    builder.merge_uses_shared_libraries(True)
145
146    builder.merge_target(targets.py1)
147    builder.merge_targets([targets.py2])
148
149    builder.merge(targets.py3[PyInfo], direct = [targets.py4[PyInfo]])
150    builder.merge_all([targets.py5[PyInfo]], direct = [targets.py6[PyInfo]])
151
152    def check(actual):
153        subject = py_info_subject(actual, meta = env.expect.meta)
154
155        subject.uses_shared_libraries().equals(True)
156        subject.has_py2_only_sources().equals(True)
157        subject.has_py3_only_sources().equals(True)
158
159        subject.transitive_sources().contains_exactly([
160            "tests/base_rules/py_info/trans.py",
161            "tests/base_rules/py_info/py1-trans.py",
162            "tests/base_rules/py_info/py2-trans.py",
163            "tests/base_rules/py_info/py3-trans.py",
164            "tests/base_rules/py_info/py4-trans.py",
165            "tests/base_rules/py_info/py5-trans.py",
166            "tests/base_rules/py_info/py6-trans.py",
167        ])
168        subject.imports().contains_exactly([
169            "import-path",
170            "py1import",
171            "py2import",
172            "py3import",
173            "py4import",
174            "py5import",
175            "py6import",
176        ])
177        if hasattr(actual, "direct_pyc_files"):
178            subject.direct_pyc_files().contains_exactly([
179                "tests/base_rules/py_info/direct.pyc",
180                "tests/base_rules/py_info/py4-direct.pyc",
181                "tests/base_rules/py_info/py6-direct.pyc",
182            ])
183            subject.transitive_pyc_files().contains_exactly([
184                "tests/base_rules/py_info/trans.pyc",
185                "tests/base_rules/py_info/py1-trans.pyc",
186                "tests/base_rules/py_info/py2-trans.pyc",
187                "tests/base_rules/py_info/py3-trans.pyc",
188                "tests/base_rules/py_info/py4-trans.pyc",
189                "tests/base_rules/py_info/py5-trans.pyc",
190                "tests/base_rules/py_info/py6-trans.pyc",
191            ])
192
193    check(builder.build())
194    if BuiltinPyInfo != None:
195        check(builder.build_builtin_py_info())
196
197    builder.set_has_py2_only_sources(False)
198    builder.set_has_py3_only_sources(False)
199    builder.set_uses_shared_libraries(False)
200
201    env.expect.that_bool(builder.get_has_py2_only_sources()).equals(False)
202    env.expect.that_bool(builder.get_has_py3_only_sources()).equals(False)
203    env.expect.that_bool(builder.get_uses_shared_libraries()).equals(False)
204
205_tests.append(_test_py_info_builder)
206
207def py_info_test_suite(name):
208    test_suite(
209        name = name,
210        tests = _tests,
211    )
212