forked from bazel-contrib/rules_python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlock.bzl
More file actions
130 lines (117 loc) · 4.65 KB
/
Copy pathlock.bzl
File metadata and controls
130 lines (117 loc) · 4.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# Copyright 2024 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A simple macro to lock the requirements.
"""
load("@bazel_skylib//rules:write_file.bzl", "write_file")
load("//python:py_binary.bzl", "py_binary")
load("//python/config_settings:transition.bzl", transition_py_binary = "py_binary")
load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED") # buildifier: disable=bzl-visibility
visibility(["//..."])
_REQUIREMENTS_TARGET_COMPATIBLE_WITH = select({
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
}) if BZLMOD_ENABLED else ["@platforms//:incompatible"]
def lock(*, name, srcs, out, upgrade = False, universal = True, python_version = None, args = [], **kwargs):
"""Pin the requirements based on the src files.
Args:
name: The name of the target to run for updating the requirements.
srcs: The srcs to use as inputs.
out: The output file.
upgrade: Tell `uv` to always upgrade the dependencies instead of
keeping them as they are.
universal: Tell `uv` to generate a universal lock file.
python_version: Tell `rules_python` to use a particular version.
Defaults to the default py toolchain.
args: Extra args to pass to the rule.
**kwargs: Extra kwargs passed to the binary rule.
Differences with the current pip-compile rule:
- This is implemented in shell and uv.
- This does not error out if the output file does not exist yet.
- Supports transitions out of the box.
"""
pkg = native.package_name()
update_target = name + ".update"
_args = [
"--custom-compile-command='bazel run //{}:{}'".format(pkg, update_target),
"--generate-hashes",
"--emit-index-url",
"--no-strip-extras",
"--python=$(PYTHON3)",
] + args + [
"$(location {})".format(src)
for src in srcs
]
if upgrade:
_args.append("--upgrade")
if universal:
_args.append("--universal")
_args.append("--output-file=$@")
cmd = "$(UV_BIN) pip compile " + " ".join(_args)
# Make a copy to ensure that we are not modifying the initial list
srcs = list(srcs)
# Check if the output file already exists, if yes, first copy it to the
# output file location in order to make `uv` not change the requirements if
# we are just running the command.
if native.glob([out]):
cmd = "cp -v $(location {}) $@; {}".format(out, cmd)
srcs.append(out)
native.genrule(
name = name,
srcs = srcs,
outs = [out + ".new"],
cmd_bash = cmd,
tags = [
"local",
"manual",
"no-cache",
],
target_compatible_with = _REQUIREMENTS_TARGET_COMPATIBLE_WITH,
toolchains = [
Label("//python/uv:current_toolchain"),
Label("//python:current_py_toolchain"),
],
)
if python_version:
py_binary_rule = lambda *args, **kwargs: transition_py_binary(python_version = python_version, *args, **kwargs)
else:
py_binary_rule = py_binary
# Write a script that can be used for updating the in-tree version of the
# requirements file
write_file(
name = name + ".update_gen",
out = update_target + ".py",
content = [
"from os import environ",
"from pathlib import Path",
"from sys import stderr",
"",
'src = Path(environ["REQUIREMENTS_FILE"])',
'assert src.exists(), f"the {src} file does not exist"',
'dst = Path(environ["BUILD_WORKSPACE_DIRECTORY"]) / "{}" / "{}"'.format(pkg, out),
'print(f"Writing requirements contents\\n from {src.absolute()}\\n to {dst.absolute()}", file=stderr)',
"dst.write_text(src.read_text())",
'print("Success!", file=stderr)',
],
)
py_binary_rule(
name = update_target,
srcs = [update_target + ".py"],
main = update_target + ".py",
data = [name],
env = {
"REQUIREMENTS_FILE": "$(rootpath {})".format(name),
},
tags = ["manual"],
**kwargs
)