• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"""This module provides the gazelle_python_manifest macro that contains targets
16for updating and testing the Gazelle manifest file.
17"""
18
19load("@io_bazel_rules_go//go:def.bzl", "GoSource", "go_binary", "go_test")
20
21def gazelle_python_manifest(
22        name,
23        requirements,
24        modules_mapping,
25        pip_repository_name = "",
26        pip_deps_repository_name = "",
27        manifest = ":gazelle_python.yaml",
28        use_pip_repository_aliases = False):
29    """A macro for defining the updating and testing targets for the Gazelle manifest file.
30
31    Args:
32        name: the name used as a base for the targets.
33        requirements: the target for the requirements.txt file or a list of
34            requirements files that will be concatenated before passing on to
35            the manifest generator.
36        pip_repository_name: the name of the pip_install or pip_repository target.
37        use_pip_repository_aliases: boolean flag to enable using user-friendly
38            python package aliases.
39        pip_deps_repository_name: deprecated - the old pip_install target name.
40        modules_mapping: the target for the generated modules_mapping.json file.
41        manifest: the target for the Gazelle manifest file.
42    """
43    if pip_deps_repository_name != "":
44        # buildifier: disable=print
45        print("DEPRECATED pip_deps_repository_name in //{}:{}. Please use pip_repository_name instead.".format(
46            native.package_name(),
47            name,
48        ))
49        pip_repository_name = pip_deps_repository_name
50
51    if pip_repository_name == "":
52        # This is a temporary check while pip_deps_repository_name exists as deprecated.
53        fail("pip_repository_name must be set in //{}:{}".format(native.package_name(), name))
54
55    update_target = "{}.update".format(name)
56    update_target_label = "//{}:{}".format(native.package_name(), update_target)
57
58    manifest_generator_hash = Label("//manifest/generate:generate_lib_sources_hash")
59
60    if type(requirements) == "list":
61        native.genrule(
62            name = name + "_requirements_gen",
63            srcs = sorted(requirements),
64            outs = [name + "_requirements.txt"],
65            cmd_bash = "cat $(SRCS) > $@",
66            cmd_bat = "type $(SRCS) > $@",
67        )
68        requirements = name + "_requirements_gen"
69
70    update_args = [
71        "--manifest-generator-hash",
72        "$(rootpath {})".format(manifest_generator_hash),
73        "--requirements",
74        "$(rootpath {})".format(requirements),
75        "--pip-repository-name",
76        pip_repository_name,
77        "--modules-mapping",
78        "$(rootpath {})".format(modules_mapping),
79        "--output",
80        "$(rootpath {})".format(manifest),
81        "--update-target",
82        update_target_label,
83    ]
84
85    if use_pip_repository_aliases:
86        update_args += [
87            "--use-pip-repository-aliases",
88            "true",
89        ]
90
91    go_binary(
92        name = update_target,
93        embed = [Label("//manifest/generate:generate_lib")],
94        data = [
95            manifest,
96            modules_mapping,
97            requirements,
98            manifest_generator_hash,
99        ],
100        args = update_args,
101        visibility = ["//visibility:private"],
102        tags = ["manual"],
103    )
104
105    go_test(
106        name = "{}.test".format(name),
107        srcs = [Label("//manifest/test:test.go")],
108        data = [
109            manifest,
110            requirements,
111            manifest_generator_hash,
112        ],
113        env = {
114            "_TEST_MANIFEST": "$(rootpath {})".format(manifest),
115            "_TEST_MANIFEST_GENERATOR_HASH": "$(rootpath {})".format(manifest_generator_hash),
116            "_TEST_REQUIREMENTS": "$(rootpath {})".format(requirements),
117        },
118        rundir = ".",
119        deps = [Label("//manifest")],
120        size = "small",
121    )
122
123    native.filegroup(
124        name = name,
125        srcs = [manifest],
126        tags = ["manual"],
127        visibility = ["//visibility:public"],
128    )
129
130# buildifier: disable=provider-params
131AllSourcesInfo = provider(fields = {"all_srcs": "All sources collected from the target and dependencies."})
132
133_rules_python_workspace = Label("@rules_python//:WORKSPACE")
134
135def _get_all_sources_impl(target, ctx):
136    is_rules_python = target.label.workspace_name == _rules_python_workspace.workspace_name
137    if not is_rules_python:
138        # Avoid adding third-party dependency files to the checksum of the srcs.
139        return AllSourcesInfo(all_srcs = depset())
140    srcs = depset(
141        target[GoSource].orig_srcs,
142        transitive = [dep[AllSourcesInfo].all_srcs for dep in ctx.rule.attr.deps],
143    )
144    return [AllSourcesInfo(all_srcs = srcs)]
145
146_get_all_sources = aspect(
147    implementation = _get_all_sources_impl,
148    attr_aspects = ["deps"],
149)
150
151def _sources_hash_impl(ctx):
152    all_srcs = ctx.attr.go_library[AllSourcesInfo].all_srcs
153    hash_file = ctx.actions.declare_file(ctx.attr.name + ".hash")
154    args = ctx.actions.args()
155    args.add(hash_file)
156    args.add_all(all_srcs)
157    ctx.actions.run(
158        outputs = [hash_file],
159        inputs = all_srcs,
160        arguments = [args],
161        executable = ctx.executable._hasher,
162    )
163    return [DefaultInfo(
164        files = depset([hash_file]),
165        runfiles = ctx.runfiles([hash_file]),
166    )]
167
168sources_hash = rule(
169    _sources_hash_impl,
170    attrs = {
171        "go_library": attr.label(
172            aspects = [_get_all_sources],
173            providers = [GoSource],
174        ),
175        "_hasher": attr.label(
176            cfg = "exec",
177            default = Label("//manifest/hasher"),
178            executable = True,
179        ),
180    },
181)
182