8000 Added `stamp` attribute to `py_wheel` (#554) · axivion/rules_python@b622c4c · GitHub
[go: up one dir, main page]

Skip to content

Commit b622c4c

Browse files
authored
Added stamp attribute to py_wheel (bazel-contrib#554)
* Added `stamp` attribute to `py_wheel` * Add stable status to wheel stamping
1 parent 2c96d82 commit b622c4c

File tree

7 files changed

+248
-16
lines changed

7 files changed

+248
-16
lines changed

docs/BUILD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ bzl_library(
7171
],
7272
)
7373

74+
bzl_library(
75+
name = "packaging_bzl",
76+
srcs = [
77+
"//python:packaging.bzl",
78+
"//python/private:stamp.bzl",
79+
],
80+
)
81+
7482
stardoc(
7583
name = "core-docs",
7684
out = "python.md_",
@@ -103,6 +111,7 @@ stardoc(
103111
name = "packaging-docs",
104112
out = "packaging.md_",
105113
input = "//python:packaging.bzl",
114+
deps = [":packaging_bzl"],
106115
)
107116

108117
[

docs/packaging.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ This rule is intended to be used as data dependency to py_wheel rule
3131
<pre>
3232
py_wheel(<a href="#py_wheel-name">name</a>, <a href="#py_wheel-abi">abi</a>, <a href="#py_wheel-author">author</a>, <a href="#py_wheel-author_email">author_email</a>, <a href="#py_wheel-classifiers">classifiers</a>, <a href="#py_wheel-console_scripts">console_scripts</a>, <a href="#py_wheel-deps">deps</a>, <a href="#py_wheel-description_file">description_file</a>,
3333
<a href="#py_wheel-distribution">distribution</a>, <a href="#py_wheel-entry_points">entry_points</a>, <a href="#py_wheel-extra_requires">extra_requires</a>, <a href="#py_wheel-homepage">homepage</a>, <a hre 8000 f="#py_wheel-license">license</a>, <a href="#py_wheel-platform">platform</a>, <a href="#py_wheel-python_requires">python_requires</a>,
34-
<a href="#py_wheel-python_tag">python_tag</a>, <a href="#py_wheel-requires">requires</a>, <a href="#py_wheel-strip_path_prefixes">strip_path_prefixes</a>, <a href="#py_wheel-version">version</a>)
34+
<a href="#py_wheel-python_tag">python_tag</a>, <a href="#py_wheel-requires">requires</a>, <a href="#py_wheel-stamp">stamp</a>, <a href="#py_wheel-strip_path_prefixes">strip_path_prefixes</a>, <a href="#py_wheel-version">version</a>)
3535
</pre>
3636

3737

@@ -101,7 +101,27 @@ py_wheel(
101101
| python_requires | A string specifying what other distributions need to be installed when this one is. See the section on [Declaring required dependency](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#declaring-dependencies) for details and examples of the format of this argument. | String | optional | "" |
102102
| python_tag | Supported Python version(s), eg <code>py3</code>, <code>cp35.cp36</code>, etc | String | optional | "py3" |
103103
| requires | List of requirements for this package | List of strings | optional | [] |
104+
| stamp | Whether to encode build information into the wheel. Possible values:<br><br>- <code>stamp = 1</code>: Always stamp the build information into the wheel, even in [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. This setting should be avoided, since it potentially kills remote caching for the target and any downstream actions that depend on it.<br><br>- <code>stamp = 0</code>: Always replace build information by constant values. This gives good build result caching.<br><br>- <code>stamp = -1</code>: Embedding of build information is controlled by the [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.<br><br>Stamped targets are not rebuilt unless their dependencies change. | Integer | optional | -1 |
104105
| strip_path_prefixes | path prefixes to strip from files added to the generated package | List of strings | optional | [] |
105-
| version | Version number of the package | String | required | |
106+
| version | Version number of the package. Note that this attribute supports stamp format strings. Eg <code>1.2.3-{BUILD_TIMESTAMP}</code> | String | required | |
107+
108+
109+
<a name="#PyWheelInfo"></a>
110+
111+
## PyWheelInfo
112+
113+
<pre>
114+
PyWheelInfo(<a href="#PyWheelInfo-name_file">name_file</a>, <a href="#PyWheelInfo-wheel">wheel</a>)
115+
</pre>
116+
117+
Information about a wheel produced by `py_wheel`
118+
119+
**FIELDS**
120+
121+
122+
| Name | Description |
123+
| :-------------: | :-------------: |
124+
| name_file | File: A file containing the canonical name of the wheel (after stamping, if enabled). |
125+
| wheel | File: The wheel file itself. |
106126

107127

examples/wheel/BUILD

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,20 @@ py_wheel(
5858
],
5959
)
6060

61+
# Package just a specific py_libraries, without their dependencies
62+
py_wheel(
63+
name = "minimal_with_py_library_with_stamp",
64+
# Package data. We're building "example_minimal_library-0.0.1-py3-none-any.whl"
65+
distribution = "example_minimal_library",
66+
python_tag = "py3",
67+
stamp = 1,
68+
version = "0.1.{BUILD_TIMESTAMP}",
69+
deps = [
70+
"//examples/wheel/lib:module_with_data",
71+
"//examples/wheel/lib:simple_module",
72+
],
73+
)
74+
6175
# Use py_package to collect all transitive dependencies of a target,
6276
# selecting just the files within a specific python package.
6377
py_package(

python/packaging.bzl

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@
1414

1515
"""Rules for building wheels."""
1616

17+
load("//python/private:stamp.bzl", "is_stamping_enabled")
18+
19+
PyWheelInfo = provider(
20+
doc = "Information about a wheel produced by `py_wheel`",
21+
fields = {
22+
"name_file": (
23+
"File: A file containing the canonical name of the wheel (after " +
24+
"stamping, if enabled)."
25+
),
26+
"wheel": "File: The wheel file itself.",
27+
},
28+
)
29+
1730
def _path_inside_wheel(input_file):
1831
# input_file.short_path is sometimes relative ("../${repository_root}/foobar")
1932
# which is not a valid path within a zip file. Fix that.
@@ -110,6 +123,8 @@ def _py_wheel_impl(ctx):
110123
_escape_filename_segment(ctx.attr.platform),
111124
]) + ".whl")
112125

126+
name_file = ctx.actions.declare_file(ctx.label.name + ".name")
127+
113128
inputs_to_package = depset(
114129
direct = ctx.files.deps,
115130
)
@@ -133,9 +148,16 @@ def _py_wheel_impl(ctx):
133148
args.add("--python_requires", ctx.attr.python_requires)
134149
args.add("--abi", ctx.attr.abi)
135150
args.add("--platform", ctx.attr.platform)
136-
args.add("--out", outfile.path)
151+
args.add("--out", outfile)
152+
args.add("--name_file", name_file)
137153
args.add_all(ctx.attr.strip_path_prefixes, format_each = "--strip_path_prefix=%s")
138154

155+
# Pass workspace status files if stamping is enabled
156+
if is_stamping_enabled(ctx.attr):
157+
args.add("--volatile_status_file", ctx.version_file)
158+
args.add("--stable_status_file", ctx.version_file)
159+
other_inputs.extend([ctx.version_file, ctx.info_file])
160+
139161
args.add("--input_file_list", packageinputfile)
140162

141163
extra_headers = []
@@ -193,15 +215,21 @@ def _py_wheel_impl(ctx):
193215

194216
ctx.actions.run(
195217
inputs = depset(direct = other_inputs, transitive = [inputs_to_package]),
196-
outputs = [outfile],
218+
outputs = [outfile, name_file],
197219
arguments = [args],
198220
executable = ctx.executable._wheelmaker,
199221
progress_message = "Building wheel",
200222
)
201-
return [DefaultInfo(
202-
files = depset([outfile]),
203-
data_runfiles = ctx.runfiles(files = [outfile]),
204-
)]
223+
return [
224+
DefaultInfo(
225+
files = depset([outfile]),
226+
runfiles = ctx.runfiles(files = [outfile]),
227+
),
228+
PyWheelInfo(
229+
wheel = outfile,
230+
name_file = name_file,
231+
),
232+
]
205233

206234
def _concat_dicts(*dicts):
207235
result = {}
@@ -247,9 +275,35 @@ platform = select({
247275
default = "py3",
248276
doc = "Supported Python version(s), eg `py3`, `cp35.cp36`, etc",
249277
),
278+
"stamp": attr.int(
279+
doc = """\
280+
Whether to encode build information into the wheel. Possible values:
281+
282+
- `stamp = 1`: Always stamp the build information into the wheel, even in \
283+
[--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. \
284+
This setting should be avoided, since it potentially kills remote caching for the target and \
285+
any downstream actions that depend on it.
286+
287+
- `stamp = 0`: Always replace build information by constant values. This gives good build result caching.
288+
289+
- `stamp = -1`: Embedding of build information is controlled by the \
290+
[--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.
291+
292+
Stamped targets are not rebuilt unless their dependencies change.
293+
""",
294+
default = -1,
295+
values = [1, 0, -1],
296+
),
250297
"version": attr.string(
251298
mandatory = True,
252-
doc = "Version number of the package",
299+
doc = (
300+
"Version number of the package. Note that this attribute " +
301+
"supports stamp format strings. Eg `1.2.3-{BUILD_TIMESTAMP}`"
302+
),
303+
),
304+
"_stamp_flag": attr.label(
305+
doc = "A setting used to determine whether or not the `--stamp` flag is enabled",
306+
default = Label("//python/private:stamp"),
253307
),
254308
}
255309

python/private/BUILD

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
load(":stamp.bzl", "stamp_build_setting")
16+
1517
licenses(["notice"]) # Apache 2.0
1618

1719
filegroup(
@@ -36,6 +38,10 @@ filegroup(
3638
exports_files(
3739
[
3840
"reexports.bzl",
41+
"stamp.bzl",
3942
],
4043
visibility = ["//docs:__pkg__"],
4144
)
45+
46< 1241 /code>+
# Used to determine the use of `--stamp` in Starlark rules
47+
stamp_build_setting(name = "stamp")

python/private/stamp.bzl

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""A small utility module dedicated to detecting whether or not the `--stamp` flag is enabled
2+
3+
This module can be removed likely after the following PRs ar addressed:
4+
- https://github.com/bazelbuild/bazel/issues/11164
5+
"""
6+
7+
StampSettingInfo = provider(
8+
doc = "Information about the `--stamp` command line flag",
9+
fields = {
10+
"value": "bool: Whether or not the `--stamp` flag was enabled",
11+
},
12+
)
13+
14+
def _stamp_build_setting_impl(ctx):
15+
return StampSettingInfo(value = ctx.attr.value)
16+
17+
_stamp_build_setting = rule(
18+
doc = """\
19+
Whether to encode build information into the binary. Possible values:
20+
21+
- stamp = 1: Always stamp the build information into the binary, even in [--nostamp][stamp] builds. \
22+
This setting should be avoided, since it potentially kills remote caching for the binary and \
23+
any downstream actions that depend on it.
24+
- stamp = 0: Always replace build information by constant values. This gives good build result caching.
25+
- stamp = -1: Embedding of build information is controlled by the [--[no]stamp][stamp] flag.
26+
27+
Stamped binaries are not rebuilt unless their dependencies change.
28+
[stamp]: https://docs.bazel.build/versions/main/user-manual.html#flag--stamp
29+
""",
30+
implementation = _stamp_build_setting_impl,
31+
attrs = {
32+
"value": attr.bool(
33+
doc = "The default value of the stamp build flag",
34+
mandatory = True,
35+
),
36+
},
37+
)
38+
39+
def stamp_build_setting(name, visibility = ["//visibility:public"]):
40+
native.config_setting(
41+
name = "stamp_detect",
42+
values = {"stamp": "1"},
43+
visibility = visibility,
44+
)
45+
46+
_stamp_build_setting(
47+
name = name,
48+
value = select({
49+
":stamp_detect": True,
50+
"//conditions:default": False,
51+
}),
52+
visibility = visibility,
53+
)
54+
55+
def is_stamping_enabled(attr):
56+
"""Determine whether or not build staming is enabled
57+
58+
Args:
59+
attr (struct): A rule's struct of attributes (`ctx.attr`)
60+
61+
Returns:
62+
bool: The stamp value
63+
"""
64+
stamp_num = getattr(attr, "stamp", -1)
65+
if stamp_num == 1:
66+
return True
67+
elif stamp_num == 0:
68+
return False
69+
elif stamp_num == -1:
70+
stamp_flag = getattr(attr, "_stamp_flag", None)
71+
return stamp_flag[StampSettingInfo].value if stamp_flag else False
72+
else:
73+
fail("Unexpected `stamp` value: {}".format(stamp_num))

0 commit comments

Comments
 (0)
0