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