• 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"""Utilities for `rules_python` pip rules"""
16
17_SRCS_TEMPLATE = """\
18\"\"\"A generated file containing all source files used for `@rules_python//python/pip_install:pip_repository.bzl` rules
19
20This file is auto-generated from the `@rules_python//python/pip_install/private:srcs_module.update` target. Please
21`bazel run` this target to apply any updates. Note that doing so will discard any local modifications.
22"\"\"
23
24# Each source file is tracked as a target so `pip_repository` rules will know to automatically rebuild if any of the
25# sources changed.
26PIP_INSTALL_PY_SRCS = [
27    {srcs}
28]
29"""
30
31def _src_label(file):
32    dir_path, file_name = file.short_path.rsplit("/", 1)
33
34    return "@rules_python//{}:{}".format(
35        dir_path,
36        file_name,
37    )
38
39def _srcs_module_impl(ctx):
40    srcs = [_src_label(src) for src in ctx.files.srcs]
41    if not srcs:
42        fail("`srcs` cannot be empty")
43    output = ctx.actions.declare_file(ctx.label.name)
44
45    ctx.actions.write(
46        output = output,
47        content = _SRCS_TEMPLATE.format(
48            srcs = "\n    ".join(["\"{}\",".format(src) for src in srcs]),
49        ),
50    )
51
52    return DefaultInfo(
53        files = depset([output]),
54    )
55
56_srcs_module = rule(
57    doc = "A rule for writing a list of sources to a templated file",
58    implementation = _srcs_module_impl,
59    attrs = {
60        "srcs": attr.label(
61            doc = "A filegroup of source files",
62            allow_files = True,
63        ),
64    },
65)
66
67_INSTALLER_TEMPLATE = """\
68#!/bin/bash
69set -euo pipefail
70cp -f "{path}" "${{BUILD_WORKSPACE_DIRECTORY}}/{dest}"
71"""
72
73def _srcs_updater_impl(ctx):
74    output = ctx.actions.declare_file(ctx.label.name + ".sh")
75    target_file = ctx.file.input
76    dest = ctx.file.dest.short_path
77
78    ctx.actions.write(
79        output = output,
80        content = _INSTALLER_TEMPLATE.format(
81            path = target_file.short_path,
82            dest = dest,
83        ),
84        is_executable = True,
85    )
86
87    return DefaultInfo(
88        files = depset([output]),
89        runfiles = ctx.runfiles(files = [target_file]),
90        executable = output,
91    )
92
93_srcs_updater = rule(
94    doc = "A rule for writing a `srcs.bzl` file back to the repository",
95    implementation = _srcs_updater_impl,
96    attrs = {
97        "dest": attr.label(
98            doc = "The target file to write the new `input` to.",
99            allow_single_file = ["srcs.bzl"],
100            mandatory = True,
101        ),
102        "input": attr.label(
103            doc = "The file to write back to the repository",
104            allow_single_file = True,
105            mandatory = True,
106        ),
107    },
108    executable = True,
109)
110
111def srcs_module(name, dest, **kwargs):
112    """A helper rule to ensure `pip_repository` rules are always up to date
113
114    Args:
115        name (str): The name of the sources module
116        dest (str): The filename the module should be written as in the current package.
117        **kwargs (dict): Additional keyword arguments
118    """
119    tags = kwargs.pop("tags", [])
120
121    _srcs_module(
122        name = name,
123        tags = tags,
124        **kwargs
125    )
126
127    _srcs_updater(
128        name = name + ".update",
129        input = name,
130        dest = dest,
131        tags = tags,
132    )
133